Skip to content
DonateБГ

Keys and addresses

Extended keys

A bitcoin xpub is an extended public key: a public key plus a chain code that makes child keys. Learn how xpub and xprv are built, and the risks.

By Dimitar Zurnov · Updated 4 Nov 2026
Adapted from Learn Me a Bitcoin by Greg Walker.

An xpub is an extended public key: a public key joined to a 32-byte chain code, so it can make child keys. An extended key is this pair, private or public, and it is the unit that an HD wallet passes down its tree. An extended private key, written xprv, makes private and public children. An extended public key, written xpub, makes only normal public children.

Both forms are written as a Base58Check string, which is why they start with fixed prefixes and run to 111 characters. Those strings are what wallets import, export and share.

What is inside an extended key?

An extended key is 78 bytes of fields, plus a 4-byte checksum. The fields record where the key sits in the tree, and the last field holds the key itself.

Field Size What it holds
Version 4 bytes The prefix, such as 0488ade4 for xprv
Depth 1 byte How many steps below the master key, which is 0 for the master
Parent fingerprint 4 bytes The first 4 bytes of the parent's HASH160 public key, or 00000000 for the master
Child number 4 bytes The index of this key under its parent, or 00000000 for the master
Chain code 32 bytes The secret that makes the children unpredictable
Key 33 bytes The private key with a 00 byte in front, or the compressed public key

A private key is 32 bytes, so the 00 byte makes it 33, the same length as a compressed public key. Both forms serialize the same way.

An extended key is 78 bytes of fields, plus a 4-byte checksum, written in Base58 as 111 characters. version, 4 bytes 0488ade4 (xprv) depth, 1 byte 01 (one step below the master) parent fingerprint, 4 bytes 3442193e child number, 4 bytes 80000000 (index 2^31, so 0') chain code, 32 bytes 47fdacbd0f1097043b78c63c20c34ef4 ed9a111d980047ad16282c7ae6236141 key, 33 bytes 00 + the 32-byte private key: edb2e14f9ee77d26dd93b4ecede8d16e d408ce149b6cd80b0715a2d911a0afea 78 bytes so far, then a 4-byte checksum. Base58 encode, 111 characters xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDi PvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesn DYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7
The example is m/0' from BIP 32 test vector 1, a public test value. Its fingerprint is that of the master key, and its child number 80000000 means index 2^31.

The fingerprint and child number do not affect the arithmetic. They only tell you where the key sits, so software can match a child to its parent without searching the whole tree. The 4-byte child number is also why an extended key can have at most 4,294,967,296 children.

Which version bytes mean what?

The version bytes tell software the key type and which kind of wallet uses it. These are the common ones.

Prefix Version bytes Used for
xprv 0488ade4 Extended private key, BIP 32
xpub 0488b21e Extended public key, BIP 32
yprv 049d7878 Extended private key, BIP 49 (nested SegWit)
ypub 049d7cb2 Extended public key, BIP 49
zprv 04b2430c Extended private key, BIP 84 (native SegWit)
zpub 04b24746 Extended public key, BIP 84

The y and z prefixes only change the version bytes. The key and chain code inside are the same kind as an xprv or xpub. Taproot, in BIP 86, has no prefix of its own. Its keys use xprv and xpub, and the script type is stated elsewhere. Bitcoin Core uses output descriptors to say which script type a key feeds.

What is the master extended key?

The master key comes straight from the seed. HMAC-SHA512 with the key Bitcoin seed splits into the master private key and the chain code, as the HD wallets page shows. The master has depth 0, a zero fingerprint and a zero child number.

For BIP 32 test vector 1, the master extended private key is:

xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi

Its extended public key is:

xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8

Both are published in the BIP, so they are safe to copy for testing. They must never hold coins.

How is a normal child private key made?

A normal child has an index from 0 to 2,147,483,647. The parent's public key is hashed with the index. Anyone who holds the parent's xpub can compute that tweak, but only the private key can finish the child's private key. Here are the steps.

  1. Take the parent's compressed public key, 33 bytes, and append the index as 4 bytes, big-endian.
  2. Run HMAC-SHA512 over that data, with the parent's chain code as the key.
  3. Split the 64-byte output. The left 32 bytes are IL, and the right 32 bytes are the child's chain code.
  4. Add IL to the parent's private key, and reduce the sum modulo the order of the curve, n. The result is the child private key.

For m/0'/1, the child of the test vector's first hardened key, these steps give the child private key 3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368. Its public key is 03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c.

How is a normal child public key made?

The public route uses the same IL and the same chain code, but it works on points rather than numbers. A public key is the curve point of its private key, so adding IL to the private key adds IL times the generator point to the public key. The public route computes IL × G, then adds it to the parent public key. The result is the public key of the private route.

That is why an xpub can make public keys without the private key. The code below runs both routes and prints both results, which must match. It is the same arithmetic as the private route, with the public key doing the work.

import hashlib, hmac, struct

N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141  # order of the curve
P = 2**256 - 2**32 - 977
G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
     0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)

def add(p, q):                       # point addition on secp256k1
    if p is None: return q
    if q is None: return p
    if p[0] == q[0] and (p[1] + q[1]) % P == 0: return None
    if p == q: s = 3 * p[0] * p[0] * pow(2 * p[1], -1, P) % P
    else: s = (q[1] - p[1]) * pow(q[0] - p[0], -1, P) % P
    x = (s * s - p[0] - q[0]) % P
    return x, (s * (p[0] - x) - p[1]) % P

def mul(k, pt=G):                    # scalar multiplication
    r = None
    while k:
        if k & 1: r = add(r, pt)
        pt = add(pt, pt)
        k >>= 1
    return r

def compress(pt):                    # 33-byte compressed public key
    return bytes([2 + pt[1] % 2]) + pt[0].to_bytes(32, "big")

def tweak(parent_pub, chain, index):
    # IL and the child chain code, from HMAC-SHA512 of the parent public key and index
    I = hmac.new(chain, parent_pub + struct.pack(">I", index), hashlib.sha512).digest()
    return int.from_bytes(I[:32], "big"), I[32:]

# parent m/0' from BIP 32 test vector 1
parent_priv = 0xedb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea
parent_chain = bytes.fromhex("47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141")
parent_point = mul(parent_priv)      # the parent public key
il, child_chain = tweak(compress(parent_point), parent_chain, 1)

child_priv = (il + parent_priv) % N          # private route: needs the private key
child_point = add(mul(il), parent_point)     # public route: needs only the xpub

print(child_priv.to_bytes(32, "big").hex())  # 3c6cb8d0...
print(compress(child_point).hex())           # 03501e45...

Run the code to get the two values in the comments. Check the public key against the private key before you rely on the code.

How is a hardened child made?

A hardened child has an index from 2,147,483,648 to 4,294,967,295. Its HMAC input uses the parent private key instead of the public key. The data is 00, then the 32-byte private key, then the 4-byte index, which is 37 bytes in total. The rest is the same: the left half of the HMAC is added to the parent key, and the right half is the chain code.

A normal child hashes the parent public key and index, a hardened child hashes the parent private key and index, and both add the left half to the parent key. normal child, index 0 to 2^31 - 1 HMAC data: parent public key (33 bytes) || index (4 bytes) HMAC key: parent chain code IL = left 32 bytes of the output child key = IL + parent key, mod n child chain code = right 32 bytes hardened child, index 2^31 or more HMAC data: 00 || parent private key (32 bytes) || index (4 bytes) HMAC key: parent chain code IL and the chain code as above child key = IL + parent key, mod n
Hardened derivation needs the private key, so an xpub cannot make a hardened child.

The master's first hardened child, m/0', is the example used in the layout diagram above. Its private key is edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea, which is the value a wallet would use to sign for that branch.

Because the input contains the private key, no public key can produce a hardened child. That is the whole point of hardening. A hardened child's public key cannot be derived by anyone who holds only the xpub of its parent.

What is the weakness of normal children?

A normal child's private key is the parent key plus a value IL that anyone holding the parent xpub can compute. So if someone has the parent xpub and one normal child's private key, they can subtract IL and recover the parent private key:

parent private key = child private key - IL

The parent's public key and chain code, both in the xpub, are enough to get IL for each index. That exposes the parent, and every normal child under it. You can check this step with the code above: (child_priv - il) % N gives back parent_priv.

Two rules follow from this. Never reveal a normal child's private key while its parent's xpub is public. And use hardened children at the level where an account begins, so a leak lower down cannot reach the account key. Derivation paths shows where that level sits.

Are extended keys addresses?

No. The word "address" appears in some wallet menus, but an xpub is not something you can pay. It is a key that makes many keys. To receive coins, a wallet takes the xpub, derives a child, and builds a payable address from the child's public key or script. The address page explains the types.

What should you remember about extended keys?

  • An extended key is a key plus a chain code, serialized in 78 bytes and written in Base58Check.
  • xprv makes private and public children, and xpub makes only normal public children.
  • The y and z prefixes change only the version bytes, not how keys are derived.
  • A normal child private key and the parent xpub together reveal the parent. Hardened children prevent this.

Next, read derivation paths to see how a wallet names one key in the tree, such as m/84'/0'/0'/0/0.

In this section: Keys and addresses

All of Bitcoin from zero

Something look wrong? Tell us