decred.org/dcrwallet/v3@v3.1.0/walletseed/seed.go (about) 1 // Copyright (c) 2016 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package walletseed 6 7 import ( 8 "bytes" 9 "crypto/sha256" 10 "encoding/hex" 11 "strings" 12 13 "decred.org/dcrwallet/v3/errors" 14 "decred.org/dcrwallet/v3/pgpwordlist" 15 "github.com/decred/dcrd/hdkeychain/v3" 16 ) 17 18 // GenerateRandomSeed returns a new seed created from a cryptographically-secure 19 // random source. If the seed size is unacceptable, 20 // hdkeychain.ErrInvalidSeedLen is returned. 21 func GenerateRandomSeed(size uint) ([]byte, error) { 22 const op errors.Op = "walletseed.GenerateRandomSeed" 23 if size >= uint(^uint8(0)) { 24 return nil, errors.E(op, errors.Invalid, hdkeychain.ErrInvalidSeedLen) 25 } 26 if size < hdkeychain.MinSeedBytes || size > hdkeychain.MaxSeedBytes { 27 return nil, errors.E(op, errors.Invalid, hdkeychain.ErrInvalidSeedLen) 28 } 29 seed, err := hdkeychain.GenerateSeed(uint8(size)) 30 if err != nil { 31 return nil, errors.E(op, err) 32 } 33 return seed, nil 34 } 35 36 // checksumByte returns the checksum byte used at the end of the seed mnemonic 37 // encoding. The "checksum" is the first byte of the double SHA256. 38 func checksumByte(data []byte) byte { 39 intermediateHash := sha256.Sum256(data) 40 return sha256.Sum256(intermediateHash[:])[0] 41 } 42 43 // EncodeMnemonicSlice encodes a seed as a mnemonic word list. 44 func EncodeMnemonicSlice(seed []byte) []string { 45 words := make([]string, len(seed)+1) // Extra word for checksumByte 46 for i, b := range seed { 47 words[i] = pgpwordlist.ByteToMnemonic(b, i) 48 } 49 checksum := checksumByte(seed) 50 words[len(words)-1] = pgpwordlist.ByteToMnemonic(checksum, len(seed)) 51 return words 52 } 53 54 // EncodeMnemonic encodes a seed as a mnemonic word list separated by spaces. 55 func EncodeMnemonic(seed []byte) string { 56 var buf bytes.Buffer 57 for i, b := range seed { 58 if i != 0 { 59 buf.WriteRune(' ') 60 } 61 buf.WriteString(pgpwordlist.ByteToMnemonic(b, i)) 62 } 63 checksum := checksumByte(seed) 64 buf.WriteRune(' ') 65 buf.WriteString(pgpwordlist.ByteToMnemonic(checksum, len(seed))) 66 return buf.String() 67 } 68 69 // DecodeUserInput decodes a seed in either hexadecimal or mnemonic word list 70 // encoding back into its binary form. 71 func DecodeUserInput(input string) ([]byte, error) { 72 const op errors.Op = "walletseed.DecodeUserInput" 73 words := strings.Split(strings.TrimSpace(input), " ") 74 var seed []byte 75 switch { 76 case len(words) == 1: 77 // Assume hex 78 var err error 79 seed, err = hex.DecodeString(words[0]) 80 if err != nil { 81 return nil, errors.E(op, errors.Encoding, err) 82 } 83 case len(words) > 1: 84 // Assume mnemonic with encoded checksum byte 85 decoded, err := pgpwordlist.DecodeMnemonics(words) 86 if err != nil { 87 return nil, errors.E(op, errors.Encoding, err) 88 } 89 if len(decoded) < 2 { // need data (0) and checksum (1) to check checksum 90 break 91 } 92 if checksumByte(decoded[:len(decoded)-1]) != decoded[len(decoded)-1] { 93 return nil, errors.E(op, errors.Encoding, "checksum mismatch") 94 } 95 seed = decoded[:len(decoded)-1] 96 } 97 98 if len(seed) < hdkeychain.MinSeedBytes || len(seed) > hdkeychain.MaxSeedBytes { 99 return nil, errors.E(op, errors.Encoding, hdkeychain.ErrInvalidSeedLen) 100 } 101 return seed, nil 102 }