github.com/hashgraph/hedera-sdk-go/v2@v2.48.0/mnemonic.go (about) 1 package hedera 2 3 /*- 4 * 5 * Hedera Go SDK 6 * 7 * Copyright (C) 2020 - 2024 Hedera Hashgraph, LLC 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 * 21 */ 22 23 import ( 24 "crypto/sha256" 25 "fmt" 26 "math/big" 27 "regexp" 28 "strconv" 29 "strings" 30 31 "crypto/sha512" 32 33 "github.com/pkg/errors" 34 "github.com/tyler-smith/go-bip39" 35 "golang.org/x/crypto/pbkdf2" 36 "golang.org/x/text/unicode/norm" 37 ) 38 39 type Mnemonic struct { 40 words string 41 } 42 43 // Deprecated 44 func (m Mnemonic) ToPrivateKey(passPhrase string) (PrivateKey, error) { 45 return PrivateKeyFromMnemonic(m, passPhrase) 46 } 47 48 // GenerateMnemonic generates a random 24-word mnemonic 49 func GenerateMnemonic24() (Mnemonic, error) { 50 entropy, err := bip39.NewEntropy(256) 51 52 if err != nil { 53 // It is only possible for there to be an error if the operating 54 // system's rng is unreadable 55 return Mnemonic{}, fmt.Errorf("could not retrieve random bytes from the operating system") 56 } 57 58 mnemonic, err := bip39.NewMnemonic(entropy) 59 60 // Note that this should never actually fail since it is being provided by library generated mnemonic 61 if err != nil { 62 return Mnemonic{}, err 63 } 64 65 return Mnemonic{mnemonic}, nil 66 } 67 68 // GenerateMnemonic12 generates a random 12-word mnemonic 69 func GenerateMnemonic12() (Mnemonic, error) { 70 entropy, err := bip39.NewEntropy(128) 71 72 if err != nil { 73 // It is only possible for there to be an error if the operating 74 // system's rng is unreadable 75 return Mnemonic{}, fmt.Errorf("could not retrieve random bytes from the operating system") 76 } 77 78 mnemonic, err := bip39.NewMnemonic(entropy) 79 80 // Note that this should never actually fail since it is being provided by library generated mnemonic 81 if err != nil { 82 return Mnemonic{}, err 83 } 84 85 return Mnemonic{mnemonic}, nil 86 } 87 88 // MnemonicFromString creates a mnemonic from a string of 24 words separated by spaces 89 // 90 // Keys are lazily generated 91 func MnemonicFromString(s string) (Mnemonic, error) { 92 return NewMnemonic(strings.Split(s, " ")) 93 } 94 95 // String returns the mnemonic as a string. 96 func (m Mnemonic) String() string { 97 return m.words 98 } 99 100 // Words returns the mnemonic as a slice of strings 101 func (m Mnemonic) Words() []string { 102 return strings.Split(m.words, " ") 103 } 104 105 // NewMnemonic Creates a mnemonic from a slice of 24 strings 106 // 107 // Keys are lazily generated 108 func NewMnemonic(words []string) (Mnemonic, error) { 109 joinedString := strings.Join(words, " ") 110 111 if len(words) == 24 || len(words) == 12 || len(words) == 22 { 112 if len(words) == 22 { //nolint 113 return Mnemonic{ 114 words: joinedString, 115 }._LegacyValidate() 116 } else if bip39.IsMnemonicValid(joinedString) { 117 return Mnemonic{ 118 words: joinedString, 119 }, nil 120 } else { 121 return Mnemonic{}, fmt.Errorf("invalid mnemonic composition") 122 } 123 } else { 124 return Mnemonic{}, fmt.Errorf("invalid mnemonic string") 125 } 126 } 127 128 func (m Mnemonic) _LegacyValidate() (Mnemonic, error) { 129 if len(strings.Split(m.words, " ")) != 22 { 130 return Mnemonic{}, fmt.Errorf("not a legacy mnemonic") 131 } 132 133 indices, err := m._Indices() 134 if err != nil { 135 return Mnemonic{}, err 136 } 137 138 entropy, checksum := m._ToLegacyEntropy(indices) 139 newchecksum := _Crc8(entropy) 140 141 if checksum != newchecksum { 142 return Mnemonic{}, fmt.Errorf("legacy mnemonic checksum mismatch") 143 } 144 145 return m, nil 146 } 147 148 func (m Mnemonic) _Indices() ([]int, error) { 149 var indices []int 150 var check bool 151 temp := strings.Split(m.words, " ") 152 if len(temp) == 22 { // nolint 153 for _, mnemonicString := range strings.Split(m.words, " ") { 154 check = false 155 for i, stringCheck := range legacy { 156 if mnemonicString == stringCheck { 157 check = true 158 indices = append(indices, i) 159 } 160 } 161 if !check { 162 return make([]int, 0), fmt.Errorf("word is not in the legacy word list") 163 } 164 } 165 } else if len(temp) == 24 { 166 for _, mnemonicString := range strings.Split(m.words, " ") { 167 t, check := bip39.GetWordIndex(mnemonicString) 168 if !check { 169 return make([]int, 0), bip39.ErrInvalidMnemonic 170 } 171 indices = append(indices, t) 172 } 173 } else { 174 return make([]int, 0), errors.New("not a 22 word or a 24 mnemonic") 175 } 176 177 return indices, nil 178 } 179 180 // ToLegacyPrivateKey converts a mnemonic to a legacy private key 181 func (m Mnemonic) ToLegacyPrivateKey() (PrivateKey, error) { 182 indices, err := m._Indices() 183 if err != nil { 184 return PrivateKey{}, err 185 } 186 187 var entropy []byte 188 if len(indices) == 22 { // nolint 189 entropy, _ = m._ToLegacyEntropy(indices) 190 } else if len(indices) == 24 { 191 entropy, err = m._ToLegacyEntropy2() 192 if err != nil { 193 return PrivateKey{}, err 194 } 195 } else { 196 return PrivateKey{}, errors.New("not a legacy key") 197 } 198 199 return PrivateKeyFromBytesEd25519(entropy) 200 } 201 202 func bytesToBits(dat []uint8) []bool { 203 bits := make([]bool, len(dat)*8) 204 205 for i := range bits { 206 bits[i] = false 207 } 208 209 for i := 0; i < len(dat); i++ { 210 for j := 0; j < 8; j++ { 211 bits[(i*8)+j] = (dat[i] & (1 << (7 - j))) != 0 212 } 213 } 214 215 return bits 216 } 217 218 func (m Mnemonic) _ToLegacyEntropy(indices []int) ([]byte, uint8) { 219 data := _ConvertRadix(indices, len(legacy), 256, 33) 220 221 checksum := data[len(data)-1] 222 result := make([]uint8, len(data)-1) 223 224 for i := 0; i < len(data)-1; i++ { 225 result[i] = data[i] ^ checksum 226 } 227 228 return result, checksum 229 } 230 231 func (m Mnemonic) _ToLegacyEntropy2() ([]byte, error) { 232 indices := strings.Split(m.words, " ") 233 concatBitsLen := len(indices) * 11 234 concatBits := make([]bool, concatBitsLen) 235 236 for i := range concatBits { 237 concatBits[i] = false 238 } 239 240 for index, word := range indices { 241 nds, check := bip39.GetWordIndex(word) 242 if !check { 243 return make([]byte, 0), bip39.ErrInvalidMnemonic 244 } 245 246 for i := 0; i < 11; i++ { 247 concatBits[(index*11)+i] = (nds & (1 << (10 - i))) != 0 248 } 249 } 250 251 checksumBitsLen := concatBitsLen / 33 252 entropyBitsLen := concatBitsLen - checksumBitsLen 253 254 entropy := make([]uint8, entropyBitsLen/8) 255 256 for i := 0; i < len(entropy); i++ { 257 for j := 0; j < 8; j++ { 258 if concatBits[(i*8)+j] { 259 entropy[i] |= 1 << (7 - j) 260 } 261 } 262 } 263 264 hash := sha256.New() 265 if _, err := hash.Write(entropy); err != nil { 266 return nil, err 267 } 268 269 hashbits := bytesToBits(hash.Sum(nil)) 270 271 for i := 0; i < checksumBitsLen; i++ { 272 if concatBits[entropyBitsLen+i] != hashbits[i] { 273 return make([]uint8, 0), errors.New("checksum mismatch") 274 } 275 } 276 277 return entropy, nil 278 } 279 280 func (m Mnemonic) _ToSeed(passPhrase string) []byte { 281 passPhraseNFKD := norm.NFKD.String(passPhrase) 282 salt := []byte("mnemonic" + passPhraseNFKD) 283 seed := pbkdf2.Key([]byte(m.String()), salt, 2048, 64, sha512.New) 284 return seed 285 } 286 287 // ToStandardEd25519PrivateKey converts a mnemonic to a standard ed25519 private key 288 func (m Mnemonic) ToStandardEd25519PrivateKey(passPhrase string, index uint32) (PrivateKey, error) { 289 seed := m._ToSeed(passPhrase) 290 derivedKey, err := _Ed25519PrivateKeyFromSeed(seed) 291 if err != nil { 292 return PrivateKey{}, err 293 } 294 295 keyBytes, chainCode := derivedKey.keyData, derivedKey.chainCode 296 for _, i := range []uint32{44, 3030, 0, 0, index} { 297 keyBytes, chainCode, err = _DeriveEd25519ChildKey(keyBytes, chainCode, i) 298 if err != nil { 299 return PrivateKey{}, err 300 } 301 } 302 303 privateKey, err := _Ed25519PrivateKeyFromBytes(keyBytes) 304 if err != nil { 305 return PrivateKey{}, err 306 } 307 308 privateKey.chainCode = chainCode 309 310 return PrivateKey{ 311 ed25519PrivateKey: privateKey, 312 }, nil 313 } 314 315 // calculateDerivationPathValues converts a derivation path string to an array of integers 316 func calculateDerivationPathValues(derivationPath string) ([]uint32, error) { 317 re := regexp.MustCompile(`m/(\d+'?)/(\d+'?)/(\d+'?)/(\d+'?)/(\d+'?)`) 318 matches := re.FindStringSubmatch(derivationPath) 319 if len(matches) != 6 { 320 return nil, fmt.Errorf("invalid derivation path format") 321 } 322 323 values := make([]uint32, 5) 324 for i, match := range matches[1:] { 325 if strings.HasSuffix(match, "'") { 326 match = strings.TrimSuffix(match, "'") 327 value, err := strconv.Atoi(match) 328 if err != nil { 329 return nil, err 330 } 331 values[i] = ToHardenedIndex(uint32(value)) 332 } else { 333 value, err := strconv.Atoi(match) 334 if err != nil { 335 return nil, err 336 } 337 values[i] = uint32(value) 338 } 339 } 340 341 return values, nil 342 } 343 344 func (m Mnemonic) toStandardECDSAsecp256k1PrivateKeyImpl(passPhrase string, derivationPathValues []uint32) (PrivateKey, error) { 345 seed := m._ToSeed(passPhrase) 346 derivedKey, err := _ECDSAPrivateKeyFromSeed(seed) 347 if err != nil { 348 return PrivateKey{}, err 349 } 350 351 keyBytes, chainCode := derivedKey.keyData.D.Bytes(), derivedKey.chainCode 352 for _, i := range derivationPathValues { 353 keyBytes, chainCode, err = _DeriveECDSAChildKey(keyBytes, chainCode, i) 354 if err != nil { 355 return PrivateKey{}, err 356 } 357 } 358 359 privateKey, err := _ECDSAPrivateKeyFromBytes(keyBytes) 360 if err != nil { 361 return PrivateKey{}, err 362 } 363 364 privateKey.chainCode = chainCode 365 366 return PrivateKey{ 367 ecdsaPrivateKey: privateKey, 368 }, nil 369 } 370 371 // ToStandardECDSAsecp256k1PrivateKey converts a mnemonic to a standard ecdsa secp256k1 private key 372 func (m Mnemonic) ToStandardECDSAsecp256k1PrivateKeyCustomDerivationPath(passPhrase string, derivationPath string) (PrivateKey, error) { 373 derivationPathValues, err := calculateDerivationPathValues(derivationPath) 374 if err != nil { 375 return PrivateKey{}, err 376 } 377 378 return m.toStandardECDSAsecp256k1PrivateKeyImpl(passPhrase, derivationPathValues) 379 } 380 381 // ToStandardECDSAsecp256k1PrivateKey converts a mnemonic to a standard ecdsa secp256k1 private key 382 // Uses the default derivation path of `m/44'/3030'/0'/0/${index}` 383 func (m Mnemonic) ToStandardECDSAsecp256k1PrivateKey(passPhrase string, index uint32) (PrivateKey, error) { 384 seed := m._ToSeed(passPhrase) 385 derivedKey, err := _ECDSAPrivateKeyFromSeed(seed) 386 if err != nil { 387 return PrivateKey{}, err 388 } 389 390 keyBytes, chainCode := derivedKey.keyData.D.Bytes(), derivedKey.chainCode 391 for _, i := range []uint32{ 392 ToHardenedIndex(44), 393 ToHardenedIndex(3030), 394 ToHardenedIndex(0), 395 0, 396 index} { 397 keyBytes, chainCode, err = _DeriveECDSAChildKey(keyBytes, chainCode, i) 398 if err != nil { 399 return PrivateKey{}, err 400 } 401 } 402 403 privateKey, err := _ECDSAPrivateKeyFromBytes(keyBytes) 404 if err != nil { 405 return PrivateKey{}, err 406 } 407 408 privateKey.chainCode = chainCode 409 410 return PrivateKey{ 411 ecdsaPrivateKey: privateKey, 412 }, nil 413 } 414 415 func _ConvertRadix(nums []int, fromRadix int, toRadix int, toLength int) []uint8 { 416 num := big.NewInt(0) 417 418 for _, element := range nums { 419 num = num.Mul(num, big.NewInt(int64(fromRadix))) 420 num = num.Add(num, big.NewInt(int64(element))) 421 } 422 423 result := make([]uint8, toLength) 424 425 for i := toLength - 1; i >= 0; i-- { 426 tem := new(big.Int).Div(num, big.NewInt(int64(toRadix))) 427 rem := new(big.Int).Mod(num, big.NewInt(int64(toRadix))) 428 num = num.Set(tem) 429 result[i] = uint8(rem.Uint64()) 430 } 431 432 return result 433 } 434 435 func _Crc8(data []uint8) uint8 { 436 var crc uint8 437 crc = 0xff 438 439 for i := 0; i < len(data)-1; i++ { 440 crc ^= data[i] 441 for j := 0; j < 8; j++ { 442 var temp uint8 443 if crc&1 == 0 { 444 temp = 0 445 } else { 446 temp = 0xb2 447 } 448 crc = crc>>1 ^ temp 449 } 450 } 451 452 return crc ^ 0xff 453 }