commit
1fe623e02d
110
.gitignore
vendored
Normal file
110
.gitignore
vendored
Normal 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
21
LICENSE
@ -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
30
README.md
Normal 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/)
|
94
src/niceware.tests/NicewareTests.cs
Normal file
94
src/niceware.tests/NicewareTests.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
14
src/niceware.tests/niceware.tests.csproj
Normal file
14
src/niceware.tests/niceware.tests.csproj
Normal 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>
|
8
src/niceware/AssemblyInfo.cs
Normal file
8
src/niceware/AssemblyInfo.cs
Normal 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
89
src/niceware/Niceware.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65556
src/niceware/Niceware.wordlist.cs
Normal file
65556
src/niceware/Niceware.wordlist.cs
Normal file
File diff suppressed because it is too large
Load Diff
27
src/niceware/niceware.csproj
Normal file
27
src/niceware/niceware.csproj
Normal 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>
|
Loading…
Reference in New Issue
Block a user