/// <summary> /// Obtains the word list for a given language for mnemonic phrases, or if it does not exist, /// creates one from the given words. /// </summary> /// <param name="language">The language of the word list to obtain.</param> /// <param name="words">The array of words to use to create the word list if one was not already parsed.</param> /// <returns>Returns a word list for the given language, used for mnemonic phrases.</returns> public static WordList GetWordList(WordListLanguage language, string[] words) { // Try to get our word list bool success = _cachedWordLists.TryGetValue(language, out WordList result); if (success) { return(result); } // If we couldn't get it, parse a new one WordList wordList = new WordList(language, words); // Set it in our lookup _cachedWordLists[language] = wordList; // Return our word list. return(wordList); }
/// <summary> /// Obtains the word list for a given language for mnemonic phrases, or if it does not exist, /// reads one from the given filename. /// </summary> /// <param name="language">The language of the word list to obtain.</param> /// <param name="fileName">The filename of the word list to read from if one was not already parsed.</param> /// <returns>Returns a word list for the given language, used for mnemonic phrases.</returns> public static WordList GetWordList(WordListLanguage language, string fileName) { // Try to get our word list bool success = _cachedWordLists.TryGetValue(language, out WordList result); if (success) { return(result); } // If we couldn't get it, parse a new one string allText = File.ReadAllText(fileName); WordList wordList = new WordList(language, allText); // Set it in our lookup _cachedWordLists[language] = wordList; // Return our word list. return(wordList); }
/// <summary> /// Initializes a mnemonic phrase from the given mnemonic string. /// </summary> /// <param name="mnemonic">The mnemonic string to initialize this mnemonic phrase from, for key derivation.</param> public MnemonicPhrase(string mnemonic) { // If our argument is null, we throw an exception, otherwise we set our mnemonic string. MnemonicString = mnemonic.Trim() ?? throw new ArgumentException("Provided mnemonic string was null or empty."); // Determine the word list (language) the mnemonic string belongs to by checking indicies. WordList = WordList.GetWordList(MnemonicString); // Obtain our words from our mnemonic. MnemonicWords = WordList.SplitMnemonic(MnemonicString); // Verify our word count is valid if (MnemonicWords.Length < MNEMONIC_WORDS_MINIMUM || MnemonicWords.Length > MNEMONIC_WORDS_MAXIMUM) { throw new ArgumentException($"Provided entropy data of this size would represent {MnemonicWords.Length} words. Should be between {MNEMONIC_WORDS_MINIMUM}-{MNEMONIC_WORDS_MAXIMUM} (inclusive)."); } // Initialize our indices array. Indices = new int[MnemonicWords.Length]; // Loop for each index we want to resolve for the words in our mnemonics for (int i = 0; i < Indices.Length; i++) { // Determine the word index for the indexed word. int currentWordIndex = WordList.GetWordIndex(MnemonicWords[i]); // If the index is invalid, throw an exception. if (currentWordIndex < 0) { throw new ArgumentException($"Could not resolve word \"{MnemonicWords[i]}\" in word list for language \"{WordList.Language}\"."); } // Set the word index. Indices[i] = currentWordIndex; } }
/// <summary> /// Obtains the word list for a given language for mnemonic phrases, or if it does not exist, /// parses one from the embedded word list resources in this assembly. /// </summary> /// <param name="language">The language of the word list to obtain.</param> /// <returns>Returns a word list for the given language, used for mnemonic phrases.</returns> public static WordList GetWordList(WordListLanguage language) { lock (_cachedWordLists) { // Try to get our word list bool success = _cachedWordLists.TryGetValue(language, out WordList result); if (success) { return(result); } // Obtain the appropriate word list from our resource files. Assembly currentAssembly = Assembly.GetExecutingAssembly(); string resourceName = null; switch (language) { case WordListLanguage.Chinese_Simplified: resourceName = "wordlist_chinese_simplified.txt"; break; case WordListLanguage.Chinese_Traditional: resourceName = "wordlist_chinese_traditional.txt"; break; case WordListLanguage.English: resourceName = "wordlist_english.txt"; break; case WordListLanguage.French: resourceName = "wordlist_french.txt"; break; case WordListLanguage.Italian: resourceName = "wordlist_italian.txt"; break; case WordListLanguage.Japanese: resourceName = "wordlist_japanese.txt"; break; case WordListLanguage.Korean: resourceName = "wordlist_korean.txt"; break; case WordListLanguage.Spanish: resourceName = "wordlist_spanish.txt"; break; } // If our resource name is null, throw an exception if (resourceName == null) { throw new ArgumentException($"Could not obtain an embedded word list for language \"{resourceName}\"."); } // Obtain our full resource name (it should end with the resource filename). resourceName = currentAssembly.GetManifestResourceNames().First(x => { int targetIndex = x.Length - resourceName.Length; if (targetIndex < 0) { return(false); } return(x.LastIndexOf(resourceName, StringComparison.OrdinalIgnoreCase) == targetIndex); }); // Open a stream on the resource Stream stream = currentAssembly.GetManifestResourceStream(resourceName); // Read all the text from the stream. StreamReader streamReader = new StreamReader(stream); string allText = streamReader.ReadToEnd(); streamReader.Close(); stream.Close(); // Create a word list instance WordList wordList = new WordList(language, allText); // Set it in our cached word lists _cachedWordLists[language] = wordList; // Return our word list return(wordList); } }
/// <summary> /// Initializes a mnemonic phrase for the given language, with the optionally given entropy data (or newly generated data if none is provided). /// </summary> /// <param name="wordListLanguage">The language to create a mnemonic phrase in.</param> /// <param name="entropy">The optionally given entropy data to generate a mnemonic phrase from. If null, generates new entropy data of the maximum size.</param> public MnemonicPhrase(WordListLanguage wordListLanguage, byte[] entropy = null) { // Set our word list WordList = WordList.GetWordList(wordListLanguage); // If our entropy is null, initialize a new set. if (entropy == null) { // Create a buffer of random bytes (entropy) using our RNG. int maximumEntropySize = (MNEMONIC_ENTROPY_BITS_PER_CHECKSUM_BIT * (MNEMONIC_WORDS_MAXIMUM / MNEMONIC_WORDS_PER_CHECKSUM_BIT)) / 8; entropy = new byte[maximumEntropySize]; _random.GetBytes(entropy); } // Verify our entropy size is divisible by the range of a checksum. int entropySizeBits = (entropy.Length * 8); if (entropySizeBits % MNEMONIC_ENTROPY_BITS_PER_CHECKSUM_BIT != 0) { throw new ArgumentException($"Provided entropy data must be divisible by {MNEMONIC_ENTROPY_BITS_PER_CHECKSUM_BIT}"); } // Calculate our component sizes. int checksumBitcount = entropySizeBits / MNEMONIC_ENTROPY_BITS_PER_CHECKSUM_BIT; int wordCount = checksumBitcount * MNEMONIC_WORDS_PER_CHECKSUM_BIT; // Verify our word count is valid if (wordCount < MNEMONIC_WORDS_MINIMUM || wordCount > MNEMONIC_WORDS_MAXIMUM) { throw new ArgumentException($"Provided entropy data of this size would represent {wordCount} words. Should be between {MNEMONIC_WORDS_MINIMUM}-{MNEMONIC_WORDS_MAXIMUM} (inclusive)."); } // Calculate our SHA256 hash of the entropy. byte[] checksum = sha256Provider.ComputeHash(entropy); // Now we write the entropy followed by the checksum bits (which we will use to interpret a mnemonic from later). BitStream bitStream = new BitStream(); bitStream.WriteBytes(entropy); bitStream.WriteBytes(checksum, checksumBitcount); // Move back to the start of the data bitStream.Position = 0; bitStream.BitPosition = 0; // Read every word index, which are represented by 11-bit unsigned integers. At the same time, we resolve our mnemonic words. int wordIndexCount = (int)(bitStream.BitLength / MNEMONIC_WORD_INDEX_BITCOUNT); Indices = new int[wordIndexCount]; MnemonicWords = new string[Indices.Length]; for (int i = 0; i < Indices.Length; i++) { Indices[i] = (int)bitStream.ReadUInt64(MNEMONIC_WORD_INDEX_BITCOUNT); MnemonicWords[i] = WordList.Words[Indices[i]]; } // Close the bitstream. bitStream.Close(); // Join all strings into a mnemonic sentence. MnemonicString = WordList.JoinMnemonic(MnemonicWords); }