166 lines
6.8 KiB
Markdown
166 lines
6.8 KiB
Markdown
# scure-bip32
|
||
|
||
Audited & minimal implementation of BIP32 hierarchical deterministic (HD) wallets over secp256k1.
|
||
|
||
- 🔒 [Audited](#security) by an independent security firm
|
||
- 🔻 Tree-shakeable: unused code is excluded from your builds
|
||
- 📦 ESM
|
||
- ➰ Only 3 audited dependencies by the same author:
|
||
[noble-curves](https://github.com/paulmillr/noble-curves),
|
||
[noble-hashes](https://github.com/paulmillr/noble-hashes),
|
||
and [scure-base](https://github.com/paulmillr/scure-base)
|
||
- 🪶 18KB gzipped with all dependencies bundled
|
||
|
||
Check out [scure-bip39](https://github.com/paulmillr/scure-bip39) if you need mnemonic phrases.
|
||
See [key-producer](https://github.com/paulmillr/micro-key-producer) if you need SLIP-0010/BIP32 ed25519 hdkey implementation.
|
||
Notice [Warnings about BIP32](#warnings-about-bip32).
|
||
|
||
### This library belongs to _scure_
|
||
|
||
> **scure** — audited micro-libraries.
|
||
|
||
- Zero or minimal dependencies
|
||
- Highly readable TypeScript / JS code
|
||
- PGP-signed releases and transparent NPM builds
|
||
- Check out [homepage](https://paulmillr.com/noble/#scure) & all libraries:
|
||
[base](https://github.com/paulmillr/scure-base),
|
||
[bip32](https://github.com/paulmillr/scure-bip32),
|
||
[bip39](https://github.com/paulmillr/scure-bip39),
|
||
[btc-signer](https://github.com/paulmillr/scure-btc-signer),
|
||
[sr25519](https://github.com/paulmillr/scure-sr25519),
|
||
[starknet](https://github.com/paulmillr/scure-starknet)
|
||
|
||
## Usage
|
||
|
||
> `npm install @scure/bip32`
|
||
|
||
> `deno add jsr:@scure/bip32`
|
||
|
||
This module exports a single class `HDKey`, which should be used like this:
|
||
|
||
```ts
|
||
import { HDKey } from '@scure/bip32';
|
||
import { sha256 } from '@noble/hashes/sha2.js';
|
||
import { randomBytes } from '@noble/hashes/utils.js';
|
||
|
||
const seed = randomBytes(32);
|
||
const root = HDKey.fromMasterSeed(seed);
|
||
const base58key = root.privateExtendedKey;
|
||
const restored = HDKey.fromExtendedKey(base58key);
|
||
const fromJson = HDKey.fromJSON({ xpriv: base58key });
|
||
const child = fromJson.derive("m/0/2147483647'/1");
|
||
const msgHash = sha256(new TextEncoder().encode('hello scure-bip32'));
|
||
|
||
// props
|
||
[root.depth, root.index, root.chainCode];
|
||
[restored.privateKey, restored.publicKey];
|
||
const sig = child.sign(msgHash);
|
||
child.verify(msgHash, sig);
|
||
```
|
||
|
||
Note: `chainCode` property is essentially a private part
|
||
of a secret "master" key, it should be guarded from unauthorized access.
|
||
|
||
The full API is:
|
||
|
||
```ts
|
||
class HDKey {
|
||
public static HARDENED_OFFSET: number;
|
||
public static fromMasterSeed(seed: Uint8Array, versions: Versions): HDKey;
|
||
public static fromExtendedKey(base58key: string, versions: Versions): HDKey;
|
||
public static fromJSON(json: { xpriv: string }): HDKey;
|
||
|
||
readonly versions: Versions;
|
||
readonly depth: number = 0;
|
||
readonly index: number = 0;
|
||
readonly chainCode: Uint8Array | null = null;
|
||
readonly parentFingerprint: number = 0;
|
||
|
||
get fingerprint(): number;
|
||
get identifier(): Uint8Array | undefined;
|
||
get pubKeyHash(): Uint8Array | undefined;
|
||
get privateKey(): Uint8Array | null;
|
||
get publicKey(): Uint8Array | null;
|
||
get privateExtendedKey(): string;
|
||
get publicExtendedKey(): string;
|
||
|
||
derive(path: string): HDKey;
|
||
deriveChild(index: number): HDKey;
|
||
sign(hash: Uint8Array): Uint8Array;
|
||
verify(hash: Uint8Array, signature: Uint8Array): boolean;
|
||
wipePrivateData(): this;
|
||
}
|
||
|
||
interface Versions {
|
||
private: number;
|
||
public: number;
|
||
}
|
||
```
|
||
|
||
The module implements [bip32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) standard:
|
||
check it out for additional documentation.
|
||
|
||
The implementation is loosely based on cryptocoinjs/hdkey, [which has MIT License](#LICENSE).
|
||
|
||
## Warnings about BIP32
|
||
|
||
BIP32 is a bad standard. It would be great if we've had something better.
|
||
|
||
- Network IDs (different currencies) are taken from a single GitHub document
|
||
called SLIP-0044
|
||
- There were new projects, which did not yet have SLIP. Exchanges added support of
|
||
those projects to their cold wallets. Then after the projects were added to SLIP,
|
||
the exchanges were required to re-generate their cold wallets - a complicated task
|
||
- BIP32 is unusable for many different elliptic curves. For example, ETH2 uses bls12-381
|
||
curve, and with bip32 54% of generated keys would be invalid. So, they’re using much better
|
||
BLS-only EIP-2333 as a replacement.
|
||
- It’s easy to shoot yourself in foot with non-hardened keys, which
|
||
could allow simple de-anonimization of all addresses
|
||
|
||
## Security
|
||
|
||
The library has been audited:
|
||
|
||
- at version 2.2.0, in Apr 2026, by ourselves (self-audited)
|
||
- Scope: everything
|
||
- [Changes since audit](https://github.com/paulmillr/scure-bip32/compare/2.2.0..main)
|
||
- at version 1.0.1, in Jan 2022, independently, by [cure53](https://cure53.de)
|
||
- PDFs: [online](https://cure53.de/pentest-report_hashing-libs.pdf), [offline](./audit/2022-01-05-cure53-audit-nbl2.pdf)
|
||
- [Changes since audit](https://github.com/paulmillr/scure-bip32/compare/1.0.0..main).
|
||
- The audit has been funded by [Ethereum Foundation](https://ethereum.org/en/) with help of [Nomic Labs](https://nomiclabs.io)
|
||
|
||
The library was initially developed for [js-ethereum-cryptography](https://github.com/ethereum/js-ethereum-cryptography).
|
||
At commit [ae00e6d7](https://github.com/ethereum/js-ethereum-cryptography/commit/ae00e6d7d24fb3c76a1c7fe10039f6ecd120b77e),
|
||
it was extracted to a separate package called `micro-bip32`.
|
||
After the audit we've decided to use `@scure` NPM namespace for security.
|
||
|
||
### Supply chain security
|
||
|
||
- **Commits** are signed with PGP keys to prevent forgery. Be sure to verify the commit signatures
|
||
- **Releases** are made transparently through token-less GitHub CI and Trusted Publishing. Be sure to verify the [provenance logs](https://docs.npmjs.com/generating-provenance-statements) for authenticity.
|
||
- **Rare releasing** is practiced to minimize the need for re-audits by end-users.
|
||
- **Dependencies** are minimized and strictly pinned to reduce supply-chain risk.
|
||
- We use as few dependencies as possible.
|
||
- Version ranges are locked, and changes are checked with npm-diff.
|
||
- **Dev dependencies** are excluded from end-user installs; they’re only used for development and build steps.
|
||
|
||
For this package, there are 3 dependencies; and a few dev dependencies:
|
||
|
||
- [noble-hashes](https://github.com/paulmillr/noble-hashes) provides cryptographic hashing functionality
|
||
- [noble-curves](https://github.com/paulmillr/noble-curves) provides ECDSA
|
||
- [scure-base](https://github.com/paulmillr/scure-base) provides base58
|
||
- jsbt is used for benchmarking / testing / build tooling and developed by the same author
|
||
- prettier, fast-check and typescript are used for code quality / test generation / ts compilation
|
||
|
||
## Contributing & testing
|
||
|
||
- `npm install && npm run build && npm test` will build the code and run tests.
|
||
- `npm run lint` / `npm run format` will run linter / fix linter issues.
|
||
- `npm run build:release` will build single file
|
||
|
||
## License
|
||
|
||
[MIT License](./LICENSE)
|
||
|
||
Copyright (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com)
|