private static string[] GetWordList(WordListLanguage wordList) { var wordLists = new Dictionary <string, string> { { WordListLanguage.ChineseSimplified.ToString(), "chinese_simplified" }, { WordListLanguage.ChineseTraditional.ToString(), "chinese_traditional" }, { WordListLanguage.English.ToString(), "english" }, { WordListLanguage.French.ToString(), "french" }, { WordListLanguage.Italian.ToString(), "italian" }, { WordListLanguage.Japanese.ToString(), "japanese" }, { WordListLanguage.Korean.ToString(), "korean" }, { WordListLanguage.Spanish.ToString(), "spanish" } }; var wordListFile = wordLists[wordList.ToString()]; var wordListFileStream = Assembly.GetAssembly(typeof(WordListLanguage)) .GetManifestResourceStream($"{typeof(WordListLanguage).Namespace}.Words.{wordListFile}.txt"); var words = new List <string>(); using (StreamReader reader = new StreamReader(wordListFileStream ?? throw new InvalidOperationException($"could not load word list for {wordList}"))) { while (reader.Peek() >= 0) { words.Add(reader.ReadLine()); } } var wordListResults = words.ToArray(); return(wordListResults); }
public static string EntropyToMnemonic(string entropy, WordListLanguage wordListType) { var wordList = GetWordList(wordListType); //How can I do this more efficiently, the multiple substrings I don't like... var entropyBytes = Enumerable.Range(0, entropy.Length / 2) .Select(x => Convert.ToByte(entropy.Substring(x * 2, 2), 16)) .ToArray(); CheckValidEntropy(entropyBytes); var entropyBits = BytesToBinary(entropyBytes); var checksumBits = DeriveChecksumBits(entropyBytes); var bits = entropyBits + checksumBits; var chunks = Regex.Matches(bits, "(.{1,11})") .OfType <Match>() .Select(m => m.Groups[0].Value) .ToArray(); var words = chunks.Select(binary => { var index = Convert.ToInt32(binary, 2); return(wordList[index]); }); var joinedText = String.Join((wordListType == WordListLanguage.Japanese ? "\u3000" : " "), words); return(joinedText); }
private (string entropy, string seedHex, string mnemonic) TestVector(WordListLanguage wordList, string password, string entropy, string mnemonic, string seedHex) { var entropyResult = Mnemonic.MnemonicToEntropy(mnemonic, wordList); var seedResult = Mnemonic.MnemonicToSeedHex(mnemonic, password); var mnemonicResult = Mnemonic.EntropyToMnemonic(entropy, wordList); return(entropyResult, seedResult, mnemonicResult); }
public static string MnemonicToEntropy(string mnemonic, WordListLanguage wordListType) { var wordList = GetWordList(wordListType); var words = mnemonic.Normalize(NormalizationForm.FormKD).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (words.Length % 3 != 0) { throw new ArgumentException(InvalidMnemonic); } var bits = string.Join("", words.Select(word => { var index = Array.IndexOf(wordList, word); if (index == -1) { throw new ArgumentException(InvalidMnemonic); } return(LeftPad(Convert.ToString(index, 2), "0", 11)); })); // split the binary string into ENT/CS var dividerIndex = (int)Math.Floor((double)bits.Length / 33) * 32; var entropyBits = bits.Substring(0, dividerIndex); var checksumBits = bits.Substring(dividerIndex); // calculate the checksum and compare var entropyBytesMatch = Regex.Matches(entropyBits, "(.{1,8})") .OfType <Match>() .Select(m => m.Groups[0].Value) .ToArray(); var entropyBytes = entropyBytesMatch .Select(bytes => Convert.ToByte(bytes, 2)).ToArray(); CheckValidEntropy(entropyBytes); var newChecksum = DeriveChecksumBits(entropyBytes); if (newChecksum != checksumBits) { throw new Exception(InvalidChecksum); } var result = BitConverter .ToString(entropyBytes) .Replace("-", "") .ToLower(); return(result); }
public static bool ValidateMnemonic(string mnemonic, WordListLanguage wordList) { try { MnemonicToEntropy(mnemonic, wordList); } catch { return(false); } return(true); }
public static string GenerateMnemonic(int strength, WordListLanguage wordListType) { if (strength % 32 != 0) { throw new NotSupportedException(InvalidEntropy); } RNGCryptoServiceProvider rngCryptoServiceProvider = new RNGCryptoServiceProvider(); byte[] buffer = new byte[strength / 8]; rngCryptoServiceProvider.GetBytes(buffer); var entropyHex = BitConverter.ToString(buffer).Replace("-", ""); return(EntropyToMnemonic(entropyHex, wordListType)); }
/// <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); }
public static HDAccountDerivation Create(WordListLanguage language = WordListLanguage.English) { return(new HDAccountDerivation(new MnemonicPhrase(language))); }
/// <summary> /// Initializes a word list to represent the given language, with the given words. /// </summary> /// <param name="language">The language which this word list will represent.</param> /// <param name="words">The words which constitute the word list to initialize.</param> private WordList(WordListLanguage language, string words) : this(language) { // Split our words out Words = words.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); }
/// <summary> /// Initializes a word list to represent the given language, with the given words. /// </summary> /// <param name="language">The language which this word list will represent.</param> /// <param name="words">The words which constitute the word list to initialize.</param> private WordList(WordListLanguage language, string[] words) : this(language) { // Set our words (normalized). Words = words.Select(s => MnemonicPhrase.NormalizeString(s)).ToArray(); }
/// <summary> /// Base constructor to initialize a word list to represent the given language. /// </summary> /// <param name="language">The language which this word list will represent.</param> private WordList(WordListLanguage language) { // Set our language Language = language; }
/// <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); }