dumb_crypto/
hmac.rs

1//! # Hmac
2//!
3//! Implementation of HMac SHA256 message authentication algorithm according to
4//! [RFC 2104][rfc].
5//!
6//! [rfc]: https://tools.ietf.org/html/rfc2104#section-2
7//!
8
9use crate::sha256::{BLOCK_SIZE, DIGEST_SIZE, SHA256};
10
11///
12/// Main hmac-sha256 structure.
13///
14/// Usage:
15/// ```rust
16/// extern crate dumb_crypto;
17///
18/// use::dumb_crypto::hmac::HMac;
19///
20/// let mut hmac = HMac::new(b"secret key");
21///
22/// hmac.update(b"hello world");
23/// assert_eq!(hmac.digest().to_vec(), vec![
24///     0xc6, 0x1b, 0x51, 0x98, 0xdf, 0x58, 0x63, 0x9e,
25///     0xdb, 0x98, 0x92, 0x51, 0x47, 0x56, 0xb8, 0x9a,
26///     0x36, 0x85, 0x6d, 0x82, 0x6e, 0x5d, 0x85, 0x02,
27///     0x3a, 0xb1, 0x81, 0xb4, 0x8e, 0xa5, 0xd0, 0x18,
28/// ]);
29/// ```
30///
31pub struct HMac {
32    inner: SHA256,
33    outer: SHA256,
34}
35
36// See https://tools.ietf.org/html/rfc2104#section-2
37
38impl HMac {
39    ///
40    /// Create new instance of Hmac-sha256 message authentication.
41    ///
42    /// Arguments:
43    ///
44    /// - `key` - secret key
45    ///
46    pub fn new(key: &[u8]) -> HMac {
47        //
48        //                ipad = the byte 0x36 repeated B times
49        //                opad = the byte 0x5C repeated B times.
50        //
51        //   To compute HMAC over the data `text' we perform
52        //
53        //                    H(K XOR opad, H(K XOR ipad, text))
54        //
55        //   Namely,
56        //
57        //    (1) append zeros to the end of K to create a B byte string
58        //        (e.g., if K is of length 20 bytes and B=64, then K will be
59        //         appended with 44 zero bytes 0x00)
60        //    (2) XOR (bitwise exclusive-OR) the B byte string computed in step
61        //        (1) with ipad
62        //    (3) append the stream of data 'text' to the B byte string resulting
63        //        from step (2)
64        //    (4) apply H to the stream generated in step (3)
65        //    (5) XOR (bitwise exclusive-OR) the B byte string computed in
66        //        step (1) with opad
67        //    (6) append the H result from step (4) to the B byte string
68        //        resulting from step (5)
69        //    (7) apply H to the stream generated in step (6) and output
70        //        the result
71        //
72
73        // Hash the key to fit it into the block
74        if key.len() > BLOCK_SIZE {
75            let mut tmp = SHA256::new();
76            tmp.update(key);
77            return HMac::new(&tmp.digest());
78        }
79
80        let mut block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE];
81
82        let mut inner = SHA256::new();
83        let mut outer = SHA256::new();
84
85        block[..key.len()].copy_from_slice(key);
86
87        for elem in block.iter_mut() {
88            *elem ^= 0x36;
89        }
90        inner.update(&block);
91
92        /* NOTE: a ^ b ^ b ^ c = a ^ c */
93        for elem in block.iter_mut() {
94            *elem ^= 0x36 ^ 0x5c;
95        }
96        outer.update(&block);
97
98        HMac { inner, outer }
99    }
100
101    ///
102    /// Add input `data` to the digest.
103    ///
104    pub fn update(self: &mut HMac, data: &[u8]) {
105        self.inner.update(data);
106    }
107
108    ///
109    /// Generate digest array.
110    ///
111    pub fn digest(self: &mut HMac) -> [u8; DIGEST_SIZE] {
112        self.outer.update(&self.inner.digest());
113        self.outer.digest()
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    fn check(key: &[u8], inputs: &[&[u8]], expected: [u8; DIGEST_SIZE]) {
122        let mut hmac = HMac::new(key);
123
124        for chunk in inputs {
125            hmac.update(chunk);
126        }
127        assert_eq!(hmac.digest(), expected);
128    }
129
130    //
131    // See https://tools.ietf.org/html/rfc4231
132    //
133
134    #[test]
135    fn it_should_compute_digest_for_vec0() {
136        check(
137            b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
138            &[b"Hi There"],
139            [
140                0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b,
141                0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
142                0x2e, 0x32, 0xcf, 0xf7,
143            ],
144        );
145    }
146
147    #[test]
148    fn it_should_compute_digest_for_vec1() {
149        check(
150            b"Jefe",
151            &[b"what do ya want ", b"for nothing?"],
152            [
153                0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95,
154                0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9,
155                0x64, 0xec, 0x38, 0x43,
156            ],
157        );
158    }
159
160    #[test]
161    fn it_should_compute_digest_for_vec2() {
162        check(
163            &[
164                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
165                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
166            ],
167            &[
168                &[
169                    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
170                    0xdd, 0xdd, 0xdd,
171                ],
172                &[
173                    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
174                    0xdd, 0xdd, 0xdd,
175                ],
176                &[
177                    0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
178                    0xdd, 0xdd, 0xdd,
179                ],
180                &[0xdd, 0xdd],
181            ],
182            [
183                0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91,
184                0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14,
185                0xce, 0xd5, 0x65, 0xfe,
186            ],
187        );
188    }
189
190    #[test]
191    fn it_should_compute_digest_for_vec3() {
192        check(
193            &[
194                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
195                0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
196            ],
197            &[
198                &[
199                    0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
200                    0xcd, 0xcd, 0xcd,
201                ],
202                &[
203                    0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
204                    0xcd, 0xcd, 0xcd,
205                ],
206                &[
207                    0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
208                    0xcd, 0xcd, 0xcd,
209                ],
210                &[0xcd, 0xcd],
211            ],
212            [
213                0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2,
214                0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4,
215                0x67, 0x29, 0x66, 0x5b,
216            ],
217        );
218    }
219
220    #[test]
221    fn it_should_compute_digest_for_vec4() {
222        check(
223            &[
224                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
225                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
226                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
227                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
228                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
229                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
230                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
231                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
232                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
233                0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
234            ],
235            &[
236                b"Test Using Large",
237                b"r Than Block-Siz",
238                b"e Key - Hash Key",
239                b" First",
240            ],
241            [
242                0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5,
243                0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f,
244                0x0e, 0xe3, 0x7f, 0x54,
245            ],
246        );
247    }
248
249    #[test]
250    fn it_should_compute_digest_for_vec5() {
251        check(
252            &[
253                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
254                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
255                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
256                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
257                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
258                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
259                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
260                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
261                0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
262                0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
263            ],
264            &[
265                b"This is a test u",
266                b"sing a larger th",
267                b"an block-size ke",
268                b"y and a larger t",
269                b"han block-size d",
270                b"ata. The key nee",
271                b"ds to be hashed ",
272                b"before being use",
273                b"d by the HMAC al",
274                b"gorithm.",
275            ],
276            [
277                0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0,
278                0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53,
279                0x5c, 0x3a, 0x35, 0xe2,
280            ],
281        );
282    }
283
284    //
285    // Just random tests
286    //
287
288    #[test]
289    fn it_should_compute_pbkdf2_stage_properly() {
290        check(
291            &[0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64],
292            &[&[
293                0xc6, 0x00, 0x40, 0x4e, 0x39, 0xc9, 0xe9, 0x7a, 0x7d, 0x7a, 0x74, 0x5b, 0x32, 0xc3,
294                0xe7, 0x42, 0x63, 0x87, 0xb3, 0x65, 0x69, 0x3c, 0x7f, 0x59, 0x30, 0x0f, 0xd8, 0xa0,
295                0x3a, 0xab, 0x4c, 0x6e,
296            ]],
297            [
298                0xbe, 0x97, 0xc8, 0x11, 0x49, 0x75, 0x0f, 0x47, 0x65, 0x9a, 0x37, 0xf6, 0x23, 0x00,
299                0x43, 0xf5, 0x79, 0xb5, 0xd8, 0x35, 0xac, 0xdd, 0xfc, 0x8e, 0x70, 0xd7, 0x4a, 0xef,
300                0x66, 0x95, 0xe1, 0x28,
301            ],
302        );
303    }
304}