1

I want to get a normal Public Key from an extended Public Key in Python. Actually I want to get the same result as here in the Extended Public Key Children section. I know I need this structure:

child_public_key = parent_public_key + point(lefthand_hash_output)

I used this to get the lefthand hash:

         0x00 not sure if I need this                                         Index
         --                                                                  --------             
pub_key="000343b337dec65a47b3362c9620a6e6ff39a1ddfa908abab1666c8a30a3f8a7cccc00000000"
chain_code="1d7d2a4c940be028b945302ad79dd2ce2afe5ed55e1a2937a5af57f8401e73dd"

h = hmac.new( chain_code.encode("utf-8"),pub_key.encode("utf-8"), hashlib.sha512 ).digest()
lefthand_hash_output=slice(0,len(h)//2)

But now I don't know how to make this point(lefthand_hash_output).

I also tried this, but got an error

seed=b'...' # replaced with the seed
root_key = bip32utils.BIP32Key.fromEntropy(seed)
root_public_hex = root_key.PublicKey().hex()
key=bip32utils.BIP32Key.CKDpub((root_public_hex) ,0)
print(key)

A sample source code would be very helpful. Thanks in advance!

1 Answer 1

1

The conversion from extended public key to child public key is specified in BIP32:

The function CKDpub((Kpar, cpar), i) → (Ki, ci) computes a child extended public key from the parent extended public key. It is only defined for non-hardened child keys.

  • Check whether i ≥ 231 (whether the child is a hardened key).
    • If so (hardened child): return failure
    • If not (normal child): let I = HMAC-SHA512(Key = cpar, Data = serP(Kpar) || ser32(i)).
  • Split I into two 32-byte sequences, IL and IR.
  • The returned child key Ki is point(parse256(IL)) + Kpar.
  • The returned chain code ci is IR.
  • In case parse256(IL) ≥ n or Ki is the point at infinity, the resulting key is invalid, and one should proceed with the next value for i.

The other used functions are defined so:

  • point(p): returns the coordinate pair resulting from EC point multiplication (repeated application of the EC group operation) of the secp256k1 base point with the integer p.
  • ser32(i): serialize a 32-bit unsigned integer i as a 4-byte sequence, most significant byte first.
  • ser256(p): serializes the integer p as a 32-byte sequence, most significant byte first.
  • serP(P): serializes the coordinate pair P = (x,y) as a byte sequence using SEC1's compressed form: (0x02 or 0x03) || ser256(x), where the header byte depends on the parity of the omitted y coordinate.
  • parse256(p): interprets a 32-byte sequence as a 256-bit number, most significant byte first.

The first problem I see in your code is that your public key is 34 bytes long. According to the specification it should be 33 bytes long and start with either 0x02 or 0x03.

Next, chain_code.encode("utf-8") doesn't convert a hexadecimal string to bytes. Use something like bytes.fromhex(chain_code) instead.

Finally, lefthand_hash_output is created wrong, should be h[:32].

For sample source code you should check out the implementation in python-bip32. It uses coincurve for secp256k1 curve operations.

def _derive_public_child(pubkey, chaincode, index):
    """A.k.a CKDpub, in bip-0032.

    :param pubkey: The parent's (compressed) public key, as bytes
    :param chaincode: The parent's chaincode, as bytes
    :param index: The index of the node to derive, as int

    :return: (child_pubkey, child_chaincode)
    """
    assert isinstance(pubkey, bytes) and isinstance(chaincode, bytes)
    assert not index & HARDENED_INDEX
    # payload is the I from the BIP. Index is 32 bits unsigned int, BE.
    payload = hmac.new(
        chaincode, pubkey + index.to_bytes(4, "big"), hashlib.sha512
    ).digest()
    try:
        tmp_pub = coincurve.PublicKey.from_secret(payload[:32])
    except ValueError:
        raise BIP32DerivationError(
            "Invalid private key at index {}, try the " "next one!".format(index)
        )
    parent_pub = coincurve.PublicKey(pubkey)
    try:
        child_pub = coincurve.PublicKey.combine_keys([tmp_pub, parent_pub])
    except ValueError:
        raise BIP32DerivationError(
            "Invalid public key at index {}, try the " "next one!".format(index)
        )
    return child_pub.format(), payload[32:]
0

Your Answer

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

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