-
Notifications
You must be signed in to change notification settings - Fork 156
/
seed.go
102 lines (93 loc) · 3.1 KB
/
seed.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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Copyright (c) 2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package walletseed
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"strings"
"decred.org/dcrwallet/v2/errors"
"decred.org/dcrwallet/v2/pgpwordlist"
"github.com/decred/dcrd/hdkeychain/v3"
)
// GenerateRandomSeed returns a new seed created from a cryptographically-secure
// random source. If the seed size is unacceptable,
// hdkeychain.ErrInvalidSeedLen is returned.
func GenerateRandomSeed(size uint) ([]byte, error) {
const op errors.Op = "walletseed.GenerateRandomSeed"
if size >= uint(^uint8(0)) {
return nil, errors.E(op, errors.Invalid, hdkeychain.ErrInvalidSeedLen)
}
if size < hdkeychain.MinSeedBytes || size > hdkeychain.MaxSeedBytes {
return nil, errors.E(op, errors.Invalid, hdkeychain.ErrInvalidSeedLen)
}
seed, err := hdkeychain.GenerateSeed(uint8(size))
if err != nil {
return nil, errors.E(op, err)
}
return seed, nil
}
// checksumByte returns the checksum byte used at the end of the seed mnemonic
// encoding. The "checksum" is the first byte of the double SHA256.
func checksumByte(data []byte) byte {
intermediateHash := sha256.Sum256(data)
return sha256.Sum256(intermediateHash[:])[0]
}
// EncodeMnemonicSlice encodes a seed as a mnemonic word list.
func EncodeMnemonicSlice(seed []byte) []string {
words := make([]string, len(seed)+1) // Extra word for checksumByte
for i, b := range seed {
words[i] = pgpwordlist.ByteToMnemonic(b, i)
}
checksum := checksumByte(seed)
words[len(words)-1] = pgpwordlist.ByteToMnemonic(checksum, len(seed))
return words
}
// EncodeMnemonic encodes a seed as a mnemonic word list separated by spaces.
func EncodeMnemonic(seed []byte) string {
var buf bytes.Buffer
for i, b := range seed {
if i != 0 {
buf.WriteRune(' ')
}
buf.WriteString(pgpwordlist.ByteToMnemonic(b, i))
}
checksum := checksumByte(seed)
buf.WriteRune(' ')
buf.WriteString(pgpwordlist.ByteToMnemonic(checksum, len(seed)))
return buf.String()
}
// DecodeUserInput decodes a seed in either hexadecimal or mnemonic word list
// encoding back into its binary form.
func DecodeUserInput(input string) ([]byte, error) {
const op errors.Op = "walletseed.DecodeUserInput"
words := strings.Split(strings.TrimSpace(input), " ")
var seed []byte
switch {
case len(words) == 1:
// Assume hex
var err error
seed, err = hex.DecodeString(words[0])
if err != nil {
return nil, errors.E(op, errors.Encoding, err)
}
case len(words) > 1:
// Assume mnemonic with encoded checksum byte
decoded, err := pgpwordlist.DecodeMnemonics(words)
if err != nil {
return nil, errors.E(op, errors.Encoding, err)
}
if len(decoded) < 2 { // need data (0) and checksum (1) to check checksum
break
}
if checksumByte(decoded[:len(decoded)-1]) != decoded[len(decoded)-1] {
return nil, errors.E(op, errors.Encoding, "checksum mismatch")
}
seed = decoded[:len(decoded)-1]
}
if len(seed) < hdkeychain.MinSeedBytes || len(seed) > hdkeychain.MaxSeedBytes {
return nil, errors.E(op, errors.Encoding, hdkeychain.ErrInvalidSeedLen)
}
return seed, nil
}