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  }