github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/others/bip39/bip39.go (about) 1 // Package bip39 is the Golang implementation of the BIP39 spec. 2 // 3 // The official BIP39 spec can be found at 4 // https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki 5 package bip39 6 7 import ( 8 "crypto/rand" 9 "crypto/sha256" 10 "crypto/sha512" 11 "encoding/binary" 12 "errors" 13 "fmt" 14 "math/big" 15 "strings" 16 17 "crypto/hmac" 18 "hash" 19 ) 20 21 var ( 22 // Some bitwise operands for working with big.Ints 23 last11BitsMask = big.NewInt(2047) 24 shift11BitsMask = big.NewInt(2048) 25 bigOne = big.NewInt(1) 26 bigTwo = big.NewInt(2) 27 28 // used to isolate the checksum bits from the entropy+checksum byte array 29 wordLengthChecksumMasksMapping = map[int]*big.Int{ 30 12: big.NewInt(15), 31 15: big.NewInt(31), 32 18: big.NewInt(63), 33 21: big.NewInt(127), 34 24: big.NewInt(255), 35 } 36 // used to use only the desired x of 8 available checksum bits. 37 // 256 bit (word length 24) requires all 8 bits of the checksum, 38 // and thus no shifting is needed for it (we would get a divByZero crash if we did) 39 wordLengthChecksumShiftMapping = map[int]*big.Int{ 40 12: big.NewInt(16), 41 15: big.NewInt(8), 42 18: big.NewInt(4), 43 21: big.NewInt(2), 44 } 45 46 // wordList is the set of words to use 47 wordList []string 48 49 // wordMap is a reverse lookup map for wordList 50 wordMap map[string]int 51 ) 52 53 var ( 54 // ErrInvalidMnemonic is returned when trying to use a malformed mnemonic. 55 ErrInvalidMnemonic = errors.New("invalid mnenomic") 56 57 // ErrEntropyLengthInvalid is returned when trying to use an entropy set with 58 // an invalid size. 59 ErrEntropyLengthInvalid = errors.New("entropy length must be [128, 256] and a multiple of 32") 60 61 // ErrValidatedSeedLengthMismatch is returned when a validated seed is not the 62 // same size as the given seed. This should never happen is present only as a 63 // sanity assertion. 64 ErrValidatedSeedLengthMismatch = errors.New("seed length does not match validated seed length") 65 66 // ErrChecksumIncorrect is returned when entropy has the incorrect checksum. 67 ErrChecksumIncorrect = errors.New("checksum incorrect") 68 ) 69 70 func init() { 71 SetWordList(wordlistsEnglish) 72 } 73 74 // SetWordList sets the list of words to use for mnemonics. Currently the list 75 // that is set is used package-wide. 76 func SetWordList(list []string) { 77 wordList = list 78 wordMap = map[string]int{} 79 for i, v := range wordList { 80 wordMap[v] = i 81 } 82 } 83 84 // GetWordList gets the list of words to use for mnemonics. 85 func GetWordList() []string { 86 return wordList 87 } 88 89 // GetWordIndex gets word index in wordMap. 90 func GetWordIndex(word string) (int, bool) { 91 idx, ok := wordMap[word] 92 return idx, ok 93 } 94 95 // NewEntropy will create random entropy bytes 96 // so long as the requested size bitSize is an appropriate size. 97 // 98 // bitSize has to be a multiple 32 and be within the inclusive range of {128, 256} 99 func NewEntropy(bitSize int) ([]byte, error) { 100 err := validateEntropyBitSize(bitSize) 101 if err != nil { 102 return nil, err 103 } 104 105 entropy := make([]byte, bitSize/8) 106 _, err = rand.Read(entropy) 107 return entropy, err 108 } 109 110 // EntropyFromMnemonic takes a mnemonic generated by this library, 111 // and returns the input entropy used to generate the given mnemonic. 112 // An error is returned if the given mnemonic is invalid. 113 func EntropyFromMnemonic(mnemonic string) ([]byte, error) { 114 mnemonicSlice, isValid := splitMnemonicWords(mnemonic) 115 if !isValid { 116 return nil, ErrInvalidMnemonic 117 } 118 119 // Decode the words into a big.Int. 120 b := big.NewInt(0) 121 for _, v := range mnemonicSlice { 122 index, found := wordMap[v] 123 if !found { 124 return nil, fmt.Errorf("word `%v` not found in reverse map", v) 125 } 126 var wordBytes [2]byte 127 binary.BigEndian.PutUint16(wordBytes[:], uint16(index)) 128 b = b.Mul(b, shift11BitsMask) 129 b = b.Or(b, big.NewInt(0).SetBytes(wordBytes[:])) 130 } 131 132 // Build and add the checksum to the big.Int. 133 checksum := big.NewInt(0) 134 checksumMask := wordLengthChecksumMasksMapping[len(mnemonicSlice)] 135 checksum = checksum.And(b, checksumMask) 136 137 b.Div(b, big.NewInt(0).Add(checksumMask, bigOne)) 138 139 // The entropy is the underlying bytes of the big.Int. Any upper bytes of 140 // all 0's are not returned so we pad the beginning of the slice with empty 141 // bytes if necessary. 142 entropy := b.Bytes() 143 entropy = padByteSlice(entropy, len(mnemonicSlice)/3*4) 144 145 // Generate the checksum and compare with the one we got from the mneomnic. 146 entropyChecksumBytes := computeChecksum(entropy) 147 entropyChecksum := big.NewInt(int64(entropyChecksumBytes[0])) 148 if l := len(mnemonicSlice); l != 24 { 149 checksumShift := wordLengthChecksumShiftMapping[l] 150 entropyChecksum.Div(entropyChecksum, checksumShift) 151 } 152 153 if checksum.Cmp(entropyChecksum) != 0 { 154 return nil, ErrChecksumIncorrect 155 } 156 157 return entropy, nil 158 } 159 160 // NewMnemonic will return a string consisting of the mnemonic words for 161 // the given entropy. 162 // If the provide entropy is invalid, an error will be returned. 163 func NewMnemonic(entropy []byte) (string, error) { 164 // Compute some lengths for convenience. 165 entropyBitLength := len(entropy) * 8 166 checksumBitLength := entropyBitLength / 32 167 sentenceLength := (entropyBitLength + checksumBitLength) / 11 168 169 // Validate that the requested size is supported. 170 err := validateEntropyBitSize(entropyBitLength) 171 if err != nil { 172 return "", err 173 } 174 175 // Add checksum to entropy. 176 entropy = addChecksum(entropy) 177 178 // Break entropy up into sentenceLength chunks of 11 bits. 179 // For each word AND mask the rightmost 11 bits and find the word at that index. 180 // Then bitshift entropy 11 bits right and repeat. 181 // Add to the last empty slot so we can work with LSBs instead of MSB. 182 183 // Entropy as an int so we can bitmask without worrying about bytes slices. 184 entropyInt := new(big.Int).SetBytes(entropy) 185 186 // Slice to hold words in. 187 words := make([]string, sentenceLength) 188 189 // Throw away big.Int for AND masking. 190 word := big.NewInt(0) 191 192 for i := sentenceLength - 1; i >= 0; i-- { 193 // Get 11 right most bits and bitshift 11 to the right for next time. 194 word.And(entropyInt, last11BitsMask) 195 entropyInt.Div(entropyInt, shift11BitsMask) 196 197 // Get the bytes representing the 11 bits as a 2 byte slice. 198 wordBytes := padByteSlice(word.Bytes(), 2) 199 200 // Convert bytes to an index and add that word to the list. 201 words[i] = wordList[binary.BigEndian.Uint16(wordBytes)] 202 } 203 204 return strings.Join(words, " "), nil 205 } 206 207 // MnemonicToByteArray takes a mnemonic string and turns it into a byte array 208 // suitable for creating another mnemonic. 209 // An error is returned if the mnemonic is invalid. 210 func MnemonicToByteArray(mnemonic string, raw ...bool) ([]byte, error) { 211 var ( 212 mnemonicSlice = strings.Split(mnemonic, " ") 213 entropyBitSize = len(mnemonicSlice) * 11 214 checksumBitSize = entropyBitSize % 32 215 fullByteSize = (entropyBitSize-checksumBitSize)/8 + 1 216 checksumByteSize = fullByteSize - (fullByteSize % 4) 217 ) 218 219 // Pre validate that the mnemonic is well formed and only contains words that 220 // are present in the word list. 221 if err := IsMnemonicValid(mnemonic); err != nil { 222 return nil, err 223 } 224 225 // Convert word indices to a big.Int representing the entropy. 226 checksummedEntropy := big.NewInt(0) 227 modulo := big.NewInt(2048) 228 for _, v := range mnemonicSlice { 229 index := big.NewInt(int64(wordMap[v])) 230 checksummedEntropy.Mul(checksummedEntropy, modulo) 231 checksummedEntropy.Add(checksummedEntropy, index) 232 } 233 234 // Calculate the unchecksummed entropy so we can validate that the checksum is 235 // correct. 236 checksumModulo := big.NewInt(0).Exp(bigTwo, big.NewInt(int64(checksumBitSize)), nil) 237 rawEntropy := big.NewInt(0).Div(checksummedEntropy, checksumModulo) 238 239 // Convert big.Ints to byte padded byte slices. 240 rawEntropyBytes := padByteSlice(rawEntropy.Bytes(), checksumByteSize) 241 checksummedEntropyBytes := padByteSlice(checksummedEntropy.Bytes(), fullByteSize) 242 243 // Validate that the checksum is correct. 244 newChecksummedEntropyBytes := padByteSlice(addChecksum(rawEntropyBytes), fullByteSize) 245 if !compareByteSlices(checksummedEntropyBytes, newChecksummedEntropyBytes) { 246 return nil, ErrChecksumIncorrect 247 } 248 249 if len(raw) > 0 && raw[0] { 250 return rawEntropyBytes, nil 251 } 252 253 return checksummedEntropyBytes, nil 254 } 255 256 // NewSeedWithErrorChecking creates a hashed seed output given the mnemonic string and a password. 257 // An error is returned if the mnemonic is not convertible to a byte array. 258 func NewSeedWithErrorChecking(mnemonic string, password string) ([]byte, error) { 259 _, err := MnemonicToByteArray(mnemonic) 260 if err != nil { 261 return nil, err 262 } 263 return NewSeed(mnemonic, password), nil 264 } 265 266 // NewSeed creates a hashed seed output given a provided string and password. 267 // No checking is performed to validate that the string provided is a valid mnemonic. 268 func NewSeed(mnemonic string, password string) []byte { 269 return pbkdf2Key([]byte(mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New) 270 } 271 272 // IsMnemonicValid attempts to verify that the provided mnemonic is valid. 273 // Validity is determined by both the number of words being appropriate, 274 // and that all the words in the mnemonic are present in the word list. 275 func IsMnemonicValid(mnemonic string) (err error) { 276 _, err = EntropyFromMnemonic(mnemonic) 277 return err 278 } 279 280 // Appends to data the first (len(data) / 32)bits of the result of sha256(data) 281 // Currently only supports data up to 32 bytes 282 func addChecksum(data []byte) []byte { 283 // Get first byte of sha256 284 hash := computeChecksum(data) 285 firstChecksumByte := hash[0] 286 287 // len() is in bytes so we divide by 4 288 checksumBitLength := uint(len(data) / 4) 289 290 // For each bit of check sum we want we shift the data one the left 291 // and then set the (new) right most bit equal to checksum bit at that index 292 // staring from the left 293 dataBigInt := new(big.Int).SetBytes(data) 294 for i := uint(0); i < checksumBitLength; i++ { 295 // Bitshift 1 left 296 dataBigInt.Mul(dataBigInt, bigTwo) 297 298 // Set rightmost bit if leftmost checksum bit is set 299 if uint8(firstChecksumByte&(1<<(7-i))) > 0 { 300 dataBigInt.Or(dataBigInt, bigOne) 301 } 302 } 303 304 return dataBigInt.Bytes() 305 } 306 307 func computeChecksum(data []byte) []byte { 308 hasher := sha256.New() 309 hasher.Write(data) 310 return hasher.Sum(nil) 311 } 312 313 // validateEntropyBitSize ensures that entropy is the correct size for being a 314 // mnemonic. 315 func validateEntropyBitSize(bitSize int) error { 316 if (bitSize%32) != 0 || bitSize < 128 || bitSize > 256 { 317 return ErrEntropyLengthInvalid 318 } 319 return nil 320 } 321 322 // padByteSlice returns a byte slice of the given size with contents of the 323 // given slice left padded and any empty spaces filled with 0's. 324 func padByteSlice(slice []byte, length int) []byte { 325 offset := length - len(slice) 326 if offset <= 0 { 327 return slice 328 } 329 newSlice := make([]byte, length) 330 copy(newSlice[offset:], slice) 331 return newSlice 332 } 333 334 // compareByteSlices returns true of the byte slices have equal contents and 335 // returns false otherwise. 336 func compareByteSlices(a, b []byte) bool { 337 if len(a) != len(b) { 338 return false 339 } 340 for i := range a { 341 if a[i] != b[i] { 342 return false 343 } 344 } 345 return true 346 } 347 348 func splitMnemonicWords(mnemonic string) ([]string, bool) { 349 // Create a list of all the words in the mnemonic sentence 350 words := strings.Fields(mnemonic) 351 352 // Get num of words 353 numOfWords := len(words) 354 355 // The number of words should be 12, 15, 18, 21 or 24 356 if numOfWords%3 != 0 || numOfWords < 12 || numOfWords > 24 { 357 return nil, false 358 } 359 return words, true 360 } 361 362 // Key derives a key from the password, salt and iteration count, returning a 363 // []byte of length keylen that can be used as cryptographic key. The key is 364 // derived based on the method described as PBKDF2 with the HMAC variant using 365 // the supplied hash function. 366 // 367 // For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you 368 // can get a derived key for e.g. AES-256 (which needs a 32-byte key) by 369 // doing: 370 // 371 // dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) 372 // 373 // Remember to get a good random salt. At least 8 bytes is recommended by the 374 // RFC. 375 // 376 // Using a higher iteration count will increase the cost of an exhaustive 377 // search but will also make derivation proportionally slower. 378 func pbkdf2Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { 379 prf := hmac.New(h, password) 380 hashLen := prf.Size() 381 numBlocks := (keyLen + hashLen - 1) / hashLen 382 383 var buf [4]byte 384 dk := make([]byte, 0, numBlocks*hashLen) 385 U := make([]byte, hashLen) 386 for block := 1; block <= numBlocks; block++ { 387 // N.B.: || means concatenation, ^ means XOR 388 // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter 389 // U_1 = PRF(password, salt || uint(i)) 390 prf.Reset() 391 prf.Write(salt) 392 buf[0] = byte(block >> 24) 393 buf[1] = byte(block >> 16) 394 buf[2] = byte(block >> 8) 395 buf[3] = byte(block) 396 prf.Write(buf[:4]) 397 dk = prf.Sum(dk) 398 T := dk[len(dk)-hashLen:] 399 copy(U, T) 400 401 // U_n = PRF(password, U_(n-1)) 402 for n := 2; n <= iter; n++ { 403 prf.Reset() 404 prf.Write(U) 405 U = U[:0] 406 U = prf.Sum(U) 407 for x := range U { 408 T[x] ^= U[x] 409 } 410 } 411 } 412 return dk[:keyLen] 413 }