0 | module Data.Cryptography.HMAC
 1 |
 2 | import Data.Bits
 3 | import Data.Vect
 4 |
 5 | import Data.Cryptography.Hash
 6 |
 7 | export
 8 | record HmacCtx (hash: HashAlgorithm) (keyLength: Nat) where
 9 |   constructor MkHmacCtx
10 |   hashCtx: hash.ctxType
11 |   key: Vect keyLength Bits8
12 |
13 | ||| Note that key is not zero-padded or hashed as mentioned in the HMAC spec. If
14 | ||| you have a long key (longer than 'block size', see HMAC spec), and you need
15 | ||| compatibility, make sure you hash the key before passing it in here. If your
16 | ||| key is short, make sure you zero pad it in the end to match the 'block size'
17 | ||| of the hash function. The block size is not available for sponge-type hash
18 | ||| algorithms. To allow using this implementation with sponge-like hash
19 | ||| algorithms, we allow an arbitrary key length here, and rely on the consumer
20 | ||| to choose whether the key is hashed or not.
21 | ||| Note that HMAC shouldn't be necessary with sponge-type constructions.
22 | ||| See https://crypto.stackexchange.com/a/39886/12089
23 | export
24 | mkHmacCtx : {hash: HashAlgorithm}
25 |          -> {keyLength: Nat}
26 |          -> (key: Vect keyLength Bits8)
27 |          -> HmacCtx hash keyLength
28 | mkHmacCtx key =
29 |   let ikey = map (xor 0x36) key
30 |   in MkHmacCtx
31 |        { hashCtx = hash.mkHashCtx |> hash.appendHash (toList ikey)
32 |        , key = key
33 |        }
34 |
35 | export
36 | appendHmac : {hash: HashAlgorithm} -> List Bits8 -> HmacCtx hash keyLength -> HmacCtx hash keyLength
37 | appendHmac text =
38 |   { hashCtx $= hash.appendHash text }
39 |
40 | export
41 | finalizeHmac : {hash: HashAlgorithm} -> HmacCtx hash keyLength -> Vect hash.outputSize Bits8
42 | finalizeHmac ctx =
43 |   let okey = map (xor 0x5c) ctx.key
44 |       innerHash : Vect hash.outputSize Bits8
45 |       innerHash = hash.finalizeHash ctx.hashCtx
46 |       outerHash : Vect hash.outputSize Bits8
47 |       outerHash =
48 |         hash.mkHashCtx |>
49 |         hash.appendHash (toList okey) |>
50 |         hash.appendHash (toList innerHash) |>
51 |         hash.finalizeHash
52 |    in outerHash
53 |