1use std::io::{Error, Read};
2
3use crate::hash::HashFn;
4
5pub struct HMAC<const B: usize, const L: usize, H: HashFn<B, L>, F: Fn() -> H> {
6 key_opad: [u8; B],
7 hash_state: H,
8 hash_new_fn: F,
9}
10
11impl<const B: usize, const L: usize, H: HashFn<B, L>, F: Fn() -> H> HMAC<B, L, H, F> {
12 pub fn new(hash_new_fn: F, key: &[u8]) -> Self {
13 let mut final_key = [0u8; B];
14 if key.len() > B {
15 let mut key_hasher = hash_new_fn();
16 key_hasher.update(key);
17 final_key[..L].copy_from_slice(&key_hasher.finalize());
18 } else {
19 final_key[..key.len()].copy_from_slice(key);
20 }
21
22 let mut key_ipad = [0x36; B];
23 let mut key_opad = [0x5c; B];
24 for i in 0..B {
25 key_ipad[i] ^= final_key[i];
26 key_opad[i] ^= final_key[i];
27 }
28
29 let mut sha1state = hash_new_fn();
30 sha1state.update(key_ipad.as_slice());
31 Self {
32 hash_state: sha1state,
33 key_opad,
34 hash_new_fn,
35 }
36 }
37}
38
39impl<const B: usize, const L: usize, H: HashFn<B, L>, F: Fn() -> H> HashFn<B, L>
40 for HMAC<B, L, H, F>
41{
42 fn update(&mut self, data: &[u8]) {
43 self.hash_state.update(data);
44 }
45
46 fn finalize(&mut self) -> [u8; L] {
47 let first_hash = self.hash_state.finalize();
48 let f = &self.hash_new_fn;
49 let mut second_sha1_state = f();
50 second_sha1_state.update(&self.key_opad);
51 second_sha1_state.update(&first_hash);
52 second_sha1_state.finalize()
53 }
54}
55
56pub fn digest_from_bytes<const B: usize, const L: usize, H: HashFn<B, L>, F: Fn() -> H>(
57 hash_new_fn: F,
58 data: &[u8],
59 key: &[u8],
60) -> [u8; L] {
61 let mut state = HMAC::new(hash_new_fn, key);
62 state.update(data);
63 state.finalize()
64}
65
66pub fn digest_from_reader<
67 const B: usize,
68 const L: usize,
69 H: HashFn<B, L>,
70 F: Fn() -> H,
71 R: Read,
72>(
73 hash_new_fn: F,
74 mut r: R,
75 key: &[u8],
76) -> Result<[u8; L], Error> {
77 let mut state = HMAC::new(hash_new_fn, key);
78 let mut buf = [0u8; 512];
79
80 loop {
81 let n = r.read(&mut buf)?;
82
83 if n == 0 {
84 break;
85 }
86
87 state.update(&buf[..n]);
88 }
89
90 Ok(state.finalize())
91}
92
93#[cfg(test)]
94mod tests {
95 use super::digest_from_bytes;
96 use crate::hex;
97 use crate::sha1::SHA1;
98
99 #[test]
100 fn test_hmac_sha1_rfc_test_vectors() {
101 struct TestCase {
102 key: Vec<u8>,
103 data: Vec<u8>,
104 expected_digest: Vec<u8>,
105 }
106 let tests_cases: Vec<TestCase> = vec![
107 TestCase {
108 key: hex::decode_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(),
109 data: Vec::from("Hi There"),
110 expected_digest: hex::decode_hex("b617318655057264e28bc0b6fb378c8ef146be00")
111 .unwrap(),
112 },
113 TestCase {
114 key: Vec::from("Jefe"),
115 data: Vec::from("what do ya want for nothing?"),
116 expected_digest: hex::decode_hex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79")
117 .unwrap(),
118 },
119 TestCase {
120 key: hex::decode_hex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(),
121 data: Vec::from([0xdd; 50]),
122 expected_digest: hex::decode_hex("125d7342b9ac11cd91a39af48aa17b4f63f175d3")
123 .unwrap(),
124 },
125 TestCase {
126 key: hex::decode_hex("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(),
127 data: Vec::from([0xcd; 50]),
128 expected_digest: hex::decode_hex("4c9007f4026250c6bc8414f9bf50c86c2d7235da")
129 .unwrap(),
130 },
131 TestCase {
132 key: hex::decode_hex("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap(),
133 data: Vec::from("Test With Truncation"),
134 expected_digest: hex::decode_hex("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04")
135 .unwrap(),
136 },
137 TestCase {
138 key: Vec::from([0xaa; 80]),
139 data: Vec::from("Test Using Larger Than Block-Size Key - Hash Key First"),
140 expected_digest: hex::decode_hex("aa4ae5e15272d00e95705637ce8a3b55ed402112")
141 .unwrap(),
142 },
143 TestCase {
144 key: Vec::from([0xaa; 80]),
145 data: Vec::from(
146 "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
147 ),
148 expected_digest: hex::decode_hex("e8e99d0f45237d786d6bbaa7965c7808bbff1a91")
149 .unwrap(),
150 },
151 ];
152
153 for test_case in tests_cases {
154 let digest = digest_from_bytes(SHA1::new, test_case.data.as_slice(), &test_case.key);
155 assert!(digest.eq(test_case.expected_digest.as_slice()))
156 }
157 }
158}