mirror of
https://github.com/luxfi/go-bip39.git
synced 2026-09-24 00:24:32 +00:00
Initial commit
This commit is contained in:
+137
@@ -0,0 +1,137 @@
|
||||
### Go ###
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
### GoLand ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### GoLand Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
# Azure Toolkit for IntelliJ plugin
|
||||
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
|
||||
.idea/**/azureSettings.xml
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/goland,go
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/go-bip39.iml" filepath="$PROJECT_DIR$/.idea/go-bip39.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,25 @@
|
||||
# GO-BIP39
|
||||
This repository contains a local copy of the original ``github.com/tyler-smith/go-bip39`` library.
|
||||
|
||||
# ⚠️ Important Notice
|
||||
The original [go-bip39](https://github.com/tyler-smith/go-bip39) library by tyler-smith has been removed from GitHub.
|
||||
To ensure continuity and compatibility for existing projects, this repository provides a preserved version of the library based on a previously cached local copy.
|
||||
|
||||
# Usage
|
||||
|
||||
There are two ways to use this fork in your project:
|
||||
|
||||
1. Replace the original path in ``go.mod``
|
||||
```
|
||||
replace github.com/tyler-smith/go-bip39 => github.com/luxfi/go-bip39 v1.1.0
|
||||
```
|
||||
|
||||
2. Import directly via ``go get``
|
||||
You can also import this fork directly using:
|
||||
|
||||
```bash
|
||||
go get github.com/luxfi/go-bip39
|
||||
```
|
||||
|
||||
# Original README
|
||||
The original README from the now-deleted repository is preserved in this repo as [tyler-smith.README.md](tyler-smith.README.md)
|
||||
@@ -0,0 +1,360 @@
|
||||
// Package bip39 is the Golang implementation of the BIP39 spec.
|
||||
//
|
||||
// The official BIP39 spec can be found at
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
||||
package bip39
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/gojuukaze/go-bip39/wordlists"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
var (
|
||||
// Some bitwise operands for working with big.Ints
|
||||
last11BitsMask = big.NewInt(2047)
|
||||
shift11BitsMask = big.NewInt(2048)
|
||||
bigOne = big.NewInt(1)
|
||||
bigTwo = big.NewInt(2)
|
||||
|
||||
// used to isolate the checksum bits from the entropy+checksum byte array
|
||||
wordLengthChecksumMasksMapping = map[int]*big.Int{
|
||||
12: big.NewInt(15),
|
||||
15: big.NewInt(31),
|
||||
18: big.NewInt(63),
|
||||
21: big.NewInt(127),
|
||||
24: big.NewInt(255),
|
||||
}
|
||||
// used to use only the desired x of 8 available checksum bits.
|
||||
// 256 bit (word length 24) requires all 8 bits of the checksum,
|
||||
// and thus no shifting is needed for it (we would get a divByZero crash if we did)
|
||||
wordLengthChecksumShiftMapping = map[int]*big.Int{
|
||||
12: big.NewInt(16),
|
||||
15: big.NewInt(8),
|
||||
18: big.NewInt(4),
|
||||
21: big.NewInt(2),
|
||||
}
|
||||
|
||||
// wordList is the set of words to use
|
||||
wordList []string
|
||||
|
||||
// wordMap is a reverse lookup map for wordList
|
||||
wordMap map[string]int
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidMnemonic is returned when trying to use a malformed mnemonic.
|
||||
ErrInvalidMnemonic = errors.New("Invalid mnenomic")
|
||||
|
||||
// ErrEntropyLengthInvalid is returned when trying to use an entropy set with
|
||||
// an invalid size.
|
||||
ErrEntropyLengthInvalid = errors.New("Entropy length must be [128, 256] and a multiple of 32")
|
||||
|
||||
// ErrValidatedSeedLengthMismatch is returned when a validated seed is not the
|
||||
// same size as the given seed. This should never happen is present only as a
|
||||
// sanity assertion.
|
||||
ErrValidatedSeedLengthMismatch = errors.New("Seed length does not match validated seed length")
|
||||
|
||||
// ErrChecksumIncorrect is returned when entropy has the incorrect checksum.
|
||||
ErrChecksumIncorrect = errors.New("Checksum incorrect")
|
||||
)
|
||||
|
||||
func init() {
|
||||
SetWordList(wordlists.English)
|
||||
}
|
||||
|
||||
// SetWordList sets the list of words to use for mnemonics. Currently the list
|
||||
// that is set is used package-wide.
|
||||
func SetWordList(list []string) {
|
||||
wordList = list
|
||||
wordMap = map[string]int{}
|
||||
for i, v := range wordList {
|
||||
wordMap[v] = i
|
||||
}
|
||||
}
|
||||
|
||||
// GetWordList gets the list of words to use for mnemonics.
|
||||
func GetWordList() []string {
|
||||
return wordList
|
||||
}
|
||||
|
||||
// GetWordIndex gets word index in wordMap.
|
||||
func GetWordIndex(word string) (int, bool) {
|
||||
idx, ok := wordMap[word]
|
||||
return idx, ok
|
||||
}
|
||||
|
||||
// NewEntropy will create random entropy bytes
|
||||
// so long as the requested size bitSize is an appropriate size.
|
||||
//
|
||||
// bitSize has to be a multiple 32 and be within the inclusive range of {128, 256}
|
||||
func NewEntropy(bitSize int) ([]byte, error) {
|
||||
err := validateEntropyBitSize(bitSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entropy := make([]byte, bitSize/8)
|
||||
_, err = rand.Read(entropy)
|
||||
return entropy, err
|
||||
}
|
||||
|
||||
// EntropyFromMnemonic takes a mnemonic generated by this library,
|
||||
// and returns the input entropy used to generate the given mnemonic.
|
||||
// An error is returned if the given mnemonic is invalid.
|
||||
func EntropyFromMnemonic(mnemonic string) ([]byte, error) {
|
||||
mnemonicSlice, isValid := splitMnemonicWords(mnemonic)
|
||||
if !isValid {
|
||||
return nil, ErrInvalidMnemonic
|
||||
}
|
||||
|
||||
// Decode the words into a big.Int.
|
||||
b := big.NewInt(0)
|
||||
for _, v := range mnemonicSlice {
|
||||
index, found := wordMap[v]
|
||||
if found == false {
|
||||
return nil, fmt.Errorf("word `%v` not found in reverse map", v)
|
||||
}
|
||||
var wordBytes [2]byte
|
||||
binary.BigEndian.PutUint16(wordBytes[:], uint16(index))
|
||||
b = b.Mul(b, shift11BitsMask)
|
||||
b = b.Or(b, big.NewInt(0).SetBytes(wordBytes[:]))
|
||||
}
|
||||
|
||||
// Build and add the checksum to the big.Int.
|
||||
checksum := big.NewInt(0)
|
||||
checksumMask := wordLengthChecksumMasksMapping[len(mnemonicSlice)]
|
||||
checksum = checksum.And(b, checksumMask)
|
||||
|
||||
b.Div(b, big.NewInt(0).Add(checksumMask, bigOne))
|
||||
|
||||
// The entropy is the underlying bytes of the big.Int. Any upper bytes of
|
||||
// all 0's are not returned so we pad the beginning of the slice with empty
|
||||
// bytes if necessary.
|
||||
entropy := b.Bytes()
|
||||
entropy = padByteSlice(entropy, len(mnemonicSlice)/3*4)
|
||||
|
||||
// Generate the checksum and compare with the one we got from the mneomnic.
|
||||
entropyChecksumBytes := computeChecksum(entropy)
|
||||
entropyChecksum := big.NewInt(int64(entropyChecksumBytes[0]))
|
||||
if l := len(mnemonicSlice); l != 24 {
|
||||
checksumShift := wordLengthChecksumShiftMapping[l]
|
||||
entropyChecksum.Div(entropyChecksum, checksumShift)
|
||||
}
|
||||
|
||||
if checksum.Cmp(entropyChecksum) != 0 {
|
||||
return nil, ErrChecksumIncorrect
|
||||
}
|
||||
|
||||
return entropy, nil
|
||||
}
|
||||
|
||||
// NewMnemonic will return a string consisting of the mnemonic words for
|
||||
// the given entropy.
|
||||
// If the provide entropy is invalid, an error will be returned.
|
||||
func NewMnemonic(entropy []byte) (string, error) {
|
||||
// Compute some lengths for convenience.
|
||||
entropyBitLength := len(entropy) * 8
|
||||
checksumBitLength := entropyBitLength / 32
|
||||
sentenceLength := (entropyBitLength + checksumBitLength) / 11
|
||||
|
||||
// Validate that the requested size is supported.
|
||||
err := validateEntropyBitSize(entropyBitLength)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Add checksum to entropy.
|
||||
entropy = addChecksum(entropy)
|
||||
|
||||
// Break entropy up into sentenceLength chunks of 11 bits.
|
||||
// For each word AND mask the rightmost 11 bits and find the word at that index.
|
||||
// Then bitshift entropy 11 bits right and repeat.
|
||||
// Add to the last empty slot so we can work with LSBs instead of MSB.
|
||||
|
||||
// Entropy as an int so we can bitmask without worrying about bytes slices.
|
||||
entropyInt := new(big.Int).SetBytes(entropy)
|
||||
|
||||
// Slice to hold words in.
|
||||
words := make([]string, sentenceLength)
|
||||
|
||||
// Throw away big.Int for AND masking.
|
||||
word := big.NewInt(0)
|
||||
|
||||
for i := sentenceLength - 1; i >= 0; i-- {
|
||||
// Get 11 right most bits and bitshift 11 to the right for next time.
|
||||
word.And(entropyInt, last11BitsMask)
|
||||
entropyInt.Div(entropyInt, shift11BitsMask)
|
||||
|
||||
// Get the bytes representing the 11 bits as a 2 byte slice.
|
||||
wordBytes := padByteSlice(word.Bytes(), 2)
|
||||
|
||||
// Convert bytes to an index and add that word to the list.
|
||||
words[i] = wordList[binary.BigEndian.Uint16(wordBytes)]
|
||||
}
|
||||
|
||||
return strings.Join(words, " "), nil
|
||||
}
|
||||
|
||||
// MnemonicToByteArray takes a mnemonic string and turns it into a byte array
|
||||
// suitable for creating another mnemonic.
|
||||
// An error is returned if the mnemonic is invalid.
|
||||
func MnemonicToByteArray(mnemonic string, raw ...bool) ([]byte, error) {
|
||||
var (
|
||||
mnemonicSlice = strings.Split(mnemonic, " ")
|
||||
entropyBitSize = len(mnemonicSlice) * 11
|
||||
checksumBitSize = entropyBitSize % 32
|
||||
fullByteSize = (entropyBitSize-checksumBitSize)/8 + 1
|
||||
checksumByteSize = fullByteSize - (fullByteSize % 4)
|
||||
)
|
||||
|
||||
// Pre validate that the mnemonic is well formed and only contains words that
|
||||
// are present in the word list.
|
||||
if !IsMnemonicValid(mnemonic) {
|
||||
return nil, ErrInvalidMnemonic
|
||||
}
|
||||
|
||||
// Convert word indices to a big.Int representing the entropy.
|
||||
checksummedEntropy := big.NewInt(0)
|
||||
modulo := big.NewInt(2048)
|
||||
for _, v := range mnemonicSlice {
|
||||
index := big.NewInt(int64(wordMap[v]))
|
||||
checksummedEntropy.Mul(checksummedEntropy, modulo)
|
||||
checksummedEntropy.Add(checksummedEntropy, index)
|
||||
}
|
||||
|
||||
// Calculate the unchecksummed entropy so we can validate that the checksum is
|
||||
// correct.
|
||||
checksumModulo := big.NewInt(0).Exp(bigTwo, big.NewInt(int64(checksumBitSize)), nil)
|
||||
rawEntropy := big.NewInt(0).Div(checksummedEntropy, checksumModulo)
|
||||
|
||||
// Convert big.Ints to byte padded byte slices.
|
||||
rawEntropyBytes := padByteSlice(rawEntropy.Bytes(), checksumByteSize)
|
||||
checksummedEntropyBytes := padByteSlice(checksummedEntropy.Bytes(), fullByteSize)
|
||||
|
||||
// Validate that the checksum is correct.
|
||||
newChecksummedEntropyBytes := padByteSlice(addChecksum(rawEntropyBytes), fullByteSize)
|
||||
if !compareByteSlices(checksummedEntropyBytes, newChecksummedEntropyBytes) {
|
||||
return nil, ErrChecksumIncorrect
|
||||
}
|
||||
|
||||
if len(raw) > 0 && raw[0] {
|
||||
return rawEntropyBytes, nil
|
||||
}
|
||||
|
||||
return checksummedEntropyBytes, nil
|
||||
}
|
||||
|
||||
// NewSeedWithErrorChecking creates a hashed seed output given the mnemonic string and a password.
|
||||
// An error is returned if the mnemonic is not convertible to a byte array.
|
||||
func NewSeedWithErrorChecking(mnemonic string, password string) ([]byte, error) {
|
||||
_, err := MnemonicToByteArray(mnemonic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSeed(mnemonic, password), nil
|
||||
}
|
||||
|
||||
// NewSeed creates a hashed seed output given a provided string and password.
|
||||
// No checking is performed to validate that the string provided is a valid mnemonic.
|
||||
func NewSeed(mnemonic string, password string) []byte {
|
||||
return pbkdf2.Key([]byte(mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New)
|
||||
}
|
||||
|
||||
// IsMnemonicValid attempts to verify that the provided mnemonic is valid.
|
||||
// Validity is determined by both the number of words being appropriate,
|
||||
// and that all the words in the mnemonic are present in the word list.
|
||||
func IsMnemonicValid(mnemonic string) bool {
|
||||
_, err := EntropyFromMnemonic(mnemonic)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Appends to data the first (len(data) / 32)bits of the result of sha256(data)
|
||||
// Currently only supports data up to 32 bytes
|
||||
func addChecksum(data []byte) []byte {
|
||||
// Get first byte of sha256
|
||||
hash := computeChecksum(data)
|
||||
firstChecksumByte := hash[0]
|
||||
|
||||
// len() is in bytes so we divide by 4
|
||||
checksumBitLength := uint(len(data) / 4)
|
||||
|
||||
// For each bit of check sum we want we shift the data one the left
|
||||
// and then set the (new) right most bit equal to checksum bit at that index
|
||||
// staring from the left
|
||||
dataBigInt := new(big.Int).SetBytes(data)
|
||||
for i := uint(0); i < checksumBitLength; i++ {
|
||||
// Bitshift 1 left
|
||||
dataBigInt.Mul(dataBigInt, bigTwo)
|
||||
|
||||
// Set rightmost bit if leftmost checksum bit is set
|
||||
if uint8(firstChecksumByte&(1<<(7-i))) > 0 {
|
||||
dataBigInt.Or(dataBigInt, bigOne)
|
||||
}
|
||||
}
|
||||
|
||||
return dataBigInt.Bytes()
|
||||
}
|
||||
|
||||
func computeChecksum(data []byte) []byte {
|
||||
hasher := sha256.New()
|
||||
hasher.Write(data)
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
// validateEntropyBitSize ensures that entropy is the correct size for being a
|
||||
// mnemonic.
|
||||
func validateEntropyBitSize(bitSize int) error {
|
||||
if (bitSize%32) != 0 || bitSize < 128 || bitSize > 256 {
|
||||
return ErrEntropyLengthInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// padByteSlice returns a byte slice of the given size with contents of the
|
||||
// given slice left padded and any empty spaces filled with 0's.
|
||||
func padByteSlice(slice []byte, length int) []byte {
|
||||
offset := length - len(slice)
|
||||
if offset <= 0 {
|
||||
return slice
|
||||
}
|
||||
newSlice := make([]byte, length)
|
||||
copy(newSlice[offset:], slice)
|
||||
return newSlice
|
||||
}
|
||||
|
||||
// compareByteSlices returns true of the byte slices have equal contents and
|
||||
// returns false otherwise.
|
||||
func compareByteSlices(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func splitMnemonicWords(mnemonic string) ([]string, bool) {
|
||||
// Create a list of all the words in the mnemonic sentence
|
||||
words := strings.Fields(mnemonic)
|
||||
|
||||
// Get num of words
|
||||
numOfWords := len(words)
|
||||
|
||||
// The number of words should be 12, 15, 18, 21 or 24
|
||||
if numOfWords%3 != 0 || numOfWords < 12 || numOfWords > 24 {
|
||||
return nil, false
|
||||
}
|
||||
return words, true
|
||||
}
|
||||
+492
@@ -0,0 +1,492 @@
|
||||
package bip39
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/gojuukaze/go-bip39/wordlists"
|
||||
)
|
||||
|
||||
type vector struct {
|
||||
entropy string
|
||||
mnemonic string
|
||||
seed string
|
||||
}
|
||||
|
||||
func TestGetWordList(t *testing.T) {
|
||||
assertEqualStringSlices(t, wordlists.English, GetWordList())
|
||||
}
|
||||
|
||||
func TestGetWordIndex(t *testing.T) {
|
||||
for expectedIdx, word := range wordList {
|
||||
actualIdx, ok := GetWordIndex(word)
|
||||
assertTrue(t, ok)
|
||||
assertEqual(t, actualIdx, expectedIdx)
|
||||
}
|
||||
|
||||
for _, word := range []string{"a", "set", "of", "invalid", "words"} {
|
||||
actualIdx, ok := GetWordIndex(word)
|
||||
assertFalse(t, ok)
|
||||
assertEqual(t, actualIdx, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewMnemonic(t *testing.T) {
|
||||
for _, vector := range testVectors() {
|
||||
entropy, err := hex.DecodeString(vector.entropy)
|
||||
assertNil(t, err)
|
||||
|
||||
mnemonic, err := NewMnemonic(entropy)
|
||||
assertNil(t, err)
|
||||
assertEqualString(t, vector.mnemonic, mnemonic)
|
||||
|
||||
_, err = NewSeedWithErrorChecking(mnemonic, "TREZOR")
|
||||
assertNil(t, err)
|
||||
|
||||
seed := NewSeed(mnemonic, "TREZOR")
|
||||
assertEqualString(t, vector.seed, hex.EncodeToString(seed))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewMnemonicInvalidEntropy(t *testing.T) {
|
||||
_, err := NewMnemonic([]byte{})
|
||||
assertNotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewSeedWithErrorCheckingInvalidMnemonics(t *testing.T) {
|
||||
for _, vector := range badMnemonicSentences() {
|
||||
_, err := NewSeedWithErrorChecking(vector.mnemonic, "TREZOR")
|
||||
assertNotNil(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsMnemonicValid(t *testing.T) {
|
||||
for _, vector := range badMnemonicSentences() {
|
||||
assertFalse(t, IsMnemonicValid(vector.mnemonic))
|
||||
}
|
||||
|
||||
for _, vector := range testVectors() {
|
||||
assertTrue(t, IsMnemonicValid(vector.mnemonic))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMnemonicToByteArrayInvalidMnemonic(t *testing.T) {
|
||||
for _, vector := range badMnemonicSentences() {
|
||||
_, err := MnemonicToByteArray(vector.mnemonic)
|
||||
assertNotNil(t, err)
|
||||
}
|
||||
|
||||
_, err := MnemonicToByteArray("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon yellow")
|
||||
assertNotNil(t, err)
|
||||
assertEqual(t, err, ErrInvalidMnemonic)
|
||||
}
|
||||
|
||||
func TestNewEntropy(t *testing.T) {
|
||||
// Good tests.
|
||||
for i := 128; i <= 256; i += 32 {
|
||||
_, err := NewEntropy(i)
|
||||
assertNil(t, err)
|
||||
}
|
||||
// Bad Values
|
||||
for i := 0; i <= 256; i++ {
|
||||
if i%8 != 0 {
|
||||
_, err := NewEntropy(i)
|
||||
assertNotNil(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMnemonicToByteArrayForDifferentArrayLangths(t *testing.T) {
|
||||
max := 1000
|
||||
for i := 0; i < max; i++ {
|
||||
//16, 20, 24, 28, 32
|
||||
length := 16 + (i%5)*4
|
||||
seed := make([]byte, length)
|
||||
if n, err := rand.Read(seed); err != nil {
|
||||
t.Errorf("%v", err)
|
||||
} else if n != length {
|
||||
t.Errorf("Wrong number of bytes read: %d", n)
|
||||
}
|
||||
|
||||
mnemonic, err := NewMnemonic(seed)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
_, err = MnemonicToByteArray(mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("Failed for %x - %v", seed, mnemonic)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestPadByteSlice(t *testing.T) {
|
||||
assertEqualByteSlices(t, []byte{0}, padByteSlice([]byte{}, 1))
|
||||
assertEqualByteSlices(t, []byte{0, 1}, padByteSlice([]byte{1}, 2))
|
||||
assertEqualByteSlices(t, []byte{1, 1}, padByteSlice([]byte{1, 1}, 2))
|
||||
assertEqualByteSlices(t, []byte{1, 1, 1}, padByteSlice([]byte{1, 1, 1}, 2))
|
||||
}
|
||||
|
||||
func TestCompareByteSlices(t *testing.T) {
|
||||
assertTrue(t, compareByteSlices([]byte{}, []byte{}))
|
||||
assertTrue(t, compareByteSlices([]byte{1}, []byte{1}))
|
||||
assertFalse(t, compareByteSlices([]byte{1}, []byte{0}))
|
||||
assertFalse(t, compareByteSlices([]byte{1}, []byte{}))
|
||||
assertFalse(t, compareByteSlices([]byte{1}, nil))
|
||||
}
|
||||
|
||||
func TestMnemonicToByteArrayForZeroLeadingSeeds(t *testing.T) {
|
||||
ms := []string{
|
||||
"00000000000000000000000000000000",
|
||||
"00a84c51041d49acca66e6160c1fa999",
|
||||
"00ca45df1673c76537a2020bfed1dafd",
|
||||
"0019d5871c7b81fd83d474ef1c1e1dae",
|
||||
"00dcb021afb35ffcdd1d032d2056fc86",
|
||||
"0062be7bd09a27288b6cf0eb565ec739",
|
||||
"00dc705b5efa0adf25b9734226ba60d4",
|
||||
"0017747418d54c6003fa64fade83374b",
|
||||
"000d44d3ee7c3dfa45e608c65384431b",
|
||||
"008241c1ef976b0323061affe5bf24b9",
|
||||
"00a6aec77e4d16bea80b50a34991aaba",
|
||||
"0011527b8c6ddecb9d0c20beccdeb58d",
|
||||
"001c938c503c8f5a2bba2248ff621546",
|
||||
"0002f90aaf7a8327698f0031b6317c36",
|
||||
"00bff43071ed7e07f77b14f615993bac",
|
||||
"00da143e00ef17fc63b6fb22dcc2c326",
|
||||
"00ffc6764fb32a354cab1a3ddefb015d",
|
||||
"0062ef47e0985e8953f24760b7598cdd",
|
||||
"003bf9765064f71d304908d906c065f5",
|
||||
"00993851503471439d154b3613947474",
|
||||
"007ad0ffe9eae753a483a76af06dfa67",
|
||||
"00091824db9ec19e663bee51d64c83cc",
|
||||
"00f48ac621f7e3cb39b2012ac3121543",
|
||||
"0072917415cdca24dfa66c4a92c885b4",
|
||||
"0027ced2b279ea8a91d29364487cdbf4",
|
||||
"00b9c0d37fb10ba272e55842ad812583",
|
||||
"004b3d0d2b9285946c687a5350479c8c",
|
||||
"00c7c12a37d3a7f8c1532b17c89b724c",
|
||||
"00f400c5545f06ae17ad00f3041e4e26",
|
||||
"001e290be10df4d209f247ac5878662b",
|
||||
"00bf0f74568e582a7dd1ee64f792ec8b",
|
||||
"00d2e43ecde6b72b847db1539ed89e23",
|
||||
"00cecba6678505bb7bfec8ed307251f6",
|
||||
"000aeed1a9edcbb4bc88f610d3ce84eb",
|
||||
"00d06206aadfc25c2b21805d283f15ae",
|
||||
"00a31789a2ab2d54f8fadd5331010287",
|
||||
"003493c5f520e8d5c0483e895a121dc9",
|
||||
"004706112800b76001ece2e268bc830e",
|
||||
"00ab31e28bb5305be56e38337dbfa486",
|
||||
"006872fe85df6b0fa945248e6f9379d1",
|
||||
"00717e5e375da6934e3cfdf57edaf3bd",
|
||||
"007f1b46e7b9c4c76e77c434b9bccd6b",
|
||||
"00dc93735aa35def3b9a2ff676560205",
|
||||
"002cd5dcd881a49c7b87714c6a570a76",
|
||||
"0013b5af9e13fac87e0c505686cfb6bf",
|
||||
"007ab1ec9526b0bc04b64ae65fd42631",
|
||||
"00abb4e11d8385c1cca905a6a65e9144",
|
||||
"00574fc62a0501ad8afada2e246708c3",
|
||||
"005207e0a815bb2da6b4c35ec1f2bf52",
|
||||
"00f3460f136fb9700080099cbd62bc18",
|
||||
"007a591f204c03ca7b93981237112526",
|
||||
"00cfe0befd428f8e5f83a5bfc801472e",
|
||||
"00987551ac7a879bf0c09b8bc474d9af",
|
||||
"00cadd3ce3d78e49fbc933a85682df3f",
|
||||
"00bfbf2e346c855ccc360d03281455a1",
|
||||
"004cdf55d429d028f715544ce22d4f31",
|
||||
"0075c84a7d15e0ac85e1e41025eed23b",
|
||||
"00807dddd61f71725d336cab844d2cb5",
|
||||
"00422f21b77fe20e367467ed98c18410",
|
||||
"00b44d0ac622907119c626c850a462fd",
|
||||
"00363f5e7f22fc49f3cd662a28956563",
|
||||
"000fe5837e68397bbf58db9f221bdc4e",
|
||||
"0056af33835c888ef0c22599686445d3",
|
||||
"00790a8647fd3dfb38b7e2b6f578f2c6",
|
||||
"00da8d9009675cb7beec930e263014fb",
|
||||
"00d4b384540a5bb54aa760edaa4fb2fe",
|
||||
"00be9b1479ed680fdd5d91a41eb926d0",
|
||||
"009182347502af97077c40a6e74b4b5c",
|
||||
"00f5c90ee1c67fa77fd821f8e9fab4f1",
|
||||
"005568f9a2dd6b0c0cc2f5ba3d9cac38",
|
||||
"008b481f8678577d9cf6aa3f6cd6056b",
|
||||
"00c4323ece5e4fe3b6cd4c5c932931af",
|
||||
"009791f7550c3798c5a214cb2d0ea773",
|
||||
"008a7baab22481f0ad8167dd9f90d55c",
|
||||
"00f0e601519aafdc8ff94975e64c946d",
|
||||
"0083b61e0daa9219df59d697c270cd31",
|
||||
}
|
||||
|
||||
for _, m := range ms {
|
||||
seed, _ := hex.DecodeString(m)
|
||||
|
||||
mnemonic, err := NewMnemonic(seed)
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
}
|
||||
|
||||
_, err = MnemonicToByteArray(mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("Failed for %x - %v", seed, mnemonic)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestEntropyFromMnemonic128(t *testing.T) {
|
||||
testEntropyFromMnemonic(t, 128)
|
||||
}
|
||||
|
||||
func TestEntropyFromMnemonic160(t *testing.T) {
|
||||
testEntropyFromMnemonic(t, 160)
|
||||
}
|
||||
|
||||
func TestEntropyFromMnemonic192(t *testing.T) {
|
||||
testEntropyFromMnemonic(t, 192)
|
||||
}
|
||||
|
||||
func TestEntropyFromMnemonic224(t *testing.T) {
|
||||
testEntropyFromMnemonic(t, 224)
|
||||
}
|
||||
|
||||
func TestEntropyFromMnemonic256(t *testing.T) {
|
||||
testEntropyFromMnemonic(t, 256)
|
||||
}
|
||||
|
||||
func TestEntropyFromMnemonicInvalidChecksum(t *testing.T) {
|
||||
_, err := EntropyFromMnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon yellow")
|
||||
assertEqual(t, ErrChecksumIncorrect, err)
|
||||
}
|
||||
|
||||
func TestEntropyFromMnemonicInvalidMnemonicSize(t *testing.T) {
|
||||
for _, mnemonic := range []string{
|
||||
"a a a a a a a a a a a a a a a a a a a a a a a a a", // Too many words
|
||||
"a", // Too few
|
||||
"a a a a a a a a a a a a a a", // Not multiple of 3
|
||||
} {
|
||||
_, err := EntropyFromMnemonic(mnemonic)
|
||||
assertEqual(t, ErrInvalidMnemonic, err)
|
||||
}
|
||||
}
|
||||
|
||||
func testEntropyFromMnemonic(t *testing.T, bitSize int) {
|
||||
for i := 0; i < 512; i++ {
|
||||
expectedEntropy, err := NewEntropy(bitSize)
|
||||
assertNil(t, err)
|
||||
assertTrue(t, len(expectedEntropy) != 0)
|
||||
|
||||
mnemonic, err := NewMnemonic(expectedEntropy)
|
||||
assertNil(t, err)
|
||||
assertTrue(t, len(mnemonic) != 0)
|
||||
|
||||
actualEntropy, err := EntropyFromMnemonic(mnemonic)
|
||||
assertNil(t, err)
|
||||
assertEqualByteSlices(t, expectedEntropy, actualEntropy)
|
||||
}
|
||||
}
|
||||
|
||||
func testVectors() []vector {
|
||||
return []vector{
|
||||
{
|
||||
entropy: "00000000000000000000000000000000",
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
seed: "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
|
||||
},
|
||||
{
|
||||
entropy: "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow",
|
||||
seed: "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607",
|
||||
},
|
||||
{
|
||||
entropy: "80808080808080808080808080808080",
|
||||
mnemonic: "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
|
||||
seed: "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8",
|
||||
},
|
||||
{
|
||||
entropy: "ffffffffffffffffffffffffffffffff",
|
||||
mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
|
||||
seed: "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069",
|
||||
},
|
||||
{
|
||||
entropy: "000000000000000000000000000000000000000000000000",
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
|
||||
seed: "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa",
|
||||
},
|
||||
{
|
||||
entropy: "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
mnemonic: "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
|
||||
seed: "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c392d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd",
|
||||
},
|
||||
{
|
||||
entropy: "808080808080808080808080808080808080808080808080",
|
||||
mnemonic: "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
|
||||
seed: "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65",
|
||||
},
|
||||
{
|
||||
entropy: "ffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",
|
||||
seed: "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528",
|
||||
},
|
||||
{
|
||||
entropy: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
|
||||
seed: "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8",
|
||||
},
|
||||
{
|
||||
entropy: "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
mnemonic: "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title",
|
||||
seed: "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87",
|
||||
},
|
||||
{
|
||||
entropy: "8080808080808080808080808080808080808080808080808080808080808080",
|
||||
mnemonic: "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless",
|
||||
seed: "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f",
|
||||
},
|
||||
{
|
||||
entropy: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote",
|
||||
seed: "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad",
|
||||
},
|
||||
{
|
||||
entropy: "77c2b00716cec7213839159e404db50d",
|
||||
mnemonic: "jelly better achieve collect unaware mountain thought cargo oxygen act hood bridge",
|
||||
seed: "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f672a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff",
|
||||
},
|
||||
{
|
||||
entropy: "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b",
|
||||
mnemonic: "renew stay biology evidence goat welcome casual join adapt armor shuffle fault little machine walk stumble urge swap",
|
||||
seed: "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941c69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5",
|
||||
},
|
||||
{
|
||||
entropy: "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982",
|
||||
mnemonic: "dignity pass list indicate nasty swamp pool script soccer toe leaf photo multiply desk host tomato cradle drill spread actor shine dismiss champion exotic",
|
||||
seed: "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da20af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67",
|
||||
},
|
||||
{
|
||||
entropy: "0460ef47585604c5660618db2e6a7e7f",
|
||||
mnemonic: "afford alter spike radar gate glance object seek swamp infant panel yellow",
|
||||
seed: "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41f8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4",
|
||||
},
|
||||
{
|
||||
entropy: "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f",
|
||||
mnemonic: "indicate race push merry suffer human cruise dwarf pole review arch keep canvas theme poem divorce alter left",
|
||||
seed: "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349dbe2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba",
|
||||
},
|
||||
{
|
||||
entropy: "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416",
|
||||
mnemonic: "clutch control vehicle tonight unusual clog visa ice plunge glimpse recipe series open hour vintage deposit universe tip job dress radar refuse motion taste",
|
||||
seed: "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f86360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449",
|
||||
},
|
||||
{
|
||||
entropy: "eaebabb2383351fd31d703840b32e9e2",
|
||||
mnemonic: "turtle front uncle idea crush write shrug there lottery flower risk shell",
|
||||
seed: "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c404f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c",
|
||||
},
|
||||
{
|
||||
entropy: "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78",
|
||||
mnemonic: "kiss carry display unusual confirm curtain upgrade antique rotate hello void custom frequent obey nut hole price segment",
|
||||
seed: "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b079403485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79",
|
||||
},
|
||||
{
|
||||
entropy: "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef",
|
||||
mnemonic: "exile ask congress lamp submit jacket era scheme attend cousin alcohol catch course end lucky hurt sentence oven short ball bird grab wing top",
|
||||
seed: "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b14d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c",
|
||||
},
|
||||
{
|
||||
entropy: "18ab19a9f54a9274f03e5209a2ac8a91",
|
||||
mnemonic: "board flee heavy tunnel powder denial science ski answer betray cargo cat",
|
||||
seed: "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe005831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8",
|
||||
},
|
||||
{
|
||||
entropy: "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4",
|
||||
mnemonic: "board blade invite damage undo sun mimic interest slam gaze truly inherit resist great inject rocket museum chief",
|
||||
seed: "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf07c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9",
|
||||
},
|
||||
{
|
||||
entropy: "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419",
|
||||
mnemonic: "beyond stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut",
|
||||
seed: "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func badMnemonicSentences() []vector {
|
||||
return []vector{
|
||||
{mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"},
|
||||
{mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow yellow"},
|
||||
{mnemonic: "letter advice cage absurd amount doctor acoustic avoid letter advice caged above"},
|
||||
{mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo, wrong"},
|
||||
{mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"},
|
||||
{mnemonic: "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will will will"},
|
||||
{mnemonic: "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always."},
|
||||
{mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo why"},
|
||||
{mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art art"},
|
||||
{mnemonic: "legal winner thank year wave sausage worth useful legal winner thanks year wave worth useful legal winner thank year wave sausage worth title"},
|
||||
{mnemonic: "letter advice cage absurd amount doctor acoustic avoid letters advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless"},
|
||||
{mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo voted"},
|
||||
{mnemonic: "jello better achieve collect unaware mountain thought cargo oxygen act hood bridge"},
|
||||
{mnemonic: "renew, stay, biology, evidence, goat, welcome, casual, join, adapt, armor, shuffle, fault, little, machine, walk, stumble, urge, swap"},
|
||||
{mnemonic: "dignity pass list indicate nasty"},
|
||||
|
||||
// From issue 32
|
||||
{mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon letter"},
|
||||
}
|
||||
}
|
||||
|
||||
func assertNil(t *testing.T, object interface{}) {
|
||||
if object != nil {
|
||||
t.Errorf("Expected nil, got %v", object)
|
||||
}
|
||||
}
|
||||
|
||||
func assertNotNil(t *testing.T, object interface{}) {
|
||||
if object == nil {
|
||||
t.Error("Expected not nil")
|
||||
}
|
||||
}
|
||||
|
||||
func assertTrue(t *testing.T, a bool) {
|
||||
if !a {
|
||||
t.Error("Expected true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func assertFalse(t *testing.T, a bool) {
|
||||
if a {
|
||||
t.Error("Expected false, got true")
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqual(t *testing.T, a, b interface{}) {
|
||||
if a != b {
|
||||
t.Errorf("Objects not equal, expected `%s` and got `%s`", a, b)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqualString(t *testing.T, a, b string) {
|
||||
if a != b {
|
||||
t.Errorf("Strings not equal, expected `%s` and got `%s`", a, b)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqualStringSlices(t *testing.T, a, b []string) {
|
||||
if len(a) != len(b) {
|
||||
t.Errorf("String slices not equal, expected %v and got %v", a, b)
|
||||
return
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
t.Errorf("String slices not equal, expected %v and got %v", a, b)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqualByteSlices(t *testing.T, a, b []byte) {
|
||||
if len(a) != len(b) {
|
||||
t.Errorf("Byte slices not equal, expected %v and got %v", a, b)
|
||||
return
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
t.Errorf("Byte slices not equal, expected %v and got %v", a, b)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package bip39_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/gojuukaze/go-bip39"
|
||||
)
|
||||
|
||||
func ExampleNewMnemonic() {
|
||||
// the entropy can be any byte slice, generated how pleased,
|
||||
// as long its bit size is a multiple of 32 and is within
|
||||
// the inclusive range of {128,256}
|
||||
entropy, _ := hex.DecodeString("066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad")
|
||||
|
||||
// generate a mnemomic
|
||||
mnemomic, _ := bip39.NewMnemonic(entropy)
|
||||
fmt.Println(mnemomic)
|
||||
// output:
|
||||
// all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform
|
||||
}
|
||||
|
||||
func ExampleNewSeed() {
|
||||
seed := bip39.NewSeed("all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform", "TREZOR")
|
||||
fmt.Println(hex.EncodeToString(seed))
|
||||
// output:
|
||||
// 26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module github.com/luxfi/go-bip39
|
||||
|
||||
go 1.24
|
||||
|
||||
require golang.org/x/crypto v0.40.0
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
@@ -0,0 +1,45 @@
|
||||
# go-bip39
|
||||
[](https://travis-ci.org/tyler-smith/go-bip39)
|
||||
[](https://github.com/tyler-smith/go-bip39/blob/master/LICENSE)
|
||||
[](http://godoc.org/github.com/tyler-smith/go-bip39)
|
||||
[](https://goreportcard.com/report/github.com/tyler-smith/go-bip39)
|
||||
[](https://github.com/tyler-smith/go-bip39/issues)
|
||||
|
||||
|
||||
A golang implementation of the BIP0039 spec for mnemonic seeds
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
"github.com/tyler-smith/go-bip32"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main(){
|
||||
// Generate a mnemonic for memorization or user-friendly seeds
|
||||
entropy, _ := bip39.NewEntropy(256)
|
||||
mnemonic, _ := bip39.NewMnemonic(entropy)
|
||||
|
||||
// Generate a Bip32 HD wallet for the mnemonic and a user supplied password
|
||||
seed := bip39.NewSeed(mnemonic, "Secret Passphrase")
|
||||
|
||||
masterKey, _ := bip32.NewMasterKey(seed)
|
||||
publicKey := masterKey.PublicKey()
|
||||
|
||||
// Display mnemonic and keys
|
||||
fmt.Println("Mnemonic: ", mnemonic)
|
||||
fmt.Println("Master private key: ", masterKey)
|
||||
fmt.Println("Master public key: ", publicKey)
|
||||
}
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
Wordlists are from the [bip39 spec](https://github.com/bitcoin/bips/tree/master/bip-0039).
|
||||
|
||||
Test vectors are from the standard Python BIP0039 implementation from the
|
||||
Trezor team: [https://github.com/trezor/python-mnemonic](https://github.com/trezor/python-mnemonic)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2071
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2071
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+2071
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user