#import "utils.typ": xor-bytes #import "sha.typ": sha1 #let _compute-block-sized-key(key, hash-func: sha1, block-size: 64) = { if key.len() > block-size { key = hash-func(key) } if key.len() < block-size { key = bytes((0,) * (block-size - key.len())) + key } return key } /// Hash-based Message Authentication Code /// ```example /// #bytes-to-hex(hmac("Key", "Hello World!")) /// ``` /// -> bytes #let hmac( /// Hashing key /// -> str | bytes key, /// Message to hash /// -> str | bytes message, /// Hashing function /// -> function hash-func: sha1, /// Block size /// -> number block-size: 64 ) = { let key = if type(key) == str {bytes(key)} else {key} let message = if type(message) == str {bytes(message)} else {message} assert(type(key) == bytes, message: "key must be a string or bytes, but is " + repr(type(key))) assert(type(message) == bytes, message: "message must be a string or bytes, but is " + repr(type(message))) let block-sized-key = _compute-block-sized-key(key, hash-func: hash-func, block-size: block-size) let i-pad = bytes((0x36,) * block-size) let o-pad = bytes((0x5c,) * block-size) let i-key-pad = xor-bytes(key, i-pad) let o-key-pad = xor-bytes(key, o-pad) return hash-func(o-key-pad + hash-func(i-key-pad + message)) }