An implementation of BIP32 hierarchical deterministic wallets and extended keys.
A sample GHCi session:
> :set -XOverloadedStrings
>
> import Crypto.HDKey.BIP32
>
> -- derive a master node from a master seed
> let Just m = master "plenty of entropy"
>
> -- use 'xpub', 'xprv', etc. to serialize
> xpub m
"xpub661MyMwAqRbcG6TPJvVs1yKFJGtN4vi785g2xDacQ9Luyw3gyAyvY5DNatPzfsUQK4nTUAmQboxw3WYDHtY4vfcGJR4FAuLLaUp2t7ejhoC"
>
> -- derive child nodes via a path
> let Just child = derive m "m/44'/0'/0'/0/0"
> xpub child
"xpub6GEwJiJFou5PH6LL8cagArvArrXhSaq35XWnT73CShNRBJa9jxHsWnPsydvmN2vcPBg9KHfRyYLiYnUKCJ8ncba4CgzF56n4kpkqMTSFy35"
>
> -- use the 'hd_key' record to extract the extended key
> let Right my_xprv = hd_key child
> xprv_key my_xprv
82064013501759548583899633460204676801585795402966146917762774758050650403971
>
> -- use 'parse' to import an extended key
> let Just hd = xprv child >>= parse
> hd == child
True
Haddocks (API documentation, etc.) are hosted at docs.ppad.tech/bip32.
The aim is best-in-class performance for pure Haskell code. Most time is spent on elliptic curve multiplication or hashing; strict BIP32 functionality is only a small layer on top of that.
Current benchmark figures on an M4 Silicon MacBook Air look like (use
cabal bench to run the benchmark suite):
benchmarking ppad-bip32 (wNAF)/derive_child_pub'
time 180.7 μs (180.6 μs .. 180.9 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 180.8 μs (180.6 μs .. 180.9 μs)
std dev 493.6 ns (382.1 ns .. 639.6 ns)
benchmarking ppad-bip32 (wNAF)/derive_child_priv'
time 167.0 μs (166.8 μs .. 167.2 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 167.0 μs (166.8 μs .. 167.2 μs)
std dev 667.4 ns (488.1 ns .. 925.3 ns)
benchmarking ppad-bip32/xpub
time 149.6 μs (149.1 μs .. 150.2 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 149.3 μs (149.0 μs .. 149.9 μs)
std dev 1.296 μs (653.2 ns .. 2.117 μs)
benchmarking ppad-bip32/xprv
time 6.512 μs (6.506 μs .. 6.519 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 6.512 μs (6.507 μs .. 6.520 μs)
std dev 19.72 ns (12.91 ns .. 34.71 ns)
benchmarking ppad-bip32/parse
time 6.905 μs (6.899 μs .. 6.913 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 6.926 μs (6.919 μs .. 6.933 μs)
std dev 23.14 ns (18.74 ns .. 28.17 ns)
You should compile with the 'llvm' flag (and ensure [ppad-fixed][fixed], [ppad-sha256][sha256], [ppad-sha512][sha512], and ppad-secp256k1 are compiled with the 'llvm' flag) for maximum performance.
This library aims at the maximum security achievable in a garbage-collected language under an optimizing compiler such as GHC, in which strict constant-timeness can be challenging to achieve.
The implementation within passes the official [BIP32 test vectors](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki# test-vectors), and all derivations involving secret keys execute in constant time, and with constant allocation -- see the "Security" notes in the README of ppad-secp256k1 for more details.
If you discover any vulnerabilities, please disclose them via security@ppad.tech.
You'll require Nix with flake support enabled. Enter a development shell with:
$ nix develop
Then do e.g.:
$ cabal repl ppad-bip32
to get a REPL for the main library.