Merge pull request #1 from trpalmer/initial-port

Initial Port
This commit is contained in:
Todd Palmer 2017-03-26 23:33:28 -06:00 committed by GitHub
commit 1fe623e02d
9 changed files with 65928 additions and 21 deletions

110
.gitignore vendored Normal file
View File

@ -0,0 +1,110 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# JetBrains Rider
.idea/
*.sln.iml
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2017 Todd Palmer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

30
README.md Normal file
View File

@ -0,0 +1,30 @@
## Niceware for .NET
A .NET port of [Niceware](https://github.com/diracdeltas/niceware) for generating random-yet-memorable passwords. Each word provides 16 bits of entropy, so a useful password requires at least 3 words.
Because the wordlist is of exactly size 2^16, Niceware is also useful for converting cryptographic keys and other sequences of random bytes into human-readable phrases. With Niceware, a 128-bit key is equivalent to an 8-word phrase.
* Free software: MIT license
* Supports .NET Standard (core), .NET 4.6
## Usage
To Install:
Install via Nuget package manager:
```
PM> Install-Package niceware
```
To generate an 8-byte passphrase:
``` c#
using niceware;
var passphrase = Niceware.GeneratePassphrase(8);
// result: ["unpeopling", "whipsawing", "sought", "rune"]
```
## Credits
Niceware for .NET is a port of [Niceware](https://github.com/diracdeltas/niceware) by [yan](https://diracdeltas.github.io/blog/about/)

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using Xunit;
using niceware;
namespace niceware.tests
{
public class NicewareTests
{
[Theory]
[MemberData(nameof(ToPassphraseData))]
public void ToPassphrase(byte[] bytes, IEnumerable<string> expected)
{
var actual = bytes.ToPassphrase();
Assert.Equal(expected, actual);
}
public static IEnumerable<object[]> ToPassphraseData()
{
yield return new object[] { new byte[0], new string[0] };
yield return new object[] { new byte[] { 0, 0}, new [] { "a" } };
yield return new object[] { new byte[] { 255, 255}, new [] { "zyzzyva" } };
yield return new object[]
{
new byte[] { 0, 0, 17, 212, 12, 140, 90, 247, 46, 83, 254, 60, 54, 169, 255, 255 },
new [] { "a", "bioengineering", "balloted", "gobbledegook", "creneled", "written", "depriving", "zyzzyva" }
};
}
[Theory]
[InlineData(2, 1)]
[InlineData(0, 0)]
[InlineData(8, 4)]
[InlineData(20, 10)]
[InlineData(512, 256)]
public void GeneratePassphrase(int length, int expectedWords)
{
var actual = Niceware.GeneratePassphrase(length);
Assert.Equal(expectedWords, actual.Count);
}
[Theory]
[InlineData(1)]
[InlineData(23)]
public void GeneratePassphrase_OddBytes(int length)
{
var ex = Assert.Throws<ArgumentException>(() => Niceware.GeneratePassphrase(length));
Assert.Equal("Only even-sized byte arrays are supported", ex.Message);
}
[Theory]
[InlineData(1025)]
[InlineData(-1)]
public void GeneratePassphrase_OutOfRange(int length)
{
var ex = Assert.Throws<ArgumentException>(() => Niceware.GeneratePassphrase(length));
Assert.Equal($"Size must be between 0 and {Niceware.MaxPassphraseSize} bytes", ex.Message);
}
[Theory]
[MemberData(nameof(PassphraseToBytesData))]
public void PassphraseToBytes(IEnumerable<string> passphrase, byte[] expected)
{
var actual = passphrase.PassphraseToBytes();
Assert.Equal(expected, actual);
}
public static IEnumerable<object[]> PassphraseToBytesData()
{
yield return new object[] { new [] { "A" }, new byte[] { 0, 0 } };
yield return new object[] { new [] { "zyzzyva" }, new byte[] { 255, 255 } };
yield return new object[]
{
new [] { "A", "bioengineering", "Balloted", "gobbledegooK", "cReneled", "wriTTen", "depriving", "zyzzyva" },
new byte[] { 0, 0, 17, 212, 12, 140, 90, 247, 46, 83, 254, 60, 54, 169, 255, 255 }
};
}
[Fact]
public void PassphraseToBytes_NullOk()
{
var actual = ((string[])null).PassphraseToBytes();
Assert.Equal(new byte[0], actual);
}
[Fact]
public void PassphraseToBytes_InvalidWord()
{
var ex = Assert.Throws<ArgumentException>(() => new [] { "You", "love", "ninetales" }.PassphraseToBytes());
Assert.StartsWith("Invalid word", ex.Message);
}
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<ProjectReference Include="../niceware/niceware.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyProduct("Niceware")]
[assembly: AssemblyVersion("0.1.0.0")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0.0")]

89
src/niceware/Niceware.cs Normal file
View File

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace niceware
{
public static partial class Niceware
{
private static Random _random = new Random((int)DateTime.Now.Ticks);
/// <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];
_random.NextBytes(bytes);
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;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.4;net45</TargetFrameworks>
<DebugType>portable</DebugType>
<AssemblyName>Niceware</AssemblyName>
<OutputType>Library</OutputType>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<PackageVersion>0.1.0.0</PackageVersion>
<Title>Niceware for .NET</Title>
<Authors>Todd Palmer</Authors>
<Description>A library for generating random-yet-memorable passwords</Description>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>niceware passphrase</PackageTags>
<PackageProjectUrl>https://github.com/trpalmer/dotnet-niceware</PackageProjectUrl>
<RepositoryUrl>https://github.com/trpalmer/dotnet-niceware</RepositoryUrl>
</PropertyGroup>
</Project>