File: hmac.ha

package info (click to toggle)
hare 0.26.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 7,352 kB
  • sloc: asm: 1,374; makefile: 123; sh: 117; lisp: 101
file content (97 lines) | stat: -rw-r--r-- 2,187 bytes parent folder | download | duplicates (2)
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);
};