dcrypt_algorithms/mac/hmac/
mod.rs1use crate::error::{Error, Result};
8use crate::hash::HashFunction;
9use dcrypt_common::security::{SecretBuffer, SecureZeroingType};
10use subtle::ConstantTimeEq;
11use zeroize::{Zeroize, ZeroizeOnDrop};
12
13const MAX_BLOCK: usize = 144; #[derive(Clone, Zeroize, ZeroizeOnDrop)]
17pub struct Hmac<H: HashFunction + Clone + Zeroize> {
18 hash: H,
20 ipad: SecretBuffer<MAX_BLOCK>,
21 opad: SecretBuffer<MAX_BLOCK>,
22 block_size: usize,
23 is_finalized: bool,
24}
25
26impl<H> Hmac<H>
27where
28 H: HashFunction + Clone + Zeroize,
29 H::Output: AsRef<[u8]> + Clone + Zeroize,
30{
31 const IPAD_BYTE: u8 = 0x36;
32 const OPAD_BYTE: u8 = 0x5c;
33
34 pub fn new(key: &[u8]) -> Result<Self> {
40 let bs = H::block_size();
41 debug_assert!(bs <= MAX_BLOCK);
42
43 let mut hk = H::new();
47 if let Err(error) = hk.update(key) {
48 hk.zeroize();
49 return Err(error);
50 }
51 let mut hashed = match hk.finalize() {
52 Ok(output) => output,
53 Err(error) => {
54 hk.zeroize();
55 return Err(error);
56 }
57 }; hk.zeroize();
59
60 let mut k_prime = SecretBuffer::<MAX_BLOCK>::zeroed();
62 let long = (key.len() > bs) as u8; let mask = long.wrapping_neg(); #[allow(clippy::needless_range_loop)] for i in 0..bs {
66 let k = *key.get(i).unwrap_or(&0);
67 let hk = hashed.as_ref().get(i).copied().unwrap_or(0);
68 k_prime.as_mut()[i] = (hk & mask) | (k & !mask);
69 }
70 hashed.zeroize();
71
72 let mut ipad = SecretBuffer::<MAX_BLOCK>::zeroed();
74 let mut opad = SecretBuffer::<MAX_BLOCK>::zeroed();
75 #[allow(clippy::needless_range_loop)] for i in 0..bs {
77 ipad.as_mut()[i] = k_prime.as_ref()[i] ^ Self::IPAD_BYTE;
78 opad.as_mut()[i] = k_prime.as_ref()[i] ^ Self::OPAD_BYTE;
79 }
80
81 k_prime.zeroize();
83
84 let mut hash = H::new();
86 if let Err(error) = hash.update(&ipad.as_ref()[..bs]) {
87 hash.zeroize();
88 return Err(error);
89 }
90
91 Ok(Self {
92 hash,
93 ipad,
94 opad,
95 block_size: bs,
96 is_finalized: false,
97 })
98 }
99
100 pub fn update(&mut self, data: &[u8]) -> Result<()> {
106 if self.is_finalized {
107 return Err(Error::param(
108 "hmac_state",
109 "Cannot update after finalization",
110 ));
111 }
112
113 self.hash.update(data).map(|_| ())
114 }
115
116 pub fn finalize(&mut self) -> Result<Vec<u8>> {
118 if self.is_finalized {
119 return Err(Error::param("hmac_state", "HMAC already finalized"));
120 }
121
122 self.is_finalized = true;
123
124 let mut inner_hash = match self.hash.finalize() {
125 Ok(output) => {
126 self.hash.zeroize();
127 output
128 }
129 Err(error) => {
130 self.hash.zeroize();
131 return Err(error);
132 }
133 };
134
135 let mut outer = H::new();
136 if let Err(error) = outer.update(&self.opad.as_ref()[..self.block_size]) {
137 inner_hash.zeroize();
138 outer.zeroize();
139 return Err(error);
140 }
141 if let Err(error) = outer.update(inner_hash.as_ref()) {
142 inner_hash.zeroize();
143 outer.zeroize();
144 return Err(error);
145 }
146 inner_hash.zeroize();
147
148 let mut output = match outer.finalize() {
149 Ok(output) => output,
150 Err(error) => {
151 outer.zeroize();
152 return Err(error);
153 }
154 };
155 outer.zeroize();
156 let tag = output.as_ref().to_vec();
157 output.zeroize();
158 Ok(tag)
159 }
160
161 pub fn mac(key: &[u8], data: &[u8]) -> Result<Vec<u8>> {
167 let mut h = Self::new(key)?;
168 h.update(data)?;
169 h.finalize()
170 }
171
172 pub fn verify(key: &[u8], data: &[u8], tag: &[u8]) -> Result<bool> {
178 let expected = Self::mac(key, data)?;
179
180 let mut diff = 0u8;
183 #[allow(clippy::needless_range_loop)] for i in 0..H::output_size() {
185 let a = expected.get(i).copied().unwrap_or(0);
186 let b = tag.get(i).copied().unwrap_or(0);
187 diff |= a ^ b;
188 }
189 diff |= tag.len().ct_eq(&H::output_size()).unwrap_u8() ^ 1;
192
193 Ok(diff.ct_eq(&0u8).unwrap_u8() == 1)
194 }
195}
196
197impl<H> SecureZeroingType for Hmac<H>
198where
199 H: HashFunction + Default + Clone + Zeroize,
200{
201 fn zeroed() -> Self {
202 Self {
203 hash: H::default(),
204 ipad: SecretBuffer::zeroed(),
205 opad: SecretBuffer::zeroed(),
206 block_size: 0,
207 is_finalized: false,
208 }
209 }
210
211 fn secure_clone(&self) -> Self {
212 Self {
213 hash: self.hash.clone(),
214 ipad: self.ipad.secure_clone(),
215 opad: self.opad.secure_clone(),
216 block_size: self.block_size,
217 is_finalized: self.is_finalized,
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests;