Derive cbk keys with BIP85 instead of an ad hoc HKDF label
The private key is now the BIP85 HEX application output for 32 bytes at
m/83696968'/128169'/32'/{index}' from the BIP32 master key of the BIP39
seed, via sneak.berlin/go/secret/pkg/bip85, so any BIP85 tool reproduces
it and one mnemonic backs many keypairs through Params.Index. Tests
check the mnemonic-to-master step and the HEX application against the
BIP85 specification vectors and pin the resulting keypair.
This commit is contained in:
@@ -129,29 +129,35 @@ directory is never read.
|
||||
### `pkg/cbk`
|
||||
|
||||
Derives NaCl crypto_box keypairs (Curve25519) deterministically from BIP39
|
||||
mnemonics, and nothing else. The derivation is fixed and documented so keys are
|
||||
reproducible:
|
||||
mnemonics, and nothing else. The entropy comes from
|
||||
[BIP85](https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki) through
|
||||
`sneak.berlin/go/secret/pkg/bip85`, so any BIP85-capable tool given the same
|
||||
mnemonic reproduces the same key, and one mnemonic can back any number of
|
||||
independent keypairs:
|
||||
|
||||
1. The mnemonic is validated and canonicalized with
|
||||
`bip39wordlist.NormalizeWordlist`.
|
||||
2. The standard 64-byte BIP39 seed is computed with PBKDF2-HMAC-SHA512 (2048
|
||||
rounds, salt `"mnemonic" + passphrase`), exactly as a BIP39 wallet would, so
|
||||
the optional passphrase works as the usual "25th word".
|
||||
3. The 32-byte private key is
|
||||
`HKDF-SHA256(seed, salt = nil, info = cbk.DerivationInfo)`.
|
||||
4. The public key is the Curve25519 base-point multiple of the private key.
|
||||
3. The seed becomes a BIP32 master key.
|
||||
4. BIP85's HEX application derives 32 bytes of entropy at
|
||||
`m/83696968'/128169'/32'/{index}'`; those bytes are the private key.
|
||||
5. The public key is the Curve25519 base-point multiple of the private key.
|
||||
|
||||
The resulting keys are plain 32-byte crypto_box keys for use with
|
||||
`golang.org/x/crypto/nacl/box`. The API:
|
||||
|
||||
- `cbk.New(cbk.Params{Mnemonic, Passphrase})` and `cbk.FromMnemonic(m)` return a
|
||||
`CryptoBoxKeypair`.
|
||||
- `cbk.New(cbk.Params{Mnemonic, Passphrase, Index})` and `cbk.FromMnemonic(m)`
|
||||
(no passphrase, index 0) return a `CryptoBoxKeypair`.
|
||||
- `CryptoBoxKey.Hex()`, `CryptoBoxKey.Bytes()`, and the `PublicKeyHex`,
|
||||
`PrivateKeyHex`, `PublicKeyBytes`, `PrivateKeyBytes` accessors on the keypair.
|
||||
|
||||
The test suite pins the keypair derived from the standard all-zero-entropy BIP39
|
||||
test vector. If that test ever fails, the derivation has changed and existing
|
||||
keys are no longer reproducible.
|
||||
The test suite checks the mnemonic-to-master-key step and the BIP85 HEX
|
||||
application against the BIP85 specification's own test vectors, verifies that
|
||||
the private key is exactly the BIP85 output, and pins the keypair for the
|
||||
specification's mnemonic at index 0. If the pinned test ever fails, the
|
||||
derivation has changed and existing keys are no longer reproducible.
|
||||
|
||||
### `pkg/bip39wordlist`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user