0

I'm trying to implement bip32 derivation and have been able to derive the child keys from the master node, but not children of the child key.

The following test shows the correct result in bytes for deriving the second non-hardened key from the first hardened key (m/0'/1) of the master node xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi

Correct output:

04 88 ad e4 02 5c 1b d6 48 00 00 00 01 2a 78 57 63 13 86 ba 23 da ca c3 41 80 dd 19 83 73 4e 44 4f
db f7 74 04 15 78 e9 b6 ad b3 7c 19 00 3c 6c b8 d0 f6 a2 64 c9 1e a8 b5 03 0f ad aa 8e 53 8b 02 0f 
0a 38 74 21 a1 2d e9 31 9d c9 33 68 b3 4b c4 42

Actual output:

04 88 ad e4 02 5c 1b d6 48 00 00 00 01 2a 78 57 63 13 86 ba 23 da ca c3 41 80 dd 19 83 73 4e 44 4f 
db f7 74 04 15 78 e9 b6 ad b3 7c 19 00 3c 6c b8 d0 f6 a2 64 c9 1e a8 b5 03 0f ad aa 8d 0e 39 de f5 
b9 81 14 5d 61 00 47 bf 6d ff 78 7a 01 be 65 76

As you can see, almost everything is correct apart from the last 20 bytes, which forms part of the private key. Since I know my derivation works for the first chain, I'm wondering if the process requires an addition of something else to make the private key for subsequent levels?

Here is my derivation function

public HDPrivateKey childAt(uint index, bool isHardened = false) {

            /*non hardened keys between 0 ~ 2^31 - 1, hardened between 2^31 ~ 2^32 - 1*/
            /*Note: A hardened key at index less than 2^31 will automatically assume it starts at 2^31*/
            if (index >= BIP32_HARDENED && !isHardened) {
                throw new Exception("Index too large for non-hardened key");
            }
            uint childNum = isHardened ? index | BIP32_HARDENED : index;


            /*perform hmac*/
            byte[] hmac;
            byte[] childNumBytes = BitConverter.GetBytes(childNum);
            Array.Reverse(childNumBytes);

            if (isHardened) {
                byte[] pkBytes = Byte.prependByte(privateKey_.toBytes(), 0x00);
                byte[] data = Byte.join(pkBytes, childNumBytes);
                hmac = Hash.HMACSHA512Encode(data, chaincode_);
            } 
            else {
                byte[] data = Byte.join(pubKey_.getCompressed(), childNumBytes);
                hmac = Hash.HMACSHA512Encode(data, chaincode_);
            }

            //HMAC is correct, produces correct chaincode

            /*Properties of child*/
            byte[] pre = hmac[0..32];
            byte[] chaincode = hmac[32..64];
            byte[] fingerprint = Hash.hash160(pubKey_.getCompressed())[0..4];
            int depth = this.depth_ + 1;

            /*Perform addition of the parent and child in the field of N*/
            FieldElement parent = new Sha256Field(new BigInteger(this.privateKey_.toBytes(), true, true));
            FieldElement child = new Sha256Field(new BigInteger(pre, true, true));
            FieldElement sum = parent + child;
            PrivateKey privateKey = new PrivateKey(sum.getNum());
            Console.WriteLine("chid key {0}", Byte.bytesToString(privateKey.toBytes()));
            HDPrivateKey childKey = new HDPrivateKey(
                privateKey, chaincode, depth, fingerprint, childNum, this.testnet_);
            return childKey;
        }

Help is really appreciated! thanks in advance

1 Answer 1

1

I answered my own question, I was doing addition in the field of P instead of mod N, in case anyone gets caught out by this!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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