2

I am coding my own keys (not for use in production, strictly for learning). I am successfully creating 512 bit seeds from mnemonic phrases, however I am failing at the step of deriving child keys from these seeds. For reference I am using the following post for testing:

https://learnmeabitcoin.com/technical/hd-wallets

What I will do is use that site to generate mnemonics for me, then using the seed I try to match the child keys in the Child Keys (Basic) section, to no avail. For instance, I have the following phrase:

lash afraid any alter object topic pony deer helmet favorite chuckle carpet

which matches the seed and keys in the post:

Extended key: e8448e8e6ac4ce400951f6d630a0963c40b263208dacee0c53b3a3ccf8be9a1540434b626e34b82f28e4fb60842d5a5470e2f8cc2777a559a3c68ac4e9f2559d
Private key: e8448e8e6ac4ce400951f6d630a0963c40b263208dacee0c53b3a3ccf8be9a15
Chain code: 40434b626e34b82f28e4fb60842d5a5470e2f8cc2777a559a3c68ac4e9f2559d
Public key: 03cf895293b95a0ce2457593b82cc6fd646c757a1e2d393932b70dafddc2d9553b

Here is where I am getting tripped up. My rough python function looks like this:

def create_child_keys(chain_code, public_key, children):
    for n in range(children):
        hex_child_index = hex(n)[2:].zfill(8)
        print(hmac.new(bytes.fromhex(chain_code), msg=bytes.fromhex(public_key + hex_child_index), digestmod=hashlib.sha512).hexdigest())

create_child_keys(extended_key, 3)

This returns different hashes for the first 3 children than the link posted above. For instance, according to learnmeabitcoin the first child should have a private key of:

d547eda1f3d7da655cafec4506d3e333f1add4415968b1994f30bd64b023e2d3

but mine is:

c9dd74741e44fbd9e763f47829f37d1b2165a5ad6e90697c719d95489c409326

Hoping something obvious stands out to those more familiar with this process.

3
  • Check out a python implementation here github.com/mcdallas/cryptotools/blob/…
    – Mike D
    Dec 30, 2021 at 23:37
  • @MikeD Thanks for this! As far as I can tell this code is validating my code - so maybe learnmeabitcoin is erroneous? The code I used to validate, with a print statement in the child method for "I": m = Xprv.from_mnemonic('flash afraid any alter object topic pony deer helmet favorite chuckle carpet') print('code:', m.code.hex()) print('msg:', m.key.to_public().encode(compressed=True).hex()) print('index 0:', int_to_bytes(0).rjust(4, b'\x00').hex()) m.child(0) I'll show print logs in another comment, but they match my findings and not the ones from learnmeabitcoin.
    – Tennyx
    Dec 31, 2021 at 19:45
  • code: 40434b626e34b82f28e4fb60842d5a5470e2f8cc2777a559a3c68ac4e9f2559d msg: 03cf895293b95a0ce2457593b82cc6fd646c757a1e2d393932b70dafddc2d9553b index 0: 00000000 child hash: c9dd74741e44fbd9e763f47829f37d1b2165a5ad6e90697c719d95489c40932608b25a850dc4eec62c74bae84b53cfa13ec4dadfa6a9fddd99a73e9de778ee9e
    – Tennyx
    Dec 31, 2021 at 19:47

1 Answer 1

2

I figured out my issue. I don't know if I'm missing something with the results in learnmeabitcoin, but instead I started using test vectors from the Bitcoin wiki:

https://en.bitcoin.it/wiki/BIP_0032_TestVectors

The results from my python function above match the unhardened tests from that link. One thing I was forgetting to do was to add the master private key with the left 256 bits of the child hash to generate the child private key. I was able to achieve this by storing the above function in variable child_hash, then ran the following:

child_left_hash = child_hash[:64]
child_chain_code = child_hash[64:]
child_private_key = hex(int(child_left_hash, 16) + int(master_private_key, 16))[2:]

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.