hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

hmac.ha (2163B)


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