/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.16.3/src/cmac.rs
Line | Count | Source |
1 | | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
2 | | // SPDX-License-Identifier: Apache-2.0 OR ISC |
3 | | |
4 | | //! CMAC is specified in [RFC 4493] and [NIST SP 800-38B]. |
5 | | //! |
6 | | //! After a `Key` is constructed, it can be used for multiple signing or |
7 | | //! verification operations. Separating the construction of the key from the |
8 | | //! rest of the CMAC operation allows the per-key precomputation to be done |
9 | | //! only once, instead of it being done in every CMAC operation. |
10 | | //! |
11 | | //! Frequently all the data to be signed in a message is available in a single |
12 | | //! contiguous piece. In that case, the module-level `sign` function can be |
13 | | //! used. Otherwise, if the input is in multiple parts, `Context` should be |
14 | | //! used. |
15 | | //! |
16 | | //! # Examples: |
17 | | //! |
18 | | //! ## Signing a value and verifying it wasn't tampered with |
19 | | //! |
20 | | //! ``` |
21 | | //! use aws_lc_rs::cmac; |
22 | | //! |
23 | | //! let key = cmac::Key::generate(cmac::AES_128)?; |
24 | | //! |
25 | | //! let msg = "hello, world"; |
26 | | //! |
27 | | //! let tag = cmac::sign(&key, msg.as_bytes())?; |
28 | | //! |
29 | | //! // [We give access to the message to an untrusted party, and they give it |
30 | | //! // back to us. We need to verify they didn't tamper with it.] |
31 | | //! |
32 | | //! cmac::verify(&key, msg.as_bytes(), tag.as_ref())?; |
33 | | //! |
34 | | //! # Ok::<(), aws_lc_rs::error::Unspecified>(()) |
35 | | //! ``` |
36 | | //! |
37 | | //! ## Using the one-shot API: |
38 | | //! |
39 | | //! ``` |
40 | | //! use aws_lc_rs::{cmac, rand}; |
41 | | //! |
42 | | //! let msg = "hello, world"; |
43 | | //! |
44 | | //! // The sender generates a secure key value and signs the message with it. |
45 | | //! // Note that in a real protocol, a key agreement protocol would be used to |
46 | | //! // derive `key_value`. |
47 | | //! let rng = rand::SystemRandom::new(); |
48 | | //! let key_value: [u8; 16] = rand::generate(&rng)?.expose(); |
49 | | //! |
50 | | //! let s_key = cmac::Key::new(cmac::AES_128, key_value.as_ref())?; |
51 | | //! let tag = cmac::sign(&s_key, msg.as_bytes())?; |
52 | | //! |
53 | | //! // The receiver (somehow!) knows the key value, and uses it to verify the |
54 | | //! // integrity of the message. |
55 | | //! let v_key = cmac::Key::new(cmac::AES_128, key_value.as_ref())?; |
56 | | //! cmac::verify(&v_key, msg.as_bytes(), tag.as_ref())?; |
57 | | //! |
58 | | //! # Ok::<(), aws_lc_rs::error::Unspecified>(()) |
59 | | //! ``` |
60 | | //! |
61 | | //! ## Using the multi-part API: |
62 | | //! ``` |
63 | | //! use aws_lc_rs::{cmac, rand}; |
64 | | //! |
65 | | //! let parts = ["hello", ", ", "world"]; |
66 | | //! |
67 | | //! // The sender generates a secure key value and signs the message with it. |
68 | | //! // Note that in a real protocol, a key agreement protocol would be used to |
69 | | //! // derive `key_value`. |
70 | | //! let rng = rand::SystemRandom::new(); |
71 | | //! let key_value: [u8; 32] = rand::generate(&rng)?.expose(); |
72 | | //! |
73 | | //! let s_key = cmac::Key::new(cmac::AES_256, key_value.as_ref())?; |
74 | | //! let mut s_ctx = cmac::Context::with_key(&s_key); |
75 | | //! for part in &parts { |
76 | | //! s_ctx.update(part.as_bytes())?; |
77 | | //! } |
78 | | //! let tag = s_ctx.sign()?; |
79 | | //! |
80 | | //! // The receiver (somehow!) knows the key value, and uses it to verify the |
81 | | //! // integrity of the message. |
82 | | //! let v_key = cmac::Key::new(cmac::AES_256, key_value.as_ref())?; |
83 | | //! let mut msg = Vec::<u8>::new(); |
84 | | //! for part in &parts { |
85 | | //! msg.extend(part.as_bytes()); |
86 | | //! } |
87 | | //! cmac::verify(&v_key, &msg.as_ref(), tag.as_ref())?; |
88 | | //! |
89 | | //! # Ok::<(), aws_lc_rs::error::Unspecified>(()) |
90 | | //! ``` |
91 | | //! [RFC 4493]: https://tools.ietf.org/html/rfc4493 |
92 | | //! [NIST SP 800-38B]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf |
93 | | |
94 | | use crate::aws_lc::{ |
95 | | CMAC_CTX_copy, CMAC_CTX_new, CMAC_Final, CMAC_Init, CMAC_Update, EVP_aes_128_cbc, |
96 | | EVP_aes_192_cbc, EVP_aes_256_cbc, EVP_des_ede3_cbc, CMAC_CTX, EVP_CIPHER, |
97 | | }; |
98 | | use crate::error::Unspecified; |
99 | | use crate::fips::indicator_check; |
100 | | use crate::ptr::{ConstPointer, LcPtr}; |
101 | | use crate::{constant_time, rand}; |
102 | | use core::mem::MaybeUninit; |
103 | | use core::ptr::null_mut; |
104 | | |
105 | | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
106 | | enum AlgorithmId { |
107 | | Aes128, |
108 | | Aes192, |
109 | | Aes256, |
110 | | Tdes, |
111 | | } |
112 | | |
113 | | /// A CMAC algorithm. |
114 | | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
115 | | pub struct Algorithm { |
116 | | id: AlgorithmId, |
117 | | key_len: usize, |
118 | | tag_len: usize, |
119 | | } |
120 | | |
121 | | impl Algorithm { |
122 | | /// The key length for this CMAC algorithm. |
123 | | #[inline] |
124 | | #[must_use] |
125 | 0 | pub fn key_len(&self) -> usize { |
126 | 0 | self.key_len |
127 | 0 | } |
128 | | |
129 | | /// The tag length for this CMAC algorithm. |
130 | | #[inline] |
131 | | #[must_use] |
132 | 0 | pub fn tag_len(&self) -> usize { |
133 | 0 | self.tag_len |
134 | 0 | } |
135 | | } |
136 | | |
137 | | impl AlgorithmId { |
138 | 0 | fn evp_cipher(&self) -> ConstPointer<'_, EVP_CIPHER> { |
139 | | unsafe { |
140 | 0 | ConstPointer::new_static(match self { |
141 | 0 | AlgorithmId::Aes128 => EVP_aes_128_cbc(), |
142 | 0 | AlgorithmId::Aes192 => EVP_aes_192_cbc(), |
143 | 0 | AlgorithmId::Aes256 => EVP_aes_256_cbc(), |
144 | 0 | AlgorithmId::Tdes => EVP_des_ede3_cbc(), |
145 | | }) |
146 | 0 | .unwrap() |
147 | | } |
148 | 0 | } |
149 | | } |
150 | | |
151 | | /// CMAC using AES-128. |
152 | | pub const AES_128: Algorithm = Algorithm { |
153 | | id: AlgorithmId::Aes128, |
154 | | key_len: 16, |
155 | | tag_len: 16, |
156 | | }; |
157 | | |
158 | | /// CMAC using AES-192. |
159 | | pub const AES_192: Algorithm = Algorithm { |
160 | | id: AlgorithmId::Aes192, |
161 | | key_len: 24, |
162 | | tag_len: 16, |
163 | | }; |
164 | | |
165 | | /// CMAC using AES-256. |
166 | | pub const AES_256: Algorithm = Algorithm { |
167 | | id: AlgorithmId::Aes256, |
168 | | key_len: 32, |
169 | | tag_len: 16, |
170 | | }; |
171 | | |
172 | | /// CMAC using 3DES (Triple DES). Obsolete |
173 | | pub const TDES_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm { |
174 | | id: AlgorithmId::Tdes, |
175 | | key_len: 24, |
176 | | tag_len: 8, |
177 | | }; |
178 | | |
179 | | /// Maximum CMAC tag length (AES block size). |
180 | | const MAX_CMAC_TAG_LEN: usize = 16; |
181 | | |
182 | | /// A CMAC tag. |
183 | | /// |
184 | | /// For a given tag `t`, use `t.as_ref()` to get the tag value as a byte slice. |
185 | | #[derive(Clone, Copy, Debug)] |
186 | | pub struct Tag { |
187 | | bytes: [u8; MAX_CMAC_TAG_LEN], |
188 | | len: usize, |
189 | | } |
190 | | |
191 | | impl AsRef<[u8]> for Tag { |
192 | | #[inline] |
193 | 0 | fn as_ref(&self) -> &[u8] { |
194 | 0 | &self.bytes[..self.len] |
195 | 0 | } |
196 | | } |
197 | | |
198 | | /// A key to use for CMAC signing. |
199 | | // |
200 | | // # FIPS |
201 | | // Use this type with one of the following algorithms: |
202 | | // * `AES_128` |
203 | | // * `AES_256` |
204 | | #[derive(Clone)] |
205 | | pub struct Key { |
206 | | algorithm: Algorithm, |
207 | | ctx: LcPtr<CMAC_CTX>, |
208 | | } |
209 | | |
210 | | impl Clone for LcPtr<CMAC_CTX> { |
211 | 0 | fn clone(&self) -> Self { |
212 | 0 | let mut new_ctx = LcPtr::new(unsafe { CMAC_CTX_new() }).expect("CMAC_CTX_new failed"); |
213 | | unsafe { |
214 | 0 | assert!( |
215 | 0 | 1 == CMAC_CTX_copy(new_ctx.as_mut_ptr(), self.as_const_ptr()), |
216 | 0 | "CMAC_CTX_copy failed" |
217 | | ); |
218 | | } |
219 | 0 | new_ctx |
220 | 0 | } |
221 | | } |
222 | | |
223 | | unsafe impl Send for Key {} |
224 | | // All uses of *mut CMAC_CTX require the creation of a Context, which will clone the Key. |
225 | | unsafe impl Sync for Key {} |
226 | | |
227 | | #[allow(clippy::missing_fields_in_debug)] |
228 | | impl core::fmt::Debug for Key { |
229 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
230 | 0 | f.debug_struct("Key") |
231 | 0 | .field("algorithm", &self.algorithm) |
232 | 0 | .finish() |
233 | 0 | } |
234 | | } |
235 | | |
236 | | impl Key { |
237 | | /// Generate a CMAC signing key using the given algorithm with a |
238 | | /// random value. |
239 | | /// |
240 | | // |
241 | | // # FIPS |
242 | | // Use this type with one of the following algorithms: |
243 | | // * `AES_128` |
244 | | // * `AES_256` |
245 | | // |
246 | | /// # Errors |
247 | | /// `error::Unspecified` if random generation or key construction fails. |
248 | 0 | pub fn generate(algorithm: Algorithm) -> Result<Self, Unspecified> { |
249 | 0 | let mut key_bytes = vec![0u8; algorithm.key_len()]; |
250 | 0 | rand::fill(&mut key_bytes)?; |
251 | 0 | Self::new(algorithm, &key_bytes) |
252 | 0 | } |
253 | | |
254 | | /// Construct a CMAC signing key using the given algorithm and key value. |
255 | | /// |
256 | | /// `key_value` should be a value generated using a secure random number |
257 | | /// generator or derived from a random key by a key derivation function. |
258 | | /// |
259 | | /// # Errors |
260 | | /// `error::Unspecified` if the key length doesn't match the algorithm or if CMAC context |
261 | | /// initialization fails. |
262 | 0 | pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Result<Self, Unspecified> { |
263 | 0 | if key_value.len() != algorithm.key_len() { |
264 | 0 | return Err(Unspecified); |
265 | 0 | } |
266 | | |
267 | 0 | let mut ctx = LcPtr::new(unsafe { CMAC_CTX_new() })?; |
268 | | |
269 | | unsafe { |
270 | 0 | let cipher = algorithm.id.evp_cipher(); |
271 | 0 | if 1 != CMAC_Init( |
272 | 0 | ctx.as_mut_ptr(), |
273 | 0 | key_value.as_ptr().cast(), |
274 | 0 | key_value.len(), |
275 | 0 | cipher.as_const_ptr(), |
276 | 0 | null_mut(), |
277 | 0 | ) { |
278 | 0 | return Err(Unspecified); |
279 | 0 | } |
280 | | } |
281 | | |
282 | 0 | Ok(Self { algorithm, ctx }) |
283 | 0 | } |
284 | | |
285 | | /// The algorithm for the key. |
286 | | #[inline] |
287 | | #[must_use] |
288 | 0 | pub fn algorithm(&self) -> Algorithm { |
289 | 0 | self.algorithm |
290 | 0 | } |
291 | | } |
292 | | |
293 | | /// A context for multi-step (Init-Update-Finish) CMAC signing. |
294 | | /// |
295 | | /// Use `sign` for single-step CMAC signing. |
296 | | pub struct Context { |
297 | | key: Key, |
298 | | } |
299 | | |
300 | | impl Clone for Context { |
301 | 0 | fn clone(&self) -> Self { |
302 | 0 | Self { |
303 | 0 | key: self.key.clone(), |
304 | 0 | } |
305 | 0 | } |
306 | | } |
307 | | |
308 | | unsafe impl Send for Context {} |
309 | | |
310 | | impl core::fmt::Debug for Context { |
311 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
312 | 0 | f.debug_struct("Context") |
313 | 0 | .field("algorithm", &self.key.algorithm) |
314 | 0 | .finish() |
315 | 0 | } |
316 | | } |
317 | | |
318 | | impl Context { |
319 | | /// Constructs a new CMAC signing context using the given key. |
320 | | #[inline] |
321 | | #[must_use] |
322 | 0 | pub fn with_key(key: &Key) -> Self { |
323 | 0 | Self { key: key.clone() } |
324 | 0 | } |
325 | | |
326 | | /// Updates the CMAC with all the data in `data`. `update` may be called |
327 | | /// zero or more times until `sign` is called. |
328 | | /// |
329 | | /// # Errors |
330 | | /// `error::Unspecified` if the CMAC cannot be updated. |
331 | 0 | pub fn update(&mut self, data: &[u8]) -> Result<(), Unspecified> { |
332 | | unsafe { |
333 | 0 | if 1 != CMAC_Update(self.key.ctx.as_mut_ptr(), data.as_ptr(), data.len()) { |
334 | 0 | return Err(Unspecified); |
335 | 0 | } |
336 | | } |
337 | 0 | Ok(()) |
338 | 0 | } |
339 | | |
340 | | /// Finalizes the CMAC calculation and returns the CMAC value. `sign` |
341 | | /// consumes the context so it cannot be (mis-)used after `sign` has been |
342 | | /// called. |
343 | | /// |
344 | | /// It is generally not safe to implement CMAC verification by comparing |
345 | | /// the return value of `sign` to a tag. Use `verify` for verification |
346 | | /// instead. |
347 | | /// |
348 | | // |
349 | | // # FIPS |
350 | | // Use this method with one of the following algorithms: |
351 | | // * `AES_128` |
352 | | // * `AES_256` |
353 | | // |
354 | | /// # Errors |
355 | | /// `error::Unspecified` if the CMAC calculation cannot be finalized. |
356 | | /// |
357 | | /// # Panics |
358 | | /// Panics if the CMAC tag length exceeds the maximum allowed length, indicating memory corruption. |
359 | 0 | pub fn sign(mut self) -> Result<Tag, Unspecified> { |
360 | 0 | let mut output = [0u8; MAX_CMAC_TAG_LEN]; |
361 | 0 | let output_len = { |
362 | 0 | let result = internal_sign(&mut self, &mut output)?; |
363 | 0 | result.len() |
364 | | }; |
365 | | |
366 | 0 | Ok(Tag { |
367 | 0 | bytes: output, |
368 | 0 | len: output_len, |
369 | 0 | }) |
370 | 0 | } |
371 | | |
372 | | /// Finalizes the CMAC calculation and verifies whether the resulting value |
373 | | /// equals the provided `tag`. |
374 | | /// |
375 | | /// `verify` consumes the context so it cannot be (mis-)used after `verify` |
376 | | /// has been called. |
377 | | /// |
378 | | /// The verification is done in constant time to prevent timing attacks. |
379 | | /// |
380 | | /// # Errors |
381 | | /// `error::Unspecified` if the tag does not match or if CMAC calculation fails. |
382 | | // |
383 | | // # FIPS |
384 | | // Use this function with one of the following algorithms: |
385 | | // * `AES_128` |
386 | | // * `AES_256` |
387 | | #[inline] |
388 | 0 | pub fn verify(mut self, tag: &[u8]) -> Result<(), Unspecified> { |
389 | 0 | let mut output = [0u8; MAX_CMAC_TAG_LEN]; |
390 | 0 | let output_len = { |
391 | 0 | let result = internal_sign(&mut self, &mut output)?; |
392 | 0 | result.len() |
393 | | }; |
394 | | |
395 | 0 | constant_time::verify_slices_are_equal(&output[0..output_len], tag) |
396 | 0 | } |
397 | | } |
398 | | |
399 | 0 | pub(crate) fn internal_sign<'in_out>( |
400 | 0 | ctx: &mut Context, |
401 | 0 | output: &'in_out mut [u8], |
402 | 0 | ) -> Result<&'in_out mut [u8], Unspecified> { |
403 | 0 | let mut out_len = MaybeUninit::<usize>::uninit(); |
404 | | |
405 | 0 | if 1 != indicator_check!(unsafe { |
406 | 0 | CMAC_Final( |
407 | 0 | ctx.key.ctx.as_mut_ptr(), |
408 | 0 | output.as_mut_ptr(), |
409 | 0 | out_len.as_mut_ptr(), |
410 | 0 | ) |
411 | 0 | }) { |
412 | 0 | return Err(Unspecified); |
413 | 0 | } |
414 | 0 | let actual_len = unsafe { out_len.assume_init() }; |
415 | | |
416 | | // This indicates a memory corruption. |
417 | 0 | debug_assert!( |
418 | 0 | actual_len <= MAX_CMAC_TAG_LEN, |
419 | 0 | "CMAC tag length {actual_len} exceeds maximum {MAX_CMAC_TAG_LEN}" |
420 | | ); |
421 | 0 | if actual_len != ctx.key.algorithm.tag_len() { |
422 | 0 | return Err(Unspecified); |
423 | 0 | } |
424 | | |
425 | 0 | Ok(&mut output[0..actual_len]) |
426 | 0 | } |
427 | | |
428 | | /// Calculates the CMAC of `data` using the key `key` in one step. |
429 | | /// |
430 | | /// Use `Context` to calculate CMACs where the input is in multiple parts. |
431 | | /// |
432 | | /// It is generally not safe to implement CMAC verification by comparing the |
433 | | /// return value of `sign` to a tag. Use `verify` for verification instead. |
434 | | // |
435 | | // # FIPS |
436 | | // Use this function with one of the following algorithms: |
437 | | // * `AES_128` |
438 | | // * `AES_256` |
439 | | // |
440 | | /// # Errors |
441 | | /// `error::Unspecified` if the CMAC calculation fails. |
442 | | #[inline] |
443 | 0 | pub fn sign(key: &Key, data: &[u8]) -> Result<Tag, Unspecified> { |
444 | 0 | let mut ctx = Context::with_key(key); |
445 | 0 | ctx.update(data)?; |
446 | 0 | ctx.sign() |
447 | 0 | } |
448 | | |
449 | | /// Calculates the CMAC of `data` using the key `key` in one step, writing the |
450 | | /// result into the provided `output` buffer. |
451 | | /// |
452 | | /// Use `Context` to calculate CMACs where the input is in multiple parts. |
453 | | /// |
454 | | /// The `output` buffer must be at least as large as the algorithm's tag length |
455 | | /// (obtainable via `key.algorithm().tag_len()`). The returned slice will be a |
456 | | /// sub-slice of `output` containing exactly the tag bytes. |
457 | | /// |
458 | | /// It is generally not safe to implement CMAC verification by comparing the |
459 | | /// return value of `sign_to_buffer` to a tag. Use `verify` for verification instead. |
460 | | // |
461 | | // # FIPS |
462 | | // Use this function with one of the following algorithms: |
463 | | // * `AES_128` |
464 | | // * `AES_256` |
465 | | // |
466 | | /// # Errors |
467 | | /// `error::Unspecified` if the output buffer is too small or if the CMAC calculation fails. |
468 | | #[inline] |
469 | 0 | pub fn sign_to_buffer<'out>( |
470 | 0 | key: &Key, |
471 | 0 | data: &[u8], |
472 | 0 | output: &'out mut [u8], |
473 | 0 | ) -> Result<&'out mut [u8], Unspecified> { |
474 | 0 | if output.len() < key.algorithm().tag_len() { |
475 | 0 | return Err(Unspecified); |
476 | 0 | } |
477 | | |
478 | 0 | let mut ctx = Context::with_key(key); |
479 | 0 | ctx.update(data)?; |
480 | | |
481 | 0 | internal_sign(&mut ctx, output) |
482 | 0 | } |
483 | | |
484 | | /// Calculates the CMAC of `data` using the signing key `key`, and verifies |
485 | | /// whether the resultant value equals `tag`, in one step. |
486 | | /// |
487 | | /// The verification is done in constant time to prevent timing attacks. |
488 | | /// |
489 | | /// # Errors |
490 | | /// `error::Unspecified` if the tag does not match or if CMAC calculation fails. |
491 | | // |
492 | | // # FIPS |
493 | | // Use this function with one of the following algorithms: |
494 | | // * `AES_128` |
495 | | // * `AES_256` |
496 | | #[inline] |
497 | 0 | pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> { |
498 | 0 | let mut output = [0u8; MAX_CMAC_TAG_LEN]; |
499 | 0 | let output_len = { |
500 | 0 | let result = sign_to_buffer(key, data, &mut output)?; |
501 | 0 | result.len() |
502 | | }; |
503 | | |
504 | 0 | constant_time::verify_slices_are_equal(&output[0..output_len], tag) |
505 | 0 | } |
506 | | |
507 | | #[cfg(test)] |
508 | | mod tests { |
509 | | use super::*; |
510 | | |
511 | | #[cfg(feature = "fips")] |
512 | | mod fips; |
513 | | |
514 | | #[test] |
515 | | fn cmac_basic_test() { |
516 | | for &algorithm in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] { |
517 | | let key = Key::generate(algorithm).unwrap(); |
518 | | let data = b"hello, world"; |
519 | | |
520 | | let tag = sign(&key, data).unwrap(); |
521 | | assert!(verify(&key, data, tag.as_ref()).is_ok()); |
522 | | assert!(verify(&key, b"hello, worle", tag.as_ref()).is_err()); |
523 | | } |
524 | | } |
525 | | |
526 | | // Make sure that `Key::generate` and `verify` aren't completely wacky. |
527 | | #[test] |
528 | | pub fn cmac_signing_key_coverage() { |
529 | | const HELLO_WORLD_GOOD: &[u8] = b"hello, world"; |
530 | | const HELLO_WORLD_BAD: &[u8] = b"hello, worle"; |
531 | | |
532 | | for algorithm in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] { |
533 | | let key = Key::generate(*algorithm).unwrap(); |
534 | | let tag = sign(&key, HELLO_WORLD_GOOD).unwrap(); |
535 | | println!("{key:?}"); |
536 | | assert!(verify(&key, HELLO_WORLD_GOOD, tag.as_ref()).is_ok()); |
537 | | assert!(verify(&key, HELLO_WORLD_BAD, tag.as_ref()).is_err()); |
538 | | } |
539 | | } |
540 | | |
541 | | #[test] |
542 | | fn cmac_coverage() { |
543 | | // Something would have gone horribly wrong for this to not pass, but we test this so our |
544 | | // coverage reports will look better. |
545 | | assert_ne!(AES_128, AES_256); |
546 | | assert_ne!(AES_192, AES_256); |
547 | | |
548 | | for &alg in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] { |
549 | | // Clone after updating context with message, then check if the final Tag is the same. |
550 | | let key_bytes = vec![0u8; alg.key_len()]; |
551 | | let key = Key::new(alg, &key_bytes).unwrap(); |
552 | | let mut ctx = Context::with_key(&key); |
553 | | ctx.update(b"hello, world").unwrap(); |
554 | | let ctx_clone = ctx.clone(); |
555 | | |
556 | | let orig_tag = ctx.sign().unwrap(); |
557 | | let clone_tag = ctx_clone.sign().unwrap(); |
558 | | assert_eq!(orig_tag.as_ref(), clone_tag.as_ref()); |
559 | | assert_eq!(orig_tag.clone().as_ref(), clone_tag.as_ref()); |
560 | | } |
561 | | } |
562 | | |
563 | | #[test] |
564 | | fn cmac_context_test() { |
565 | | let key = Key::generate(AES_192).unwrap(); |
566 | | |
567 | | let mut ctx = Context::with_key(&key); |
568 | | ctx.update(b"hello").unwrap(); |
569 | | ctx.update(b", ").unwrap(); |
570 | | ctx.update(b"world").unwrap(); |
571 | | let tag1 = ctx.sign().unwrap(); |
572 | | |
573 | | let tag2 = sign(&key, b"hello, world").unwrap(); |
574 | | assert_eq!(tag1.as_ref(), tag2.as_ref()); |
575 | | } |
576 | | |
577 | | #[test] |
578 | | fn cmac_multi_part_test() { |
579 | | let parts = ["hello", ", ", "world"]; |
580 | | |
581 | | for &algorithm in &[AES_128, AES_256] { |
582 | | let key = Key::generate(algorithm).unwrap(); |
583 | | |
584 | | // Multi-part signing |
585 | | let mut ctx = Context::with_key(&key); |
586 | | for part in &parts { |
587 | | ctx.update(part.as_bytes()).unwrap(); |
588 | | } |
589 | | let tag = ctx.sign().unwrap(); |
590 | | |
591 | | // Verification with concatenated message |
592 | | let mut msg = Vec::<u8>::new(); |
593 | | for part in &parts { |
594 | | msg.extend(part.as_bytes()); |
595 | | } |
596 | | assert!(verify(&key, &msg, tag.as_ref()).is_ok()); |
597 | | } |
598 | | } |
599 | | |
600 | | #[test] |
601 | | fn cmac_key_new_test() { |
602 | | // Test Key::new with explicit key values |
603 | | let key_128 = [0u8; 16]; |
604 | | let key_192 = [0u8; 24]; |
605 | | let key_256 = [0u8; 32]; |
606 | | let key_3des = [0u8; 24]; |
607 | | |
608 | | let k1 = Key::new(AES_128, &key_128).unwrap(); |
609 | | let k2 = Key::new(AES_192, &key_192).unwrap(); |
610 | | let k3 = Key::new(AES_256, &key_256).unwrap(); |
611 | | let k4 = Key::new(TDES_FOR_LEGACY_USE_ONLY, &key_3des).unwrap(); |
612 | | |
613 | | let data = b"test message"; |
614 | | |
615 | | // All should produce valid tags |
616 | | let _ = sign(&k1, data).unwrap(); |
617 | | let _ = sign(&k2, data).unwrap(); |
618 | | let _ = sign(&k3, data).unwrap(); |
619 | | let _ = sign(&k4, data).unwrap(); |
620 | | } |
621 | | |
622 | | #[test] |
623 | | fn cmac_key_new_wrong_length_test() { |
624 | | let key_256 = [0u8; 32]; |
625 | | // Wrong key length should return error |
626 | | assert!(Key::new(AES_128, &key_256).is_err()); |
627 | | } |
628 | | |
629 | | #[test] |
630 | | fn cmac_algorithm_properties() { |
631 | | assert_eq!(AES_128.key_len(), 16); |
632 | | assert_eq!(AES_128.tag_len(), 16); |
633 | | |
634 | | assert_eq!(AES_192.key_len(), 24); |
635 | | assert_eq!(AES_192.tag_len(), 16); |
636 | | |
637 | | assert_eq!(AES_256.key_len(), 32); |
638 | | assert_eq!(AES_256.tag_len(), 16); |
639 | | |
640 | | assert_eq!(TDES_FOR_LEGACY_USE_ONLY.key_len(), 24); |
641 | | assert_eq!(TDES_FOR_LEGACY_USE_ONLY.tag_len(), 8); |
642 | | } |
643 | | |
644 | | #[test] |
645 | | fn cmac_empty_data() { |
646 | | let key = Key::generate(AES_128).unwrap(); |
647 | | |
648 | | // CMAC should work with empty data |
649 | | let tag = sign(&key, b"").unwrap(); |
650 | | assert!(verify(&key, b"", tag.as_ref()).is_ok()); |
651 | | |
652 | | // Context version |
653 | | let ctx = Context::with_key(&key); |
654 | | let tag2 = ctx.sign().unwrap(); |
655 | | assert_eq!(tag.as_ref(), tag2.as_ref()); |
656 | | } |
657 | | |
658 | | #[test] |
659 | | fn des_ede3_cmac_test() { |
660 | | let key = Key::generate(TDES_FOR_LEGACY_USE_ONLY).unwrap(); |
661 | | let data = b"test data for 3DES CMAC"; |
662 | | |
663 | | let tag = sign(&key, data).unwrap(); |
664 | | assert_eq!(tag.as_ref().len(), 8); // 3DES block size |
665 | | assert!(verify(&key, data, tag.as_ref()).is_ok()); |
666 | | } |
667 | | |
668 | | #[test] |
669 | | fn cmac_sign_to_buffer_test() { |
670 | | for &algorithm in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] { |
671 | | let key = Key::generate(algorithm).unwrap(); |
672 | | let data = b"hello, world"; |
673 | | |
674 | | // Test with exact size buffer |
675 | | let mut output = vec![0u8; algorithm.tag_len()]; |
676 | | let result = sign_to_buffer(&key, data, &mut output).unwrap(); |
677 | | assert_eq!(result.len(), algorithm.tag_len()); |
678 | | |
679 | | // Verify the tag matches sign() |
680 | | let tag = sign(&key, data).unwrap(); |
681 | | assert_eq!(result, tag.as_ref()); |
682 | | |
683 | | // Test with larger buffer |
684 | | let mut large_output = vec![0u8; algorithm.tag_len() + 10]; |
685 | | let result2 = sign_to_buffer(&key, data, &mut large_output).unwrap(); |
686 | | assert_eq!(result2.len(), algorithm.tag_len()); |
687 | | assert_eq!(result2, tag.as_ref()); |
688 | | } |
689 | | } |
690 | | |
691 | | #[test] |
692 | | fn cmac_sign_to_buffer_too_small_test() { |
693 | | let key = Key::generate(AES_128).unwrap(); |
694 | | let data = b"hello"; |
695 | | |
696 | | // Buffer too small should fail |
697 | | let mut small_buffer = vec![0u8; AES_128.tag_len() - 1]; |
698 | | assert!(sign_to_buffer(&key, data, &mut small_buffer).is_err()); |
699 | | |
700 | | // Empty buffer should fail |
701 | | let mut empty_buffer = vec![]; |
702 | | assert!(sign_to_buffer(&key, data, &mut empty_buffer).is_err()); |
703 | | } |
704 | | |
705 | | #[test] |
706 | | fn cmac_context_verify_test() { |
707 | | for &algorithm in &[AES_128, AES_192, AES_256, TDES_FOR_LEGACY_USE_ONLY] { |
708 | | let key = Key::generate(algorithm).unwrap(); |
709 | | let data = b"hello, world"; |
710 | | |
711 | | // Generate a valid tag |
712 | | let tag = sign(&key, data).unwrap(); |
713 | | |
714 | | // Verify with Context::verify |
715 | | let mut ctx = Context::with_key(&key); |
716 | | ctx.update(data).unwrap(); |
717 | | assert!(ctx.verify(tag.as_ref()).is_ok()); |
718 | | |
719 | | // Verify with wrong tag should fail |
720 | | let mut ctx2 = Context::with_key(&key); |
721 | | ctx2.update(data).unwrap(); |
722 | | let wrong_tag = vec![0u8; algorithm.tag_len()]; |
723 | | assert!(ctx2.verify(&wrong_tag).is_err()); |
724 | | |
725 | | // Verify with different data should fail |
726 | | let mut ctx3 = Context::with_key(&key); |
727 | | ctx3.update(b"wrong data").unwrap(); |
728 | | assert!(ctx3.verify(tag.as_ref()).is_err()); |
729 | | } |
730 | | } |
731 | | |
732 | | #[test] |
733 | | fn cmac_context_verify_multipart_test() { |
734 | | let key = Key::generate(AES_256).unwrap(); |
735 | | let parts = ["hello", ", ", "world"]; |
736 | | |
737 | | // Create tag from concatenated message |
738 | | let mut full_msg = Vec::new(); |
739 | | for part in &parts { |
740 | | full_msg.extend_from_slice(part.as_bytes()); |
741 | | } |
742 | | let tag = sign(&key, &full_msg).unwrap(); |
743 | | |
744 | | // Verify using multi-part context |
745 | | let mut ctx = Context::with_key(&key); |
746 | | for part in &parts { |
747 | | ctx.update(part.as_bytes()).unwrap(); |
748 | | } |
749 | | assert!(ctx.verify(tag.as_ref()).is_ok()); |
750 | | |
751 | | // Verify with missing part should fail |
752 | | let mut ctx2 = Context::with_key(&key); |
753 | | ctx2.update(parts[0].as_bytes()).unwrap(); |
754 | | ctx2.update(parts[1].as_bytes()).unwrap(); |
755 | | // Missing parts[2] |
756 | | assert!(ctx2.verify(tag.as_ref()).is_err()); |
757 | | } |
758 | | } |