using System; using System.Collections.Generic; using System.Linq; namespace niceware { public static partial class Niceware { /// Maximum number of bytes for a passphrase public const int MaxPassphraseSize = 1024; /// Convert an array of bytes into a passphrase /// Bytes to convert /// A passphrase /// Must be a even size array of bytes public static List ToPassphrase(this byte[] bytes) { if (bytes.Length % 2 == 1) { throw new ArgumentException("Only even-sized byte arrays are supported"); } var result = new List(); for (var i = 0; i < bytes.Length; i = i + 2) { result.Add(WordList[bytes[i] * 256 + bytes[i + 1]]); } return result; } /// Generates a random passphrase with the specified number of bytes /// Number of random bytes to use. /// A passphrase /// Size must be an even number public static List GeneratePassphrase(int size) { return Generate(size).Item1; } /// Generates random bytes corresponding passphrase with the specified number of bytes /// Number of random bytes to use. /// Tuple of the passphrase and generated bytes /// Size must be an even number and less than public static Tuple, byte[]> Generate(int size) { if (size < 0 || size > MaxPassphraseSize) { throw new ArgumentException($"Size must be between 0 and {MaxPassphraseSize} bytes"); } var bytes = new Byte[size]; var rng = System.Security.Cryptography.RandomNumberGenerator.Create(); rng.GetBytes(bytes); rng.Dispose(); return new Tuple, byte[]>(bytes.ToPassphrase(), bytes); } /// Converts a phrase back into the original byte array /// Passphrase /// Array of bytes /// Throws an ArgumentException if a word is not in the list public static byte[] PassphraseToBytes(this IEnumerable passphrase) { if (passphrase == null) { return new byte[0]; } var result = new byte[passphrase.Count() * 2]; for (int i = 0; i < passphrase.Count(); i++) { int wordIndex = -1; var word = passphrase.ElementAtOrDefault(i)?.ToLower(); if (word != null) { wordIndex = WordList.IndexOf(word); } if (wordIndex == -1) { throw new ArgumentException($"Invalid word {word}"); } result[2 * i] = (byte)Math.Floor(wordIndex / 256d); result[2 * i + 1] = (byte)(wordIndex % 256); } return result; } } }