1module Cryptography.Hmac exposing (encrypt128, encrypt64)
2
3{-| Cryptography – HMAC
4-}
5
6import Binary exposing (Bits)
7
8
9type alias HashFunction =
10 Bits -> Bits
11
12
13{-| HMAC encryption for hashing algorithms with a `blockSize` of 64.
14These include: SHA-0, SHA-1, SHA-224, SHA-256, MD5, etc.
15
16 >>> import Binary
17 >>> import SHA
18
19 >>> Binary.fromStringAsUtf8 ""
20 ..> |> encrypt64 SHA.sha256 ""
21 ..> |> Binary.toHex
22 ..> |> String.toLower
23 "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"
24
25 >>> Binary.fromStringAsUtf8 "key"
26 ..> |> encrypt64 SHA.sha256 "The quick brown fox jumps over the lazy dog"
27 ..> |> Binary.toHex
28 ..> |> String.toLower
29 "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"
30
31 >>> Binary.fromHex "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
32 ..> |> encrypt64 SHA.sha256 "Hi There"
33 ..> |> Binary.toHex
34 ..> |> String.toLower
35 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
36
37 >>> Binary.fromHex "4a656665"
38 ..> |> encrypt64 SHA.sha256 "what do ya want for nothing?"
39 ..> |> Binary.toHex
40 ..> |> String.toLower
41 "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"
42
43 >>> Binary.fromHex "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
44 ..> |> encrypt64 SHA.sha256 "Test Using Larger Than Block-Size Key - Hash Key First"
45 ..> |> Binary.toHex
46 ..> |> String.toLower
47 "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"
48
49-}
50encrypt64 : HashFunction -> String -> Bits -> Bits
51encrypt64 =
52 encrypt (64 * 8)
53
54
55{-| HMAC encryption for hashing algorithms with a `blockSize` of 128.
56These include: SHA-384, SHA-512, etc.
57-}
58encrypt128 : HashFunction -> String -> Bits -> Bits
59encrypt128 =
60 encrypt (128 * 8)
61
62
63
64-- ENCRYPT
65
66
67encrypt : Int -> HashFunction -> String -> Bits -> Bits
68encrypt blockSize hash messageString key =
69 let
70 keySize =
71 Binary.width key
72
73 keyWithBlockSize =
74 if keySize > blockSize then
75 padRight blockSize (hash key)
76
77 else if keySize < blockSize then
78 padRight blockSize key
79
80 else
81 key
82
83 ( binSeqOne, binSeqTwo ) =
84 Tuple.mapBoth
85 (Binary.xor keyWithBlockSize)
86 (Binary.xor keyWithBlockSize)
87 (padding <| blockSize // 8)
88 in
89 messageString
90 |> Binary.fromString 8
91 |> Binary.append binSeqOne
92 |> hash
93 |> Binary.append binSeqTwo
94 |> hash
95
96
97padRight : Int -> Bits -> Bits
98padRight int bits =
99 let
100 size =
101 Binary.width bits
102 in
103 False
104 |> List.repeat (int - size)
105 |> List.append (Binary.toBooleans bits)
106 |> Binary.fromBooleans
107
108
109
110-- PADDING
111
112
113padding : Int -> ( Bits, Bits )
114padding blockSize =
115 case blockSize of
116 64 ->
117 padding64
118
119 128 ->
120 padding128
121
122 _ ->
123 ( Binary.concat (List.repeat blockSize <| Binary.fromHex "36")
124 , Binary.concat (List.repeat blockSize <| Binary.fromHex "5C")
125 )
126
127
128padding64 : ( Bits, Bits )
129padding64 =
130 ( Binary.concat (List.repeat 64 <| Binary.fromHex "36")
131 , Binary.concat (List.repeat 64 <| Binary.fromHex "5C")
132 )
133
134
135padding128 : ( Bits, Bits )
136padding128 =
137 ( Binary.concat (List.repeat 128 <| Binary.fromHex "36")
138 , Binary.concat (List.repeat 128 <| Binary.fromHex "5C")
139 )