// seedPhrase provides functions to read/create seed phrases, and derive seed phrase hashes package seedPhrase // Seed Phrase is 15 words, separated by " " // A seed phrase hash is a 32 bytes long blake3 hash of the seed phrase unicode bytes import "seekia/resources/wordLists" import "seekia/internal/cryptography/blake3" import "crypto/rand" import "math/big" import "errors" import "strings" func VerifySeedPhrase(inputSeedPhrase string)bool{ seedPhraseCharacterCount := len(inputSeedPhrase) if (seedPhraseCharacterCount < 44){ // There are 15 words, each word is at least 2 characters long // 15 words * 2 characters each = 30 characters // Each word is separated by a space // 30 + 14 == 44 characters return false } numberOfSpaces := 0 currentNumberOfCharacters := 0 finalIndex := seedPhraseCharacterCount - 1 for index, character := range inputSeedPhrase{ if (character == ' '){ if (currentNumberOfCharacters < 2){ // The seed phrase contains a word with less than 2 characters return false } currentNumberOfCharacters = 0 numberOfSpaces += 1 } else { currentNumberOfCharacters += 1 } if (index == finalIndex && currentNumberOfCharacters < 2){ // The seed phrase ends with a word containing less than 2 characters return false } } if (numberOfSpaces != 14){ return false } return true } func ConvertSeedPhraseToSeedPhraseHash(inputSeedPhrase string)([32]byte, error){ isValid := VerifySeedPhrase(inputSeedPhrase) if (isValid == false){ return [32]byte{}, errors.New("ConvertSeedPhraseToSeedPhraseHash called with invalid seed phrase.") } seedPhraseBytes := []byte(inputSeedPhrase) seedPhraseHash, err := blake3.Get32ByteBlake3Hash(seedPhraseBytes) if (err != nil) { return [32]byte{}, err } return seedPhraseHash, nil } // This function is slower, only use it for generating a few seed phrases // For many generations, use GetNewSeedPhraseFromWordList, which is faster //Outputs: // -string: New seed phrase // -[32]byte: New seed phrase hash // -error func GetNewRandomSeedPhrase(languageName string)(string, [32]byte, error){ wordList, err := wordLists.GetWordListFromLanguage(languageName) if (err != nil) { return "", [32]byte{}, err } newSeedPhrase, newSeedPhraseHash, err := GetNewSeedPhraseFromWordList(wordList) if (err != nil) { return "", [32]byte{}, err } return newSeedPhrase, newSeedPhraseHash, nil } // wordList must be retrieved from resources/wordLists/wordLists.go //Outputs: // -string: Seed phrase // -[32]byte: Seed phrase hash // -error func GetNewSeedPhraseFromWordList(wordList []string)(string, [32]byte, error){ lengthOfWordList := len(wordList) if (lengthOfWordList < 2048) { return "", [32]byte{}, errors.New("GetNewSeedPhraseFromWordList called with word list that is too short.") } upperLimitInt64 := int64(lengthOfWordList-1) upperLimit := big.NewInt(upperLimitInt64) // We use this to build the seed phrase var seedPhraseBuilder strings.Builder for i := 0; i < 15; i++ { randomNumber, err := rand.Int(rand.Reader, upperLimit) if (err != nil) { return "", [32]byte{}, err } wordIndex := int(randomNumber.Int64()) randomWord := wordList[wordIndex] _, err = seedPhraseBuilder.WriteString(randomWord) if (err != nil) { return "", [32]byte{}, err } if (i < 14){ // There is a space between every word, and no trailing space _, err := seedPhraseBuilder.WriteString(" ") if (err != nil) { return "", [32]byte{}, err } } } newSeedPhrase := seedPhraseBuilder.String() newSeedPhraseBytes := []byte(newSeedPhrase) newSeedPhraseHash, err := blake3.Get32ByteBlake3Hash(newSeedPhraseBytes) if (err != nil) { return "", [32]byte{}, err } return newSeedPhrase, newSeedPhraseHash, nil }