|
@@ -0,0 +1,103 @@
|
|
|
|
+/*
|
|
|
|
+package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
|
|
|
|
+Derivation Function.
|
|
|
|
+
|
|
|
|
+See: https://www.rfc-editor.org/rfc/rfc5869
|
|
|
|
+*/
|
|
|
|
+package hkdf
|
|
|
|
+
|
|
|
|
+import "core:crypto/hash"
|
|
|
|
+import "core:crypto/hmac"
|
|
|
|
+import "core:mem"
|
|
|
|
+
|
|
|
|
+// extract_and_expand derives output keying material (OKM) via the
|
|
|
|
+// HKDF-Extract and HKDF-Expand algorithms, with the specified has
|
|
|
|
+// function, salt, input keying material (IKM), and optional info.
|
|
|
|
+// The dst buffer must be less-than-or-equal to 255 HMAC tags.
|
|
|
|
+extract_and_expand :: proc(algorithm: hash.Algorithm, salt, ikm, info, dst: []byte) {
|
|
|
|
+ h_len := hash.DIGEST_SIZES[algorithm]
|
|
|
|
+
|
|
|
|
+ tmp: [hash.MAX_DIGEST_SIZE]byte
|
|
|
|
+ prk := tmp[:h_len]
|
|
|
|
+ defer mem.zero_explicit(raw_data(prk), h_len)
|
|
|
|
+
|
|
|
|
+ extract(algorithm, salt, ikm, prk)
|
|
|
|
+ expand(algorithm, prk, info, dst)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// extract derives a pseudorandom key (PRK) via the HKDF-Extract algorithm,
|
|
|
|
+// with the specified hash function, salt, and input keying material (IKM).
|
|
|
|
+// It requires that the dst buffer be the HMAC tag size for the specified
|
|
|
|
+// hash function.
|
|
|
|
+extract :: proc(algorithm: hash.Algorithm, salt, ikm, dst: []byte) {
|
|
|
|
+ // PRK = HMAC-Hash(salt, IKM)
|
|
|
|
+ hmac.sum(algorithm, dst, ikm, salt)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// expand derives output keying material (OKM) via the HKDF-Expand algorithm,
|
|
|
|
+// with the specified hash function, pseudorandom key (PRK), and optional
|
|
|
|
+// info. The dst buffer must be less-than-or-equal to 255 HMAC tags.
|
|
|
|
+expand :: proc(algorithm: hash.Algorithm, prk, info, dst: []byte) {
|
|
|
|
+ h_len := hash.DIGEST_SIZES[algorithm]
|
|
|
|
+
|
|
|
|
+ // (<= 255*HashLen)
|
|
|
|
+ dk_len := len(dst)
|
|
|
|
+ switch {
|
|
|
|
+ case dk_len == 0:
|
|
|
|
+ return
|
|
|
|
+ case dk_len > h_len * 255:
|
|
|
|
+ panic("crypto/hkdf: derived key too long")
|
|
|
|
+ case:
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // The output OKM is calculated as follows:
|
|
|
|
+ //
|
|
|
|
+ // N = ceil(L/HashLen)
|
|
|
|
+ // T = T(1) | T(2) | T(3) | ... | T(N)
|
|
|
|
+ // OKM = first L octets of T
|
|
|
|
+ //
|
|
|
|
+ // where:
|
|
|
|
+ // T(0) = empty string (zero length)
|
|
|
|
+ // T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
|
|
|
|
+ // T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
|
|
|
|
+ // T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
|
|
|
|
+ // ...
|
|
|
|
+
|
|
|
|
+ n := dk_len / h_len
|
|
|
|
+ r := dk_len % h_len
|
|
|
|
+
|
|
|
|
+ base: hmac.Context
|
|
|
|
+ defer hmac.reset(&base)
|
|
|
|
+
|
|
|
|
+ hmac.init(&base, algorithm, prk)
|
|
|
|
+
|
|
|
|
+ dst_blk := dst
|
|
|
|
+ prev: []byte
|
|
|
|
+
|
|
|
|
+ for i in 1 ..= n {
|
|
|
|
+ _F(&base, prev, info, i, dst_blk[:h_len])
|
|
|
|
+
|
|
|
|
+ prev = dst_blk[:h_len]
|
|
|
|
+ dst_blk = dst_blk[h_len:]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if r > 0 {
|
|
|
|
+ tmp: [hash.MAX_DIGEST_SIZE]byte
|
|
|
|
+ blk := tmp[:h_len]
|
|
|
|
+ defer mem.zero_explicit(raw_data(blk), h_len)
|
|
|
|
+
|
|
|
|
+ _F(&base, prev, info, n + 1, blk)
|
|
|
|
+ copy(dst_blk, blk)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@(private)
|
|
|
|
+_F :: proc(base: ^hmac.Context, prev, info: []byte, i: int, dst_blk: []byte) {
|
|
|
|
+ prf: hmac.Context
|
|
|
|
+
|
|
|
|
+ hmac.clone(&prf, base)
|
|
|
|
+ hmac.update(&prf, prev)
|
|
|
|
+ hmac.update(&prf, info)
|
|
|
|
+ hmac.update(&prf, []byte{u8(i)})
|
|
|
|
+ hmac.final(&prf, dst_blk)
|
|
|
|
+}
|