Skip to content

BIP32/BIP39++ (single-header) — Deterministic wallet keys without the footguns

Project goal A compact, proprietary C++ header that implements BIP-39 seed derivation and BIP-32 child-key derivation with clean types and predictable behavior. It gives you hardened/normal CKD, path parsing (BIP-44 style), public-key fingerprints, and smooth interop with your secp256k1 stack—ideal for Cosmos/EVM backends, HSM shims, and test harnesses.


Why it exists

  • HD wallets are easy to get subtly wrong. Off-by-one indices, endianness, or mixing hardened/normal steps will bite you.
  • You want minimal, auditable code. Single header, clear return types, no build gymnastics.
  • Common flows should be one-liners. Mnemonic → seed → master (k,c) → path → child key → (optional) address.

Features

  • BIP-39 → seed mnemonic2seed(mnemonic, passphrase) using PBKDF2-HMAC-SHA512 (2048 iters).

  • BIP-32 CKD (private) ckd_priv(k_parent, c_parent, index) returns {k_child, c_child}; supports hardened (index ≥ 0x80000000) and normal derivation.

  • BIP-44 path parser path2indices("m/44'/118'/0'/0/0")std::vector<uint32_t> with hardened bits set; works for any coin path (60' for Ethereum, 118' for Cosmos, etc.).

  • Master key derivation derive_master_key(seed){key, chain_code} per BIP-32 (“Bitcoin seed”).

  • Fingerprints fingerprint(compressed_pubkey) → 4-byte parent fingerprint (SHA-256 → RIPEMD-160).

  • Bytes/BN interop Helpers like ser_32, ser_256, parse256 align with CKD spec notation; plays nicely with your existing secp256k1/EVP wrappers.

  • Single header, exception-based errors Clear failure modes; integrates in minutes.

Example workflows

1) Mnemonic → Cosmos account key (default BIP-44 path)

#include "bip32_bip39.hpp"  // your single header

std::string mnemonic =
  "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog";

openssl::bytes_t prv = bip39::mnemonic2private_key(mnemonic, "m/44'/118'/0'/0/0"); // Cosmos

2) Ethereum key at m/44'/60'/0'/0/0 and address

using bytes_t = openssl::bytes_t;

bytes_t seed      = bip39::mnemonic2seed(mnemonic);
auto [k, c]       = bip32::derive_master_key(seed);
for (uint32_t i : bip32::path2indices("m/44'/60'/0'/0/0"))
  std::tie(k, c) = bip32::ckd_priv(k, c, i);

// derive compressed/uncompressed pubkey as needed, then address (EVM)
bytes_t pub       = w3::derive_public_key(k, /*compressed=*/false);
bytes_t address   = ethereum::publickey2address(pub);          // last 20 bytes of keccak(pub[1:])

3) Parent fingerprint (for xpub/xprv hierarchies)

bytes_t pub_c = w3::derive_public_key(k, /*compressed=*/true);
bytes_t fpr   = bip32::fingerprint(pub_c);   // 4 bytes

Supported conventions (examples)

  • Cosmos: m/44'/118'/0'/0/0
  • Ethereum / EVM chains: m/44'/60'/0'/0/0
  • Bitcoin-style: m/44'/0'/0'/0/0

Any SLIP-44 coin type works; pass the path you need and iterate with ckd_priv.

Security & correctness notes

  • Deterministic HMAC/CKD per BIP-32/BIP-39; hardened vs. normal indices handled by spec.
  • Endianness safe helpers (ser_32, ser_256) match the notation in the original BIP-32 text.
  • Use high-entropy mnemonics and secure passphrases; guard seeds/keys in memory, and clear when appropriate.
  • Public-key serialization (compressed/uncompressed) is caller-selectable to match downstream protocol requirements.

Minimal API (at a glance)

namespace bip39 {
  openssl::bytes_t mnemonic2seed(const std::string& mnemonic,
                                 const std::string& passphrase = "mnemonic");
  openssl::bytes_t mnemonic2private_key(const std::string& mnemonic,
                                        std::string path = "m/44'/118'/0'/0/0");
}

namespace bip32 {
  std::pair<openssl::bytes_t, openssl::bytes_t> derive_master_key(const openssl::bytes_t& seed);
  std::pair<openssl::bytes_t, openssl::bytes_t> ckd_priv(const openssl::bytes_t& kpar,
                                                          const openssl::bytes_t& cpar,
                                                          uint32_t index);
  std::vector<uint32_t> path2indices(const std::string& path);
  openssl::bytes_t fingerprint(const openssl::bytes_t& compressed_pubkey);
}

Licensing & availability Proprietary, single-header library. Commercial licensing via Varga Consulting (Toronto, Canada). Contact: info@vargaconsulting.ca.