Password hashing
Example 1: key derivation
const string PASSWORD = "Correct Horse Battery Staple";
const string SALT = "qa~t](84z<1t<1oz:ik.@IRNyhG=8q(o";
const long OUTPUT_LENGTH = 512;
//this will produce a 512 byte hash
var hash = PasswordHash.ScryptHashBinary(PASSWORD, SALT, PasswordHash.Strength.Medium, OUTPUT_LENGTH);
Example 2: password storage
const string PASSWORD = "Correct Horse Battery Staple";
//this will produce a 32 byte hash
var hash = PasswordHash.ScryptHashString(PASSWORD, PasswordHash.Strength.Medium);
if (PasswordHash.ScryptHashStringVerify(hash, PASSWORD)
{
//correct password
}
Purpose
Secret keys used to encrypt or sign confidential data have to be chosen from a very large keyspace. However, passwords are usually short, human-generated strings, making dictionary attacks practical.
The PasswordHash
operation derives a secret key of any size from a password and a salt.
- The generated key has the size defined by the application, no matter what the password length is.
- The same password hashed with same parameters will always produce the same key.
- The same password hashed with different salts will produce different keys.
- The function deriving a key from a password and a salt is CPU intensive and intentionally requires a fair amount of memory. Therefore, it mitigates brute-force attacks by requiring a significant effort to verify each password.
Common use cases:
- Protecting an on-disk secret key with a password,
- Password storage, or rather: storing what it takes to verify a password without having to store the actual password.
Random Helpers
public static byte[] GenerateSalt()
Namespace: Sodium.PasswordHash
Uses Sodium.SodiumCore.GetRandomBytes()
to generate a 32 byte salt.
Predefined Values
/// <summary>Represents predefined and useful limits for ScryptHashBinary() and ScryptHashString().</summary>
public enum Strength
{
/// <summary>For interactive sessions (fast: uses 16MB of RAM).</summary>
Interactive,
/// <summary>For normal use (moderate: uses 100MB of RAM).</summary>
[Obsolete("Use Strength.Medium instead.")]
Moderate,
/// <summary>For normal use (moderate: uses 128MB of RAM).</summary>
Medium,
/// <summary>For more sensitive use (moderate: uses 128MB of RAM but higher opslimit).</summary>
MediumSlow,
/// <summary>For highly sensitive data (slow: uses more than 1GB of RAM).</summary>
Sensitive
}
Namespace: Sodium.PasswordHash
Key derivation
public static byte[] ScryptHashBinary(byte[] password, byte[] salt, long opsLimit, int memLimit, long outputLength = SCRYPT_SALSA208_SHA256_SALTBYTES)
//there exists some overloaded versions:
public static byte[] ScryptHashBinary(string password, string salt, long opsLimit, int memLimit, long outputLength = SCRYPT_SALSA208_SHA256_SALTBYTES)
public static byte[] ScryptHashBinary(byte[] password, byte[] salt, Strength limit = Strength.Interactive, long outputLength = SCRYPT_SALSA208_SHA256_SALTBYTES)
public static byte[] ScryptHashBinary(string password, string salt, Strength limit = Strength.Interactive, long outputLength = SCRYPT_SALSA208_SHA256_SALTBYTES)
This is the .NET equivalent of crypto_pwhash_scryptsalsa208sha256
.
Namespace: Sodium.PasswordHash
The ScryptHashBinary()
function derives an outputLength
bytes long key from a password
and a salt
whose fixed length is 32
bytes.
opslimit
represents a maximum amount of computations to perform. Raising this number will make the function require more CPU cycles to compute a key.
memlimit
is the maximum amount of RAM that the function will use, in bytes. It is highly recommended to allow the function to use at least 16 megabytes.
In libsodium-net are some predefined values: Strength.Interactive
, Strength.Moderate
(obsolete), Strength.Medium
, Strength.MediumSlow
and Strength.Sensitive
.
For interactive sessions, Strength.Interactive
provide a safe base line for the opsLimit
and memLimit
parameters. However, using higher values may improve security. The Strength.Interactive
uses 16777216
bytes of dedicated RAM and 524288
CPU cycles.
For normal data, Strength.Medium
can be used. These setting use 128MB of RAM, and takes roughly 5 seconds to complete. The Strength.Medium
uses 134217728
bytes of dedicated RAM and 8388608
CPU cycles.
For highly sensitive data, Strength.Sensitive
can be used as an alternative. But with these parameters, deriving a key takes more than 10 seconds on a 2.8 Ghz Core i7 CPU and requires up to 1 gigabyte of dedicated RAM. The Strength.Sensitive
uses 1073741824
bytes of dedicated RAM and 33554432
CPU cycles.
The salt
should be unpredictable. GenerateSalt()
is the easiest way to fill the bytes of the salt.
Keep in mind that in order to produce the same key from the same password, the same salt, and the same values for opslimit
and memlimit
have to be used. Therefore, these parameters have to be stored for each user.
The salt
must be 32
bytes, otherwise the function throws a SaltOutOfRangeException
.
The opsLimit
, memlimit
and outputLength
must be greater 0
, otherwise the function throws an ArgumentOutOfRangeException
.
If there is no password
or salt
specified the function will throw an ArgumentNullException
.
The function returns a byte array on success, and throws an OutOfMemoryException()
if the computation didn't complete, usually because the operating system refused to allocate the amount of requested memory.
Password storage
Hash
public static string ScryptHashString(string password, long opsLimit, int memLimit)
//there exists an overloaded version:
public static string ScryptHashString(string password, Strength limit = Strength.Interactive)
This is the .NET equivalent of crypto_pwhash_scryptsalsa208sha256_str
.
Namespace: Sodium.PasswordHash
The ScryptHashString()
function returns an UTF8 encoded string, which includes:
- the result of a memory-hard, CPU-intensive hash function applied to the
password
- the automatically generated salt used for the previous computation
- the other parameters required to verify the password:
opsLimit
andmemLimit
.
The opsLimit
and memLimit
values for ScryptHashString()
are the same as for ScryptHashBinary
. So you also can use the predefined values.
The output string is zero-terminated. No additional information has to be stored in order to verify the password.
The opsLimit
and memlimit
must be greater 0
, otherwise the function throws an ArgumentOutOfRangeException
.
If there is no password
specified the function will throw an ArgumentNullException
.
The function returns a byte array on success, and throws an OutOfMemoryException()
if the computation didn't complete, usually because the operating system refused to allocate the amount of requested memory.
Verify
public static bool ScryptHashStringVerify(byte[] hash, byte[] password)
//there exists an overloaded version:
public static bool ScryptHashStringVerify(string hash, string password)
This is the .NET equivalent of crypto_pwhash_scryptsalsa208sha256_str_verify
.
Namespace: Sodium.PasswordHash
This function verifies that the password hash
is a valid password verification string (as generated by ScryptHashString()
) for password
.
If there is no hash
or password
specified the function will throw an ArgumentNullException
.
It returns true
if the verification succeeds, and false
on error.