gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/crypto/mnemonics/bip39_lite.go (about) 1 // From: https://github.com/tyler-smith/go-bip39/blob/master/bip39.go 2 3 package mnemonics 4 5 import ( 6 "crypto/rand" 7 "crypto/sha256" 8 "encoding/binary" 9 "errors" 10 "math/big" 11 "strings" 12 ) 13 14 var ( 15 // Some bitwise operands for working with big.Ints. 16 last11BitsMask = big.NewInt(2047) 17 shift11BitsMask = big.NewInt(2048) 18 bigOne = big.NewInt(1) 19 bigTwo = big.NewInt(2) 20 21 // wordList is the set of words to use. 22 wordList []string 23 24 // wordMap is a reverse lookup map for wordList. 25 wordMap map[string]int 26 ) 27 28 var ( 29 // ErrEntropyLengthInvalid is returned when trying to use an entropy set with 30 // an invalid size. 31 ErrEntropyLengthInvalid = errors.New("Entropy length must be [128, 256] and a multiple of 32") 32 ) 33 34 func init() { 35 SetWordList(English) 36 } 37 38 // SetWordList sets the list of words to use for mnemonics. Currently the list 39 // that is set is used package-wide. 40 func SetWordList(list []string) { 41 wordList = list 42 wordMap = map[string]int{} 43 44 for i, v := range wordList { 45 wordMap[v] = i 46 } 47 } 48 49 // NewEntropy will create random entropy bytes 50 // so long as the requested size bitSize is an appropriate size. 51 // 52 // bitSize has to be a multiple 32 and be within the inclusive range of {128, 256}. 53 func NewEntropy(bitSize int) ([]byte, error) { 54 if err := validateEntropyBitSize(bitSize); err != nil { 55 return nil, err 56 } 57 58 entropy := make([]byte, bitSize/8) 59 _, _ = rand.Read(entropy) // err is always nil 60 61 return entropy, nil 62 } 63 64 // NewMnemonic will return a string consisting of the mnemonic words for 65 // the given entropy. 66 // If the provide entropy is invalid, an error will be returned. 67 func NewMnemonic(entropy []byte) (string, error) { 68 // Compute some lengths for convenience. 69 entropyBitLength := len(entropy) * 8 70 checksumBitLength := entropyBitLength / 32 71 sentenceLength := (entropyBitLength + checksumBitLength) / 11 72 73 // Validate that the requested size is supported. 74 err := validateEntropyBitSize(entropyBitLength) 75 if err != nil { 76 return "", err 77 } 78 79 // Add checksum to entropy. 80 entropy = addChecksum(entropy) 81 82 // Break entropy up into sentenceLength chunks of 11 bits. 83 // For each word AND mask the rightmost 11 bits and find the word at that index. 84 // Then bitshift entropy 11 bits right and repeat. 85 // Add to the last empty slot so we can work with LSBs instead of MSB. 86 87 // Entropy as an int so we can bitmask without worrying about bytes slices. 88 entropyInt := new(big.Int).SetBytes(entropy) 89 90 // Slice to hold words in. 91 words := make([]string, sentenceLength) 92 93 // Throw away big.Int for AND masking. 94 word := big.NewInt(0) 95 96 for i := sentenceLength - 1; i >= 0; i-- { 97 // Get 11 right most bits and bitshift 11 to the right for next time. 98 word.And(entropyInt, last11BitsMask) 99 entropyInt.Div(entropyInt, shift11BitsMask) 100 101 // Get the bytes representing the 11 bits as a 2 byte slice. 102 wordBytes := padByteSlice(word.Bytes(), 2) 103 104 // Convert bytes to an index and add that word to the list. 105 words[i] = wordList[binary.BigEndian.Uint16(wordBytes)] 106 } 107 108 return strings.Join(words, " "), nil 109 } 110 111 // Appends to data the first (len(data) / 32)bits of the result of sha256(data) 112 // Currently only supports data up to 32 bytes. 113 func addChecksum(data []byte) []byte { 114 // Get first byte of sha256 115 hash := computeChecksum(data) 116 firstChecksumByte := hash[0] 117 118 // len() is in bytes so we divide by 4 119 checksumBitLength := uint(len(data) / 4) 120 121 // For each bit of check sum we want we shift the data one the left 122 // and then set the (new) right most bit equal to checksum bit at that index 123 // staring from the left 124 dataBigInt := new(big.Int).SetBytes(data) 125 126 for i := uint(0); i < checksumBitLength; i++ { 127 // Bitshift 1 left 128 dataBigInt.Mul(dataBigInt, bigTwo) 129 130 // Set rightmost bit if leftmost checksum bit is set 131 if firstChecksumByte&(1<<(7-i)) > 0 { 132 dataBigInt.Or(dataBigInt, bigOne) 133 } 134 } 135 136 return dataBigInt.Bytes() 137 } 138 139 func computeChecksum(data []byte) []byte { 140 hasher := sha256.New() 141 _, _ = hasher.Write(data) // This error is guaranteed to be nil 142 143 return hasher.Sum(nil) 144 } 145 146 // validateEntropyBitSize ensures that entropy is the correct size for being a 147 // mnemonic. 148 func validateEntropyBitSize(bitSize int) error { 149 if (bitSize%32) != 0 || bitSize < 128 || bitSize > 256 { 150 return ErrEntropyLengthInvalid 151 } 152 153 return nil 154 } 155 156 // padByteSlice returns a byte slice of the given size with contents of the 157 // given slice left padded and any empty spaces filled with 0's. 158 func padByteSlice(slice []byte, length int) []byte { 159 offset := length - len(slice) 160 if offset <= 0 { 161 return slice 162 } 163 164 newSlice := make([]byte, length) 165 copy(newSlice[offset:], slice) 166 167 return newSlice 168 }