2

I am implementing bip32 for my wallet as a project to learn Bitcoin.

I am following this documentation: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki

Since I am not implementing bip39 yet, I am getting the master private key and code chain from the BIP39 Root Key from this web: https://iancoleman.io/bip39/#english

using a random set of words which are:

praise vague cruise stem test gesture twin long donate violin special hamster horror rough loan

which throws:

xprv9s21ZrQH143K2E7weJFDreP9A8dFkx18swFoAJ6DBdsVuSPoFBLzekLca8vEG1i6ikCcvzZb79joaEZAjeM8Nz1DZ549aCnv3YFXy33mLoW

Comparing this output with another web implementation here: https://iancoleman.io/bip39/

These two outputs match. However, after this point they don't. And my implementation doesn't match with any of those implementations!

Here is my python code where I deserialize the BIP32 extended key. This matches perfectly with the master private key and chain code from the second web app:

def deserialize_xprvk(s):
    version = s.read(4)
    depth = s.read(1)
    fingerprint = s.read(4)
    index= s.read(4)
    chain_code = s.read(32)
    s.read(1)
    privkey = s.read(32)
    return [version,depth, fingerprint,index,chain_code,privkey]

decoded =deserialize_xprvk( BytesIO(decode_base58_extended(xtprvk)))

printing "decoded" I get:

0x488ade4
0x0
0x0
0x0
0x10e85c06826fdcd006c244ff6c44200044c1418bdd82cc088c33e48ae7b4c2ca
0xf214c0afd9b311544fdb686cae5100f1ac0ac8aba2b6c55719ba41ac2d4f2d12

which matches perfectly with the 2nd implementation (the firs one doesn't show the master private key or chain code).

Then I try to follow the instructions from here: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki to derivate the child PRIVATE KEY with index 0 for this master key and chain code like this:

#The order of the ciclic group in the finite field of the ecliptic curve
N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141

#creates a string to emulate the index in a 4-byte hex format.
def str_i(i):
    i_str = hex(i)[2:]
    while len(i_str)<8:
        i_str="0"+i_str
    return i_str

pub_key = PrivateKey(int.from_bytes(master_priv_key,"big")).point.sec()

#I put an extra "0" because every public key begins with a "02" or "03" but
#the hex() function commits this character.
#The str_i(0) means I am trying to get the index 0.
msg=("0" + hex(int.from_bytes(pub_key,"big"))[2:] + str_i(0)).encode() 

I= hmac.new(
            key = hex(int.from_bytes(chain_code,"big"))[2:].encode() ,
            msg=msg ,
            digestmod=sha512).digest()

I_L, I_R = I[:32], I[32:]

child_privkey_int = (int.from_bytes(I_L,"big") + int.from_bytes(master_priv_key,"big"))%N

child_privkey = PrivateKey(child_privkey_int).wif(compressed= True)

child_chain_code = I_R

If I run this code and try to get the child private key for path m\0\0\ I get:

"L29Rc5PyDkbUr2z1VcL2DuAhTQkNo3qwF7ENgRotS86ADUkiXPJF"

But in the first web app they get: "KwuPS3DGMnCc3jMJWtqVVzkCoMyhybxGZrf68fPBEBtFfujBdPGs"

And in the second one they get: "L1TcvgkPZFa6DyXhv9vYWceTXZhoAR7ZQMdt5rqV6LW3Zutpsqr8"

Is it ok to disagree on different implementations?? or am I doing something really wrong? I am starting to pull off my hair trying to find out where I am messing up.

Thanks.

  • 1
    Have you tried implementing the BIP32 test vectors? – Pieter Wuille Mar 26 at 1:03
  • never heard of it. – Oscar Serna Mar 26 at 1:06
  • I'll try that. Thanks a lot. – Oscar Serna Mar 26 at 1:09
1

This line:

msg=("0" + hex(int.from_bytes(pub_key,"big"))[2:] + str_i(0)).encode() 

is incorrect. You are converting the bytes of the pubkey and index into a hex string which you are then hashing. That is not correct. You are supposed to hash the bytes directly, not the hex string.

| improve this answer | |
  • Thanks for your answer. I changed the code, and actually I was doing an unnecessary extra step. However, the outcome is the same. I have looked to other implementations and they look just like mine, but I get a different result. – Oscar Serna Mar 26 at 16:19
  • My line of code now: msg=pub_key + i.to_bytes(4,"big") – Oscar Serna Mar 26 at 16:20
  • Ok. I got it! In my desperation, I had also converted to int the chain code and back to bytes (unnecessary as well) and that was messing up my result. Changing that back along side your suggestion, my results match the vector tests from the Bitcoin Wiki page. Thanks a lot man!! – Oscar Serna Mar 26 at 16:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.