Menu
BIP-bip-32 Final

BIP-32: HD Wallets

Hierarchical Deterministic Wallets specification. Derive unlimited keys from a single seed for Bitcoin agents.

Type Bitcoin Improvement Proposal
Number bip-32
Status Final
Authors Pieter Wuille
Original https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki

BIP-32: Hierarchical Deterministic Wallets

BIP-32 defines a standard for deriving an entire tree of key pairs from a single seed. This enables wallet backup with just one secret and organized key management.

Motivation

Before HD wallets, each key was generated independently:

  • Backup burden: New backup needed after each new key
  • No organization: Keys had no logical structure
  • Privacy risk: Key reuse to avoid backup complexity

HD wallets solve this:

  • Single backup: One seed backs up all current and future keys
  • Hierarchy: Organized tree structure (accounts, chains, addresses)
  • Delegation: Share xpubs for watch-only access

Key Concepts

Extended Keys

An extended key contains:

  • Key material: 256-bit private key or public key point
  • Chain code: 256-bit entropy for child derivation
  • Metadata: Depth, parent fingerprint, index

Total: 78 bytes before encoding

Serialization Format

4 bytes  - version (mainnet: 0x0488ADE4 private, 0x0488B21E public)
1 byte   - depth (0x00 for master)
4 bytes  - parent fingerprint (0x00000000 for master)
4 bytes  - child index
32 bytes - chain code
33 bytes - key (0x00 + 32-byte private, or 33-byte compressed public)

Base58Check encoded with prefix:

  • xprv - mainnet private
  • xpub - mainnet public
  • tprv - testnet private
  • tpub - testnet public

Derivation Types

TypeNotationIndex RangeUse
Normal0 to 2³¹-10x00000000 - 0x7FFFFFFFPublic derivation possible
Hardened0' or 0h0x80000000 - 0xFFFFFFFFPrivate key required

Critical: Hardened derivation prevents parent key recovery from child + parent public key.

Derivation Algorithm

Child Key Derivation Function (CKD)

Private Parent → Private Child:

if index >= 0x80000000 (hardened):
    data = 0x00 || ser256(kpar) || ser32(i)
else:
    data = serP(point(kpar)) || ser32(i)

I = HMAC-SHA512(Key = cpar, Data = data)
IL = I[:32]  # left 256 bits
IR = I[32:]  # right 256 bits

ki = parse256(IL) + kpar (mod n)
ci = IR

Public Parent → Public Child (normal only):

if index >= 0x80000000:
    return failure  # Cannot derive hardened from public

data = serP(Kpar) || ser32(i)
I = HMAC-SHA512(Key = cpar, Data = data)
IL = I[:32]
IR = I[32:]

Ki = point(parse256(IL)) + Kpar
ci = IR

Master Key Generation

From seed (typically 128-512 bits from BIP-39):

I = HMAC-SHA512(Key = "Bitcoin seed", Data = seed)
IL = I[:32]  # master private key
IR = I[32:]  # master chain code

Path Notation

Standard path format:

m / purpose' / coin_type' / account' / change / address_index
ComponentMeaning
mMaster key
/Derive child
' or hHardened derivation
NumberChild index

Example: m/84'/0'/0'/0/5

  • Master → purpose 84 (hardened) → Bitcoin mainnet (hardened) → account 0 (hardened) → external chain → address 5

Implementation

JavaScript (bitcoinjs-lib)

const { BIP32Factory } = require('bip32');
const ecc = require('tiny-secp256k1');
const bip32 = BIP32Factory(ecc);

// From seed
const seed = Buffer.from('...', 'hex');
const root = bip32.fromSeed(seed);

// Derive path
const child = root.derivePath("m/84'/0'/0'/0/0");

console.log('Private:', child.privateKey.toString('hex'));
console.log('Public:', child.publicKey.toString('hex'));

// Get xpub for account
const account = root.derivePath("m/84'/0'/0'");
const xpub = account.neutered().toBase58();
console.log('xpub:', xpub);

Python (bip32)

from bip32 import BIP32

# From seed
seed = bytes.fromhex('...')
bip32 = BIP32.from_seed(seed)

# Derive
privkey = bip32.get_privkey_from_path("m/84'/0'/0'/0/0")
pubkey = bip32.get_pubkey_from_path("m/84'/0'/0'/0/0")

# Get xpub
xpub = bip32.get_xpub_from_path("m/84'/0'/0'")

Security Considerations

Hardened vs Normal Derivation

ScenarioRisk
xpub leakedAttacker sees all addresses (privacy loss)
xpub + any child private key leakedAll keys compromised (normal derivation)
xpub + hardened child private keyOnly that child compromised

Rule: Always use hardened derivation for sensitive levels (purpose, coin_type, account).

Key Exposure Prevention

# WRONG: Don't share full path xprv
xprv_bad = root.derivePath("m/84'/0'/0'/0/0").toBase58()

# CORRECT: Share xpub at account level
xpub_good = root.derivePath("m/84'/0'/0'").neutered().toBase58()

Invalid Keys

Derivation can (rarely) produce invalid keys:

  • Private key = 0 or ≥ curve order n
  • Public key is point at infinity

Implementations must handle by skipping to next index.

Extended Key Versions

Different address types use different version bytes:

TypePrivatePublicAddress
P2PKH (BIP-44)xprvxpub1…
P2SH-P2WPKH (BIP-49)yprvypub3…
P2WPKH (BIP-84)zprvzpubbc1q…
P2TR (BIP-86)xprvxpubbc1p…

Note: Taproot uses standard xprv/xpub (no special version).

Common Derivation Paths

StandardPathPurpose
BIP-44m/44’/0’/account’/change/indexLegacy addresses
BIP-49m/49’/0’/account’/change/indexWrapped SegWit
BIP-84m/84’/0’/account’/change/indexNative SegWit
BIP-86m/86’/0’/account’/change/indexTaproot

Agent Best Practices

  1. Generate master from BIP-39 mnemonic (not raw entropy)
  2. Use hardened derivation for purpose/coin/account
  3. Share xpubs for watch-only, never xprvs
  4. Implement gap limit (typically 20 unused addresses)
  5. Backup seed phrase, not derived keys

Test Vectors

Vector 1

Seed: 000102030405060708090a0b0c0d0e0f

Pathxpub
mxpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8
m/0’xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw
m/0’/1xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ

Machine-Readable Summary

{
  "bip": 32,
  "title": "Hierarchical Deterministic Wallets",
  "status": "final",
  "key_derivation": "hmac-sha512",
  "derivation_types": ["normal", "hardened"],
  "path_format": "m / purpose' / coin' / account' / change / index",
  "libraries": {
    "javascript": ["bip32", "bitcoinjs-lib"],
    "python": ["bip32", "hdwallet"],
    "rust": ["bitcoin", "bip32"]
  }
}