Skip to content

Commit 0c86c93

Browse files
author
neocogent
committed
bip38: add multiproc with bip38scanx
1 parent 2ed0016 commit 0c86c93

File tree

5 files changed

+138
-7
lines changed

5 files changed

+138
-7
lines changed

README

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ scripts:
77
supernova - generate a Bitcoin black hole address matching prefix
88
watchaddr - insert addresses from stdin into Electrum (2.0) watch-only wallet
99
mchat - test multicast local network peer messaging
10-
pwscan - simple Electrum password cracker, wild char sub. (needs pycrypto)
11-
pwscanx - simple Electrum password cracker, wild char sub. (multi-processing)(needs pycrypto)
12-
bip38pwd - simple BIP38 password cracker, wild char sub.
13-
bip38scan - simple BIP38 password cracker, token selection
10+
11+
need pycrypto ( pip install pycrypto ):
12+
pwscan - simple Electrum password cracker, template char sub.
13+
pwscanx - simple Electrum password cracker, template char sub., multi-processing
14+
bip38pwd - simple BIP38 password cracker, template char sub.
15+
bip38scan - simple BIP38 password cracker, token combination
16+
bip38scanx - simple BIP38 password cracker, token combination, multi-processing
1417

1518
greasemonkey:
1619
collapsetips - script to collapse ChangeTips on /r/bitcoin

scripts/bip38pwd

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55

66
import sys, time
7-
import aes, hashlib, base64, binascii, ecdsa
7+
import hashlib, base64, binascii, ecdsa
88

99
wildchar = '?'
1010
pwd_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

scripts/bip38scanx

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env python
2+
#
3+
# BIP38 password scanner with tokens
4+
#
5+
6+
import sys, time, multiprocessing
7+
import hashlib, base64, binascii, ecdsa
8+
9+
alphabet="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
10+
11+
# secp256k1, http://www.oid-info.com/get/1.3.132.0.10
12+
_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
13+
_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
14+
_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
15+
_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
16+
_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
17+
_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
18+
curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
19+
generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
20+
oid_secp256k1 = (1,3,132,0,10)
21+
SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 )
22+
23+
def b58encode(num, pad=''):
24+
out = ''
25+
while num >= 58:
26+
num,m = divmod(num, 58)
27+
out = alphabet[m] + out
28+
return pad + alphabet[num] + out
29+
30+
def b58hex(s58):
31+
num = 0L
32+
for (i, c) in enumerate(s58[::-1]):
33+
num += alphabet.find(c) * (58**i)
34+
return hex(num)[2:-1].upper()
35+
36+
def priv2pub(priv, compress=False):
37+
verkey = ecdsa.SigningKey.from_secret_exponent(long(priv, 16), curve=SECP256k1 ).get_verifying_key()
38+
prefix = '04' if not compress else ( '03' if verkey.pubkey.point.y() % 2 == 1 else '02' )
39+
return prefix + ( '%032x' % verkey.pubkey.point.x() if compress else verkey.to_string() )
40+
41+
def mkaddr(pubkey):
42+
pad = ""
43+
rmd = hashlib.new('ripemd160')
44+
rmd.update(hashlib.sha256(pubkey).digest())
45+
an = chr(0) + rmd.digest()
46+
for c in an:
47+
if c == '\0': pad += '1'
48+
else: break
49+
return b58encode(long(binascii.hexlify(an + hashlib.sha256(hashlib.sha256(an).digest()).digest()[0:4]), 16), pad)
50+
51+
def bip38_decrypt(encrypted_privkey, passphrase):
52+
from Crypto.Cipher import AES
53+
import scrypt
54+
55+
data = binascii.unhexlify("0"+b58hex(encrypted_privkey))
56+
compress = '' if data[2] == '\xc0' else '01'
57+
key = scrypt.hash(passphrase, data[3:7], 16384, 8, 8)
58+
aes = AES.new(key[32:64])
59+
priv = aes.decrypt(data[7:23]) + aes.decrypt(data[23:39])
60+
priv = '%064x' % (long(binascii.hexlify(priv), 16) ^ long(binascii.hexlify(key[0:32]), 16))
61+
pub = priv2pub(priv, data[2] != '\xc0')
62+
if hashlib.sha256(hashlib.sha256(mkaddr(pub)).digest()).digest()[0:4] != data[3:7]:
63+
return None
64+
chksum = binascii.hexlify(hashlib.sha256(hashlib.sha256(binascii.unhexlify('80'+priv)).digest()).digest()[:4])
65+
return b58encode(long('80'+priv+chksum+compress, 16))
66+
67+
def scanproc(pwd):
68+
for token in tokens:
69+
privkey = bip38_decrypt( sys.argv[2], pwd+token )
70+
if privkey == None:
71+
continue
72+
else:
73+
print "\nPWD:", pwd+token
74+
print "KEY:", privkey
75+
print "Took %.1f seconds" % (time.time() - started,)
76+
sys.exit(2)
77+
78+
def pwd_scan(pwd, depth):
79+
global done, procs
80+
if len(procs) == max_procs:
81+
for p in procs:
82+
p.join()
83+
if p.exitcode == 2:
84+
done = True
85+
procs = []
86+
if not done:
87+
if depth == 0:
88+
proc = multiprocessing.Process(target = scanproc, args = (pwd,))
89+
procs.append(proc)
90+
proc.start()
91+
else:
92+
for token in tokens:
93+
pwd_scan(pwd+token, depth-1)
94+
95+
if __name__ == '__main__':
96+
97+
max_procs = multiprocessing.cpu_count()
98+
procs = []
99+
done = False
100+
101+
if len(sys.argv) < 3:
102+
print "BIP38 password scanner (multi-processing)"
103+
print "Reads tokens from file and tries every combination as password for BIP38 key"
104+
print "Default depth is 6. ie. scan up to 6 token combinations."
105+
print "Usage: %s <token file> <BIP38 encoded key> [depth]\n" % sys.argv[0]
106+
sys.exit(0)
107+
108+
with open(sys.argv[1]) as f:
109+
tokens = f.read()
110+
tokens = tokens.split()
111+
112+
started = time.time()
113+
depth = int(sys.argv[3]) if len(sys.argv) > 3 else 6
114+
for n in range(depth):
115+
pwd_scan("", n)
116+
117+
for p in procs:
118+
p.join()
119+
if p.exitcode == 2:
120+
done = True
121+
122+
if not done:
123+
print "Password not found"
124+
125+
126+
127+
128+
129+
130+

scripts/pwscan

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ def sha256(x):
1414

1515
def pwd_subst(pwd):
1616
if pwd.count(wildchar) == 1:
17-
sys.stdout.flush()
1817
p = pwd.find(wildchar)
1918
for c in pwd_chars:
2019
try:

scripts/pwscanx

-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ def pwd_subst(pwd):
4444
procs = []
4545
if not done:
4646
if pwd.count(wildchar) <= 2:
47-
sys.stdout.flush()
4847
proc = multiprocessing.Process(target = scanproc, args = (pwd,))
4948
procs.append(proc)
5049
proc.start()

0 commit comments

Comments
 (0)