-
Notifications
You must be signed in to change notification settings - Fork 0
/
mnemonic.go
76 lines (61 loc) · 1.99 KB
/
mnemonic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package bip39
import (
"crypto/sha256"
"io"
"math/big"
"strings"
"github.com/sammyne/bip39/dict"
)
// wordIndexBitMask is the bits masker assisting in decoding word indices
// out of the full entropy
var wordIndexBitMask = new(big.Int).SetInt64(1<<WordIndexBitSize - 1)
// GenerateMnemonic constructs a mnemonic randomly based on the n bytes read
// out of the provided random sources for the provided language (as the
// global default if none)
func GenerateMnemonic(rand io.Reader, n EntropyLen,
lang ...dict.Language) (string, error) {
if !EntropyLenCompatible(n) {
return "", ErrEntropyLen
}
entropy := make([]byte, n)
if _, err := io.ReadFull(rand, entropy); nil != err {
return "", err
}
return NewMnemonic(entropy, lang...)
}
// NewMnemonic constructs the mnemonic w.r.t a language (the global default
// configured by dict pkg if none provided) for the given entropy
func NewMnemonic(entropy []byte, lang ...dict.Language) (string, error) {
n := len(entropy)
if !EntropyLenCompatible(n) {
return "", ErrEntropyLen
}
wordlist, language, err := dict.WordlistToUse(lang...)
if nil != err {
return "", err
}
// make up the full entropy as a big int
// checksumLen=n*8/32=n/4
checksumB, checksumLen := sha256.Sum256(entropy)[0], n/4
entropy = append(entropy, checksumB)
x := new(big.Int).SetBytes(entropy)
x.Rsh(x, uint(8-checksumLen))
// MS=(ENT+CS)/11=(ENT+ENT/32)/11=3*ENT/32
// if measured in bytes, we got
// MS=(3*ENT/8)/(32/8)=3*(ENT/8)/4
nWord := 3 * n / 4
words := make([]string, nWord)
wordIndex := new(big.Int)
for i := nWord - 1; i >= 0; i-- {
wordIndex.And(x, wordIndexBitMask)
x.Rsh(x, WordIndexBitSize)
words[i] = wordlist[wordIndex.Int64()]
}
return strings.Join(words, dict.Whitespace(language)), nil
}
// ValidateMnemonic checks if the mnemonic is valid against a language
// (default as the one set by dict pkg)
func ValidateMnemonic(mnemonic string, lang ...dict.Language) bool {
_, err := RecoverFullEntropy(mnemonic, lang...)
return nil == err
}