dotnet-niceware-secure/src/niceware/Niceware.cs

90 lines
3.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace niceware
{
public static partial class Niceware
{
/// <summary>Maximum number of bytes for a passphrase</summary>
public const int MaxPassphraseSize = 1024;
/// <summary>Convert an array of bytes into a passphrase</summary>
/// <param name="bytes">Bytes to convert</param>
/// <returns>A passphrase</returns>
/// <remarks>Must be a even size array of bytes</remarks>
public static List<string> ToPassphrase(this byte[] bytes)
{
if (bytes.Length % 2 == 1)
{
throw new ArgumentException("Only even-sized byte arrays are supported");
}
var result = new List<string>();
for (var i = 0; i < bytes.Length; i = i + 2)
{
result.Add(WordList[bytes[i] * 256 + bytes[i + 1]]);
}
return result;
}
/// <summary>Generates a random passphrase with the specified number of bytes</summary>
/// <param name="size">Number of random bytes to use.</param>
/// <returns>A passphrase</returns>
/// <remarks>Size must be an even number</remarks>
public static List<string> GeneratePassphrase(int size)
{
return Generate(size).Item1;
}
/// <summary>Generates random bytes corresponding passphrase with the specified number of bytes</summary>
/// <param name="size">Number of random bytes to use.</param>
/// <returns>Tuple of the passphrase and generated bytes</returns>
/// <remarks>Size must be an even number and less than <see cref="MaxPassphraseSize" /></remarks>
public static Tuple<List<string>, 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<List<string>, byte[]>(bytes.ToPassphrase(), bytes);
}
/// <summary>Converts a phrase back into the original byte array</summary>
/// <param name="passphrase">Passphrase</param>
/// <returns>Array of bytes</returns>
/// <remarks>Throws an ArgumentException if a word is not in the list</remarks>
public static byte[] PassphraseToBytes(this IEnumerable<string> 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;
}
}
}