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;
}
}
}