check / check (push) Successful in 4m53s
go-bip39's repository no longer exists, so keyfunc now carries the part of v1.1.0 it uses, with the upstream LICENSE beside it, and imports it from internal/bip39. Upstream's tests and test vectors for the kept code come along unchanged; where they called a removed function, crypto/rand stands in for NewEntropy and EntropyFromMnemonic for MnemonicToByteArray. Changes beyond the trimming are what the linter asked for: the package-level variables moved into the functions that use them, three error strings were lower-cased, and the unknown-word error now wraps ErrInvalidMnemonic. go.mod no longer requires go-bip39. go mod tidy keeps its two go.sum lines, because a test in sneak/secret's bip85 package imports it, and drops five lines that only go-bip39's own requirements needed. Model: opus-5-5
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
// Package derive turns a mnemonic into the bytes a key is made from.
|
|
package derive
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.eeqj.de/sneak/secret/pkg/bip85"
|
|
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"sneak.berlin/go/keyfunc/internal/bip39"
|
|
)
|
|
|
|
const (
|
|
// purpose is the number BIP-85 reserves for itself.
|
|
purpose = 83696968
|
|
|
|
// Size is how many bytes every key type is given.
|
|
Size = 32
|
|
|
|
// MaxIndex is the largest key index there is. Every element of
|
|
// the path is hardened, and a hardened BIP-32 child index stops
|
|
// here.
|
|
MaxIndex = 1<<31 - 1
|
|
)
|
|
|
|
// ErrIndexTooLarge is returned for a key index above MaxIndex. Such an
|
|
// index has no hardened child to derive, so there is no key to give
|
|
// back rather than a key nobody else would reproduce.
|
|
var ErrIndexTooLarge = errors.New("the key index is too large")
|
|
|
|
// Path returns the derivation path for an application number and a key
|
|
// index.
|
|
func Path(application, index uint32) string {
|
|
return fmt.Sprintf("m/%d'/%d'/%d'", purpose, application, index)
|
|
}
|
|
|
|
// CheckIndex refuses a key index above MaxIndex, so that every key
|
|
// type turns such an index down before deriving anything.
|
|
func CheckIndex(index uint32) error {
|
|
if index > MaxIndex {
|
|
return fmt.Errorf(
|
|
"%w: %d is above %d",
|
|
ErrIndexTooLarge, index, MaxIndex,
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Master returns the BIP-32 master key a mnemonic stands for: the
|
|
// mnemonic becomes a seed with an empty passphrase, and the seed
|
|
// becomes the key.
|
|
func Master(words string) (*hdkeychain.ExtendedKey, error) {
|
|
seed := bip39.NewSeed(words, "")
|
|
|
|
master, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("making the master key: %w", err)
|
|
}
|
|
|
|
return master, nil
|
|
}
|
|
|
|
// Bytes returns the bytes for an application number and a key index.
|
|
// The mnemonic becomes a seed with an empty passphrase, the seed
|
|
// becomes a master key, the master key gives BIP-85 entropy at the
|
|
// path, and the entropy seeds the generator the bytes are read from.
|
|
// An index above MaxIndex is refused before any of that happens.
|
|
func Bytes(words string, application, index uint32) ([]byte, error) {
|
|
err := CheckIndex(index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
master, err := Master(words)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
entropy, err := bip85.DeriveBIP85Entropy(master, Path(application, index))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("deriving entropy: %w", err)
|
|
}
|
|
|
|
out := make([]byte, Size)
|
|
|
|
_, err = bip85.NewBIP85DRNG(entropy).Read(out)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading derived bytes: %w", err)
|
|
}
|
|
|
|
return out, nil
|
|
}
|