mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-29 15:15:08 +00:00
123 lines
3.0 KiB
Odin
123 lines
3.0 KiB
Odin
/*
|
|
`PBKDF2` password-based key derivation function.
|
|
|
|
See: [[ https://www.rfc-editor.org/rfc/rfc2898 ]]
|
|
*/
|
|
package pbkdf2
|
|
|
|
import "core:crypto"
|
|
import "core:crypto/hash"
|
|
import "core:crypto/hmac"
|
|
import "core:encoding/endian"
|
|
|
|
// derive invokes PBKDF2-HMAC with the specified hash algorithm, password,
|
|
// salt, iteration count, and outputs the derived key to dst.
|
|
derive :: proc(
|
|
hmac_hash: hash.Algorithm,
|
|
password: []byte,
|
|
salt: []byte,
|
|
iterations: u32,
|
|
dst: []byte,
|
|
) {
|
|
h_len := hash.DIGEST_SIZES[hmac_hash]
|
|
|
|
// 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long"
|
|
// and stop.
|
|
|
|
dk_len := len(dst)
|
|
switch {
|
|
case dk_len == 0:
|
|
return
|
|
case u64(dk_len) > u64(max(u32)) * u64(h_len):
|
|
// This is so beyond anything that is practical or reasonable,
|
|
// so just panic instead of returning an error.
|
|
panic("crypto/pbkdf2: derived key too long")
|
|
case:
|
|
}
|
|
|
|
// 2. Let l be the number of hLen-octet blocks in the derived key,
|
|
// rounding up, and let r be the number of octets in the last block.
|
|
|
|
l := dk_len / h_len // Don't need to round up.
|
|
r := dk_len % h_len
|
|
|
|
// 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.
|
|
//
|
|
// 4. Concatenate the blocks and extract the first dkLen octets to
|
|
// produce a derived key DK.
|
|
//
|
|
// 5. Output the derived key DK.
|
|
|
|
// Each iteration of F is always `PRF (P, ...)`, so instantiate the
|
|
// PRF, and clone since memcpy is faster than having to re-initialize
|
|
// HMAC repeatedly.
|
|
|
|
base: hmac.Context
|
|
defer hmac.reset(&base)
|
|
|
|
hmac.init(&base, hmac_hash, password)
|
|
|
|
// Process all of the blocks that will be written directly to dst.
|
|
dst_blk := dst
|
|
for i in 1 ..= l { // F expects i starting at 1.
|
|
_F(&base, salt, iterations, u32(i), dst_blk[:h_len])
|
|
dst_blk = dst_blk[h_len:]
|
|
}
|
|
|
|
// Instead of rounding l up, just proceass the one extra block iff
|
|
// r != 0.
|
|
if r > 0 {
|
|
tmp: [hash.MAX_DIGEST_SIZE]byte
|
|
blk := tmp[:h_len]
|
|
defer crypto.zero_explicit(raw_data(blk), h_len)
|
|
|
|
_F(&base, salt, iterations, u32(l + 1), blk)
|
|
copy(dst_blk, blk)
|
|
}
|
|
}
|
|
|
|
@(private)
|
|
_F :: proc(base: ^hmac.Context, salt: []byte, c: u32, i: u32, dst_blk: []byte) {
|
|
h_len := len(dst_blk)
|
|
|
|
tmp: [hash.MAX_DIGEST_SIZE]byte
|
|
u := tmp[:h_len]
|
|
defer crypto.zero_explicit(raw_data(u), h_len)
|
|
|
|
// 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.
|
|
|
|
prf: hmac.Context
|
|
|
|
// U_1: PRF (P, S || INT (i))
|
|
hmac.clone(&prf, base)
|
|
hmac.update(&prf, salt)
|
|
endian.unchecked_put_u32be(u, i) // Use u as scratch space.
|
|
hmac.update(&prf, u[:4])
|
|
hmac.final(&prf, u)
|
|
copy(dst_blk, u)
|
|
|
|
// U_2 ... U_c: U_n = PRF (P, U_(n-1))
|
|
for _ in 1 ..< c {
|
|
hmac.clone(&prf, base)
|
|
hmac.update(&prf, u)
|
|
hmac.final(&prf, u)
|
|
|
|
// XOR dst_blk and u.
|
|
for v, i in u {
|
|
dst_blk[i] ~= v
|
|
}
|
|
}
|
|
}
|