Post
Topic
Board Development & Technical Discussion
Re: [Bounty 50BTC] Looking for a GPU implementation of this algorithm
by
Wotan777
on 02/07/2014, 09:40:45 UTC
I have made an interesting discovery: if you know the encoded seed of an Electrum wallet, then you can recover the unencrypted seed without the password stretching and fancy Elliptic Curve stuff. Simply try to decode the password, and if you get a valid hexadecimal number of 32 characters, then the password candidate is good, otherwise it is bad.

Sample Python code:


# -*- coding: utf-8 -*-

import aes
import hashlib
import base64

DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))

def sha256(x):
    return hashlib.sha256(x).digest()

def Hash(x):
    if type(x) is unicode: x=x.encode('utf-8')
    return sha256(sha256(x))

def pw_decode(s, password):
    if password is not None:
        secret = Hash(password)
        try:
            d = DecodeAES(secret, s)
        except Exception, e:
            raise Exception('Invalid password')
        return d
    else:
        return s

def try_pw(encoded_seed, pw_cand):
  seed = ''
  try:
    seed = pw_decode(encoded_seed, pw_cand)
  except Exception:
    seed = ''
  finally:
    pass
  return seed

def chk_seed(seed):
  if len(seed) == 0:
    return False
  cnt=0;
  for c in seed:
    cnt+=1
    if cnt>32:
      return True
    i = ord(c)
    if i<48:
      return False
    if i>57:
      if i<97:
        return False
      if i>102:
        return False
  return True

def test():
  encoded_seed = 'Ww9jsiumblVPSM5owcLS6wODqxh0YDLIg/g+mNv+nuNP+f7yyhqOomTlK9tDv8xV0kYt/nUDeTZNtUOr3Zfp2w=='
  # pw_cand = 'test'
 
  cnt = 0
  cnt_good = 0
  for c1 in 't': #'abcdefghijklmnopqrstuvwxyz':
    for c2 in 'abcdefghijklmnopqrstuvwxyz':
      for c3 in 'abcdefghijklmnopqrstuvwxyz':
        for c4 in 'abcdefghijklmnopqrstuvwxyz':
          pw_cand1 = c1 + c2 + c3 + c4
          cnt += 1
          seed = try_pw(encoded_seed, pw_cand1)
          # print seed
          if chk_seed(seed):
            print 'pw is good: %s' % pw_cand1
            cnt_good +=1
  print 'cnt: %d' % cnt
  print 'cnt_good: %d' % cnt_good

test()


If you run it, it correctly finds the password:


$ python m2.py
pw is good: test
cnt: 676
cnt_good: 1


This "proof of a concept" code uses only  two SHA256 and one AES256 to try a password.
Its speed can be optimized to 10**7 (maybe 10**8 ) trials/sec/CPU_core.

So, with 128 cores, 10**15 (maybe 10**16) passwords can be checked in a day (at least in theory).

It is up to cedivad, what to do with that discovery...