Skip to content

Commit

Permalink
mlock, and memzero plain test passwords for Argon2i and Scrypt functions
Browse files Browse the repository at this point in the history
  • Loading branch information
charlesportwoodii committed Jun 8, 2017
1 parent 24294ee commit 9f22ffa
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 101 deletions.
12 changes: 2 additions & 10 deletions Test/KDFTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,7 @@ public void HSalsa20Test()
public void Argon2iTest()
{
string password = "correct horse battery staple";
var options = new PasswordHashOptions
{
time_cost = 3,
memory_cost = 1<<8
};
var options = PasswordHash.CreateOptions(1 << 8, 3);

var result = Sodium.KDF.Argon2i(password, options);
Assert.AreEqual(32, result.Length);
Expand All @@ -359,11 +355,7 @@ public void Argon2iTest()
public void ScryptTest()
{
string password = "correct horse battery staple";
var options = new PasswordHashOptions
{
time_cost = 1 << 8,
memory_cost = 1 << 8
};
var options = PasswordHash.CreateOptions(1 << 8, 3);

var result = Sodium.KDF.Scrypt(password, options);
Assert.AreEqual(32, result.Length);
Expand Down
14 changes: 2 additions & 12 deletions Test/PasswordHashTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ public class PasswordHashTest
[TestMethod]
public void Argon2iTest()
{
// Run this test with low memory so it doesn't take forever
var options = new PasswordHashOptions
{
time_cost = 3,
memory_cost = 1<<8
};
var options = PasswordHash.CreateOptions(1 << 8, 3);

string password = "correct horse battery staple";
var hash = PasswordHash.Hash(password, PasswordHash.Argon2i, options);
Expand All @@ -28,12 +23,7 @@ public void Argon2iTest()
[TestMethod]
public void ScryptTest()
{
// Run this test with low memory so it doesn't take forever
var options = new PasswordHashOptions
{
time_cost = 1 << 10,
memory_cost = 1 << 9
};
var options = PasswordHash.CreateOptions(1 << 8, 3);

string password = "correct horse battery staple";
var hash = PasswordHash.Hash(password, PasswordHash.Scrypt, options);
Expand Down
2 changes: 2 additions & 0 deletions docs/PasswordHash.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Options can be summarized by the following structure for both Argon2i and Scrypt
var options = PasswordHash.CreateOptions(int memory, int time);
```

> Avoid calling `new PasswordHashOptions { }` directly. Use the wrapper function.
The recommended minimum values are outlined below:

__Argon2i__
Expand Down
10 changes: 10 additions & 0 deletions libsodium-uwp/KDF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ Array<unsigned char>^ Sodium::KDF::Argon2i(String^ password, const Array<unsigne
Array<unsigned char>^ key = ref new Array<unsigned char>(crypto_box_SEEDBYTES);
const Array<unsigned char>^ sPassword = Sodium::internal::StringToUnsignedCharArray(password);

sodium_mlock(sPassword->Data, sPassword->Length);

int result = crypto_pwhash(
key->Data,
key->Length,
Expand All @@ -278,6 +280,9 @@ Array<unsigned char>^ Sodium::KDF::Argon2i(String^ password, const Array<unsigne
(options.memory_cost * 1024U),
crypto_pwhash_ALG_DEFAULT
);

sodium_munlock(sPassword->Data, sPassword->Length);
sodium_memzero(sPassword->Data, sPassword->Length);

if (result != 0) {
throw ref new Platform::Exception(0, "Out of memory");
Expand Down Expand Up @@ -328,6 +333,8 @@ Array<unsigned char>^ Sodium::KDF::Scrypt(String^ password, const Array<unsigned
Array<unsigned char>^ key = ref new Array<unsigned char>(crypto_box_SEEDBYTES);
const Array<unsigned char>^ sPassword = Sodium::internal::StringToUnsignedCharArray(password);

sodium_mlock(sPassword->Data, sPassword->Length);

int result = crypto_pwhash_scryptsalsa208sha256(
key->Data,
key->Length,
Expand All @@ -338,6 +345,9 @@ Array<unsigned char>^ Sodium::KDF::Scrypt(String^ password, const Array<unsigned
(options.memory_cost * 1024U)
);

sodium_munlock(sPassword->Data, sPassword->Length);
sodium_memzero(sPassword->Data, sPassword->Length);

if (result != 0) {
throw ref new Platform::Exception(0, "Out of memory");
}
Expand Down
217 changes: 139 additions & 78 deletions libsodium-uwp/PasswordHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,61 +21,10 @@ String^ Sodium::PasswordHash::Hash(String^ password, int algorithm, PasswordHash
throw ref new Platform::InvalidArgumentException("Password must not be null");
}

char hash;
const Array<unsigned char>^ sPassword = Sodium::internal::StringToUnsignedCharArray(password);

// Argon2i
if (algorithm == PasswordHash::Argon2i) {
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");
}

char hash[crypto_pwhash_STRBYTES];

int result = crypto_pwhash_str(
hash,
(const char*)sPassword->Data,
sPassword->Length,
options.time_cost,
(options.memory_cost * 1024U)
);

if (result != 0) {
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,
(const char*)sPassword->Data,
sPassword->Length,
options.time_cost,
(options.memory_cost * 1024U)
);

if (result != 0) {
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());
return PasswordHash::HashArgon2i(password, options);
} else if (algorithm == PasswordHash::Scrypt) {
return PasswordHash::HashScrypt(password, options);
} else {
throw ref new Platform::InvalidArgumentException("Algorithm must be defined");
}
Expand All @@ -88,26 +37,11 @@ String^ Sodium::PasswordHash::Hash(String^ password, int algorithm, PasswordHash
bool Sodium::PasswordHash::Verify(String^ hash, String^ password)
{
int algorithm = PasswordHash::DetermineAlgorithm(hash);
std::string sHash(hash->Begin(), hash->End());
std::string sPassword(password->Begin(), password->End());

// Argon2i
if (algorithm == PasswordHash::Argon2i) {
int result = crypto_pwhash_str_verify(
sHash.c_str(),
sPassword.c_str(),
strlen(sPassword.c_str())
);

return result == 0;
} else if (algorithm == PasswordHash::Scrypt) { // Scrypt
int result = crypto_pwhash_scryptsalsa208sha256_str_verify(
sHash.c_str(),
sPassword.c_str(),
strlen(sPassword.c_str())
);

return result == 0;
return PasswordHash::VerifyArgon2i(hash, password);
} else if (algorithm == PasswordHash::Scrypt) {
return PasswordHash::VerifyScrypt(hash, password);
} else {
throw ref new Platform::InvalidArgumentException("Hash does not match a known algorithm type");
}
Expand All @@ -130,21 +64,148 @@ int Sodium::PasswordHash::DetermineAlgorithm(String^ hash)
}
}

/// <summary>Internal method to has an Argon2i password</summary>
/// <param name="password">The password to hash</param>
/// <param name="algorithm">The PasswordHash algorithm to use</param>
/// <param name="options">PasswordHashOptions struct</param>
/// <returns>The hash string</returns>
String^ Sodium::PasswordHash::HashArgon2i(String^ password, PasswordHashOptions options)
{
const Array<unsigned char>^ sPassword = Sodium::internal::StringToUnsignedCharArray(password);

sodium_mlock(sPassword->Data, sPassword->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");
}

char hash[crypto_pwhash_STRBYTES];

int result = crypto_pwhash_str(
hash,
(const char*)sPassword->Data,
sPassword->Length,
options.time_cost,
(options.memory_cost * 1024U)
);

sodium_munlock(sPassword->Data, sPassword->Length);
sodium_memzero(sPassword->Data, sPassword->Length);

if (result != 0) {
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());
}

/// <summary>Internal method to hash an Scrypt password</summary>
/// <param name="password">The password to hash</param>
/// <param name="algorithm">The PasswordHash algorithm to use</param>
/// <param name="options">PasswordHashOptions struct</param>
/// <returns>The hash string</returns>
String^ Sodium::PasswordHash::HashScrypt(String^ password, PasswordHashOptions options)
{
const Array<unsigned char>^ sPassword = Sodium::internal::StringToUnsignedCharArray(password);

sodium_mlock(sPassword->Data, sPassword->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");
}

char hash[crypto_pwhash_scryptsalsa208sha256_STRBYTES];
int result = crypto_pwhash_scryptsalsa208sha256_str(
hash,
(const char*)sPassword->Data,
sPassword->Length,
options.time_cost,
(options.memory_cost * 1024U)
);

sodium_munlock(sPassword->Data, sPassword->Length);
sodium_memzero(sPassword->Data, sPassword->Length);

if (result != 0) {
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());
}

/// <summary>Internal method to verify an Argon2i password</summary>
/// <param name="hash">The hash to check</param>
/// <param name="password">The password to check</param>
/// <returns>True of the provided password matches the string</returns>
bool Sodium::PasswordHash::VerifyArgon2i(String^ hash, String ^ password)
{
std::string sHash(hash->Begin(), hash->End());
std::string sPassword(password->Begin(), password->End());

sodium_mlock((void*)sPassword.c_str(), strlen(sPassword.c_str()));

int result = crypto_pwhash_str_verify(
sHash.c_str(),
sPassword.c_str(),
strlen(sPassword.c_str())
);

sodium_munlock((void*)sPassword.c_str(), strlen(sPassword.c_str()));
sodium_memzero((void*)sPassword.c_str(), strlen(sPassword.c_str()));

return result == 0;
}

/// <summary>Internal method to verify an Scrypt password</summary>
/// <param name="hash">The hash to check</param>
/// <param name="password">The password to check</param>
/// <returns>True of the provided password matches the string</returns>
bool Sodium::PasswordHash::VerifyScrypt(String^ hash, String ^ password)
{
std::string sHash(hash->Begin(), hash->End());
std::string sPassword(password->Begin(), password->End());

sodium_mlock((void*)sPassword.c_str(), strlen(sPassword.c_str()));

int result = crypto_pwhash_scryptsalsa208sha256_str_verify(
sHash.c_str(),
sPassword.c_str(),
strlen(sPassword.c_str())
);

sodium_munlock((void*)sPassword.c_str(), strlen(sPassword.c_str()));
sodium_memzero((void*)sPassword.c_str(), strlen(sPassword.c_str()));

return result == 0;
}

/// <summary>Creates a PasswordHashOptions struct</summary>
/// <param name="m">The memory cost </param>
/// <param name="t">The time cost </param>
/// <param name="memory_cost">The memory cost </param>
/// <param name="time_cost">The time cost </param>
/// <returns>PasswordHashOptions</returns>
PasswordHashOptions Sodium::PasswordHash::CreateOptions(int m, int t)
PasswordHashOptions Sodium::PasswordHash::CreateOptions(int memory_cost, int time_cost)
{
if (m <= 0) {
if (memory_cost <= 0) {
throw ref new Platform::InvalidArgumentException("memory_cost must be greater than 0");
}

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

PasswordHashOptions options = { m, t };
PasswordHashOptions options = { memory_cost, time_cost };

return options;
}
8 changes: 7 additions & 1 deletion libsodium-uwp/PasswordHash.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ namespace Sodium
private:
static int DetermineAlgorithm(String^ hash);

static String^ HashArgon2i(String^ password, PasswordHashOptions options);
static String^ HashScrypt(String^ password, PasswordHashOptions options);

static bool VerifyArgon2i(String^ hash, String^ password);
static bool VerifyScrypt(String^ hash, String^ password);

public:
static property int Argon2i
{
Expand All @@ -36,6 +42,6 @@ namespace Sodium
static String^ Hash(String^ password, int algorithm, PasswordHashOptions options);
static bool Verify(String^ hash, String^ password);

static PasswordHashOptions CreateOptions(int m, int t);
static PasswordHashOptions CreateOptions(int memory_cost, int time_cost);
};
}

0 comments on commit 9f22ffa

Please sign in to comment.