1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use bytes;
use crypto::mac;
use hash;
use io;
export type state = struct {
mac::mac,
h: *hash::hash,
keypad: []u8,
};
const hmac_vtable: io::vtable = io::vtable {
writer = &write,
...
};
// Creates a [[crypto::mac::mac]] that computes an HMAC using the provided hash
// function 'h' with given 'key'. The caller must provide a 'buf' of
// [[hash::bsz]] bytes. Use the BLOCKSZ constant of the given hash function to
// allocate the memory statically.
//
// The caller must take extra care to call [[crypto::mac::finish]] when they are
// finished using the MAC function, which, in addition to freeing state
// associated with the MAC, will securely erase state which contains secret
// information.
export fn hmac(h: *hash::hash, key: const []u8, buf: []u8) state = {
const bsz = hash::bsz(h);
assert(len(buf) >= bsz, "buf must be at least the size of one "
"block of the given hash function");
let keypad = buf[..bsz];
init(h, key, keypad);
return state {
h = h,
stream = &hmac_vtable,
sz = hash::sz(h),
bsz = bsz,
sum = &gensum,
finish = &finish,
keypad = keypad,
...
};
};
fn init(h: *hash::hash, key: []u8, keypad: []u8) void = {
const bsz = hash::bsz(h);
keypad[..] = [0...];
if (len(key) > bsz) {
hash::write(h, key);
hash::sum(h, keypad);
hash::reset(h);
} else {
keypad[..len(key)] = key[..];
};
for (let i = 0z; i < bsz; i += 1) {
keypad[i] = 0x36 ^ keypad[i];
};
hash::write(h, keypad);
for (let i = 0z; i < bsz; i += 1) {
// keypad for the outer hash is xored with 0x5c instead of 0x36
keypad[i] = keypad[i] ^ 0x36 ^ 0x5c;
};
};
fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
let hm = st: *state;
return hash::write(hm.h, buf);
};
fn sum(h: *hash::hash, keypad: []u8, dest: []u8) void = {
hash::sum(h, dest);
hash::reset(h);
hash::write(h, keypad);
hash::write(h, dest);
hash::sum(h, dest);
};
fn gensum(mac: *mac::mac, dest: []u8) void = {
let hm = mac: *state;
sum(hm.h, hm.keypad, dest);
};
fn finish(mac: *mac::mac) void = {
let hm = mac: *state;
bytes::zero(hm.keypad);
hash::close(hm.h);
};
|