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 };