structstd.crypto.pwhash[src]

A password hashing function derives a uniform key from low-entropy input material such as passwords. It is intentionally slow or expensive.

With the standard definition of a key derivation function, if a key space is small, an exhaustive search may be practical. Password hashing functions make exhaustive searches way slower or way more expensive, even when implemented on GPUs and ASICs, by using different, optionally combined strategies:

  • Requiring a lot of computation cycles to complete
  • Requiring a lot of memory to complete
  • Requiring multiple CPU cores to complete
  • Requiring cache-local data to complete in reasonable time
  • Requiring large static tables
  • Avoiding precomputations and time/memory tradeoffs
  • Requiring multi-party computations
  • Combining the input material with random per-entry data (salts), application-specific contexts and keys

Password hashing functions must be used whenever sensitive data has to be directly derived from a password.

Error Sets

Error SetError[src]

Errors

anyerror means the error set is known only at runtime.

AllocatorRequired
AuthenticationFailed AuthenticationError
IdentityElement IdentityElementError
InvalidEncoding EncodingError
KeyMismatch KeyMismatchError
LockedMemoryLimitExceeded SpawnError

mlockall is enabled, and the memory needed to spawn the thread would exceed the limit.

NoSpaceLeft Error
NonCanonical NonCanonicalError
NotSquare NotSquareError
OutOfMemory SpawnError

Not enough userland memory to spawn the thread.

OutputTooLong OutputTooLongError
PasswordVerificationFailed PasswordVerificationError
SignatureVerificationFailed SignatureVerificationError
SystemResources SpawnError

The kernel cannot allocate sufficient memory to allocate a task structure for the child, or to copy those parts of the caller's context that need to be copied.

ThreadQuotaExceeded SpawnError

A system-imposed limit on the number of threads was encountered. There are a number of limits that may trigger this error:

  • the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which limits the number of processes and threads for a real user ID, was reached;
  • the kernel's system-wide limit on the number of processes and threads, /proc/sys/kernel/threads-max, was reached (see proc(5));
  • the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)); or
  • the PID limit (pids.max) imposed by the cgroup "process num‐ ber" (PIDs) controller was reached.
Unexpected SpawnError
UnexpectedSubgroup UnexpectedSubgroupError
WeakParameters WeakParametersError
WeakPublicKey WeakPublicKeyError

Source Code

Source code
pub const Error = HasherError || error{AllocatorRequired}

Error SetHasherError[src]

Errors

anyerror means the error set is known only at runtime.

AuthenticationFailed AuthenticationError
IdentityElement IdentityElementError
InvalidEncoding EncodingError
KeyMismatch KeyMismatchError
LockedMemoryLimitExceeded SpawnError

mlockall is enabled, and the memory needed to spawn the thread would exceed the limit.

NoSpaceLeft Error
NonCanonical NonCanonicalError
NotSquare NotSquareError
OutOfMemory SpawnError

Not enough userland memory to spawn the thread.

OutputTooLong OutputTooLongError
PasswordVerificationFailed PasswordVerificationError
SignatureVerificationFailed SignatureVerificationError
SystemResources SpawnError

The kernel cannot allocate sufficient memory to allocate a task structure for the child, or to copy those parts of the caller's context that need to be copied.

ThreadQuotaExceeded SpawnError

A system-imposed limit on the number of threads was encountered. There are a number of limits that may trigger this error:

  • the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which limits the number of processes and threads for a real user ID, was reached;
  • the kernel's system-wide limit on the number of processes and threads, /proc/sys/kernel/threads-max, was reached (see proc(5));
  • the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)); or
  • the PID limit (pids.max) imposed by the cgroup "process num‐ ber" (PIDs) controller was reached.
Unexpected SpawnError
UnexpectedSubgroup UnexpectedSubgroupError
WeakParameters WeakParametersError
WeakPublicKey WeakPublicKeyError

Source Code

Source code
pub const HasherError = KdfError || phc_format.Error

Error SetKdfError[src]

Errors

anyerror means the error set is known only at runtime.

AuthenticationFailed AuthenticationError
IdentityElement IdentityElementError
InvalidEncoding EncodingError
KeyMismatch KeyMismatchError
LockedMemoryLimitExceeded SpawnError

mlockall is enabled, and the memory needed to spawn the thread would exceed the limit.

NonCanonical NonCanonicalError
NotSquare NotSquareError
OutOfMemory SpawnError

Not enough userland memory to spawn the thread.

OutputTooLong OutputTooLongError
PasswordVerificationFailed PasswordVerificationError
SignatureVerificationFailed SignatureVerificationError
SystemResources SpawnError

The kernel cannot allocate sufficient memory to allocate a task structure for the child, or to copy those parts of the caller's context that need to be copied.

ThreadQuotaExceeded SpawnError

A system-imposed limit on the number of threads was encountered. There are a number of limits that may trigger this error:

  • the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which limits the number of processes and threads for a real user ID, was reached;
  • the kernel's system-wide limit on the number of processes and threads, /proc/sys/kernel/threads-max, was reached (see proc(5));
  • the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)); or
  • the PID limit (pids.max) imposed by the cgroup "process num‐ ber" (PIDs) controller was reached.
Unexpected SpawnError
UnexpectedSubgroup UnexpectedSubgroupError
WeakParameters WeakParametersError
WeakPublicKey WeakPublicKeyError

Source Code

Source code
pub const KdfError = errors.Error || std.mem.Allocator.Error || std.Thread.SpawnError

Functions

Functionpbkdf2[src]

pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) (WeakParametersError || OutputTooLongError)!void

Apply PBKDF2 to generate a key from a password.

PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.

dk: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length. May be uninitialized. All bytes will be overwritten. Maximum size is maxInt(u32) * Hash.digest_length It is a programming error to pass buffer longer than the maximum size.

password: Arbitrary sequence of bytes of any length, including empty.

salt: Arbitrary sequence of bytes of any length, including empty. A common length is 8 bytes.

rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000. Larger iteration counts improve security by increasing the time required to compute the dk. It is common to tune this parameter to achieve approximately 100ms.

Prf: Pseudo-random function to use. A common choice is std.crypto.auth.hmac.sha2.HmacSha256.

Parameters

dk: []u8
password: []const u8
salt: []const u8
rounds: u32
Prf: type

Source Code

Source code
pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) (WeakParametersError || OutputTooLongError)!void {
    if (rounds < 1) return error.WeakParameters;

    const dk_len = dk.len;
    const h_len = Prf.mac_length;
    comptime std.debug.assert(h_len >= 1);

    // FromSpec:
    //
    //   1. If dk_len > maxInt(u32) * h_len, output "derived key too long" and
    //      stop.
    //
    if (dk_len / h_len >= maxInt(u32)) {
        // Counter starts at 1 and is 32 bit, so if we have to return more blocks, we would overflow
        return error.OutputTooLong;
    }

    // FromSpec:
    //
    //   2. Let l be the number of h_len-long blocks of bytes in the derived key,
    //      rounding up, and let r be the number of bytes in the last
    //      block
    //

    const blocks_count = @as(u32, @intCast(std.math.divCeil(usize, dk_len, h_len) catch unreachable));
    var r = dk_len % h_len;
    if (r == 0) {
        r = h_len;
    }

    // FromSpec:
    //
    //   3. For each block of the derived key apply the function F defined
    //      below to the password P, the salt S, the iteration count c, and
    //      the block index to compute the block:
    //
    //                T_1 = F (P, S, c, 1) ,
    //                T_2 = F (P, S, c, 2) ,
    //                ...
    //                T_l = F (P, S, c, l) ,
    //
    //      where the function F is defined as the exclusive-or sum of the
    //      first c iterates of the underlying pseudorandom function PRF
    //      applied to the password P and the concatenation of the salt S
    //      and the block index i:
    //
    //                F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
    //
    //  where
    //
    //            U_1 = PRF (P, S || INT (i)) ,
    //            U_2 = PRF (P, U_1) ,
    //            ...
    //            U_c = PRF (P, U_{c-1}) .
    //
    //  Here, INT (i) is a four-octet encoding of the integer i, most
    //  significant octet first.
    //
    //  4. Concatenate the blocks and extract the first dk_len octets to
    //  produce a derived key DK:
    //
    //            DK = T_1 || T_2 ||  ...  || T_l<0..r-1>

    var block: u32 = 0;
    while (block < blocks_count) : (block += 1) {
        var prev_block: [h_len]u8 = undefined;
        var new_block: [h_len]u8 = undefined;

        // U_1 = PRF (P, S || INT (i))
        const block_index = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001
        var ctx = Prf.init(password);
        ctx.update(salt);
        ctx.update(block_index[0..]);
        ctx.final(prev_block[0..]);

        // Choose portion of DK to write into (T_n) and initialize
        const offset = block * h_len;
        const block_len = if (block != blocks_count - 1) h_len else r;
        const dk_block: []u8 = dk[offset..][0..block_len];
        @memcpy(dk_block, prev_block[0..dk_block.len]);

        var i: u32 = 1;
        while (i < rounds) : (i += 1) {
            // U_c = PRF (P, U_{c-1})
            Prf.create(&new_block, prev_block[0..], password);
            prev_block = new_block;

            // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
            for (dk_block, 0..) |_, j| {
                dk_block[j] ^= new_block[j];
            }
        }
    }
}

Source Code

Source code
pub const pwhash = struct {
    pub const Encoding = enum {
        phc,
        crypt,
    };

    pub const Error = HasherError || error{AllocatorRequired};
    pub const HasherError = KdfError || phc_format.Error;
    pub const KdfError = errors.Error || std.mem.Allocator.Error || std.Thread.SpawnError;

    pub const argon2 = @import("crypto/argon2.zig");
    pub const bcrypt = @import("crypto/bcrypt.zig");
    pub const scrypt = @import("crypto/scrypt.zig");
    pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2;

    pub const phc_format = @import("crypto/phc_encoding.zig");
}