const hmacBlake2b = require('./hmac')
const assert = require('nanoassert')
const b4a = require('b4a')
const HASHLEN = 64
module.exports = {
hkdf,
HASHLEN
}
// HMAC-based Extract-and-Expand KDF
// https://www.ietf.org/rfc/rfc5869.txt
function hkdf (salt, inputKeyMaterial, info = '', length = 2 * HASHLEN) {
const pseudoRandomKey = hkdfExtract(salt, inputKeyMaterial)
const result = hkdfExpand(pseudoRandomKey, info, length)
const results = []
let offset = 0
while (offset < result.length) {
results.push(result.subarray(offset, offset + HASHLEN))
offset += HASHLEN
}
return results
function hkdfExtract (salt, inputKeyMaterial) {
return hmacDigest(salt, inputKeyMaterial)
}
function hkdfExpand (key, info, length) {
const T = [b4a.from(info)]
const lengthRatio = length / HASHLEN
for (let i = 0; i < lengthRatio; i++) {
const infoBuf = b4a.from(info)
const toHash = b4a.concat([T[i], infoBuf, b4a.from([i + 1])])
T[i + 1] = hmacDigest(key, toHash)
}
const result = b4a.concat(T.slice(1))
assert(result.byteLength === length, 'key expansion failed, length not as expected')
return result
}
}
function hmacDigest (key, input) {
const hmac = b4a.alloc(HASHLEN)
hmacBlake2b(hmac, input, key)
return hmac
}