<p>The conversion from extended public key to child public key is specified in <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#public-parent-key--public-child-key" rel="nofollow noreferrer">BIP32</a>:</p>
<blockquote>
<p>The function CKDpub((K<sub>par</sub>, c<sub>par</sub>), i) →
(K<sub>i</sub>, c<sub>i</sub>) computes a child extended public key
from the parent extended public key. It is only defined for
non-hardened child keys.</p>
<ul>
<li>Check whether i ≥ 2<sup>31</sup> (whether the child is a hardened key).
<ul>
<li>If so (hardened child): return failure</li>
<li>If not (normal
child): let I = HMAC-SHA512(Key = c<sub>par</sub>, Data =
ser<sub>P</sub>(K<sub>par</sub>) || ser<sub>32</sub>(i)).</li>
</ul>
</li>
<li>Split I into two 32-byte sequences, I<sub>L</sub> and I<sub>R</sub>.</li>
<li>The returned child key K<sub>i</sub> is point(parse<sub>256</sub>(I<sub>L</sub>)) + K<sub>par</sub>.</li>
<li>The returned chain code c<sub>i</sub> is I<sub>R</sub>.</li>
<li>In case parse<sub>256</sub>(I<sub>L</sub>) ≥ n or K<sub>i</sub> is the point at infinity, the resulting key is invalid, and one should
proceed with the next value for i.</li>
</ul>
</blockquote>
<p>The other used functions are defined so:</p>
<blockquote>
<ul>
<li>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.</li>
<li>ser<sub>32</sub>(i): serialize a 32-bit unsigned integer i as a 4-byte sequence, most significant byte first.</li>
<li>ser<sub>256</sub>(p): serializes the integer p as a 32-byte sequence, most significant byte first.</li>
<li>ser<sub>P</sub>(P): serializes the coordinate pair P = (x,y) as a byte sequence using SEC1's compressed form: (0x02 or 0x03) ||
ser<sub>256</sub>(x), where the header byte depends on the parity of
the omitted y coordinate.</li>
<li>parse<sub>256</sub>(p): interprets a 32-byte sequence as a 256-bit number, most significant byte first.</li>
</ul>
</blockquote>
<hr />
<p>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.</p>
<p>Next, <code>chain_code.encode(&quot;utf-8&quot;)</code> doesn't convert a hexadecimal string to bytes. Use something like <code>bytes.fromhex(chain_code)</code> instead.</p>
<p>Finally, <code>lefthand_hash_output</code> is created wrong, should be <code>h[:32]</code>.</p>
<p>For sample source code you should check out the implementation in <a href="https://github.com/darosior/python-bip32/blob/ed2bc075b8fff4dbd16b0356925f926ae55f29f9/bip32/utils.py#L97" rel="nofollow noreferrer">python-bip32</a>. It uses <a href="https://github.com/ofek/coincurve" rel="nofollow noreferrer">coincurve</a> for secp256k1 curve operations.</p>
<pre><code>def _derive_public_child(pubkey, chaincode, index):
    &quot;&quot;&quot;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)
    &quot;&quot;&quot;
    assert isinstance(pubkey, bytes) and isinstance(chaincode, bytes)
    assert not index &amp; 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, &quot;big&quot;), hashlib.sha512
    ).digest()
    try:
        tmp_pub = coincurve.PublicKey.from_secret(payload[:32])
    except ValueError:
        raise BIP32DerivationError(
            &quot;Invalid private key at index {}, try the &quot; &quot;next one!&quot;.format(index)
        )
    parent_pub = coincurve.PublicKey(pubkey)
    try:
        child_pub = coincurve.PublicKey.combine_keys([tmp_pub, parent_pub])
    except ValueError:
        raise BIP32DerivationError(
            &quot;Invalid public key at index {}, try the &quot; &quot;next one!&quot;.format(index)
        )
    return child_pub.format(), payload[32:]
</code></pre>

CC BY-SA 4.0
2022-07-22T01:09:54.897Z
2022-07-26T17:47:21.817Z
2022-07-26T17:47:21.817Z
Vojtěch Strnad
<p>I want to get a normal Public Key from an extended Public Key in Python. Actually I want to get the same result as <a href="https://learnmeabitcoin.com/technical/hd-wallets" rel="nofollow noreferrer">here</a> in the Extended Public Key Children section. I know I need this structure:</p>
<pre><code>child_public_key = parent_public_key + point(lefthand_hash_output)
</code></pre>
<p>I used this to get the lefthand hash:</p>
<pre><code>         0x00 not sure if I need this                                         Index
         --                                                                  --------             
pub_key=&quot;000343b337dec65a47b3362c9620a6e6ff39a1ddfa908abab1666c8a30a3f8a7cccc00000000&quot;
chain_code=&quot;1d7d2a4c940be028b945302ad79dd2ce2afe5ed55e1a2937a5af57f8401e73dd&quot;

h = hmac.new( chain_code.encode(&quot;utf-8&quot;),pub_key.encode(&quot;utf-8&quot;), hashlib.sha512 ).digest()
lefthand_hash_output=slice(0,len(h)//2)
</code></pre>
<p>But now I don't know how to make this <code>point(lefthand_hash_output)</code>.</p>
<p>I also tried this, but got an error</p>
<pre><code>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)
</code></pre>
<p>A sample source code would be very helpful. Thanks in advance!</p>

<bip32-hd-wallets><public-key><python><xpub>
Generate Public Keys from Extended Public Key in python
Julian
# Generate Public Keys from Extended Public Key in python

(Asked by: username_0 on 2022-07-21)

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](https://learnmeabitcoin.com/technical/hd-wallets) 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!

**Tags:** bip32-hd-wallets, public-key, python, xpub


---

## 1 answers

### Answer by username_1 on 2022-07-22. Score: 1 | Accepted answer

The conversion from extended public key to child public key is specified in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#public-parent-key--public-child-key):

> 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](https://github.com/darosior/python-bip32/blob/ed2bc075b8fff4dbd16b0356925f926ae55f29f9/bip32/utils.py#L97). It uses [coincurve](https://github.com/ofek/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:]

```


---

