hmac.bend

bend-osdev-crypto@0.1.0.0 · 80 lines · 12 definitions

1# HMAC over any hash (RFC 2104; FIPS 198-1), on the hash interface
2# (hash.bend):
3#   K0 = the key, hashed first if it is longer than the block size B, then
4#        zero-padded to B bytes
5#   HMAC(K, m) = H((K0 xor opad) || H((K0 xor ipad) || m)),
6#   ipad = B bytes of 0x36, opad = B bytes of 0x5c.
7#
8# The key is prepared once (CrHmac.key: the two padded keys); the tag is
9# computed in one call (CrHmac.mac, with the hash's one-shot ~digest) or in
10# pieces (CrHmac.start, update, finish, with its streaming interface). As a
11# MAC (mac.bend), HMAC over a hash (~H, ~bsize, ~dsize, ~init, ~update,
12# ~final, ~digest) is the instance
13#   ~CrHmacKey, ~CrHmac.key_of(~bsize, ~digest), ~CrHmac.mac(~digest),
14#   ~CrMac.verify_tag(~CrHmacKey, ~CrHmac.mac(~digest))
15import Base
16import bend-osdev-bytes@0.1.0.0/bytes.bend as B
17import bend-osdev-crypto@0.1.0.0/crypto.bend as Cr
18import bend-osdev-crypto@0.1.0.0/mac.bend as Mc
19
20# The prepared key: K0 xor ipad and K0 xor opad (open type)
21type CrHmacKey is Data:
22  CrHmacKey{ipad: B.Octets, opad: B.Octets}
23
24# The state in pieces: the inner hash's state, and the outer padded key
25type CrHmacState<-H: Data> is Data:
26  CrHmacState{inner: H, opad: B.Octets}
27
28def cr.hmac.pad(xs: List<&2, U32>, +c: U32) -> List<&2, U32>:
29  match xs:
30    case Nil{}:
31      Nil{}
32    case Con{x, t}:
33      Con{B.byte(U32.xor(x, c)), cr.hmac.pad(t, c)}
34
35def cr.hmac.k0(~digest: B.Octets -> B.Octets, long: Bool, +key: B.Octets) -> B.Octets:
36  match long:
37    case True{}:
38      digest(key)
39    case False{}:
40      key
41
42# K0: the key (hashed if longer than bsize), zero-padded to bsize bytes
43def cr.hmac.block(~bsize: Nat, ~digest: B.Octets -> B.Octets, +key: B.Octets) -> List<&2, U32>:
44  +k = cr.hmac.k0(~digest, Nat.is_lt(bsize, B.Octets.len(key)), key)
45  B.append(B.Octets.to_list(k), B.zeros(Nat.sub(bsize, B.Octets.len(k))))
46
47# The prepared key of a raw key of any length
48def CrHmac.key(~bsize: Nat, ~digest: B.Octets -> B.Octets, key: B.Octets) -> CrHmacKey:
49  +k0 = cr.hmac.block(~bsize, ~digest, key)
50  CrHmacKey{B.Octets.of_list(cr.hmac.pad(k0, 54)), B.Octets.of_list(cr.hmac.pad(k0, 92))}
51
52# the same as the MAC interface's ~key (every key length is allowed)
53def CrHmac.key_of(~bsize: Nat, ~digest: B.Octets -> B.Octets, key: B.Octets) -> Result<&2, &2, Cr.CrError, CrHmacKey>:
54  Done{CrHmac.key(~bsize, ~digest, key)}
55
56# The tag of m, in one call
57def CrHmac.mac(~digest: B.Octets -> B.Octets, kk: CrHmacKey, m: B.Octets) -> B.Octets:
58  match kk:
59    case CrHmacKey{i, o}:
60      digest(B.Octets.append(o, digest(B.Octets.append(i, m))))
61
62# whether t is m's tag (every byte compared)
63def CrHmac.verify(~digest: B.Octets -> B.Octets, kk: CrHmacKey, m: B.Octets, t: B.Octets) -> Bool:
64  Mc.CrMac.verify_tag(~CrHmacKey, ~CrHmac.mac(~digest), kk, m, t)
65
66# The tag in pieces: start, update for each piece, finish
67def CrHmac.start(~H: Data, ~init: H, ~update: H -> B.Octets -> H, kk: CrHmacKey) -> CrHmacState<H>:
68  match kk:
69    case CrHmacKey{i, o}:
70      CrHmacState{update(init, i), o}
71
72def CrHmac.update(~H: Data, ~update: H -> B.Octets -> H, s: CrHmacState<H>, m: B.Octets) -> CrHmacState<H>:
73  match s:
74    case CrHmacState{h, o}:
75      CrHmacState{update(h, m), o}
76
77def CrHmac.finish(~H: Data, ~init: H, ~update: H -> B.Octets -> H, ~final: H -> B.Octets, s: CrHmacState<H>) -> B.Octets:
78  match s:
79    case CrHmacState{h, o}:
80      final(update(update(init, o), final(h)))