From b64b22ee6dfc154ca8bea3431207de2bb3d3f1e0 Mon Sep 17 00:00:00 2001 From: moocowmoo Date: Sat, 7 Dec 2013 12:28:14 -0500 Subject: [PATCH] enh - added bip32 seed parsing This patch allows the creation of bip38 keys for addresses created by Richard Kiss' BIP32 pycoin (genwallet) utility. test vector outputs: $ python -c 'import binascii; open("master-private-key-entropy", "w+b").write(binascii.unhexlify("000102030405060708090a0b0c0d0e0f"))' > test.bip3 $ genwallet -f test.bip32.seed -i xprv9s21ZrQH143K3LhEXZMXowo9hkNiaiDVhfvnGpXCimZtiRc5gJmq1YLKjLeg7iR2xDU8gks56oB1JXxwBzNkX5GHQtnjActTpWudHkpbgNu main network private key secret exponent: 64984153768961157829826368741579665624823626924420053033036842588453764406980 public pair x: 68560981314052299508403304659895617070088830480209293291335193415198847031373 public pair y: 90409367661552839451235119846362341380971283312313541569547197748756276715666 tree depth: 0 fingerprint: a4ea9e94 parent f'print: 00000000 child index: 0 chain code: 80bbda1ee5fdc3d94a217325d2fd7290cbd69cb91fc69dc8d21e84c9a7fe870d WIF: L22zGSFr75Cix3h8BUKxfyRUTpxEnNC442pvjnwP2F2GNEppUKuc uncompressed: 5JuZTcBAK9CigCwQNrh6N4io6dEshdyRi1sHFgQecdFnd7eMcgL Bitcoin address: 1G2zt786BsvKJXAvTamW3Bds64e6pcQBMx uncompressed: 1PqxhQ4mJNUVUPoWGznMhqf33HGcfaonaH tested with above values: copy secret exponent, paste seed into PyBrainwallet 64984153768961157829826368741579665624823626924420053033036842588453764406980 check bip32 checkbox, uncompressed key and address will appear 5JuZTcBAK9CigCwQNrh6N4io6dEshdyRi1sHFgQecdFnd7eMcgL 1PqxhQ4mJNUVUPoWGznMhqf33HGcfaonaH check bip38 checkbox, encrypt with string 'secret', get bip38 key 6PRSKsniRtueuhY2LM47EvXtfzKkAbrtsDasXhUpZgNBRqBdy56cHVLCMj import key using mycelium wallet, enter password 'secret' key successfully imported --- src/PyBrainwallet.py | 52 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/PyBrainwallet.py b/src/PyBrainwallet.py index fccc9f3..e2c2887 100644 --- a/src/PyBrainwallet.py +++ b/src/PyBrainwallet.py @@ -55,10 +55,12 @@ def __init__(self,parent,id): # Buttons and checkboxes self.compressCB = wx.CheckBox(panel, -1, "Compress", (6, 522), (-1, -1)) - self.bip38CB = wx.CheckBox(panel, -1, "BIP38", + self.bip32seedCB = wx.CheckBox(panel, -1, "BIP32 secret", (106, 522), (-1, -1)) + self.bip38CB = wx.CheckBox(panel, -1, "BIP38", + (212, 522), (-1, -1)) self.multihashCB = wx.CheckBox(panel, -1, "Multihash", - (182, 522), (-1, -1)) + (282, 522), (-1, -1)) gen_button=wx.Button(panel, label='Text', @@ -93,6 +95,7 @@ def __init__(self,parent,id): # Bindings self.Bind(wx.EVT_TEXT_ENTER, self.seed_changed) self.Bind(wx.EVT_CHECKBOX, self.set_multihash, self.multihashCB) + self.Bind(wx.EVT_CHECKBOX, self.set_bip32seed, self.bip32seedCB) self.Bind(wx.EVT_CHECKBOX, self.set_bip38, self.bip38CB) self.Bind(wx.EVT_CHECKBOX, self.set_compress, self.compressCB) self.Bind(wx.EVT_BUTTON,self.generate,gen_button) @@ -157,6 +160,7 @@ def __init__(self,parent,id): self.filelast = False self.compressed = False self.bip38 = False + self.bip32 = False self.tests_passed = 'Untested' # initial keypair @@ -288,6 +292,11 @@ def set_bip38(self,event): self.encrypt_priv() self.update_output() + def set_bip32seed(self,event): + self.bip32 = event.IsChecked() + self.seed_changed(event) + self.update_output() + def set_compress(self,event): self.compressed = event.IsChecked() if self.bip38: # re-encrypt, using compressed wif @@ -376,12 +385,49 @@ def keypair_from_fileseed(self, filelist, filepaths): return {'privkeywif':self.privkeywif, 'address':self.address} + def to_bytes_32(self,v): + """ + The MIT License (MIT) + Copyright (c) 2013 by Richard Kiss + """ + v = self.from_long(v, 0, 256, lambda x: x) + if len(v) > 32: + raise ValueError("input to to_bytes_32 is too large") + return ((b'\0' * 32) + v)[-32:] + return v + + def from_long(self, v, prefix, base, charset): + """ + The MIT License (MIT) + Copyright (c) 2013 by Richard Kiss + """ + """The inverse of to_long. Convert an integer to an arbitrary base. + + v: the integer value to convert + prefix: the number of prefixed 0s to include + base: the new base + charset: an array indicating what printable character to use for each value. + """ + l = bytearray() + while v > 0: + try: + v, mod = divmod(v, base) + l.append(charset(mod)) + except Exception: + raise ValueError("can't convert to character corresponding to ") + l.extend([charset(0)] * prefix) + l.reverse() + return bytes(l) + def determine_keys(self, seed): if not self.seed == 'N/A': if self.multihash: for i in range(1,self.multihash_numrounds): seed = sha256(seed) - self.privkey = sha256(seed) + if self.bip32: + self.privkey = binascii.hexlify(self.to_bytes_32(int(seed))) + else: + self.privkey = sha256(seed) self.cprivkey = encode_privkey(self.privkey,'hex_compressed') self.pubkey = privtopub(self.privkey) self.cpubkey = encode_pubkey(self.pubkey,'hex_compressed')