1+ try :
2+ import os
3+ import hashlib
4+ import hmac
5+ import binascii
6+ import unicodedata
7+ import ctypes
8+ ctypes .windll .kernel32 .SetConsoleTitleA ("Check MPK Against Armory Seed to Recover BTC" )
9+
10+ os .system ("mode con: cols=100 lines=35" )
11+
12+ __all__ = ['new' , 'digest_size' ]
13+
14+ __revision__ = "$Id$"
15+
16+ import struct
17+
18+ def u32 (n ):
19+ return n & 0xFFFFffffL
20+
21+ rho = [7 , 4 , 13 , 1 , 10 , 6 , 15 , 3 , 12 , 0 , 9 , 5 , 2 , 14 , 11 , 8 ]
22+
23+ pi = [(9 * i + 5 ) & 15 for i in range (16 )]
24+
25+ rl = [range (16 )]
26+ rl += [[rho [j ] for j in rl [- 1 ]]]
27+ rl += [[rho [j ] for j in rl [- 1 ]]]
28+ rl += [[rho [j ] for j in rl [- 1 ]]]
29+ rl += [[rho [j ] for j in rl [- 1 ]]]
30+
31+ rr = [list (pi )]
32+ rr += [[rho [j ] for j in rr [- 1 ]]]
33+ rr += [[rho [j ] for j in rr [- 1 ]]]
34+ rr += [[rho [j ] for j in rr [- 1 ]]]
35+ rr += [[rho [j ] for j in rr [- 1 ]]]
36+
37+ f1 = lambda x , y , z : x ^ y ^ z
38+
39+ f2 = lambda x , y , z : (x & y ) | (~ x & z )
40+
41+ f3 = lambda x , y , z : (x | ~ y ) ^ z
42+
43+ f4 = lambda x , y , z : (x & z ) | (y & ~ z )
44+
45+ f5 = lambda x , y , z : x ^ (y | ~ z )
46+
47+ fl = [f1 , f2 , f3 , f4 , f5 ]
48+
49+ fr = [f5 , f4 , f3 , f2 , f1 ]
50+
51+ _shift1 = [11 , 14 , 15 , 12 , 5 , 8 , 7 , 9 , 11 , 13 , 14 , 15 , 6 , 7 , 9 , 8 ]
52+ _shift2 = [12 , 13 , 11 , 15 , 6 , 9 , 9 , 7 , 12 , 15 , 11 , 13 , 7 , 8 , 7 , 7 ]
53+ _shift3 = [13 , 15 , 14 , 11 , 7 , 7 , 6 , 8 , 13 , 14 , 13 , 12 , 5 , 5 , 6 , 9 ]
54+ _shift4 = [14 , 11 , 12 , 14 , 8 , 6 , 5 , 5 , 15 , 12 , 15 , 14 , 9 , 9 , 8 , 6 ]
55+ _shift5 = [15 , 12 , 13 , 13 , 9 , 5 , 8 , 6 , 14 , 11 , 12 , 11 , 8 , 6 , 5 , 5 ]
56+
57+ sl = [[_shift1 [rl [0 ][i ]] for i in range (16 )]]
58+ sl .append ([_shift2 [rl [1 ][i ]] for i in range (16 )])
59+ sl .append ([_shift3 [rl [2 ][i ]] for i in range (16 )])
60+ sl .append ([_shift4 [rl [3 ][i ]] for i in range (16 )])
61+ sl .append ([_shift5 [rl [4 ][i ]] for i in range (16 )])
62+
63+ sr = [[_shift1 [rr [0 ][i ]] for i in range (16 )]]
64+ sr .append ([_shift2 [rr [1 ][i ]] for i in range (16 )])
65+ sr .append ([_shift3 [rr [2 ][i ]] for i in range (16 )])
66+ sr .append ([_shift4 [rr [3 ][i ]] for i in range (16 )])
67+ sr .append ([_shift5 [rr [4 ][i ]] for i in range (16 )])
68+
69+ _kg = lambda x , y : int (2 ** 30 * (y ** (1.0 / x )))
70+
71+ KL = [
72+ 0 ,
73+ _kg (2 , 2 ),
74+ _kg (2 , 3 ),
75+ _kg (2 , 5 ),
76+ _kg (2 , 7 ),
77+ ]
78+
79+ KR = [
80+ _kg (3 , 2 ),
81+ _kg (3 , 3 ),
82+ _kg (3 , 5 ),
83+ _kg (3 , 7 ),
84+ 0 ,
85+ ]
86+
87+ def rol (s , n ):
88+ assert 0 <= s <= 31
89+ assert 0 <= n <= 0xFFFFffffL
90+ return u32 ((n << s ) | (n >> (32 - s )))
91+
92+ initial_h = tuple (struct .unpack ("<5L" , "0123456789ABCDEFFEDCBA9876543210F0E1D2C3" .decode ('hex' )))
93+
94+ def box (h , f , k , x , r , s ):
95+ assert len (s ) == 16
96+ assert len (x ) == 16
97+ assert len (r ) == 16
98+ (a , b , c , d , e ) = h
99+ for word in range (16 ):
100+ T = u32 (a + f (b , c , d ) + x [r [word ]] + k )
101+ T = u32 (rol (s [word ], T ) + e )
102+ (b , c , d , e , a ) = (T , b , rol (10 , c ), d , e )
103+ return (a , b , c , d , e )
104+
105+ def _compress (h , x ):
106+ hl = hr = h
107+
108+ for round in range (5 ):
109+ hl = box (hl , fl [round ], KL [round ], x , rl [round ], sl [round ])
110+ hr = box (hr , fr [round ], KR [round ], x , rr [round ], sr [round ])
111+
112+ h = (u32 (h [1 ] + hl [2 ] + hr [3 ]),
113+ u32 (h [2 ] + hl [3 ] + hr [4 ]),
114+ u32 (h [3 ] + hl [4 ] + hr [0 ]),
115+ u32 (h [4 ] + hl [0 ] + hr [1 ]),
116+ u32 (h [0 ] + hl [1 ] + hr [2 ]))
117+
118+ return h
119+
120+ def compress (h , s ):
121+ assert len (s ) % 64 == 0
122+ p = 0
123+ while p < len (s ):
124+ h = _compress (h , struct .unpack ("<16L" , s [p :p + 64 ]))
125+ p += 64
126+ assert p == len (s )
127+ return h
128+
129+ class RIPEMD160 (object ):
130+
131+ digest_size = 20
132+
133+ def __init__ (self , data = "" ):
134+ self .h = initial_h
135+ self .bytes = 0
136+ self .buf = ""
137+ self .update (data )
138+
139+ def update (self , data ):
140+ self .buf += data
141+ self .bytes += len (data )
142+
143+ p = len (self .buf ) & ~ 63
144+ if p > 0 :
145+ self .h = compress (self .h , self .buf [:p ])
146+ self .buf = self .buf [p :]
147+ assert len (self .buf ) < 64
148+
149+ def digest (self ):
150+
151+ length = (self .bytes << 3 ) & (2 ** 64 - 1 )
152+
153+ assert len (self .buf ) < 64
154+ data = self .buf + "\x80 "
155+ if len (data ) <= 56 :
156+ assert len (data ) <= 56
157+ data = struct .pack ("<56sQ" , data , length )
158+ else :
159+ assert len (data ) <= 120
160+ data = struct .pack ("<120sQ" , data , length )
161+
162+ h = compress (self .h , data )
163+ return struct .pack ("<5L" , * h )
164+
165+ def hexdigest (self ):
166+ return self .digest ().encode ('hex' )
167+
168+ def copy (self ):
169+ obj = self .__class__ ()
170+ obj .h = self .h
171+ obj .bytes = self .bytes
172+ obj .buf = self .buf
173+ return obj
174+
175+ def ripenew (data = "" ):
176+ return RIPEMD160 (data )
177+
178+ digest_size = 20
179+
180+ Pcurve = 2 ** 256 - 2 ** 32 - 2 ** 9 - 2 ** 8 - 2 ** 7 - 2 ** 6 - 2 ** 4 - 1
181+ N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
182+ Acurve = 0 ; Bcurve = 7
183+ Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
184+ Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
185+ GPoint = (Gx ,Gy )
186+
187+ def modinv (a ,n = Pcurve ):
188+ lm , hm = 1 ,0
189+ low , high = a % n ,n
190+ while low > 1 :
191+ ratio = high / low
192+ nm , new = hm - lm * ratio , high - low * ratio
193+ lm , low , hm , high = nm , new , lm , low
194+ return lm % n
195+
196+ def ECadd (a ,b ):
197+ LamAdd = ((b [1 ]- a [1 ]) * modinv (b [0 ]- a [0 ],Pcurve )) % Pcurve
198+ x = (LamAdd * LamAdd - a [0 ]- b [0 ]) % Pcurve
199+ y = (LamAdd * (a [0 ]- x )- a [1 ]) % Pcurve
200+ return (x ,y )
201+
202+ def ECdouble (a ):
203+ Lam = ((3 * a [0 ]* a [0 ]+ Acurve ) * modinv ((2 * a [1 ]),Pcurve )) % Pcurve
204+ x = (Lam * Lam - 2 * a [0 ]) % Pcurve
205+ y = (Lam * (a [0 ]- x )- a [1 ]) % Pcurve
206+ return (x ,y )
207+
208+ def EccMultiply (GenPoint ,ScalarHex ):
209+ if ScalarHex == 0 or ScalarHex >= N : raise Exception ("Invalid Scalar/Private Key" )
210+ ScalarBin = str (bin (ScalarHex ))[2 :]
211+ Q = GenPoint
212+ for i in range (1 , len (ScalarBin )):
213+ Q = ECdouble (Q );
214+ if ScalarBin [i ] == "1" :
215+ Q = ECadd (Q ,GenPoint );
216+ return (Q )
217+
218+ __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
219+ __b58base = len (__b58chars )
220+
221+ def b58encode (v ):
222+
223+ long_value = 0L
224+ for (i , c ) in enumerate (v [::- 1 ]):
225+ long_value += (256 ** i ) * ord (c )
226+
227+ result = ''
228+ while long_value >= __b58base :
229+ div , mod = divmod (long_value , __b58base )
230+ result = __b58chars [mod ] + result
231+ long_value = div
232+ result = __b58chars [long_value ] + result
233+
234+ nPad = 0
235+ for c in v :
236+ if c == '\0 ' : nPad += 1
237+ else : break
238+
239+ return (__b58chars [0 ]* nPad ) + result
240+
241+ HEXCODE = '0123 4567 89ab cdef' .replace (' ' ,'' )
242+ LETCODE = 'asdf ghjk wert uion' .replace (' ' ,'' )
243+
244+ def seed_to_bin (seed ):
245+ lines = seed .replace (' ' , '' ).split ('<>' )
246+ newlines = []
247+ new = ''
248+ for i in range (2 ):
249+ for j in range (len (lines [i ])):
250+ new += HEXCODE [LETCODE .find (lines [i ][j ])]
251+ newlines .append (new )
252+ new = ''
253+ newseed = newlines [0 ][:32 ] + newlines [1 ][:32 ]
254+ chk1 = hashlib .sha256 (hashlib .sha256 (newlines [0 ][:32 ].decode ('hex' )).digest ()).hexdigest ()[:4 ]
255+ chk2 = hashlib .sha256 (hashlib .sha256 (newlines [1 ][:32 ].decode ('hex' )).digest ()).hexdigest ()[:4 ]
256+ assert chk1 == newlines [0 ][32 :], "Invalid Checksum: Check to make sure you typed your Backup in right."
257+ assert chk2 == newlines [1 ][32 :], "Invalid Checksum: Check to make sure you typed your Backup in right."
258+ return newseed
259+
260+
261+ def gen_addy_priv (MPK , secret , n , c = 0 ):
262+ index = int (hashlib .sha256 (hashlib .sha256 ("%d:%d:" % (n ,c ) + MPK .decode ('hex' )).digest ()).hexdigest (),16 )
263+ addy_pt = ECadd (EccMultiply (GPoint ,index ),(int (MPK [:64 ],16 ),int (MPK [64 :],16 )))
264+ privsec = (secret + index ) % N
265+ assert EccMultiply (GPoint ,privsec ) == addy_pt , "Private Key and Generated Public Key don't match!"
266+ h160 = ripenew (hashlib .sha256 (("04" + ("%064x" % addy_pt [0 ]) + ("%064x" % addy_pt [1 ])).decode ('hex' )).digest ()).digest ()
267+ h160chk = hashlib .sha256 (hashlib .sha256 (chr (0 ) + h160 ).digest ()).digest ()[:4 ]
268+ addy = b58encode (chr (0 ) + h160 + h160chk )
269+
270+ privchk = hashlib .sha256 (hashlib .sha256 (chr (128 ) + ("%064x" % privsec ).decode ('hex' )).digest ()).digest ()[:4 ]
271+ wifpriv = b58encode (chr (128 ) + ("%064x" % privsec ).decode ('hex' ) + privchk )
272+
273+ return addy , wifpriv
274+
275+
276+ ##############################################################################################
277+ #
278+ print "Please type in your \" Paper Backup Phrase\" from Armory."
279+ print "Type in the first line, then the second line. It will ask for each line separately."
280+ print
281+ print "Also, for the second input, please paste in the \" Master Public Key\" from Electrum."
282+ print "We must check and make sure Electrum has the correct MPK that matches your Backup Phrase"
283+ print "from Armory, so open the watch-only wallet in Electrum, and \" Wallet\" > \" Master Public Key\" "
284+ print "and paste that long number into the MPK input line."
285+ print
286+ print "Windows command window can only paste by right clicking the bar at the top, clicking \" Edit\" ,"
287+ print "then clicking paste."
288+ print
289+ #
290+ bckup = raw_input ("Armory Backup Phrase? (1st Line) " )
291+ bckup2 = raw_input ("Armory Backup Phrase? (2nd Line) " )
292+ bckup = bckup + " <> " + bckup2
293+ print
294+ #'aagh hjfj sihk ietj giik wwai awtd uodh hnji <> soss uaku egod utai itos fijj ihgi jhau jtoo'
295+ #'aaghhjfjsihkietjgiikwwaiawtduodhhnji <> sossuakuegodutaiitosfijjihgijhaujtoo'
296+ #
297+ chkMPK = raw_input ("Electrum MPK? " )
298+ #'5a09a3286873a72f164476bde9d1d8e5c2bc044e35aa47eb6e798e325a86417f7c35b61d9905053533e0b4f2a26eca0330aadf21c638969e45aaace50e4c0c87'
299+ #
300+ ##############################################################################################
301+
302+
303+ secpriv = int (seed_to_bin (bckup ),16 )
304+ pt = EccMultiply (GPoint ,secpriv )
305+ MPK = ("%064x" % pt [0 ]) + ("%064x" % pt [1 ])
306+ assert MPK == chkMPK , "Your MPK and the backup phrase MPK don't match."
307+
308+ print
309+ print "Receive Address + Private Key"
310+ for i in range (5 ):
311+ addy , wifpriv = gen_addy_priv (MPK , secpriv , i )
312+ print addy , wifpriv
313+ print
314+ print "Change Address + Private Key"
315+ for i in range (3 ):
316+ addy , wifpriv = gen_addy_priv (MPK , secpriv , i , 1 )
317+ print addy , wifpriv
318+ raw_input ()
319+ except Exception ,e :
320+ print "ERROR: " + str (e )
321+ raw_input ()
0 commit comments