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  }