146 lines
3.7 KiB
Go
146 lines
3.7 KiB
Go
|
|
// 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
|
|
}
|
|
|
|
|