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)) 15 import Base 16 import bend-osdev-bytes@0.1.0.0/bytes.bend as B 17 import ./crypto.bend as Cr 18 import ./mac.bend as Mc 19 20 # The prepared key: K0 xor ipad and K0 xor opad (open type) 21 type 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 25 type CrHmacState<-H: Data> is Data: 26 CrHmacState{inner: H, opad: B.Octets} 27 28 def 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 35 def 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 43 def 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 48 def 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) 53 def 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 57 def 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) 63 def 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 67 def CrHmac.start(~H: Data, ~init: H, ~update: H -> B.Octets -> H, kk: CrHmacKey) -> CrHmacState: 68 match kk: 69 case CrHmacKey{i, o}: 70 CrHmacState{update(init, i), o} 71 72 def CrHmac.update(~H: Data, ~update: H -> B.Octets -> H, s: CrHmacState, m: B.Octets) -> CrHmacState: 73 match s: 74 case CrHmacState{h, o}: 75 CrHmacState{update(h, m), o} 76 77 def CrHmac.finish(~H: Data, ~init: H, ~update: H -> B.Octets -> H, ~final: H -> B.Octets, s: CrHmacState) -> B.Octets: 78 match s: 79 case CrHmacState{h, o}: 80 final(update(update(init, o), final(h)))