1use crate::api::errors::{PqcError, PqcResult};
9use crate::api::traits::Mac;
10use hmac::{Hmac, Mac as HmacMac};
11use sha3::{Sha3_256, Sha3_512};
12use subtle::ConstantTimeEq;
13use zeroize::Zeroizing;
14
15pub struct HmacSha3_256 {
17 mac: Hmac<Sha3_256>,
18}
19
20#[derive(Clone)]
22pub struct HmacSha3_256Output([u8; 32]);
23
24impl AsRef<[u8]> for HmacSha3_256Output {
25 fn as_ref(&self) -> &[u8] {
26 &self.0
27 }
28}
29
30impl Mac for HmacSha3_256 {
31 type Output = HmacSha3_256Output;
32
33 fn new(key: &[u8]) -> PqcResult<Self> {
34 use hmac::Mac as HmacMac;
35 Ok(Self {
36 mac: <Hmac<Sha3_256> as HmacMac>::new_from_slice(key)
37 .map_err(|_| PqcError::InvalidKeyLength)?,
38 })
39 }
40
41 fn update(&mut self, data: &[u8]) {
42 self.mac.update(data);
43 }
44
45 fn finalize(self) -> Self::Output {
46 let result = self.mac.finalize();
47 let mut output = [0u8; 32];
48 output.copy_from_slice(&result.into_bytes());
49 HmacSha3_256Output(output)
50 }
51
52 fn verify(&self, tag: &[u8]) -> PqcResult<()> {
53 if tag.len() != 32 {
54 return Err(PqcError::InvalidSignature);
55 }
56
57 let mac_clone = self.mac.clone();
59 let result = mac_clone.finalize();
60
61 if result.into_bytes().ct_eq(tag).into() {
63 Ok(())
64 } else {
65 Err(PqcError::InvalidSignature)
66 }
67 }
68
69 fn output_size() -> usize {
70 32
71 }
72
73 fn name() -> &'static str {
74 "HMAC-SHA3-256"
75 }
76}
77
78pub struct HmacSha3_512 {
80 mac: Hmac<Sha3_512>,
81}
82
83#[derive(Clone)]
85pub struct HmacSha3_512Output([u8; 64]);
86
87impl AsRef<[u8]> for HmacSha3_512Output {
88 fn as_ref(&self) -> &[u8] {
89 &self.0
90 }
91}
92
93impl Mac for HmacSha3_512 {
94 type Output = HmacSha3_512Output;
95
96 fn new(key: &[u8]) -> PqcResult<Self> {
97 use hmac::Mac as HmacMac;
98 Ok(Self {
99 mac: <Hmac<Sha3_512> as HmacMac>::new_from_slice(key)
100 .map_err(|_| PqcError::InvalidKeyLength)?,
101 })
102 }
103
104 fn update(&mut self, data: &[u8]) {
105 self.mac.update(data);
106 }
107
108 fn finalize(self) -> Self::Output {
109 let result = self.mac.finalize();
110 let mut output = [0u8; 64];
111 output.copy_from_slice(&result.into_bytes());
112 HmacSha3_512Output(output)
113 }
114
115 fn verify(&self, tag: &[u8]) -> PqcResult<()> {
116 if tag.len() != 64 {
117 return Err(PqcError::InvalidSignature);
118 }
119
120 let mac_clone = self.mac.clone();
121 let result = mac_clone.finalize();
122
123 if result.into_bytes().ct_eq(tag).into() {
124 Ok(())
125 } else {
126 Err(PqcError::InvalidSignature)
127 }
128 }
129
130 fn output_size() -> usize {
131 64
132 }
133
134 fn name() -> &'static str {
135 "HMAC-SHA3-512"
136 }
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum HmacAlgorithm {
142 HmacSha3_256,
144 HmacSha3_512,
146}
147
148impl HmacAlgorithm {
149 pub fn mac(&self, key: &[u8], data: &[u8]) -> PqcResult<Vec<u8>> {
155 match self {
156 Self::HmacSha3_256 => {
157 let tag = HmacSha3_256::mac(key, data)?;
158 Ok(tag.as_ref().to_vec())
159 }
160 Self::HmacSha3_512 => {
161 let tag = HmacSha3_512::mac(key, data)?;
162 Ok(tag.as_ref().to_vec())
163 }
164 }
165 }
166
167 pub fn verify(&self, key: &[u8], data: &[u8], tag: &[u8]) -> PqcResult<()> {
173 let computed = self.mac(key, data)?;
174
175 if computed.len() != tag.len() {
176 return Err(PqcError::InvalidSignature);
177 }
178
179 if computed.ct_eq(tag).into() {
180 Ok(())
181 } else {
182 Err(PqcError::InvalidSignature)
183 }
184 }
185
186 #[must_use]
188 pub fn output_size(&self) -> usize {
189 match self {
190 Self::HmacSha3_256 => HmacSha3_256::output_size(),
191 Self::HmacSha3_512 => HmacSha3_512::output_size(),
192 }
193 }
194
195 #[must_use]
197 pub fn name(&self) -> &'static str {
198 match self {
199 Self::HmacSha3_256 => HmacSha3_256::name(),
200 Self::HmacSha3_512 => HmacSha3_512::name(),
201 }
202 }
203}
204
205pub mod helpers {
207 use super::{HmacAlgorithm, HmacSha3_256, HmacSha3_512, Mac, PqcResult, Zeroizing};
208
209 pub fn hmac_sha3_256(key: &[u8], data: &[u8]) -> PqcResult<[u8; 32]> {
215 let tag = HmacSha3_256::mac(key, data)?;
216 let mut result = [0u8; 32];
217 result.copy_from_slice(tag.as_ref());
218 Ok(result)
219 }
220
221 pub fn hmac_sha3_512(key: &[u8], data: &[u8]) -> PqcResult<[u8; 64]> {
227 let tag = HmacSha3_512::mac(key, data)?;
228 let mut result = [0u8; 64];
229 result.copy_from_slice(tag.as_ref());
230 Ok(result)
231 }
232
233 pub fn verify_hmac_sha3_256(key: &[u8], data: &[u8], tag: &[u8; 32]) -> PqcResult<()> {
239 HmacAlgorithm::HmacSha3_256.verify(key, data, tag)
240 }
241
242 pub fn verify_hmac_sha3_512(key: &[u8], data: &[u8], tag: &[u8; 64]) -> PqcResult<()> {
248 HmacAlgorithm::HmacSha3_512.verify(key, data, tag)
249 }
250
251 pub fn derive_mac_key(key_material: &[u8], context: &[u8]) -> PqcResult<Zeroizing<[u8; 32]>> {
257 use crate::api::kdf::HkdfSha3_256;
258 use crate::api::traits::Kdf;
259
260 let mut mac_key = Zeroizing::new([0u8; 32]);
261 HkdfSha3_256::derive(key_material, None, context, &mut mac_key[..])?;
262 Ok(mac_key)
263 }
264
265 pub fn key_confirmation(
271 shared_secret: &[u8],
272 initiator_data: &[u8],
273 responder_data: &[u8],
274 ) -> PqcResult<[u8; 32]> {
275 let mut combined = Vec::new();
276 combined.extend_from_slice(initiator_data);
277 combined.extend_from_slice(responder_data);
278
279 hmac_sha3_256(shared_secret, &combined)
280 }
281}
282
283#[cfg(test)]
284#[allow(clippy::indexing_slicing)]
285#[allow(clippy::unwrap_used, clippy::expect_used)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_hmac_sha3_256_basic() {
291 let key = b"test key";
292 let data = b"test data";
293
294 let tag1 = HmacSha3_256::mac(key, data).unwrap();
295 let tag2 = helpers::hmac_sha3_256(key, data).unwrap();
296
297 assert_eq!(tag1.as_ref(), &tag2);
298 assert_eq!(HmacSha3_256::output_size(), 32);
299 assert_eq!(HmacSha3_256::name(), "HMAC-SHA3-256");
300 }
301
302 #[test]
303 fn test_hmac_sha3_512_basic() {
304 let key = b"test key";
305 let data = b"test data";
306
307 let tag1 = HmacSha3_512::mac(key, data).unwrap();
308 let tag2 = helpers::hmac_sha3_512(key, data).unwrap();
309
310 assert_eq!(tag1.as_ref(), &tag2);
311 assert_eq!(HmacSha3_512::output_size(), 64);
312 assert_eq!(HmacSha3_512::name(), "HMAC-SHA3-512");
313 }
314
315 #[test]
316 fn test_hmac_incremental() {
317 let key = b"test key";
318 let data1 = b"first part";
319 let data2 = b"second part";
320 let combined = b"first partsecond part";
321
322 let mut mac = HmacSha3_256::new(key).unwrap();
324 mac.update(data1);
325 mac.update(data2);
326 let incremental_tag = mac.finalize();
327
328 let direct_tag = HmacSha3_256::mac(key, combined).unwrap();
329 assert_eq!(incremental_tag.as_ref(), direct_tag.as_ref());
330
331 let mut mac = HmacSha3_512::new(key).unwrap();
333 mac.update(data1);
334 mac.update(data2);
335 let incremental_tag = mac.finalize();
336
337 let direct_tag = HmacSha3_512::mac(key, combined).unwrap();
338 assert_eq!(incremental_tag.as_ref(), direct_tag.as_ref());
339 }
340
341 #[test]
342 fn test_hmac_verification_success() {
343 let key = b"test key";
344 let data = b"test data";
345
346 let tag = helpers::hmac_sha3_256(key, data).unwrap();
348 assert!(helpers::verify_hmac_sha3_256(key, data, &tag).is_ok());
349
350 let tag = helpers::hmac_sha3_512(key, data).unwrap();
352 assert!(helpers::verify_hmac_sha3_512(key, data, &tag).is_ok());
353 }
354
355 #[test]
356 fn test_hmac_verification_failure() {
357 let key = b"test key";
358 let data = b"test data";
359
360 let mut wrong_tag = helpers::hmac_sha3_256(key, data).unwrap();
362 wrong_tag[0] ^= 0x01; assert!(helpers::verify_hmac_sha3_256(key, data, &wrong_tag).is_err());
364
365 let mut wrong_tag = helpers::hmac_sha3_512(key, data).unwrap();
367 wrong_tag[0] ^= 0x01;
368 assert!(helpers::verify_hmac_sha3_512(key, data, &wrong_tag).is_err());
369
370 let tag = helpers::hmac_sha3_256(key, data).unwrap();
372 assert!(helpers::verify_hmac_sha3_256(key, b"wrong data", &tag).is_err());
373
374 let tag = helpers::hmac_sha3_256(key, data).unwrap();
376 assert!(helpers::verify_hmac_sha3_256(b"wrong key", data, &tag).is_err());
377 }
378
379 #[test]
380 fn test_hmac_algorithm_enum() {
381 let key = b"test key";
382 let data = b"test data";
383
384 let tag1 = HmacAlgorithm::HmacSha3_256.mac(key, data).unwrap();
385 assert_eq!(tag1.len(), 32);
386 assert_eq!(HmacAlgorithm::HmacSha3_256.output_size(), 32);
387 assert_eq!(HmacAlgorithm::HmacSha3_256.name(), "HMAC-SHA3-256");
388
389 let tag2 = HmacAlgorithm::HmacSha3_512.mac(key, data).unwrap();
390 assert_eq!(tag2.len(), 64);
391
392 assert!(HmacAlgorithm::HmacSha3_256.verify(key, data, &tag1).is_ok());
394 assert!(HmacAlgorithm::HmacSha3_512.verify(key, data, &tag2).is_ok());
395 }
396
397 #[test]
398 fn test_derive_mac_key() {
399 let key_material = b"key material";
400 let context = b"MAC key derivation";
401
402 let mac_key1 = helpers::derive_mac_key(key_material, context).unwrap();
403 assert_eq!(mac_key1.len(), 32);
404
405 let mac_key2 = helpers::derive_mac_key(key_material, context).unwrap();
407 assert_eq!(&mac_key1[..], &mac_key2[..]);
408
409 let mac_key3 = helpers::derive_mac_key(key_material, b"different context").unwrap();
411 assert_ne!(&mac_key1[..], &mac_key3[..]);
412 }
413
414 #[test]
415 fn test_key_confirmation() {
416 let shared_secret = b"shared secret from key exchange";
417 let party_a = b"Alice's public data";
418 let party_b = b"Bob's public data";
419
420 let confirmation1 = helpers::key_confirmation(shared_secret, party_a, party_b).unwrap();
421 assert_eq!(confirmation1.len(), 32);
422
423 let confirmation2 = helpers::key_confirmation(shared_secret, party_a, party_b).unwrap();
425 assert_eq!(confirmation1, confirmation2);
426
427 let confirmation3 = helpers::key_confirmation(shared_secret, party_b, party_a).unwrap();
429 assert_ne!(confirmation1, confirmation3);
430 }
431}