Skip to content

Commit

Permalink
Closes #15
Browse files Browse the repository at this point in the history
Implements Argon2i and Scrypt key derivation functions, tests, and
documentation
  • Loading branch information
charlesportwoodii committed Nov 1, 2016
1 parent 0dfc6e8 commit 1593edf
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 6 deletions.
8 changes: 6 additions & 2 deletions .nuget/libsodium-uwp.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>libsodium-uwp</id>
<version>1.0.0-alpha6</version>
<version>1.0.0-rc1</version>
<title>libsodium for UWP</title>
<authors>Charles R. Portwood II (charlesportwoodii)</authors>
<licenseUrl>https://github.com/charlesportwoodii/libsodium-uwp/blob/master/LICENSE.md</licenseUrl>
Expand All @@ -12,23 +12,27 @@
<releaseNotes>Initial release. Please see README.md for full installation instructions.</releaseNotes>
<copyright>Copyright (c) 2016 Charles R. Portwood II &amp; Contributors</copyright>
<language>en-US</language>
<tags>libsodium NaCL encryption cryptography XSalsa20 Curve25519 Ed25519 Poly1305 hash kdf pbdfk2 hkdf Agon2i Scrypt Bcrypt</tags>
<tags>libsodium NaCL encryption cryptography XSalsa20 Curve25519 Ed25519 Poly1305 hash kdf pbdfk2 hkdf Agon2i Scrypt</tags>
</metadata>
<files>
<file src="..\README.md" target="" />
<!-- Visual Studio Targets -->
<file src="libsodium-uwp.targets" target="build\native" />

<!-- WinMd and IntelliSense -->
<file src="..\Release\libsodium-uwp\Sodium.winmd" target="lib\uap10.0"/>
<file src="..\Release\libsodium-uwp\Sodium.xml" target="lib\uap10.0"/>

<!-- x86 runtime DLL, PRI -->
<file src="..\Release\libsodium-uwp\*.dll" target="runtimes\win10-x86\native" />
<file src="..\Release\libsodium-uwp\*.pri" target="runtimes\win10-x86\native" />
<file src="..\Release\libsodium-uwp\*.lib" target="runtimes\win10-x86\native" />

<!-- x64 runtime DLL, PRI -->
<file src="..\x64\Release\libsodium-uwp\*.dll" target="runtimes\win10-x64\native" />
<file src="..\x64\Release\libsodium-uwp\*.pri" target="runtimes\win10-x64\native" />
<file src="..\x64\Release\libsodium-uwp\*.lib" target="runtimes\win10-x64\native" />

<!-- ARM runtime DLL, PRI -->
<file src="..\ARM\Release\libsodium-uwp\*.dll" target="runtimes\win10-arm\native" />
<file src="..\ARM\Release\libsodium-uwp\*.pri" target="runtimes\win10-arm\native" />
Expand Down
40 changes: 39 additions & 1 deletion Test/KDFTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using Sodium;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Windows.Security.Cryptography.Core;
Expand Down Expand Up @@ -334,5 +334,43 @@ public void HSalsa20Test()
Assert.AreEqual(Convert.ToBase64String(expected), Convert.ToBase64String(result));
Assert.IsTrue(result.Length == 32);
}

[TestCategory("Argon2i")]
[TestMethod]
public void Argon2iTest()
{
string password = "correct horse battery staple";
var options = new PasswordHashOptions
{
time_cost = 3,
memory_cost = 1<<8
};

var result = Sodium.KDF.Argon2i(password, options);
Assert.AreEqual(32, result.Length);

var salt = Sodium.Core.GetRandomBytes(32);
result = Sodium.KDF.Argon2i(password, salt, options);
Assert.AreEqual(32, result.Length);
}

[TestCategory("Scrypt")]
[TestMethod]
public void ScryptTest()
{
string password = "correct horse battery staple";
var options = new PasswordHashOptions
{
time_cost = 1 << 8,
memory_cost = 1 << 8
};

var result = Sodium.KDF.Scrypt(password, options);
Assert.AreEqual(32, result.Length);

var salt = Sodium.Core.GetRandomBytes(32);
result = Sodium.KDF.Scrypt(password, salt, options);
Assert.AreEqual(32, result.Length);
}
}
}
32 changes: 31 additions & 1 deletion docs/KDF.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,34 @@ __Namespace:__ _Sodium.KDF_
public static byte[] Sodium.KDF.HSalsa20(byte[] in, byte[] k, byte[] c);
```

_Internally this function uses `crypto_core_hsalsa20`._
_Internally this function uses `crypto_core_hsalsa20`._

## Argon2i Key Derivation

__Namespace:__ _Sodium.KDF_

```C#
public static byte[] Sodium.KDF.Argon2i(string password, PasswordHashOptions options)
public static byte[] Sodium.KDF.Argon2i(string password, byte[] salt, PasswordHashOptions options)
```

This method will generate a 32 byte key using Argon2i provided a string `password` and `PasswordHashOptions` `options`. For more information about `PasswordHashOptions`, and acceptable parameters, see [PasswordHash](PasswordHash.md);

If a salt is not provided, a 16 byte salt will be generated.

_Internally this method uses `crypto_pwhash`._

## Scrypt Key Derivation

__Namespace:__ _Sodium.KDF_

```C#
public static byte[] Sodium.KDF.Scrypt(string password, PasswordHashOptions options)
public static byte[] Sodium.KDF.Scrypt(string password, byte[] salt, PasswordHashOptions options)
```

This method will generate a 32 byte key using Scrypt provided a string `password` and `PasswordHashOptions` `options`. For more information about `PasswordHashOptions`, and acceptable parameters, see [PasswordHash](PasswordHash.md);

If a salt is not provided, a 32 byte salt will be generated.

_Internally this method uses `crypto_pwhash_scryptsalsa208sha256`._
120 changes: 120 additions & 0 deletions libsodium-uwp/KDF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,123 @@ Array<unsigned char>^ Sodium::KDF::HSalsa20(const Array<unsigned char>^ in, cons

return out;
}

/// <summary>Argon2i key derivation</summary>
/// <param name="password">The string password</param>
/// <param name="salt">A 32 byte salt</param>
/// <param name="options">PasswordHash options</param>
/// <returns>32 byte key</returns>
Array<unsigned char>^ Sodium::KDF::Argon2i(String^ password, const Array<unsigned char>^ salt, PasswordHashOptions options)
{
// If the salt isn't provided, generate a random salt value
if (salt->Length == 0 || salt == nullptr) {
salt = Sodium::Core::GetRandomBytes(crypto_pwhash_SALTBYTES);
}

if (salt->Length != crypto_pwhash_SALTBYTES) {
throw ref new Platform::InvalidArgumentException("Salt must be " + crypto_pwhash_SALTBYTES + " bytes in length");
}

if (options.memory_cost <= 0) {
throw ref new Platform::InvalidArgumentException("options.memory_cost must be greater than 0");
}

if (options.time_cost < 3) {
throw ref new Platform::InvalidArgumentException("options.time_cost must be greater than 3");
}

Array<unsigned char>^ key = ref new Array<unsigned char>(crypto_box_SEEDBYTES);
std::string sPassword(password->Begin(), password->End());

int result = crypto_pwhash(
key->Data,
key->Length,
sPassword.c_str(),
strlen(sPassword.c_str()),
salt->Data,
options.time_cost,
(options.memory_cost * 1024U),
crypto_pwhash_ALG_DEFAULT
);

if (result != 0) {
throw ref new Platform::Exception(0, "Out of memory");
}


return key;
}

/// <summary>Argon2i key derivation</summary>
/// <param name="password">The string password</param>
/// <param name="options">PasswordHash options</param>
/// <returns>32 byte key</returns>
Array<unsigned char>^ Sodium::KDF::Argon2i(String^ password, PasswordHashOptions options)
{
Array<unsigned char>^ salt = Sodium::Core::GetRandomBytes(crypto_pwhash_SALTBYTES);
return Sodium::KDF::Argon2i(
password,
salt,
options
);
}

/// <summary>The scrypt Password-Based Key Derivation Function</summary>
/// <remarks>https://tools.ietf.org/html/rfc7914.html</remarks>
/// <param name="password">The string password</param>
/// <param name="salt">A 32 byte salt</param>
/// <param name="options">PasswordHash options</param>
/// <returns>32 byte key</returns>
Array<unsigned char>^ Sodium::KDF::Scrypt(String^ password, const Array<unsigned char>^ salt, PasswordHashOptions options)
{
// If the salt isn't provided, generate a random salt value
if (salt->Length == 0 || salt == nullptr) {
salt = Sodium::Core::GetRandomBytes(crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
}

if (salt->Length != crypto_pwhash_scryptsalsa208sha256_SALTBYTES) {
throw ref new Platform::InvalidArgumentException("Salt must be " + crypto_pwhash_scryptsalsa208sha256_SALTBYTES + " bytes in length");
}

if (options.memory_cost <= 0) {
throw ref new Platform::InvalidArgumentException("options.memory_cost must be greater than 0");
}

if (options.time_cost <= 0) {
throw ref new Platform::InvalidArgumentException("options.time_cost must be greater than 0");
}

Array<unsigned char>^ key = ref new Array<unsigned char>(crypto_box_SEEDBYTES);
std::string sPassword(password->Begin(), password->End());

int result = crypto_pwhash_scryptsalsa208sha256(
key->Data,
key->Length,
sPassword.c_str(),
strlen(sPassword.c_str()),
salt->Data,
options.time_cost,
(options.memory_cost * 1024U)
);

if (result != 0) {
throw ref new Platform::Exception(0, "Out of memory");
}

return key;
}

/// <summary>The scrypt Password-Based Key Derivation Function</summary>
/// <remarks>https://tools.ietf.org/html/rfc7914.html</remarks>
/// <param name="password">The string password</param>
/// <param name="options">PasswordHash options</param>
/// <returns>32 byte key</returns>
Array<unsigned char>^ Sodium::KDF::Scrypt(String^ password, PasswordHashOptions options)
{
Array<unsigned char>^ salt = Sodium::Core::GetRandomBytes(crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
return Sodium::KDF::Scrypt(
password,
salt,
options
);
}
8 changes: 8 additions & 0 deletions libsodium-uwp/KDF.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "PasswordHash.h"

using namespace Platform;
using namespace Platform::Collections;
Expand All @@ -19,9 +20,16 @@ namespace Sodium
[Windows::Foundation::Metadata::DefaultOverloadAttribute]
static Array<unsigned char>^ PBKDF2(String^ algorithm, String^ password, const Array<unsigned char>^ salt, int iterationCount, int targetSize);
static Array<unsigned char>^ PBKDF2(String^ algorithm, String^ password, String^ salt, int iterationCount, int targetSize);

[Windows::Foundation::Metadata::DefaultOverloadAttribute]
static Array<unsigned char>^ HKDF(String^ algorithm, const Array<unsigned char>^ ikm, const Array<unsigned char>^ salt, const Array<unsigned char>^ info, int outputLength);
static Array<unsigned char>^ HKDF(String^ algorithm, const Array<unsigned char>^ ikm, const Array<unsigned char>^ salt, String^ info, int outputLength);
static Array<unsigned char>^ HSalsa20(const Array<unsigned char>^ in, const Array<unsigned char>^ k, const Array<unsigned char>^ c);

static Array<unsigned char>^ Argon2i(String^ password, const Array<unsigned char>^ salt, PasswordHashOptions options);
static Array<unsigned char>^ Argon2i(String^ password, PasswordHashOptions options);

static Array<unsigned char>^ Scrypt(String^ password, const Array<unsigned char>^ salt, PasswordHashOptions options);
static Array<unsigned char>^ Scrypt(String^ password, PasswordHashOptions options);
};
}
12 changes: 10 additions & 2 deletions libsodium-uwp/PasswordHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,21 @@ String^ Sodium::PasswordHash::Hash(String^ password, int algorithm, PasswordHash
);

if (result != 0) {
throw ref new Platform::InvalidArgumentException("Out of memory");
throw ref new Platform::Exception(0, "Out of memory");
}

std::string hash_str = std::string(hash);
std::wstring whash_str = std::wstring(hash_str.begin(), hash_str.end());
return ref new Platform::String(whash_str.c_str());
} else if (algorithm == PasswordHash::Scrypt) { // Scrypt
if (options.memory_cost <= 0) {
throw ref new Platform::InvalidArgumentException("options.memory_cost must be greater than 0");
}

if (options.time_cost <= 0 ) {
throw ref new Platform::InvalidArgumentException("options.time_cost must be greater than 0");
}

char hash[crypto_pwhash_scryptsalsa208sha256_STRBYTES];
int result = crypto_pwhash_scryptsalsa208sha256_str(
hash,
Expand All @@ -62,7 +70,7 @@ String^ Sodium::PasswordHash::Hash(String^ password, int algorithm, PasswordHash
);

if (result != 0) {
throw ref new Platform::InvalidArgumentException("Out of memory");
throw ref new Platform::Exception(0, "Out of memory");
}

std::string hash_str = std::string(hash);
Expand Down
23 changes: 23 additions & 0 deletions libsodium-uwp/libsodium-uwp.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@
<PreBuildEvent>
<Command>copy "$(ProjectDir)\libsodium\builds\msvc\version.h" "$(ProjectDir)\libsodium\src\libsodium\include\sodium\"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
Expand Down Expand Up @@ -187,6 +191,9 @@
<PreBuildEvent>
<Command>copy "$(ProjectDir)\libsodium\builds\msvc\version.h" "$(ProjectDir)\libsodium\src\libsodium\include\sodium\"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>copy "$(ProjectDir)..\Release\libsodium-uwp\libsodium-uwp.xml" "$(ProjectDir)..\Release\libsodium-uwp\Sodium.xml"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
Expand Down Expand Up @@ -214,6 +221,10 @@
<PreBuildEvent>
<Command>copy "$(ProjectDir)\libsodium\builds\msvc\version.h" "$(ProjectDir)\libsodium\src\libsodium\include\sodium\"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
Expand Down Expand Up @@ -241,6 +252,10 @@
<PreBuildEvent>
<Command>copy "$(ProjectDir)\libsodium\builds\msvc\version.h" "$(ProjectDir)\libsodium\src\libsodium\include\sodium\"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
Expand Down Expand Up @@ -268,6 +283,10 @@
<PreBuildEvent>
<Command>copy "$(ProjectDir)\libsodium\builds\msvc\version.h" "$(ProjectDir)\libsodium\src\libsodium\include\sodium\"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
Expand Down Expand Up @@ -295,6 +314,10 @@
<PreBuildEvent>
<Command>copy "$(ProjectDir)\libsodium\builds\msvc\version.h" "$(ProjectDir)\libsodium\src\libsodium\include\sodium\"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="GenericHash.h" />
Expand Down

0 comments on commit 1593edf

Please sign in to comment.