bip85

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 8, 2026 License: MIT Imports: 20 Imported by: 0

README

go-bip85

Go Reference

The first Go implementation of BIP85 (Deterministic Entropy From BIP32 Keychains). Implements the BIP85 specification v2.0.0.

BIP85 derives deterministic entropy from a single BIP32 master key. One backup recovers everything: mnemonics, private keys, passwords, symmetric keys, dice rolls.

Install

go get github.com/shurlinet/go-bip85

Requires Go 1.25 or later.

Quick Start

From mnemonic to derived mnemonic, end to end:

package main

import (
    "fmt"
    "log"

    // Step 1: use any BIP32 library to go from mnemonic -> master xprv.
    // go-bip85 starts from the xprv.
    "github.com/btcsuite/btcd/btcutil/hdkeychain"
    "github.com/btcsuite/btcd/chaincfg"
    "github.com/tyler-smith/go-bip39" // or any BIP39 library

    "github.com/shurlinet/go-bip85"
)

func main() {
    // Your master mnemonic (NEVER hardcode real mnemonics).
    mnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

    // Mnemonic -> BIP39 seed -> BIP32 master key.
    seed := bip39.NewSeed(mnemonic, "") // empty passphrase
    masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
    if err != nil {
        log.Fatal(err)
    }
    defer masterKey.Zero()

    // go-bip85: derive a 12-word English mnemonic at index 0.
    path := bip85.BIP39Path(bip85.LangEnglish, 12, 0)
    entropy, err := bip85.DeriveEntropy(masterKey, path)
    if err != nil {
        log.Fatal(err)
    }
    defer bip85.ZeroBytes(entropy)

    childMnemonic, err := bip85.DeriveBIP39(entropy, bip85.LangEnglish, 12)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(childMnemonic)
    // Each index produces a completely independent mnemonic.
}

If you already have an xprv string (e.g., from a wallet export):

entropy, err := bip85.DeriveEntropyFromString("xprv9s21ZrQH143K...", path)

Applications

Application Function Path Output
BIP39 DeriveBIP39 m/83696968'/39'/{lang}'/{words}'/{idx}' Mnemonic string
WIF DeriveWIF m/83696968'/2'/{idx}' Compressed WIF string
XPRV DeriveXPRV m/83696968'/32'/{idx}' Extended private key string
HEX DeriveHex m/83696968'/128169'/{bytes}'/{idx}' Hex-encoded bytes
BASE64 DeriveBase64 m/83696968'/707764'/{len}'/{idx}' Base64 password
BASE85 DeriveBase85 m/83696968'/707785'/{len}'/{idx}' RFC 1924 Base85 password
DICE DeriveDice m/83696968'/89101'/{sides}'/{rolls}'/{idx}' Dice roll values
DRNG NewDRNG (from entropy) io.Reader (unlimited bytes)

All application functions accept raw entropy bytes. They are independent of how the entropy was derived.

Path Builders

Type-safe path constructors prevent typos:

bip85.BIP39Path(0, 12, 0)     // m/83696968'/39'/0'/12'/0'
bip85.WIFPath(0)               // m/83696968'/2'/0'
bip85.XPRVPath(0)              // m/83696968'/32'/0'
bip85.HexPath(32, 0)           // m/83696968'/128169'/32'/0'
bip85.Base64Path(21, 0)        // m/83696968'/707764'/21'/0'
bip85.Base85Path(12, 0)        // m/83696968'/707785'/12'/0'
bip85.DicePath(6, 10, 0)       // m/83696968'/89101'/6'/10'/0'
bip85.RSAPath(2048, 0)         // m/83696968'/828365'/2048'/0'

String paths also work: bip85.ParsePath("m/83696968'/39'/0'/12'/0'").

BIP39 Languages

All 10 BIP39 languages are supported. Japanese uses ideographic space (U+3000) per spec.

Code Language
0 English
1 Japanese
2 Korean
3 Spanish
4 Chinese (Simplified)
5 Chinese (Traditional)
6 French
7 Italian
8 Czech
9 Portuguese
Network Support

WIF and XPRV accept *chaincfg.Params for non-Bitcoin networks:

// Bitcoin mainnet (default when nil)
wif, _ := bip85.DeriveWIF(entropy, nil)

// Bitcoin testnet
wif, _ := bip85.DeriveWIF(entropy, &chaincfg.TestNet3Params)

// Any BIP32-compatible chain (Litecoin, Komodo, etc.)
wif, _ := bip85.DeriveWIF(entropy, &myCustomParams)

Chain-Agnostic API

For chains that don't use BIP32 (Solana, Cardano, Polkadot), use EntropyFromRawKey:

// Your Ed25519/Sr25519/custom-derived private key.
myKey := deriveViaSlip10(seed, path) // your derivation

// Apply BIP85 HMAC extraction directly. Zero btcsuite types.
// Any key length works - 32 bytes (Ed25519), 48, 64, etc.
entropy, err := bip85.EntropyFromRawKey(myKey)

This applies HMAC-SHA512("bip-entropy-from-k", key) to any key bytes. The result feeds into the same application functions. See examples/multichain for a complete runnable example.

RSA

RSA key generation (DeriveRSA) is intentionally not provided as a high-level function.

Why: Go's crypto/rsa.GenerateKey (since Go 1.24) mixes system entropy into prime generation for FIPS 140-3 compliance. This makes RSA output non-deterministic: the same BIP85 entropy produces a different RSA key on each call. The key is not recoverable from the seed backup alone, which breaks the core BIP85 promise.

This is a Go platform constraint, not a go-bip85 limitation. The BIP85 spec's RSA application works correctly when the RSA implementation uses only the provided DRNG as its randomness source.

Consumer workaround: Use the DRNG directly as an io.Reader with an RSA implementation that does not mix system entropy:

entropy, _ := bip85.DeriveEntropy(key, bip85.RSAPath(2048, 0))
drng := bip85.NewDRNG(entropy)
// Pass drng to an RSA implementation that uses ONLY the provided reader.

The full RSA implementation is preserved on the rsa-non-deterministic branch for reference.

Kept: RSAPath (path builder), AppRSA (app code 828365'), RSAMinBits, RSAMaxBits, GPGCreationTimestamp (Bitcoin genesis block timestamp for GPG key creation dates per spec).

DRNG

BIP85-DRNG-SHAKE256 produces an unlimited deterministic byte stream:

drng := bip85.NewDRNG(entropy) // 64 bytes of BIP85 entropy
buf := make([]byte, 32)
drng.Read(buf) // deterministic output

The DRNG implements io.Reader and can be passed to any function that accepts a randomness source. It never returns io.EOF.

Important: DRNG instances must not be shared between goroutines. Each goroutine must create its own instance.

Extensibility

Override behavior without modifying go-bip85:

// Custom HMAC key (non-standard output)
entropy, _ := bip85.DeriveEntropy(key, path, bip85.WithHMACKey([]byte("my-key")))

// Custom derivation (your own key derivation scheme)
entropy, _ := bip85.DeriveEntropy(key, path, bip85.WithCustomDeriver(myDeriver))

// Post-processing (extra HKDF layer, etc.)
entropy, _ := bip85.DeriveEntropy(key, path, bip85.WithEntropyPostProcessor(myPostProcessor))

Consumer Responsibilities

EC key validation: If you use raw HEX entropy as an elliptic curve private key (outside of DeriveWIF/DeriveXPRV which validate automatically), call ValidateSecp256k1Key first. Invalid keys (zero or >= curve order) must be rejected by trying the next BIP85 index.

No caching: BIP85 derivation is deterministic and fast. There is no need to cache derived outputs. Re-derive on every use from the master key. Caching introduces storage security requirements that derivation-on-demand avoids.

Path tracking: You are responsible for recording which derivation path produced which output. The library cannot reverse-derive a path from an output. Losing path metadata means brute-force recovery across all possible paths.

DRNG isolation: DRNG instances must not be shared between goroutines. Each goroutine must create its own instance. Sharing produces interleaved bytes with no error - silently wrong output.

What's Not Implemented

RSA GPG sub-keys: The GPG sub-key hierarchy (sub_key path extension + frozen timestamp) from the BIP85 spec is not implemented. The basic RSA application path and GPGCreationTimestamp constant are provided for consumers who build GPG keys externally.

FromMnemonic: This library takes an xprv string or pre-parsed key as input, not a mnemonic. Converting mnemonic to BIP39 seed to BIP32 master key is the consumer's responsibility. See the Quick Start section for a complete example chain.

Both can be extended via the WithCustomDeriver option or by using EntropyFromRawKey with your own derivation.

Security

Memory zeroing: This library zeros secret key material after use via ZeroBytes and defer. However, the Go garbage collector may copy data before zeroing occurs. For hardware-grade security, use a hardware wallet.

Master key security: All derived outputs are only as secure as the master key. A weak or compromised master key compromises all derived entropy.

Logging: Do not pass ExtendedKey values or entropy byte slices to loggers or tracing frameworks. ExtendedKey.String() outputs the full xprv.

COLDCARD compatibility: This library produces identical output to COLDCARD and other BIP85-compliant implementations when given the same master key and derivation path. All 12 BIP85 spec test vectors pass byte-for-byte.

Concurrency: Derivation functions (DeriveEntropy, DeriveKeyAndEntropy, EntropyFromRawKey) are safe for concurrent use with the same parsed key. DRNG instances are NOT safe for concurrent use.

Testing

  • 12 BIP85 spec vectors (mandatory gate)
  • 268 cross-implementation vectors (generated from bipsea, verified against ethankosakovsky, bip85-js, rust-bip85, Ledger)
  • 188+ smoke tests across all applications and parameter ranges
  • 21 hardcoded regression vectors (survive JSON corruption)
  • 22 property and behavioral tests (determinism, round-trip, independence, copy isolation, range, rejection sampling)
  • 8 fuzz targets
  • 5 AI threat defense tests (HMAC argument order, full entropy verification, independent HMAC oracle, error message key leak scan, XPRV field order)
  • Independent oracle (anti-tamper DICE test)
  • SHA256 wordlist integrity verification
  • Anti-tamper tests for HMAC key content and secp256k1 order
go test -race -count=1 ./...

Examples

See the examples/ directory:

  • basic - BIP32 master key to BIP39 mnemonic
  • dice - PIN generation and alphanumeric passwords via DICE
  • multichain - Chain-agnostic entropy for Solana, Cardano, Polkadot
  • drng - DRNG as io.Reader for custom key generation
  • password - Base64 and Base85 password generation

Dependencies

Dependency Purpose
btcsuite/btcd BIP32 HD key derivation, secp256k1, WIF encoding
golang.org/x/crypto SHAKE256 (DRNG)
golang.org/x/text Unicode NFC normalization (test-only, BIP39 cross-impl comparison)

AI Transparency

This library was developed with assistance from Claude (Anthropic). All code was reviewed, tested against reference implementations, and verified against the BIP85 specification. The test suite includes anti-tamper tests designed to catch both human and AI-introduced errors.

Acknowledgments

License

MIT

Documentation

Overview

Package bip85 implements BIP85 (Deterministic Entropy From BIP32 Keychains), version 2.0.0 of the specification.

BIP85 derives deterministic entropy from a BIP32 master key using HMAC-SHA512. A single master backup recovers all derived material: mnemonics, private keys, passwords, symmetric keys, and dice rolls. The derivation is one-way and hardened - knowledge of any derived output reveals nothing about the master key or other derived outputs.

Basic usage (BIP32 master key to BIP39 mnemonic):

key, _ := bip85.ParseKey("xprv9s21ZrQH143K...")
defer key.Zero()

path := bip85.BIP39Path(bip85.LangEnglish, 12, 0)
entropy, _ := bip85.DeriveEntropy(key, path)
defer bip85.ZeroBytes(entropy)

mnemonic, _ := bip85.DeriveBIP39(entropy, bip85.LangEnglish, 12)

Derivation Pipeline

The BIP85 derivation pipeline has three stages:

  1. Key derivation: BIP32 hardened child derivation to a leaf key, or custom derivation via WithCustomDeriver, or direct key input via EntropyFromRawKey.
  2. Entropy extraction: HMAC-SHA512(key="bip-entropy-from-k", msg=leafKey) produces 64 bytes of deterministic entropy.
  3. Application: the 64-byte entropy is transformed into the target format (mnemonic, WIF, XPRV, hex, password, dice rolls, or unlimited DRNG stream).

Applications

The following BIP85 applications are supported:

  • BIP39: Mnemonic derivation via DeriveBIP39 (10 languages, 12-24 words)
  • WIF: Compressed private key via DeriveWIF (secp256k1, network-aware)
  • XPRV: Extended private key via DeriveXPRV (reversed field order per spec)
  • HEX: Raw hex entropy via DeriveHex (16-64 bytes)
  • BASE64: Password via DeriveBase64 (20-86 characters, RFC 4648)
  • BASE85: Password via DeriveBase85 (10-80 characters, RFC 1924)
  • DICE: Dice rolls via DeriveDice (rejection sampling, 2 to 2^32-1 sides)
  • DRNG: Unlimited byte stream via NewDRNG (SHAKE256 XOF, io.Reader)

RSA key generation is not provided because Go 1.24+ mixes system entropy into crypto/rsa for FIPS 140-3, making output non-deterministic. Use the DRNG directly with a deterministic RSA implementation. See RSAPath and GPGCreationTimestamp.

Two-Tier API

The library provides two entry points for entropy derivation:

BIP32-native (Bitcoin and compatible chains):

entropy, err := bip85.DeriveEntropy(parsedKey, path)

Chain-agnostic (Solana, Cardano, Polkadot, or any key bytes):

entropy, err := bip85.EntropyFromRawKey(myDerivedKeyBytes)

Application functions (DeriveBIP39, DeriveHex, DeriveWIF, DeriveXPRV, DeriveBase64, DeriveBase85, DeriveDice) accept raw entropy bytes and are independent of the derivation method. Network parameters for WIF and XPRV are configurable via chaincfg.Params; pass nil for Bitcoin mainnet.

Concurrency

ParseKey returns an *hdkeychain.ExtendedKey that is safe for concurrent use from multiple goroutines. Each derivation allocates fresh child keys internally. DeriveEntropy, DeriveKeyAndEntropy, and EntropyFromRawKey are safe for concurrent use with the same parsed key.

DRNG instances are NOT safe for concurrent use. Each goroutine must create its own instance via NewDRNG.

Security

All returned byte slices (entropy, derived keys) are fresh allocations owned by the caller. Call ZeroBytes on them when no longer needed. Internally, all intermediate key material is zeroed via defer. The Go garbage collector may copy data before zeroing occurs; for hardware-grade security, use a hardware wallet.

All functions that return secret strings (DeriveBIP39, DeriveWIF, DeriveXPRV, DeriveHex, DeriveBase64, DeriveBase85) document the output as secret material. Do not log, trace, or serialize these values. *hdkeychain.ExtendedKey.String() outputs the full xprv in base58 - do not pass parsed keys to loggers or fmt.Print.

All derived outputs are only as secure as the master key. A weak or compromised master key compromises all derived entropy.

Specification

BIP85: https://github.com/bitcoin/bips/blob/master/bip-0085.mediawiki

Example
// Parse a BIP32 master private key.
key, err := bip85.ParseKey("xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb")
if err != nil {
	log.Fatal(err)
}
defer key.Zero()

// Derive 64 bytes of entropy at path m/83696968'/0'/0'.
path, err := bip85.ParsePath("m/83696968'/0'/0'")
if err != nil {
	log.Fatal(err)
}

entropy, err := bip85.DeriveEntropy(key, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

fmt.Println(hex.EncodeToString(entropy[:8]) + "...")
Output:
efecfbccffea3132...

Index

Examples

Constants

View Source
const (
	// Base64MinLen is the minimum password length for the BASE64 application.
	Base64MinLen = 20
	// Base64MaxLen is the maximum password length for the BASE64 application.
	Base64MaxLen = 86
)
View Source
const (
	// Base85MinLen is the minimum password length for the BASE85 application.
	Base85MinLen = 10
	// Base85MaxLen is the maximum password length for the BASE85 application.
	Base85MaxLen = 80
)
View Source
const (
	LangEnglish            uint32 = 0
	LangJapanese           uint32 = 1
	LangKorean             uint32 = 2
	LangSpanish            uint32 = 3
	LangChineseSimplified  uint32 = 4
	LangChineseTraditional uint32 = 5
	LangFrench             uint32 = 6
	LangItalian            uint32 = 7
	LangCzech              uint32 = 8
	LangPortuguese         uint32 = 9
)

BIP39 language codes as defined in the BIP85 spec.

View Source
const (
	// DiceMinSides is the minimum number of sides for the DICE application.
	DiceMinSides = 2
	// DiceMaxSides is the maximum number of sides for the DICE application (2^32 - 1).
	DiceMaxSides uint32 = 1<<32 - 1
)
View Source
const (
	// HexMinBytes is the minimum byte count for the HEX application (16).
	HexMinBytes = 16
	// HexMaxBytes is the maximum byte count for the HEX application (64).
	HexMaxBytes = 64
)
View Source
const (
	AppBIP39  uint32 = 39     // BIP39 mnemonic derivation
	AppWIF    uint32 = 2      // HD-Seed WIF (compressed private key)
	AppXPRV   uint32 = 32     // BIP32 extended private key
	AppHex    uint32 = 128169 // Raw hex entropy (16-64 bytes)
	AppBase64 uint32 = 707764 // Base64-encoded password (20-86 chars)
	AppBase85 uint32 = 707785 // RFC 1924 Base85-encoded password (10-80 chars)
	AppRSA    uint32 = 828365 // RSA key generation via DRNG
	AppDice   uint32 = 89101  // Dice roll generation via rejection sampling
)

BIP85 application codes as defined in the specification. These are used as the second hardened component in derivation paths.

View Source
const (
	// RSAMinBits is the minimum RSA key size for the RSA application (1024).
	RSAMinBits = 1024
	// RSAMaxBits is the maximum RSA key size for the RSA application (16384).
	RSAMaxBits = 16384

	// GPGCreationTimestamp is the UNIX epoch timestamp that MUST be used as
	// the creation date for GPG keys derived from BIP85 RSA output. This is
	// the Bitcoin genesis block timestamp (2009-01-03 18:15:05 UTC).
	//
	// BIP85 spec: "the creation date MUST be fixed to UNIX Epoch timestamp
	// 1231006505 [...] because the key fingerprint is affected by the
	// creation date."
	//
	// This library does not generate GPG or RSA keys directly. Consumers
	// building GPG keys from BIP85 RSA output via the DRNG must use this
	// constant as the key creation timestamp.
	GPGCreationTimestamp int64 = 1231006505
)

Variables

View Source
var (

	// ErrInvalidPath indicates a malformed or non-BIP85 derivation path.
	ErrInvalidPath = errors.New("bip85: invalid derivation path")

	// ErrIncompletePath indicates a path with too few segments for the
	// target application.
	ErrIncompletePath = errors.New("bip85: incomplete derivation path")

	// ErrNonHardenedComponent indicates a path component that is not hardened.
	// BIP85 requires ALL path components to be hardened.
	ErrNonHardenedComponent = errors.New("bip85: all path components must be hardened")

	// ErrPublicKeyNotAllowed indicates the caller passed a public extended key
	// (xpub/tpub). BIP85 requires a private extended key (xprv/tprv).
	ErrPublicKeyNotAllowed = errors.New("bip85: private extended key required, got public key")

	// ErrInvalidKey indicates the extended key string could not be parsed.
	ErrInvalidKey = errors.New("bip85: invalid extended key")

	// ErrInvalidKeyRange indicates a derived private key is zero or exceeds
	// the secp256k1 curve order. The caller should try the next index.
	ErrInvalidKeyRange = errors.New("bip85: derived key out of valid range, try next index")

	// ErrNilKey indicates a nil key was passed to a function.
	ErrNilKey = errors.New("bip85: nil key")

	// ErrEmptyKeyMaterial indicates nil or empty key material was provided,
	// either from a custom deriver or directly to EntropyFromRawKey.
	ErrEmptyKeyMaterial = errors.New("bip85: empty key material")

	// ErrInvalidHMACKey indicates the custom HMAC key option is nil or empty.
	ErrInvalidHMACKey = errors.New("bip85: HMAC key must not be nil or empty")

	// ErrPostProcessorShort indicates a post-processor returned fewer than
	// 64 bytes, which is insufficient for BIP85 applications.
	ErrPostProcessorShort = errors.New("bip85: post-processor must return at least 64 bytes")

	// ErrInvalidLength indicates a length parameter is out of the allowed
	// range for the target application.
	ErrInvalidLength = errors.New("bip85: invalid length parameter")

	// ErrCorruptedWordlist indicates an embedded BIP39 wordlist failed
	// SHA256 integrity verification. This likely indicates a tampered binary.
	ErrCorruptedWordlist = errors.New("bip85: corrupted BIP39 wordlist (SHA256 mismatch)")

	// ErrInvalidWordCount indicates the requested word count is not valid
	// for BIP39. Valid counts: 12, 15, 18, 21, 24.
	ErrInvalidWordCount = errors.New("bip85: invalid word count")

	// ErrInvalidLanguage indicates an unsupported BIP39 language code.
	// Valid codes: 0 (English) through 9 (Portuguese).
	ErrInvalidLanguage = errors.New("bip85: invalid language code")

	// ErrInvalidDiceSides indicates the sides parameter is out of the
	// allowed range for the DICE application. Valid: 2 to 2^32-1.
	ErrInvalidDiceSides = errors.New("bip85: invalid dice sides")

	// ErrInvalidDiceRolls indicates the rolls parameter is less than 1.
	ErrInvalidDiceRolls = errors.New("bip85: invalid dice rolls")
)

Sentinel errors returned by the BIP85 derivation pipeline. Callers can use errors.Is to match these. Grouped by subsystem.

Functions

func DecodeWIF

func DecodeWIF(wif string, net *chaincfg.Params) (key []byte, compressed bool, err error)

DecodeWIF decodes a WIF string and returns the 32-byte raw private key and whether it was compressed. The returned key is a fresh allocation; call ZeroBytes on it when done.

If net is non-nil, the WIF is verified to match the given network. If net is nil, the network check is skipped.

func DeriveBIP39

func DeriveBIP39(entropy []byte, language uint32, words int) (string, error)

DeriveBIP39 derives a BIP39 mnemonic from BIP85 entropy.

The returned mnemonic is a secret recovery phrase. Do not log it, display it in UI without user intent, or pass it to tracing frameworks.

The entropy is truncated to the byte length required for the given word count, then a SHA256 checksum is appended per BIP39. The combined bits are split into 11-bit groups (MSB-first), each group indexing into the wordlist for the given language.

Valid word counts: 12, 15, 18, 21, 24. Language codes: 0=English through 9=Portuguese.

Japanese mnemonics use the ideographic space (U+3000) as separator per the BIP39 specification. All other languages use ASCII space.

The output words use the same Unicode normalization form as the embedded BIP39 wordlists (NFKD, per BIP39 spec). Other implementations may output NFC-normalized text. Both forms represent the same words and produce the same BIP39 seed when processed through PBKDF2.

Path: m/83696968'/39'/{language}'/{words}'/{index}'.

Example
key, err := bip85.ParseKey("xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb")
if err != nil {
	log.Fatal(err)
}
defer key.Zero()

path := bip85.BIP39Path(bip85.LangEnglish, 12, 0)
entropy, err := bip85.DeriveEntropy(key, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

mnemonic, err := bip85.DeriveBIP39(entropy, bip85.LangEnglish, 12)
if err != nil {
	log.Fatal(err)
}

fmt.Println(mnemonic)
fmt.Println(strings.Count(mnemonic, " ")+1, "words")
Output:
girl mad pet galaxy egg matter matrix prison refuse sense ordinary nose
12 words

func DeriveBase64

func DeriveBase64(entropy []byte, pwdLen int) (string, error)

DeriveBase64 derives a Base64-encoded password from BIP85 entropy. pwdLen (20-86) controls the length of the output password string.

The returned string is a secret password derived from key material. Do not log it or pass it to tracing frameworks.

All 64 bytes of entropy are Base64-encoded using standard RFC 4648 encoding (NOT URL-safe), then the result is truncated to pwdLen characters. Passwords up to 86 characters never contain padding because 64 bytes encode to 86 data characters plus 2 padding characters (88 total), and the maximum pwdLen of 86 stops before any padding.

Path: m/83696968'/707764'/{pwdLen}'/{index}'.

func DeriveBase85

func DeriveBase85(entropy []byte, pwdLen int) (string, error)

DeriveBase85 derives an RFC 1924 Base85-encoded password from BIP85 entropy. pwdLen (10-80) controls the length of the output password string.

The returned string is a secret password derived from key material. Do not log it or pass it to tracing frameworks.

All 64 bytes of entropy are encoded using the RFC 1924 Base85 alphabet (NOT Adobe Ascii85, NOT Go's encoding/ascii85), then the result is truncated to pwdLen characters. 64 bytes -> 80 Base85 characters, so the maximum pwd_len of 80 uses the full encoding.

Path: m/83696968'/707785'/{pwdLen}'/{index}'.

func DeriveDice

func DeriveDice(entropy []byte, sides uint32, rolls int) (values []int, formatted string, err error)

DeriveDice derives formatted dice rolls from BIP85 entropy. This is a convenience function that calls DeriveRolls and FormatRolls.

Returns both the raw roll values and the formatted string.

Example
key, err := bip85.ParseKey("xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb")
if err != nil {
	log.Fatal(err)
}
defer key.Zero()

// Roll a 6-sided die 10 times at index 0.
path := bip85.DicePath(6, 10, 0)
entropy, err := bip85.DeriveEntropy(key, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

values, formatted, err := bip85.DeriveDice(entropy, 6, 10)
if err != nil {
	log.Fatal(err)
}

fmt.Println(formatted)
fmt.Println(len(values), "rolls")
Output:
1,0,0,2,0,1,5,5,2,4
10 rolls

func DeriveEntropy

func DeriveEntropy(key *hdkeychain.ExtendedKey, path Path, opts ...Option) ([]byte, error)

DeriveEntropy derives BIP85 entropy from the given pre-parsed master key and derivation path. It returns the 64-byte HMAC-SHA512 output.

The returned entropy slice is a fresh allocation owned by the caller. Call ZeroBytes on it when done to clear secret material.

For batch derivation, parse the key once with ParseKey and reuse it across multiple calls.

func DeriveEntropyFromString

func DeriveEntropyFromString(xprv string, path Path, opts ...Option) ([]byte, error)

DeriveEntropyFromString is a convenience wrapper that parses the xprv string and calls DeriveEntropy. The parsed key is zeroed automatically on return. For batch derivation, prefer ParseKey + DeriveEntropy to avoid repeated parsing.

Example
xprv := "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb"
path, _ := bip85.ParsePath("m/83696968'/0'/0'")

entropy, err := bip85.DeriveEntropyFromString(xprv, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

fmt.Println(len(entropy), "bytes")
Output:
64 bytes

func DeriveHex

func DeriveHex(entropy []byte, numBytes int) (string, error)

DeriveHex derives a hex-encoded entropy string from BIP85 entropy. numBytes (16-64) controls how many bytes are kept from the entropy before hex encoding. Per the spec, "truncate trailing (least significant) bytes" means keeping the first numBytes.

The returned string is secret material (raw key entropy in hex form). Do not log it, include it in error messages, or pass it to tracing frameworks. Go strings cannot be zeroed after use.

The entropy slice must be at least numBytes long.

Path: m/83696968'/128169'/{numBytes}'/{index}'.

Example
key, err := bip85.ParseKey("xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb")
if err != nil {
	log.Fatal(err)
}
defer key.Zero()

path := bip85.HexPath(32, 0)
entropy, err := bip85.DeriveEntropy(key, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

hexStr, err := bip85.DeriveHex(entropy, 32)
if err != nil {
	log.Fatal(err)
}

fmt.Println(hexStr)
fmt.Println(len(hexStr)/2, "bytes")
Output:
ea3ceb0b02ee8e587779c63f4b7b3a21e950a213f1ec53cab608d13e8796e6dc
32 bytes

func DeriveKeyAndEntropy

func DeriveKeyAndEntropy(key *hdkeychain.ExtendedKey, path Path, opts ...Option) (derivedKey, entropy []byte, err error)

DeriveKeyAndEntropy derives both the BIP85 intermediate key and entropy.

The derivedKey is the 32-byte private key at the final path depth (the "k" in the BIP85 spec). The entropy is the 64-byte HMAC-SHA512 output. Both are secret material - call ZeroBytes on each when no longer needed.

func DeriveRolls

func DeriveRolls(entropy []byte, sides uint32, rolls int) ([]int, error)

DeriveRolls derives a DICE application result from BIP85 entropy.

Roll values are zero-indexed: an N-sided die produces values in [0, N-1]. The DRNG is seeded with the 64-byte entropy, then rejection sampling produces each roll by reading the minimum number of bytes, retaining the most significant bits, and discarding values >= sides.

sides must be in [2, 2^32-1]. rolls must be in [1, 2^32-1].

Path: m/83696968'/89101'/{sides}'/{rolls}'/{index}'.

func DeriveWIF

func DeriveWIF(entropy []byte, net *chaincfg.Params) (string, error)

DeriveWIF derives a compressed WIF-encoded private key from BIP85 entropy. The first 32 bytes of entropy are used as the private key.

The returned string is a secret private key. Do not log it or pass it to tracing frameworks.

The net parameter controls the version byte in the WIF encoding. Pass &chaincfg.MainNetParams for Bitcoin mainnet (prefix K/L), &chaincfg.TestNet3Params for testnet (prefix c), or custom chaincfg.Params for other BIP32-compatible chains. If nil, defaults to Bitcoin mainnet.

The key is validated against the secp256k1 curve order. If invalid (probability < 1 in 2^127), ErrInvalidKeyRange is returned and the caller should try the next index.

Example
key, err := bip85.ParseKey("xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb")
if err != nil {
	log.Fatal(err)
}
defer key.Zero()

path := bip85.WIFPath(0)
entropy, err := bip85.DeriveEntropy(key, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

wif, err := bip85.DeriveWIF(entropy, nil) // nil = Bitcoin mainnet
if err != nil {
	log.Fatal(err)
}

fmt.Println(wif)
Output:
Kzyv4uF39d4Jrw2W7UryTHwZr1zQVNk4dAFyqE6BuMrMh1Za7uhp

func DeriveXPRV

func DeriveXPRV(entropy []byte, net *chaincfg.Params) (string, error)

DeriveXPRV derives a BIP32 extended private key from the given 64-byte BIP85 entropy.

The returned string is a secret extended private key. Do not log it or pass it to tracing frameworks.

The net parameter controls the version bytes in the serialized key. Pass &chaincfg.MainNetParams for Bitcoin mainnet (xprv prefix), &chaincfg.TestNet3Params for testnet (tprv prefix), or custom chaincfg.Params for other BIP32-compatible chains. If nil, defaults to Bitcoin mainnet.

Per the BIP85 spec, the field ordering is REVERSED from BIP32 convention:

  • First 32 bytes of entropy = chain code
  • Second 32 bytes of entropy = private key

The private key is validated against the secp256k1 curve order. If invalid, ErrInvalidKeyRange is returned and the caller should try the next index.

Depth, parent fingerprint, and child number are forced to zero per spec.

Example
key, err := bip85.ParseKey("xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb")
if err != nil {
	log.Fatal(err)
}
defer key.Zero()

path := bip85.XPRVPath(0)
entropy, err := bip85.DeriveEntropy(key, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

xprv, err := bip85.DeriveXPRV(entropy, nil) // nil = Bitcoin mainnet
if err != nil {
	log.Fatal(err)
}

fmt.Println(xprv)
Output:
xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX

func EntropyFromRawKey

func EntropyFromRawKey(privateKey []byte, opts ...Option) ([]byte, error)

EntropyFromRawKey applies the BIP85 HMAC-SHA512 entropy extraction to a raw private key of any length. This is the chain-agnostic foundation of BIP85: HMAC-SHA512(key="bip-entropy-from-k", msg=privateKey) -> 64 bytes.

Use this when you have your own key derivation (SLIP-10, Ed25519, Sr25519, or any non-BIP32 scheme) and want to apply the BIP85 entropy extraction step. The privateKey can be any length - the HMAC accepts arbitrary input.

For standard BIP32 derivation, use DeriveEntropy instead.

The caller is responsible for zeroing the input privateKey after use. The returned entropy is a fresh allocation owned by the caller.

Example
// Chain-agnostic: apply BIP85 HMAC extraction to any raw private key.
// No BIP32 types needed. Works with Ed25519, Sr25519, or any key bytes.
rawKey, _ := hex.DecodeString("cca20ccb0e9a90feb0912870c3323b24874b0ca3d8018c4b96d0b97c0e82ded0")
defer bip85.ZeroBytes(rawKey)

entropy, err := bip85.EntropyFromRawKey(rawKey)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

fmt.Println(len(entropy), "bytes")
fmt.Println(hex.EncodeToString(entropy[:8]) + "...")
Output:
64 bytes
efecfbccffea3132...

func FormatRolls

func FormatRolls(rollValues []int, sides uint32) string

FormatRolls formats dice roll values as a comma-separated string. Values are zero-padded to the width of (sides-1) for readability when sides >= 10.

func ParseKey

func ParseKey(xprv string) (*hdkeychain.ExtendedKey, error)

ParseKey parses a base58check-encoded extended private key (xprv/tprv). Whitespace is trimmed automatically. Public keys are rejected.

The returned key can be reused across multiple derivations without re-parsing. It is safe for concurrent use from multiple goroutines since derivation creates new child keys at each level.

Security: the returned ExtendedKey's String() method outputs the full xprv in base58. Do not pass the key to loggers, fmt.Print, or any framework that auto-serializes function arguments.

func ValidateSecp256k1Key

func ValidateSecp256k1Key(key []byte) error

ValidateSecp256k1Key checks whether a 32-byte value is a valid secp256k1 private key (non-zero and less than the curve order). Returns nil if valid.

BIP85 applications that use raw entropy as an EC private key (WIF, XPRV) must call this before encoding. If the key is invalid, the caller should try the next BIP85 index.

func ZeroBytes

func ZeroBytes(buf []byte)

ZeroBytes overwrites buf with zeros. Use this to clear secret material (entropy, derived keys) returned by DeriveEntropy, DeriveKeyAndEntropy, and EntropyFromRawKey.

ZeroBytes is safe to call with nil or empty slices (no-op).

This is a best-effort measure. The Go garbage collector may have already copied the data to another location, so this is not a guarantee against memory forensics.

Types

type DRNG

type DRNG struct {
	// contains filtered or unexported fields
}

DRNG is a deterministic random number generator seeded with BIP85 entropy. It implements io.Reader by wrapping SHAKE256 (FIPS 202).

BIP85-DRNG-SHAKE256 seeds a SHAKE256 XOF with the 64-byte HMAC-SHA512 output and produces an unlimited deterministic byte stream.

DRNG is forward-only: once bytes are read, they cannot be re-read. Create a new DRNG from the same entropy to restart the sequence.

A DRNG instance must not be shared between goroutines. Each goroutine must create its own instance.

func NewDRNG

func NewDRNG(entropy []byte) *DRNG

NewDRNG creates a BIP85-DRNG-SHAKE256 from 64 bytes of BIP85 entropy. The entropy is consumed immediately; the caller may zero it afterward.

Panics if entropy is not exactly 64 bytes (the caller has a bug).

Example
key, err := bip85.ParseKey("xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb")
if err != nil {
	log.Fatal(err)
}
defer key.Zero()

// Derive entropy and create a DRNG (deterministic random number generator).
path, _ := bip85.ParsePath("m/83696968'/0'/0'")
entropy, err := bip85.DeriveEntropy(key, path)
if err != nil {
	log.Fatal(err)
}
defer bip85.ZeroBytes(entropy)

drng := bip85.NewDRNG(entropy)

// Read 16 bytes of deterministic output.
buf := make([]byte, 16)
drng.Read(buf)
fmt.Println(hex.EncodeToString(buf))
Output:
b78b1ee6b345eae6836c2d53d33c64cd

func (*DRNG) Read

func (d *DRNG) Read(p []byte) (int, error)

Read fills p with deterministic bytes from the SHAKE256 stream. It always returns len(p), nil. DRNG never returns io.EOF.

Read is not safe for concurrent use. Each goroutine must use its own DRNG instance.

Read is suitable as a rand source for crypto/rsa.GenerateKey and similar functions that accept an io.Reader.

type Option

type Option func(*derivationConfig)

Option configures the BIP85 derivation pipeline. Options are applied in order. Nil options are silently skipped. All options that modify cryptographic behavior produce non-standard output that will not match any reference BIP85 implementation.

func WithCustomDeriver

func WithCustomDeriver(fn func(root *hdkeychain.ExtendedKey, path Path) ([]byte, error)) Option

WithCustomDeriver replaces the default BIP32 derivation with a custom function. The function receives the parsed root key and path, and must return a freshly allocated slice of raw private key material. The returned slice will be zeroed by the library after the HMAC step.

This option is only compatible with DeriveEntropy and DeriveKeyAndEntropy. It is rejected by EntropyFromRawKey, which accepts pre-derived key material directly.

Security: the deriver receives the full master key. Only use derivers from your own trust domain. A malicious deriver can read or copy the master key. Do not accept Options from untrusted sources.

func WithEntropyPostProcessor

func WithEntropyPostProcessor(fn func(entropy []byte) ([]byte, error)) Option

WithEntropyPostProcessor adds a post-processing step after the HMAC-SHA512 entropy extraction. The function receives the 64-byte entropy and must return at least 64 bytes. The result is copied to a fresh allocation before the original entropy is zeroed, so the post-processor may safely return a sub-slice of its input.

func WithHMACKey

func WithHMACKey(key []byte) Option

WithHMACKey overrides the default HMAC key ("bip-entropy-from-k"). The key must be non-nil and non-empty.

Setting a custom HMAC key produces non-standard output that will not match any reference BIP85 implementation.

type Path

type Path struct {
	// contains filtered or unexported fields
}

Path represents a validated BIP85 derivation path. All components are hardened. The purpose code (83696968') is always the first component.

The zero value is an empty path. Passing it to DeriveEntropy returns ErrIncompletePath. Construct paths with ParsePath or the builder functions (BIP39Path, WIFPath, HexPath, etc.).

A Path is immutable after construction. It is safe for concurrent use.

func BIP39Path

func BIP39Path(language, words, index uint32) Path

BIP39Path returns a path for BIP39 mnemonic derivation. Language codes: 0=English, 1=Japanese, 2=Korean, 3=Spanish, 4=Chinese (Simplified), 5=Chinese (Traditional), 6=French, 7=Italian, 8=Czech, 9=Portuguese. Valid word counts: 12, 15, 18, 21, 24.

Path: m/83696968'/39'/{language}'/{words}'/{index}'.

Example
// Build a path for deriving a 12-word English BIP39 mnemonic at index 0.
path := bip85.BIP39Path(bip85.LangEnglish, 12, 0)
fmt.Println(path.String())
Output:
m/83696968'/39'/0'/12'/0'

func Base64Path

func Base64Path(pwdLen, index uint32) Path

Base64Path returns a path for Base64-encoded password derivation. pwdLen must be in [20, 86].

Path: m/83696968'/707764'/{pwdLen}'/{index}'.

func Base85Path

func Base85Path(pwdLen, index uint32) Path

Base85Path returns a path for RFC 1924 Base85-encoded password derivation. pwdLen must be in [10, 80].

Path: m/83696968'/707785'/{pwdLen}'/{index}'.

func CorePath

func CorePath(appCode, index uint32) Path

CorePath returns a path for direct BIP85 derivation without a registered application: m/83696968'/{appCode}'/{index}'. Use the application-specific builders (BIP39Path, WIFPath, etc.) for standard BIP85 applications.

func DicePath

func DicePath(sides, rolls, index uint32) Path

DicePath returns a path for dice roll generation via rejection sampling. sides must be >= 2. rolls is the number of dice to roll.

Path: m/83696968'/89101'/{sides}'/{rolls}'/{index}'.

func HexPath

func HexPath(numBytes, index uint32) Path

HexPath returns a path for raw hex entropy derivation. numBytes must be in [16, 64].

Path: m/83696968'/128169'/{numBytes}'/{index}'.

func ParsePath

func ParsePath(s string) (Path, error)

ParsePath parses a BIP85 derivation path string. The path must start with "m/83696968'" and all components must be hardened (marked with ', h, H, or p).

Accepted formats:

m/83696968'/0'/0'
m/83696968h/0h/0h
m/83696968H/0H/0H
m/83696968p/0p/0p
Example
// All hardened marker styles are accepted and normalized.
p1, _ := bip85.ParsePath("m/83696968'/0'/0'")
p2, _ := bip85.ParsePath("m/83696968h/0h/0h")
fmt.Println(p1.String())
fmt.Println(p2.String())
Output:
m/83696968'/0'/0'
m/83696968'/0'/0'

func RSAPath

func RSAPath(keyBits, keyIndex uint32) Path

RSAPath returns a path for RSA key generation via BIP85-DRNG. keyBits is the RSA key size (e.g., 2048, 4096).

Path: m/83696968'/828365'/{keyBits}'/{keyIndex}'.

func WIFPath

func WIFPath(index uint32) Path

WIFPath returns a path for HD-Seed WIF (compressed private key) derivation.

Path: m/83696968'/2'/{index}'.

func XPRVPath

func XPRVPath(index uint32) Path

XPRVPath returns a path for BIP32 extended private key derivation. The derived entropy uses reversed field ordering per the BIP85 spec: first 32 bytes = chain code, second 32 bytes = private key.

Path: m/83696968'/32'/{index}'.

func (Path) Components

func (p Path) Components() []uint32

Components returns the raw (un-hardened) path component values. The returned slice is a copy.

func (Path) Len

func (p Path) Len() int

Len returns the number of components (including the purpose code).

func (Path) String

func (p Path) String() string

String returns the canonical string form of the path using the ' hardened marker (e.g. "m/83696968'/0'/0'"). Returns an empty string for the zero-value Path (no components).

Directories

Path Synopsis
examples
basic command
Command basic demonstrates deriving a BIP39 mnemonic from a BIP32 master key using go-bip85.
Command basic demonstrates deriving a BIP39 mnemonic from a BIP32 master key using go-bip85.
dice command
Command dice demonstrates using BIP85 DICE for deterministic PIN generation and alphanumeric password generation via dice-to-alphabet mapping.
Command dice demonstrates using BIP85 DICE for deterministic PIN generation and alphanumeric password generation via dice-to-alphabet mapping.
drng command
Command drng demonstrates using the BIP85-DRNG-SHAKE256 as an io.Reader for custom key generation.
Command drng demonstrates using the BIP85-DRNG-SHAKE256 as an io.Reader for custom key generation.
multichain command
Command multichain demonstrates go-bip85's chain-agnostic design using EntropyFromRawKey.
Command multichain demonstrates go-bip85's chain-agnostic design using EntropyFromRawKey.
password command
Command password demonstrates generating Base64 and Base85 passwords from BIP85 entropy.
Command password demonstrates generating Base64 and Base85 passwords from BIP85 entropy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL