783 lines
23 KiB
Go
783 lines
23 KiB
Go
package bip85
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/tyler-smith/go-bip39"
|
|
)
|
|
|
|
const (
|
|
// Test master BIP32 root key from the BIP85 specification
|
|
testMasterKey = "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb"
|
|
)
|
|
|
|
// logTestVector logs test information to make it clear what's being tested
|
|
func logTestVector(t *testing.T, title, description string) {
|
|
t.Logf("TEST VECTOR: %s", title)
|
|
t.Logf("FROM BIP85 SPECIFICATION: %s", description)
|
|
}
|
|
|
|
// TestDerivedKey is a helper function to test the derived key directly
|
|
func TestDerivedKey(t *testing.T) {
|
|
logTestVector(t, "Derived Child Keys", "Testing direct key derivation for BIP85 paths")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Test case 1
|
|
path := "m/83696968'/0'/0'"
|
|
t.Logf("Deriving key for path: %s", path)
|
|
derivedKeyBytes, err := DeriveChildKey(masterKey, path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive child key: %v", err)
|
|
}
|
|
|
|
derivedKeyHex := hex.EncodeToString(derivedKeyBytes)
|
|
expectedKeyHex := "cca20ccb0e9a90feb0912870c3323b24874b0ca3d8018c4b96d0b97c0e82ded0"
|
|
t.Logf("EXPECTED: %s", expectedKeyHex)
|
|
t.Logf("ACTUAL: %s", derivedKeyHex)
|
|
|
|
if derivedKeyHex != expectedKeyHex {
|
|
t.Errorf("Expected derived key bytes %s, got %s", expectedKeyHex, derivedKeyHex)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
|
|
// Test case 2
|
|
path = "m/83696968'/0'/1'"
|
|
t.Logf("Deriving key for path: %s", path)
|
|
derivedKeyBytes, err = DeriveChildKey(masterKey, path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive child key: %v", err)
|
|
}
|
|
|
|
derivedKeyHex = hex.EncodeToString(derivedKeyBytes)
|
|
expectedKeyHex = "503776919131758bb7de7beb6c0ae24894f4ec042c26032890c29359216e21ba"
|
|
t.Logf("EXPECTED: %s", expectedKeyHex)
|
|
t.Logf("ACTUAL: %s", derivedKeyHex)
|
|
|
|
if derivedKeyHex != expectedKeyHex {
|
|
t.Errorf("Expected derived key bytes %s, got %s", expectedKeyHex, derivedKeyHex)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestCase1 tests the first test vector from the BIP85 specification
|
|
func TestCase1(t *testing.T) {
|
|
logTestVector(t, "Test Case 1", "Basic entropy derivation with path m/83696968'/0'/0'")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
path := "m/83696968'/0'/0'"
|
|
t.Logf("Test path: %s", path)
|
|
entropy, err := DeriveBIP85Entropy(masterKey, path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive entropy: %v", err)
|
|
}
|
|
|
|
derivedEntropyHex := hex.EncodeToString(entropy)
|
|
expectedDerivedEntropy := "efecfbccffea313214232d29e71563d941229afb4338c21f9517c41aaa0d16f00b83d2a09ef747e7a64e8e2bd5a14869e693da66ce94ac2da570ab7ee48618f7"
|
|
t.Logf("EXPECTED: %s", expectedDerivedEntropy)
|
|
t.Logf("ACTUAL: %s", derivedEntropyHex)
|
|
|
|
if derivedEntropyHex != expectedDerivedEntropy {
|
|
t.Errorf("Expected derived entropy %s, got %s", expectedDerivedEntropy, derivedEntropyHex)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestCase2 tests the second test vector from the BIP85 specification
|
|
func TestCase2(t *testing.T) {
|
|
logTestVector(t, "Test Case 2", "Basic entropy derivation with path m/83696968'/0'/1'")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
path := "m/83696968'/0'/1'"
|
|
t.Logf("Test path: %s", path)
|
|
entropy, err := DeriveBIP85Entropy(masterKey, path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive entropy: %v", err)
|
|
}
|
|
|
|
derivedEntropyHex := hex.EncodeToString(entropy)
|
|
expectedDerivedEntropy := "70c6e3e8ebee8dc4c0dbba66076819bb8c09672527c4277ca8729532ad711872218f826919f6b67218adde99018a6df9095ab2b58d803b5b93ec9802085a690e"
|
|
t.Logf("EXPECTED: %s", expectedDerivedEntropy)
|
|
t.Logf("ACTUAL: %s", derivedEntropyHex)
|
|
|
|
if derivedEntropyHex != expectedDerivedEntropy {
|
|
t.Errorf("Expected derived entropy %s, got %s", expectedDerivedEntropy, derivedEntropyHex)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestBIP39_12EnglishWords tests the BIP39 12 English words test vector
|
|
func TestBIP39_12EnglishWords(t *testing.T) {
|
|
logTestVector(t, "BIP39 12 English Words", "Deriving a 12-word English BIP39 mnemonic")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
t.Logf("Path: m/83696968'/39'/0'/12'/0'")
|
|
t.Logf("Parameters: Language=English(0), Words=12, Index=0")
|
|
|
|
// BIP39 English 12 word mnemonic
|
|
entropy, err := DeriveBIP39Entropy(masterKey, 0, 12, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive BIP39 entropy: %v", err)
|
|
}
|
|
|
|
derivedEntropyHex := hex.EncodeToString(entropy)
|
|
expectedDerivedEntropy := "6250b68daf746d12a24d58b4787a714b"
|
|
t.Logf("EXPECTED ENTROPY: %s", expectedDerivedEntropy)
|
|
t.Logf("ACTUAL ENTROPY: %s", derivedEntropyHex)
|
|
|
|
if derivedEntropyHex != expectedDerivedEntropy {
|
|
t.Errorf("Expected derived entropy %s, got %s", expectedDerivedEntropy, derivedEntropyHex)
|
|
} else {
|
|
t.Logf("ENTROPY MATCH: PASS ✓")
|
|
}
|
|
|
|
// Convert entropy to mnemonic
|
|
mnemonic, err := bip39.NewMnemonic(entropy)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create mnemonic: %v", err)
|
|
}
|
|
|
|
expectedMnemonic := "girl mad pet galaxy egg matter matrix prison refuse sense ordinary nose"
|
|
t.Logf("EXPECTED MNEMONIC: %s", expectedMnemonic)
|
|
t.Logf("ACTUAL MNEMONIC: %s", mnemonic)
|
|
|
|
if mnemonic != expectedMnemonic {
|
|
t.Errorf("Expected mnemonic '%s', got '%s'", expectedMnemonic, mnemonic)
|
|
} else {
|
|
t.Logf("MNEMONIC MATCH: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestBIP39_18EnglishWords tests the BIP39 18 English words test vector
|
|
func TestBIP39_18EnglishWords(t *testing.T) {
|
|
logTestVector(t, "BIP39 18 English Words", "Deriving an 18-word English BIP39 mnemonic")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
t.Logf("Path: m/83696968'/39'/0'/18'/0'")
|
|
t.Logf("Parameters: Language=English(0), Words=18, Index=0")
|
|
|
|
// BIP39 English 18 word mnemonic
|
|
entropy, err := DeriveBIP39Entropy(masterKey, 0, 18, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive BIP39 entropy: %v", err)
|
|
}
|
|
|
|
derivedEntropyHex := hex.EncodeToString(entropy)
|
|
expectedDerivedEntropy := "938033ed8b12698449d4bbca3c853c66b293ea1b1ce9d9dc"
|
|
t.Logf("EXPECTED ENTROPY: %s", expectedDerivedEntropy)
|
|
t.Logf("ACTUAL ENTROPY: %s", derivedEntropyHex)
|
|
|
|
if derivedEntropyHex != expectedDerivedEntropy {
|
|
t.Errorf("Expected derived entropy %s, got %s", expectedDerivedEntropy, derivedEntropyHex)
|
|
} else {
|
|
t.Logf("ENTROPY MATCH: PASS ✓")
|
|
}
|
|
|
|
// Convert entropy to mnemonic
|
|
mnemonic, err := bip39.NewMnemonic(entropy)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create mnemonic: %v", err)
|
|
}
|
|
|
|
expectedMnemonic := "near account window bike charge season chef number sketch tomorrow excuse sniff circle vital hockey outdoor supply token"
|
|
t.Logf("EXPECTED MNEMONIC: %s", expectedMnemonic)
|
|
t.Logf("ACTUAL MNEMONIC: %s", mnemonic)
|
|
|
|
if mnemonic != expectedMnemonic {
|
|
t.Errorf("Expected mnemonic '%s', got '%s'", expectedMnemonic, mnemonic)
|
|
} else {
|
|
t.Logf("MNEMONIC MATCH: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestBIP39_24EnglishWords tests the BIP39 24 English words test vector
|
|
func TestBIP39_24EnglishWords(t *testing.T) {
|
|
logTestVector(t, "BIP39 24 English Words", "Deriving a 24-word English BIP39 mnemonic")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
t.Logf("Path: m/83696968'/39'/0'/24'/0'")
|
|
t.Logf("Parameters: Language=English(0), Words=24, Index=0")
|
|
|
|
// BIP39 English 24 word mnemonic
|
|
entropy, err := DeriveBIP39Entropy(masterKey, 0, 24, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive BIP39 entropy: %v", err)
|
|
}
|
|
|
|
derivedEntropyHex := hex.EncodeToString(entropy)
|
|
expectedDerivedEntropy := "ae131e2312cdc61331542efe0d1077bac5ea803adf24b313a4f0e48e9c51f37f"
|
|
t.Logf("EXPECTED ENTROPY: %s", expectedDerivedEntropy)
|
|
t.Logf("ACTUAL ENTROPY: %s", derivedEntropyHex)
|
|
|
|
if derivedEntropyHex != expectedDerivedEntropy {
|
|
t.Errorf("Expected derived entropy %s, got %s", expectedDerivedEntropy, derivedEntropyHex)
|
|
} else {
|
|
t.Logf("ENTROPY MATCH: PASS ✓")
|
|
}
|
|
|
|
// Convert entropy to mnemonic
|
|
mnemonic, err := bip39.NewMnemonic(entropy)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create mnemonic: %v", err)
|
|
}
|
|
|
|
expectedMnemonic := "puppy ocean match cereal symbol another shed magic wrap hammer bulb intact gadget divorce twin tonight reason outdoor destroy simple truth cigar social volcano"
|
|
t.Logf("EXPECTED MNEMONIC: %s", expectedMnemonic)
|
|
t.Logf("ACTUAL MNEMONIC: %s", mnemonic)
|
|
|
|
if mnemonic != expectedMnemonic {
|
|
t.Errorf("Expected mnemonic '%s', got '%s'", expectedMnemonic, mnemonic)
|
|
} else {
|
|
t.Logf("MNEMONIC MATCH: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestHD_WIF tests the WIF test vector
|
|
func TestHD_WIF(t *testing.T) {
|
|
logTestVector(t, "HD-Seed WIF", "Deriving a WIF-encoded private key for Bitcoin Core hdseed")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
t.Logf("Path: m/83696968'/2'/0'")
|
|
wif, err := DeriveWIFKey(masterKey, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive WIF key: %v", err)
|
|
}
|
|
|
|
expectedWIF := "Kzyv4uF39d4Jrw2W7UryTHwZr1zQVNk4dAFyqE6BuMrMh1Za7uhp"
|
|
t.Logf("EXPECTED WIF: %s", expectedWIF)
|
|
t.Logf("ACTUAL WIF: %s", wif)
|
|
|
|
if wif != expectedWIF {
|
|
t.Errorf("Expected WIF %s, got %s", expectedWIF, wif)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestXPRV tests the XPRV test vector
|
|
func TestXPRV(t *testing.T) {
|
|
logTestVector(t, "XPRV", "Deriving an extended private key (XPRV)")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
t.Logf("Path: m/83696968'/32'/0'")
|
|
derivedKey, err := DeriveXPRV(masterKey, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive XPRV: %v", err)
|
|
}
|
|
|
|
expectedXPRV := "xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX"
|
|
derivedXPRV := derivedKey.String()
|
|
t.Logf("EXPECTED XPRV: %s", expectedXPRV)
|
|
t.Logf("ACTUAL XPRV: %s", derivedXPRV)
|
|
|
|
if derivedXPRV != expectedXPRV {
|
|
t.Errorf("Expected XPRV %s, got %s", expectedXPRV, derivedXPRV)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestDRNG_SHAKE256 tests the BIP85-DRNG-SHAKE256 test vector
|
|
func TestDRNG_SHAKE256(t *testing.T) {
|
|
logTestVector(t, "DRNG-SHAKE256", "Testing the deterministic random number generator with SHAKE256")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Derive entropy for the DRNG
|
|
path := "m/83696968'/0'/0'"
|
|
t.Logf("Deriving entropy from path: %s", path)
|
|
entropy, err := DeriveBIP85Entropy(masterKey, path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive entropy: %v", err)
|
|
}
|
|
|
|
// Create DRNG
|
|
drng := NewBIP85DRNG(entropy)
|
|
|
|
// Read 80 bytes
|
|
t.Logf("Reading 80 bytes from DRNG")
|
|
buffer := make([]byte, 80)
|
|
n, err := drng.Read(buffer)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read from DRNG: %v", err)
|
|
}
|
|
if n != 80 {
|
|
t.Errorf("Expected to read 80 bytes, got %d", n)
|
|
}
|
|
|
|
hexOutput := hex.EncodeToString(buffer)
|
|
expectedOutput := "b78b1ee6b345eae6836c2d53d33c64cdaf9a696487be81b03e822dc84b3f1cd883d7559e53d175f243e4c349e822a957bbff9224bc5dde9492ef54e8a439f6bc8c7355b87a925a37ee405a7502991111"
|
|
t.Logf("EXPECTED: %s", expectedOutput)
|
|
t.Logf("ACTUAL: %s", hexOutput)
|
|
|
|
if !strings.EqualFold(hexOutput, expectedOutput) {
|
|
t.Errorf("Expected DRNG output %s, got %s", expectedOutput, hexOutput)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestDiceRolls tests the dice rolls application
|
|
func TestDiceRolls(t *testing.T) {
|
|
logTestVector(t, "Dice Rolls", "Generating deterministic dice rolls")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Derive 10 rolls of a 6-sided die
|
|
t.Logf("Path: m/83696968'/89101'/6'/10'/0'")
|
|
t.Logf("Parameters: Sides=6, Rolls=10, Index=0")
|
|
rolls, err := DeriveDiceRolls(masterKey, 6, 10, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive dice rolls: %v", err)
|
|
}
|
|
|
|
// Expected rolls from the specification
|
|
expectedRolls := []uint32{1, 0, 0, 2, 0, 1, 5, 5, 2, 4}
|
|
|
|
t.Logf("EXPECTED ROLLS: %v", expectedRolls)
|
|
t.Logf("ACTUAL ROLLS: %v", rolls)
|
|
|
|
if len(rolls) != len(expectedRolls) {
|
|
t.Errorf("Expected %d rolls, got %d", len(expectedRolls), len(rolls))
|
|
} else {
|
|
match := true
|
|
for i, expected := range expectedRolls {
|
|
if rolls[i] != expected {
|
|
t.Errorf("Roll %d: expected %d, got %d", i, expected, rolls[i])
|
|
match = false
|
|
}
|
|
}
|
|
if match {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// Show rolls in a more readable format
|
|
rollStr := ""
|
|
for _, roll := range rolls {
|
|
rollStr += fmt.Sprintf("%d,", roll)
|
|
}
|
|
rollStr = strings.TrimSuffix(rollStr, ",")
|
|
t.Logf("Roll sequence: %s", rollStr)
|
|
}
|
|
|
|
// TestPWDBase64 tests the Base64 password test vector
|
|
func TestPWDBase64(t *testing.T) {
|
|
logTestVector(t, "PWD Base64", "Deriving a Base64-encoded password")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Testing with the example from the BIP85 spec - 21 characters
|
|
t.Logf("Path: m/83696968'/707764'/21'/0'")
|
|
t.Logf("Parameters: Length=21, Index=0")
|
|
|
|
pwd, err := DeriveBase64Password(masterKey, 21, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive Base64 password: %v", err)
|
|
}
|
|
|
|
// The test vector from the BIP85 specification
|
|
expectedPwd := "dKLoepugzdVJvdL56ogNV"
|
|
|
|
t.Logf("EXPECTED PASSWORD: %s", expectedPwd)
|
|
t.Logf("ACTUAL PASSWORD: %s", pwd)
|
|
|
|
if pwd != expectedPwd {
|
|
t.Errorf("Expected password '%s', got '%s'", expectedPwd, pwd)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
|
|
t.Logf("Password length: %d characters", len(pwd))
|
|
}
|
|
|
|
// TestPWDBase85 tests the Base85 password test vector
|
|
func TestPWDBase85(t *testing.T) {
|
|
logTestVector(t, "PWD Base85", "Deriving a Base85-encoded password")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Testing with the example from the BIP85 spec - 12 characters
|
|
t.Logf("Path: m/83696968'/707785'/12'/0'")
|
|
t.Logf("Parameters: Length=12, Index=0")
|
|
|
|
pwd, err := DeriveBase85Password(masterKey, 12, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive Base85 password: %v", err)
|
|
}
|
|
|
|
// The test vector from the BIP85 specification
|
|
expectedPwd := "_s`{TW89)i4`"
|
|
|
|
t.Logf("EXPECTED PASSWORD: %s", expectedPwd)
|
|
t.Logf("ACTUAL PASSWORD: %s", pwd)
|
|
|
|
if pwd != expectedPwd {
|
|
t.Errorf("Expected password '%s', got '%s'", expectedPwd, pwd)
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
|
|
t.Logf("Password length: %d characters", len(pwd))
|
|
}
|
|
|
|
// TestInvalidParameters tests error conditions for parameter validation
|
|
func TestInvalidParameters(t *testing.T) {
|
|
logTestVector(t, "Invalid Parameters", "Testing error handling for invalid inputs")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Test cases for parameter validation
|
|
testCases := []struct {
|
|
name string
|
|
testFunc func() error
|
|
}{
|
|
{
|
|
name: "BIP39 invalid word count",
|
|
testFunc: func() error {
|
|
_, err := DeriveBIP39Entropy(masterKey, 0, 13, 0) // 13 is not valid (must be 12, 15, 18, 21, 24)
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Base64 password too short",
|
|
testFunc: func() error {
|
|
_, err := DeriveBase64Password(masterKey, 19, 0) // Min is 20
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Base64 password too long",
|
|
testFunc: func() error {
|
|
_, err := DeriveBase64Password(masterKey, 87, 0) // Max is 86
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Base85 password too short",
|
|
testFunc: func() error {
|
|
_, err := DeriveBase85Password(masterKey, 9, 0) // Min is 10
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Base85 password too long",
|
|
testFunc: func() error {
|
|
_, err := DeriveBase85Password(masterKey, 81, 0) // Max is 80
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Hex data too small",
|
|
testFunc: func() error {
|
|
_, err := DeriveHex(masterKey, 15, 0) // Min is 16
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Hex data too large",
|
|
testFunc: func() error {
|
|
_, err := DeriveHex(masterKey, 65, 0) // Max is 64
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Dice roll invalid sides",
|
|
testFunc: func() error {
|
|
_, err := DeriveDiceRolls(masterKey, 1, 10, 0) // Min sides is 2
|
|
return err
|
|
},
|
|
},
|
|
{
|
|
name: "Dice roll zero rolls",
|
|
testFunc: func() error {
|
|
_, err := DeriveDiceRolls(masterKey, 6, 0, 0) // Min rolls is 1
|
|
return err
|
|
},
|
|
},
|
|
}
|
|
|
|
// Run all validation test cases
|
|
for _, tc := range testCases {
|
|
t.Logf("Testing: %s", tc.name)
|
|
err := tc.testFunc()
|
|
if err == nil {
|
|
t.Errorf("Expected error for %s, but got nil", tc.name)
|
|
} else {
|
|
t.Logf("Got expected error: %v", err)
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestAdditionalDeriveHex tests additional hex derivation scenarios
|
|
func TestAdditionalDeriveHex(t *testing.T) {
|
|
logTestVector(t, "Additional Hex Derivation", "Testing hex data derivation with various lengths")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Test min size (16 bytes)
|
|
hexMinBytes, err := DeriveHex(masterKey, 16, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive 16-byte hex: %v", err)
|
|
}
|
|
t.Logf("16-byte hex: %s", hexMinBytes)
|
|
if len(hexMinBytes) != 32 { // 16 bytes = 32 hex chars
|
|
t.Errorf("Expected 32 hex chars (16 bytes), got %d chars", len(hexMinBytes))
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
|
|
// Test max size (64 bytes)
|
|
hexMaxBytes, err := DeriveHex(masterKey, 64, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive 64-byte hex: %v", err)
|
|
}
|
|
t.Logf("64-byte hex: %s", hexMaxBytes)
|
|
if len(hexMaxBytes) != 128 { // 64 bytes = 128 hex chars
|
|
t.Errorf("Expected 128 hex chars (64 bytes), got %d chars", len(hexMaxBytes))
|
|
} else {
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
|
|
// Test different index values
|
|
hex1, err := DeriveHex(masterKey, 32, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive hex with index 0: %v", err)
|
|
}
|
|
|
|
hex2, err := DeriveHex(masterKey, 32, 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive hex with index 1: %v", err)
|
|
}
|
|
|
|
t.Logf("Hex index 0: %s", hex1)
|
|
t.Logf("Hex index 1: %s", hex2)
|
|
|
|
if hex1 == hex2 {
|
|
t.Errorf("Expected different hex values for different indexes")
|
|
} else {
|
|
t.Logf("Different indexes produced different outputs: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestDRNGDifferentSizes tests the DRNG with different buffer sizes
|
|
func TestDRNGDifferentSizes(t *testing.T) {
|
|
logTestVector(t, "DRNG Different Sizes", "Testing the DRNG with different buffer sizes")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
path := "m/83696968'/0'/0'"
|
|
entropy, err := DeriveBIP85Entropy(masterKey, path)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive entropy: %v", err)
|
|
}
|
|
|
|
// Create DRNG
|
|
drng := NewBIP85DRNG(entropy)
|
|
|
|
// Test reading different sizes
|
|
for _, size := range []int{32, 64, 128, 256} {
|
|
t.Logf("Reading %d bytes from DRNG", size)
|
|
buffer := make([]byte, size)
|
|
n, err := drng.Read(buffer)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read %d bytes from DRNG: %v", size, err)
|
|
}
|
|
if n != size {
|
|
t.Errorf("Expected to read %d bytes, got %d", size, n)
|
|
} else {
|
|
t.Logf("Successfully read %d bytes: PASS ✓", size)
|
|
}
|
|
}
|
|
|
|
// Test deterministic behavior - two DRNGs with the same seed should produce the same output
|
|
drng1 := NewBIP85DRNG(entropy)
|
|
drng2 := NewBIP85DRNG(entropy)
|
|
|
|
buffer1 := make([]byte, 32)
|
|
buffer2 := make([]byte, 32)
|
|
|
|
_, err = drng1.Read(buffer1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read from first DRNG: %v", err)
|
|
}
|
|
|
|
_, err = drng2.Read(buffer2)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read from second DRNG: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(buffer1, buffer2) {
|
|
t.Errorf("Expected identical outputs from DRNGs with same seed")
|
|
} else {
|
|
t.Logf("DRNGs with same seed produced identical outputs: PASS ✓")
|
|
}
|
|
|
|
// Reading another 32 bytes should produce different output from the first read
|
|
buffer3 := make([]byte, 32)
|
|
_, err = drng1.Read(buffer3)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read second buffer from DRNG: %v", err)
|
|
}
|
|
|
|
if bytes.Equal(buffer1, buffer3) {
|
|
t.Errorf("Expected different outputs from sequential reads")
|
|
} else {
|
|
t.Logf("Sequential reads produced different outputs: PASS ✓")
|
|
}
|
|
}
|
|
|
|
// TestMasterKeyParsing tests parsing of different master key formats
|
|
func TestMasterKeyParsing(t *testing.T) {
|
|
logTestVector(t, "Master Key Parsing", "Testing parsing of master keys in different formats")
|
|
|
|
// Test valid master key
|
|
t.Logf("Testing valid master key")
|
|
_, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Errorf("Failed to parse valid master key: %v", err)
|
|
} else {
|
|
t.Logf("Valid master key parsed successfully: PASS ✓")
|
|
}
|
|
|
|
// Test invalid master key (wrong checksum)
|
|
t.Logf("Testing invalid master key (corrupted)")
|
|
invalidKey := "xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbXX"
|
|
_, err = ParseMasterKey(invalidKey)
|
|
if err == nil {
|
|
t.Errorf("Expected error for invalid master key, but got nil")
|
|
} else {
|
|
t.Logf("Got expected error for invalid master key: %v", err)
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
|
|
// Test testnet master key (tprv)
|
|
t.Logf("Testing testnet master key format")
|
|
testnetKey := "tprv8ZgxMBicQKsPeWHBt7a68nPnvgTnuDhUgDWC8wZCgA8GahrQ3f3uWpq7wE7Uc1dLBnCe1hhCZ886K6ND37memRDWqsA9HgSKDXtwh2Qxo6J"
|
|
testnetMasterKey, err := ParseMasterKey(testnetKey)
|
|
if err != nil {
|
|
t.Errorf("Failed to parse testnet master key: %v", err)
|
|
} else {
|
|
t.Logf("Testnet master key parsed successfully: PASS ✓")
|
|
|
|
// Test that XPRV derivation using a testnet master key produces a testnet XPRV
|
|
derivedKey, err := DeriveXPRV(testnetMasterKey, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive XPRV from testnet key: %v", err)
|
|
}
|
|
|
|
derivedKeyStr := derivedKey.String()
|
|
if !strings.HasPrefix(derivedKeyStr, "tprv") {
|
|
t.Errorf("Expected derived key to be testnet (tprv prefix), got: %s", derivedKeyStr)
|
|
} else {
|
|
t.Logf("Testnet XPRV derived successfully: %s", derivedKeyStr)
|
|
t.Logf("RESULT: PASS ✓")
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestDifferentPathFormats tests different path format expressions
|
|
func TestDifferentPathFormats(t *testing.T) {
|
|
logTestVector(t, "Path Formats", "Testing different path format expressions")
|
|
|
|
masterKey, err := ParseMasterKey(testMasterKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse master key: %v", err)
|
|
}
|
|
|
|
// Define equivalent paths in different formats
|
|
paths := []string{
|
|
"m/83696968'/0'/0'",
|
|
"m/83696968h/0h/0h",
|
|
"/83696968'/0'/0'",
|
|
"83696968'/0'/0'",
|
|
}
|
|
|
|
var results [][]byte
|
|
|
|
// Derive entropy using each path
|
|
for i, path := range paths {
|
|
t.Logf("Testing path format %d: %s", i+1, path)
|
|
entropy, err := DeriveBIP85Entropy(masterKey, path)
|
|
if err != nil {
|
|
t.Errorf("Failed to derive entropy with path %s: %v", path, err)
|
|
continue
|
|
}
|
|
|
|
results = append(results, entropy)
|
|
t.Logf("Derivation succeeded: PASS ✓")
|
|
}
|
|
|
|
// Verify all results are the same
|
|
for i := 1; i < len(results); i++ {
|
|
if !bytes.Equal(results[0], results[i]) {
|
|
t.Errorf("Path %s produced different entropy than path %s", paths[0], paths[i])
|
|
}
|
|
}
|
|
|
|
if len(results) > 1 {
|
|
t.Logf("All equivalent path formats produced the same entropy: PASS ✓")
|
|
}
|
|
}
|