first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
# HD key derivation
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`hd-key.ts` turns a BIP39 mnemonic into deterministic key material that the
rest of the package can fan out into a full hierarchy. The module performs
two jobs: BIP39 seed derivation (PBKDF2-HMAC-SHA512 with 2048 rounds and the
mnemonic + passphrase salt scheme) and indexed child-key derivation
(HMAC-SHA512 over the parent + 32-bit big-endian index). Together they
provide a flat, safe, BIP32-shaped HD path without requiring callers to
maintain hardened-derivation chain codes.
## Features
- `deriveMasterKeyFromMnemonic(mnemonic, passphrase?)` — 32-byte master key
(first half of the BIP39 seed).
- `deriveSeedFromMnemonic(mnemonic, passphrase?)` — full 64-byte seed for
callers that need both halves.
- `deriveChildKey(parent, index)` — 32-byte HMAC-SHA512 child.
- All entry points reject invalid mnemonics (word-count, unknown word,
checksum failure) with `CryptoVaultError(CRYPTOVAULT.HD.INVALID_MNEMONIC)`.
## Usage: Basic Example
```ts
import { generateMnemonic } from "@suaveplan/crypto-vault/bip39/bip39";
import { deriveMasterKeyFromMnemonic } from "@suaveplan/crypto-vault/hd-key/hd-key";
const phrase = generateMnemonic(128);
const master = deriveMasterKeyFromMnemonic(phrase);
console.log(master.length); // 32
```
## Usage: Advanced Example
```ts
import {
deriveChildKey,
deriveMasterKeyFromMnemonic,
deriveSeedFromMnemonic,
} from "@suaveplan/crypto-vault/hd-key/hd-key";
const phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
// Two devices, same mnemonic + passphrase => same master key (deterministic).
const m1 = deriveMasterKeyFromMnemonic(phrase, "shared-passphrase");
const m2 = deriveMasterKeyFromMnemonic(phrase, "shared-passphrase");
console.log(Buffer.from(m1).equals(Buffer.from(m2))); // true
// Different passphrases => different masters.
const m3 = deriveMasterKeyFromMnemonic(phrase, "different");
console.log(Buffer.from(m1).equals(Buffer.from(m3))); // false
// Indexed children.
const seed = deriveSeedFromMnemonic(phrase, "shared-passphrase");
const c0 = deriveChildKey(seed.slice(0, 32), 0);
const c1 = deriveChildKey(seed.slice(0, 32), 1);
console.log(Buffer.from(c0).equals(Buffer.from(c1))); // false
```
## API Reference
### `deriveMasterKeyFromMnemonic(mnemonic, passphrase?): Uint8Array`
- `mnemonic: string` — must be 12 / 15 / 18 / 21 / 24 BIP39 English words
with a valid checksum.
- `passphrase?: string` — optional BIP39 passphrase (defaults to `""`).
Returns a 32-byte master key (first half of the BIP39 seed).
Throws `CryptoVaultError(CRYPTOVAULT.HD.INVALID_MNEMONIC)` on empty input,
unsupported word counts, unknown words, or checksum failures.
### `deriveSeedFromMnemonic(mnemonic, passphrase?): Uint8Array`
Same validation as above; returns the full 64-byte seed.
### `deriveChildKey(parent, index): Uint8Array`
- `parent: Uint8Array` — any byte string (typically a master key or another
derived key).
- `index: number` — non-negative integer ≤ `0xffffffff`.
Returns a 32-byte child key (`HMAC-SHA512(parent, uint32-be(index))[:32]`).
Throws `CryptoVaultError(CRYPTOVAULT.HD.INVALID_INDEX)` for non-integer,
negative, or > 32-bit indices.
## Implementation Notes
- BIP39 seed derivation is intentionally synchronous (`mnemonicToSeedSync`)
so it can be called from constructors and worker init. PBKDF2 with 2048
rounds completes in ~5 ms on modern hardware.
- The validator runs both word-list membership and the BIP39 checksum
(`@scure/bip39.validateMnemonic`) so callers cannot bypass the checksum
by re-importing words.
- Child-key derivation deliberately does not implement BIP32 hardened paths
with chain codes. The spec-driven hierarchy in `key-hierarchy/` uses HKDF
for fan-out and only needs a single deterministic 32-byte parent.