Coverage Report

Created: 2025-12-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.12.4/src/cipher.rs
Line
Count
Source
1
// Copyright 2018 Brian Smith.
2
// SPDX-License-Identifier: ISC
3
// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6
//! Block and Stream Ciphers for Encryption and Decryption.
7
//!
8
//! # 🛑 Read Before Using
9
//!
10
//! This module provides access to block and stream cipher algorithms.
11
//! The modes provided here only provide confidentiality, but **do not**
12
//! provide integrity or authentication verification of ciphertext.
13
//!
14
//! These algorithms are provided solely for applications requiring them
15
//! in order to maintain backwards compatibility in legacy applications.
16
//!
17
//! If you are developing new applications requiring data encryption see
18
//! the algorithms provided in [`aead`](crate::aead).
19
//!
20
//! # Examples
21
//!
22
//! ## Encryption Modes
23
//!
24
//! ### AES-128 CBC
25
//!
26
//! ```rust
27
//! # use std::error::Error;
28
//! #
29
//! # fn main() -> Result<(), Box<dyn Error>> {
30
//! use aws_lc_rs::cipher::{
31
//!     PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128,
32
//! };
33
//! use std::io::Read;
34
//!
35
//! let original_message = "This is a secret message!".as_bytes();
36
//! let mut in_out_buffer = Vec::from(original_message);
37
//!
38
//! let key_bytes: &[u8] = &[
39
//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
40
//!     0xd1,
41
//! ];
42
//!
43
//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
44
//! let mut encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key)?;
45
//! let context = encrypting_key.encrypt(&mut in_out_buffer)?;
46
//!
47
//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
48
//! let mut decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key)?;
49
//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
50
//! assert_eq!(original_message, plaintext);
51
//! #
52
//! #
53
//! # Ok(())
54
//! # }
55
//! ```
56
//!
57
//! ### AES-128 CTR
58
//!
59
//! ```rust
60
//! # use std::error::Error;
61
//! #
62
//! # fn main() -> Result<(), Box<dyn Error>> {
63
//! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128};
64
//!
65
//! let original_message = "This is a secret message!".as_bytes();
66
//! let mut in_out_buffer = Vec::from(original_message);
67
//!
68
//! let key_bytes: &[u8] = &[
69
//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
70
//!     0xd1,
71
//! ];
72
//!
73
//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
74
//! let mut encrypting_key = EncryptingKey::ctr(key)?;
75
//! let context = encrypting_key.encrypt(&mut in_out_buffer)?;
76
//!
77
//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
78
//! let mut decrypting_key = DecryptingKey::ctr(key)?;
79
//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
80
//! assert_eq!(original_message, plaintext);
81
//! #
82
//! # Ok(())
83
//! # }
84
//! ```
85
//!
86
//! ### AES-128 CBC Streaming Cipher
87
//!
88
//! ```rust
89
//! # use std::error::Error;
90
//! #
91
//! # fn main() -> Result<(), Box<dyn Error>> {
92
//! use aws_lc_rs::cipher::{
93
//!     StreamingDecryptingKey, StreamingEncryptingKey, UnboundCipherKey, AES_128,
94
//! };
95
//! let original_message = "This is a secret message!".as_bytes();
96
//! let key_bytes: &[u8] = &[
97
//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c,
98
//!     0xb6, 0xd1,
99
//! ];
100
//! // Prepare ciphertext buffer
101
//! let mut ciphertext_buffer = vec![0u8; original_message.len() + AES_128.block_len()];
102
//! let ciphertext_slice = ciphertext_buffer.as_mut_slice();
103
//!
104
//! // Create StreamingEncryptingKey
105
//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap();
106
//! let mut encrypting_key = StreamingEncryptingKey::cbc_pkcs7(key).unwrap();
107
//!
108
//! // Encrypt
109
//! let mut first_update = encrypting_key
110
//!                            .update(original_message, ciphertext_slice)
111
//!                            .unwrap();
112
//! let first_update_len = first_update.written().len();
113
//! let (context, final_update) = encrypting_key.finish(first_update.remainder_mut()).unwrap();
114
//! let ciphertext_len = first_update_len + final_update.written().len();
115
//! let ciphertext = &ciphertext_slice[0..ciphertext_len];
116
//!
117
//! // Prepare plaintext buffer
118
//! let mut plaintext_buffer = vec![0u8; ciphertext_len + AES_128.block_len()];
119
//! let plaintext_slice = plaintext_buffer.as_mut_slice();
120
//!
121
//! // Create StreamingDecryptingKey
122
//! let key = UnboundCipherKey::new(&AES_128, key_bytes).unwrap();
123
//! let mut decrypting_key = StreamingDecryptingKey::cbc_pkcs7(key, context).unwrap();
124
//!
125
//! // Decrypt
126
//! let mut first_update = decrypting_key.update(ciphertext, plaintext_slice).unwrap();
127
//! let first_update_len = first_update.written().len();
128
//! let final_update = decrypting_key.finish(first_update.remainder_mut()).unwrap();
129
//! let plaintext_len = first_update_len + final_update.written().len();
130
//! let plaintext = &plaintext_slice[0..plaintext_len];
131
//!
132
//! assert_eq!(original_message, plaintext);
133
//! #
134
//! # Ok(())
135
//! # }
136
//! ```
137
//!
138
//! ### AES-128 CFB 128-bit mode
139
//!
140
//! ```rust
141
//! # use std::error::Error;
142
//! #
143
//! # fn main() -> Result<(), Box<dyn Error>> {
144
//! use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128};
145
//!
146
//! let original_message = "This is a secret message!".as_bytes();
147
//! let mut in_out_buffer = Vec::from(original_message);
148
//!
149
//! let key_bytes: &[u8] = &[
150
//!     0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
151
//!     0xd1,
152
//! ];
153
//!
154
//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
155
//! let mut encrypting_key = EncryptingKey::cfb128(key)?;
156
//! let context = encrypting_key.encrypt(&mut in_out_buffer)?;
157
//!
158
//! let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
159
//! let mut decrypting_key = DecryptingKey::cfb128(key)?;
160
//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
161
//! assert_eq!(original_message, plaintext);
162
//! #
163
//! # Ok(())
164
//! # }
165
//! ```
166
//!
167
//! ## Constructing a `DecryptionContext` for decryption.
168
//!
169
//! ```rust
170
//! # use std::error::Error;
171
//! # fn main() -> Result<(), Box<dyn Error>> {
172
//! use aws_lc_rs::cipher::{DecryptingKey, DecryptionContext, UnboundCipherKey, AES_128};
173
//! use aws_lc_rs::iv::{FixedLength, IV_LEN_128_BIT};
174
//!
175
//! let context = DecryptionContext::Iv128(FixedLength::<IV_LEN_128_BIT>::from(&[
176
//!     0x8d, 0xdb, 0x7d, 0xf1, 0x56, 0xf5, 0x1c, 0xde, 0x63, 0xe3, 0x4a, 0x34, 0xb0, 0xdf, 0x28,
177
//!     0xf0,
178
//! ]));
179
//!
180
//! let ciphertext: &[u8] = &[
181
//!     0x79, 0x8c, 0x04, 0x58, 0xcf, 0x98, 0xb1, 0xe9, 0x97, 0x6b, 0xa1, 0xce,
182
//! ];
183
//!
184
//! let mut in_out_buffer = Vec::from(ciphertext);
185
//!
186
//! let key = UnboundCipherKey::new(
187
//!     &AES_128,
188
//!     &[
189
//!         0x5b, 0xfc, 0xe7, 0x5e, 0x57, 0xc5, 0x4d, 0xda, 0x2d, 0xd4, 0x7e, 0x07, 0x0a, 0xef,
190
//!         0x43, 0x29,
191
//!     ],
192
//! )?;
193
//! let mut decrypting_key = DecryptingKey::ctr(key)?;
194
//! let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
195
//! assert_eq!("Hello World!".as_bytes(), plaintext);
196
//!
197
//! # Ok(())
198
//! # }
199
//! ```
200
//!
201
//! ## Getting an immutable reference to the IV slice.
202
//!
203
//! `TryFrom<&DecryptionContext>` is implemented for `&[u8]` allowing immutable references
204
//! to IV bytes returned from cipher encryption operations. Note this is implemented as a `TryFrom` as it
205
//! may fail for future enum variants that aren't representable as a single slice.
206
//!
207
//! ```rust
208
//! # use std::error::Error;
209
//! # fn main() -> Result<(), Box<dyn Error>> {
210
//! # use aws_lc_rs::cipher::DecryptionContext;
211
//! # use aws_lc_rs::iv::FixedLength;
212
//! # let x: DecryptionContext = DecryptionContext::Iv128(FixedLength::from([0u8; 16]));
213
//! // x is type `DecryptionContext`
214
//! let iv: &[u8] = (&x).try_into()?;
215
//! # Ok(())
216
//! # }
217
//! ```
218
219
#![allow(clippy::module_name_repetitions)]
220
221
pub(crate) mod aes;
222
pub(crate) mod block;
223
pub(crate) mod chacha;
224
pub(crate) mod key;
225
mod padded;
226
mod streaming;
227
228
pub use padded::{PaddedBlockDecryptingKey, PaddedBlockEncryptingKey};
229
pub use streaming::{BufferUpdate, StreamingDecryptingKey, StreamingEncryptingKey};
230
231
use crate::aws_lc::{
232
    EVP_aes_128_cbc, EVP_aes_128_cfb128, EVP_aes_128_ctr, EVP_aes_128_ecb, EVP_aes_192_cbc,
233
    EVP_aes_192_cfb128, EVP_aes_192_ctr, EVP_aes_192_ecb, EVP_aes_256_cbc, EVP_aes_256_cfb128,
234
    EVP_aes_256_ctr, EVP_aes_256_ecb, EVP_CIPHER,
235
};
236
use crate::buffer::Buffer;
237
use crate::error::Unspecified;
238
use crate::hkdf;
239
use crate::hkdf::KeyType;
240
use crate::iv::{FixedLength, IV_LEN_128_BIT};
241
use crate::ptr::ConstPointer;
242
use core::fmt::Debug;
243
use key::SymmetricCipherKey;
244
245
/// The number of bytes in an AES 128-bit key
246
pub use crate::cipher::aes::AES_128_KEY_LEN;
247
248
/// The number of bytes in an AES 192-bit key
249
pub use crate::cipher::aes::AES_192_KEY_LEN;
250
251
/// The number of bytes in an AES 256-bit key
252
pub use crate::cipher::aes::AES_256_KEY_LEN;
253
254
const MAX_CIPHER_KEY_LEN: usize = AES_256_KEY_LEN;
255
256
/// The number of bytes for an AES-CBC initialization vector (IV)
257
pub use crate::cipher::aes::AES_CBC_IV_LEN;
258
259
/// The number of bytes for an AES-CTR initialization vector (IV)
260
pub use crate::cipher::aes::AES_CTR_IV_LEN;
261
262
/// The number of bytes for an AES-CFB initialization vector (IV)
263
pub use crate::cipher::aes::AES_CFB_IV_LEN;
264
265
use crate::cipher::aes::AES_BLOCK_LEN;
266
267
const MAX_CIPHER_BLOCK_LEN: usize = AES_BLOCK_LEN;
268
269
/// The cipher operating mode.
270
#[non_exhaustive]
271
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
272
pub enum OperatingMode {
273
    /// Cipher block chaining (CBC) mode.
274
    CBC,
275
276
    /// Counter (CTR) mode.
277
    CTR,
278
279
    /// CFB 128-bit mode.
280
    CFB128,
281
282
    /// Electronic Code Book (ECB) mode.
283
    ECB,
284
}
285
286
impl OperatingMode {
287
0
    fn evp_cipher(&self, algorithm: &Algorithm) -> ConstPointer<EVP_CIPHER> {
288
0
        ConstPointer::new(match (self, algorithm.id) {
289
0
            (OperatingMode::CBC, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cbc() },
290
0
            (OperatingMode::CTR, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ctr() },
291
0
            (OperatingMode::CFB128, AlgorithmId::Aes128) => unsafe { EVP_aes_128_cfb128() },
292
0
            (OperatingMode::ECB, AlgorithmId::Aes128) => unsafe { EVP_aes_128_ecb() },
293
0
            (OperatingMode::CBC, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cbc() },
294
0
            (OperatingMode::CTR, AlgorithmId::Aes192) => unsafe { EVP_aes_192_ctr() },
295
0
            (OperatingMode::CFB128, AlgorithmId::Aes192) => unsafe { EVP_aes_192_cfb128() },
296
0
            (OperatingMode::ECB, AlgorithmId::Aes192) => unsafe { EVP_aes_192_ecb() },
297
0
            (OperatingMode::CBC, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cbc() },
298
0
            (OperatingMode::CTR, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ctr() },
299
0
            (OperatingMode::CFB128, AlgorithmId::Aes256) => unsafe { EVP_aes_256_cfb128() },
300
0
            (OperatingMode::ECB, AlgorithmId::Aes256) => unsafe { EVP_aes_256_ecb() },
301
        })
302
0
        .unwrap()
303
0
    }
304
}
305
306
macro_rules! define_cipher_context {
307
    ($name:ident, $other:ident) => {
308
        /// The contextual data used to encrypt or decrypt data.
309
        #[non_exhaustive]
310
        pub enum $name {
311
            /// A 128-bit Initialization Vector.
312
            Iv128(FixedLength<IV_LEN_128_BIT>),
313
314
            /// No Cipher Context
315
            None,
316
        }
317
318
        impl<'a> TryFrom<&'a $name> for &'a [u8] {
319
            type Error = Unspecified;
320
321
0
            fn try_from(value: &'a $name) -> Result<Self, Unspecified> {
322
0
                match value {
323
0
                    $name::Iv128(iv) => Ok(iv.as_ref()),
324
0
                    _ => Err(Unspecified),
325
                }
326
0
            }
Unexecuted instantiation: <&[u8] as core::convert::TryFrom<&aws_lc_rs::cipher::EncryptionContext>>::try_from
Unexecuted instantiation: <&[u8] as core::convert::TryFrom<&aws_lc_rs::cipher::DecryptionContext>>::try_from
327
        }
328
329
        impl Debug for $name {
330
0
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
331
0
                match self {
332
0
                    Self::Iv128(_) => write!(f, "Iv128"),
333
0
                    Self::None => write!(f, "None"),
334
                }
335
0
            }
Unexecuted instantiation: <aws_lc_rs::cipher::EncryptionContext as core::fmt::Debug>::fmt
Unexecuted instantiation: <aws_lc_rs::cipher::DecryptionContext as core::fmt::Debug>::fmt
336
        }
337
338
        impl From<$other> for $name {
339
0
            fn from(value: $other) -> Self {
340
0
                match value {
341
0
                    $other::Iv128(iv) => $name::Iv128(iv),
342
0
                    $other::None => $name::None,
343
                }
344
0
            }
Unexecuted instantiation: <aws_lc_rs::cipher::EncryptionContext as core::convert::From<aws_lc_rs::cipher::DecryptionContext>>::from
Unexecuted instantiation: <aws_lc_rs::cipher::DecryptionContext as core::convert::From<aws_lc_rs::cipher::EncryptionContext>>::from
345
        }
346
    };
347
}
348
349
define_cipher_context!(EncryptionContext, DecryptionContext);
350
define_cipher_context!(DecryptionContext, EncryptionContext);
351
352
#[non_exhaustive]
353
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
354
/// Cipher algorithm identifier.
355
pub enum AlgorithmId {
356
    /// AES 128-bit
357
    Aes128,
358
359
    /// AES 256-bit
360
    Aes256,
361
362
    /// AES 192-bit
363
    Aes192,
364
}
365
366
/// A cipher algorithm.
367
#[derive(Debug, PartialEq, Eq)]
368
pub struct Algorithm {
369
    id: AlgorithmId,
370
    key_len: usize,
371
    block_len: usize,
372
}
373
374
/// AES 128-bit cipher
375
pub static AES_128: Algorithm = Algorithm {
376
    id: AlgorithmId::Aes128,
377
    key_len: AES_128_KEY_LEN,
378
    block_len: AES_BLOCK_LEN,
379
};
380
381
/// AES 192-bit cipher
382
pub static AES_192: Algorithm = Algorithm {
383
    id: AlgorithmId::Aes192,
384
    key_len: AES_192_KEY_LEN,
385
    block_len: AES_BLOCK_LEN,
386
};
387
388
/// AES 256-bit cipher
389
pub static AES_256: Algorithm = Algorithm {
390
    id: AlgorithmId::Aes256,
391
    key_len: AES_256_KEY_LEN,
392
    block_len: AES_BLOCK_LEN,
393
};
394
395
impl Algorithm {
396
0
    fn id(&self) -> &AlgorithmId {
397
0
        &self.id
398
0
    }
399
400
    /// The block length of this cipher algorithm.
401
    #[must_use]
402
0
    pub const fn block_len(&self) -> usize {
403
0
        self.block_len
404
0
    }
405
406
0
    fn new_encryption_context(
407
0
        &self,
408
0
        mode: OperatingMode,
409
0
    ) -> Result<EncryptionContext, Unspecified> {
410
0
        match self.id {
411
            // TODO: Hopefully support CFB1, and CFB8
412
0
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
413
                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
414
0
                    Ok(EncryptionContext::Iv128(FixedLength::new()?))
415
                }
416
0
                OperatingMode::ECB => Ok(EncryptionContext::None),
417
            },
418
        }
419
0
    }
420
421
0
    fn is_valid_encryption_context(&self, mode: OperatingMode, input: &EncryptionContext) -> bool {
422
0
        match self.id {
423
            // TODO: Hopefully support CFB1, and CFB8
424
0
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
425
                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
426
0
                    matches!(input, EncryptionContext::Iv128(_))
427
                }
428
                OperatingMode::ECB => {
429
0
                    matches!(input, EncryptionContext::None)
430
                }
431
            },
432
        }
433
0
    }
434
435
0
    fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool {
436
        // TODO: Hopefully support CFB1, and CFB8
437
0
        match self.id {
438
0
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode {
439
                OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => {
440
0
                    matches!(input, DecryptionContext::Iv128(_))
441
                }
442
                OperatingMode::ECB => {
443
0
                    matches!(input, DecryptionContext::None)
444
                }
445
            },
446
        }
447
0
    }
448
}
449
450
#[allow(clippy::missing_fields_in_debug)]
451
impl Debug for UnboundCipherKey {
452
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
453
0
        f.debug_struct("UnboundCipherKey")
454
0
            .field("algorithm", &self.algorithm)
455
0
            .finish()
456
0
    }
457
}
458
459
impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundCipherKey {
460
0
    fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self {
461
0
        let mut key_bytes = [0; MAX_CIPHER_KEY_LEN];
462
0
        let key_bytes = &mut key_bytes[..okm.len().key_len];
463
0
        let algorithm = *okm.len();
464
0
        okm.fill(key_bytes).unwrap();
465
0
        Self::new(algorithm, key_bytes).unwrap()
466
0
    }
467
}
468
469
impl KeyType for &'static Algorithm {
470
0
    fn len(&self) -> usize {
471
0
        self.key_len
472
0
    }
473
}
474
475
/// A key bound to a particular cipher algorithm.
476
pub struct UnboundCipherKey {
477
    algorithm: &'static Algorithm,
478
    key_bytes: Buffer<'static, &'static [u8]>,
479
}
480
481
impl UnboundCipherKey {
482
    /// Constructs an [`UnboundCipherKey`].
483
    ///
484
    /// # Errors
485
    ///
486
    /// * [`Unspecified`] if `key_bytes.len()` does not match the length required by `algorithm`.
487
0
    pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> {
488
0
        let key_bytes = Buffer::new(key_bytes.to_vec());
489
0
        Ok(UnboundCipherKey {
490
0
            algorithm,
491
0
            key_bytes,
492
0
        })
493
0
    }
494
495
    #[inline]
496
    #[must_use]
497
    /// Returns the algorithm associated with this key.
498
0
    pub fn algorithm(&self) -> &'static Algorithm {
499
0
        self.algorithm
500
0
    }
501
}
502
503
impl TryInto<SymmetricCipherKey> for UnboundCipherKey {
504
    type Error = Unspecified;
505
506
0
    fn try_into(self) -> Result<SymmetricCipherKey, Self::Error> {
507
0
        match self.algorithm.id() {
508
0
            AlgorithmId::Aes128 => SymmetricCipherKey::aes128(self.key_bytes.as_ref()),
509
0
            AlgorithmId::Aes192 => SymmetricCipherKey::aes192(self.key_bytes.as_ref()),
510
0
            AlgorithmId::Aes256 => SymmetricCipherKey::aes256(self.key_bytes.as_ref()),
511
        }
512
0
    }
513
}
514
515
/// A cipher encryption key that does not perform block padding.
516
pub struct EncryptingKey {
517
    algorithm: &'static Algorithm,
518
    key: SymmetricCipherKey,
519
    mode: OperatingMode,
520
}
521
522
impl EncryptingKey {
523
    /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key.
524
    ///
525
    // # FIPS
526
    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
527
    // * `AES_128`
528
    // * `AES_256`
529
    //
530
    /// # Errors
531
    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
532
0
    pub fn ctr(key: UnboundCipherKey) -> Result<Self, Unspecified> {
533
0
        Self::new(key, OperatingMode::CTR)
534
0
    }
535
536
    /// Constructs an `EncryptingKey` operating in cipher feedback 128-bit mode (CFB128) using the provided key.
537
    ///
538
    // # FIPS
539
    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
540
    // * `AES_128`
541
    // * `AES_256`
542
    //
543
    /// # Errors
544
    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
545
0
    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
546
0
        Self::new(key, OperatingMode::CFB128)
547
0
    }
548
549
    /// Constructs an `EncryptingKey` operating in electronic code book mode (ECB) using the provided key.
550
    ///
551
    /// # ☠️ ️️️DANGER ☠️
552
    /// Offered for computability purposes only. This is an extremely dangerous mode, and
553
    /// very likely not what you want to use.
554
    ///
555
    // # FIPS
556
    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
557
    // * `AES_128`
558
    // * `AES_256`
559
    //
560
    /// # Errors
561
    /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`.
562
0
    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
563
0
        Self::new(key, OperatingMode::ECB)
564
0
    }
565
566
    #[allow(clippy::unnecessary_wraps)]
567
0
    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
568
0
        let algorithm = key.algorithm();
569
0
        let key = key.try_into()?;
570
0
        Ok(Self {
571
0
            algorithm,
572
0
            key,
573
0
            mode,
574
0
        })
575
0
    }
576
577
    /// Returns the cipher algorithm.
578
    #[must_use]
579
0
    pub fn algorithm(&self) -> &Algorithm {
580
0
        self.algorithm
581
0
    }
582
583
    /// Returns the cipher operating mode.
584
    #[must_use]
585
0
    pub fn mode(&self) -> OperatingMode {
586
0
        self.mode
587
0
    }
588
589
    /// Encrypts the data provided in `in_out` in-place.
590
    /// Returns a [`DecryptionContext`] with the randomly generated IV that was used to encrypt
591
    /// the data provided.
592
    ///
593
    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
594
    /// of the block length.
595
    ///
596
    /// # Errors
597
    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
598
    ///   and `in_out.len()` is not. Otherwise, returned if encryption fails.
599
0
    pub fn encrypt(&self, in_out: &mut [u8]) -> Result<DecryptionContext, Unspecified> {
600
0
        let context = self.algorithm.new_encryption_context(self.mode)?;
601
0
        self.less_safe_encrypt(in_out, context)
602
0
    }
603
604
    /// Encrypts the data provided in `in_out` in-place using the provided `EncryptionContext`.
605
    /// This is considered "less safe" because the caller could potentially construct
606
    /// a `EncryptionContext` from a previously used IV (initialization vector).
607
    /// Returns a [`DecryptionContext`] produced from the provided `EncryptionContext`.
608
    ///
609
    /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
610
    /// of the block length.
611
    ///
612
    /// # Errors
613
    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
614
    ///   and `in_out.len()` is not. Otherwise returned if encryption fails.
615
0
    pub fn less_safe_encrypt(
616
0
        &self,
617
0
        in_out: &mut [u8],
618
0
        context: EncryptionContext,
619
0
    ) -> Result<DecryptionContext, Unspecified> {
620
0
        if !self
621
0
            .algorithm()
622
0
            .is_valid_encryption_context(self.mode, &context)
623
        {
624
0
            return Err(Unspecified);
625
0
        }
626
0
        encrypt(self.algorithm(), &self.key, self.mode, in_out, context)
627
0
    }
628
}
629
630
impl Debug for EncryptingKey {
631
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
632
0
        f.debug_struct("EncryptingKey")
633
0
            .field("algorithm", self.algorithm)
634
0
            .field("mode", &self.mode)
635
0
            .finish_non_exhaustive()
636
0
    }
637
}
638
639
/// A cipher decryption key that does not perform block padding.
640
pub struct DecryptingKey {
641
    algorithm: &'static Algorithm,
642
    key: SymmetricCipherKey,
643
    mode: OperatingMode,
644
}
645
646
impl DecryptingKey {
647
    /// Constructs a cipher decrypting key operating in counter (CTR) mode using the provided key and context.
648
    ///
649
    // # FIPS
650
    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
651
    // * `AES_128`
652
    // * `AES_256`
653
    //
654
    /// # Errors
655
    /// * [`Unspecified`]: Returned if there is an error during decryption.
656
0
    pub fn ctr(key: UnboundCipherKey) -> Result<DecryptingKey, Unspecified> {
657
0
        Self::new(key, OperatingMode::CTR)
658
0
    }
659
660
    /// Constructs a cipher decrypting key operating in cipher feedback 128-bit mode (CFB128) using the provided key and context.
661
    ///
662
    // # FIPS
663
    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
664
    // * `AES_128`
665
    // * `AES_256`
666
    //
667
    /// # Errors
668
    /// * [`Unspecified`]: Returned if there is an error during decryption.
669
0
    pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> {
670
0
        Self::new(key, OperatingMode::CFB128)
671
0
    }
672
673
    /// Constructs an `DecryptingKey` operating in electronic code book (ECB) mode using the provided key.
674
    ///
675
    /// # ☠️ ️️️DANGER ☠️
676
    /// Offered for computability purposes only. This is an extremely dangerous mode, and
677
    /// very likely not what you want to use.
678
    ///
679
    // # FIPS
680
    // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms:
681
    // * `AES_128`
682
    // * `AES_256`
683
    //
684
    /// # Errors
685
    /// * [`Unspecified`]: Returned if there is an error constructing the `DecryptingKey`.
686
0
    pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> {
687
0
        Self::new(key, OperatingMode::ECB)
688
0
    }
689
690
    #[allow(clippy::unnecessary_wraps)]
691
0
    fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> {
692
0
        let algorithm = key.algorithm();
693
0
        let key = key.try_into()?;
694
0
        Ok(Self {
695
0
            algorithm,
696
0
            key,
697
0
            mode,
698
0
        })
699
0
    }
700
701
    /// Returns the cipher algorithm.
702
    #[must_use]
703
0
    pub fn algorithm(&self) -> &Algorithm {
704
0
        self.algorithm
705
0
    }
706
707
    /// Returns the cipher operating mode.
708
    #[must_use]
709
0
    pub fn mode(&self) -> OperatingMode {
710
0
        self.mode
711
0
    }
712
713
    /// Decrypts the data provided in `in_out` in-place.
714
    /// Returns a references to the decrypted data.
715
    ///
716
    /// If `DecryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple
717
    /// of the block length.
718
    ///
719
    /// # Errors
720
    /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length,
721
    ///   and `in_out.len()` is not. Also returned if decryption fails.
722
0
    pub fn decrypt<'in_out>(
723
0
        &self,
724
0
        in_out: &'in_out mut [u8],
725
0
        context: DecryptionContext,
726
0
    ) -> Result<&'in_out mut [u8], Unspecified> {
727
0
        decrypt(self.algorithm, &self.key, self.mode, in_out, context)
728
0
    }
729
}
730
731
impl Debug for DecryptingKey {
732
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
733
0
        f.debug_struct("DecryptingKey")
734
0
            .field("algorithm", &self.algorithm)
735
0
            .field("mode", &self.mode)
736
0
            .finish_non_exhaustive()
737
0
    }
738
}
739
740
0
fn encrypt(
741
0
    algorithm: &Algorithm,
742
0
    key: &SymmetricCipherKey,
743
0
    mode: OperatingMode,
744
0
    in_out: &mut [u8],
745
0
    context: EncryptionContext,
746
0
) -> Result<DecryptionContext, Unspecified> {
747
0
    let block_len = algorithm.block_len();
748
749
0
    match mode {
750
        OperatingMode::CBC | OperatingMode::ECB => {
751
0
            if in_out.len() % block_len != 0 {
752
0
                return Err(Unspecified);
753
0
            }
754
        }
755
0
        _ => {}
756
    }
757
758
0
    match mode {
759
0
        OperatingMode::CBC => match algorithm.id() {
760
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
761
0
                aes::encrypt_cbc_mode(key, context, in_out)
762
            }
763
        },
764
0
        OperatingMode::CTR => match algorithm.id() {
765
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
766
0
                aes::encrypt_ctr_mode(key, context, in_out)
767
            }
768
        },
769
        // TODO: Hopefully support CFB1, and CFB8
770
0
        OperatingMode::CFB128 => match algorithm.id() {
771
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
772
0
                aes::encrypt_cfb_mode(key, mode, context, in_out)
773
            }
774
        },
775
0
        OperatingMode::ECB => match algorithm.id() {
776
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
777
0
                aes::encrypt_ecb_mode(key, context, in_out)
778
            }
779
        },
780
    }
781
0
}
782
783
0
fn decrypt<'in_out>(
784
0
    algorithm: &'static Algorithm,
785
0
    key: &SymmetricCipherKey,
786
0
    mode: OperatingMode,
787
0
    in_out: &'in_out mut [u8],
788
0
    context: DecryptionContext,
789
0
) -> Result<&'in_out mut [u8], Unspecified> {
790
0
    let block_len = algorithm.block_len();
791
792
0
    match mode {
793
        OperatingMode::CBC | OperatingMode::ECB => {
794
0
            if in_out.len() % block_len != 0 {
795
0
                return Err(Unspecified);
796
0
            }
797
        }
798
0
        _ => {}
799
    }
800
801
0
    match mode {
802
0
        OperatingMode::CBC => match algorithm.id() {
803
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
804
0
                aes::decrypt_cbc_mode(key, context, in_out)
805
            }
806
        },
807
0
        OperatingMode::CTR => match algorithm.id() {
808
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
809
0
                aes::decrypt_ctr_mode(key, context, in_out)
810
            }
811
        },
812
        // TODO: Hopefully support CFB1, and CFB8
813
0
        OperatingMode::CFB128 => match algorithm.id() {
814
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
815
0
                aes::decrypt_cfb_mode(key, mode, context, in_out)
816
            }
817
        },
818
0
        OperatingMode::ECB => match algorithm.id() {
819
            AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => {
820
0
                aes::decrypt_ecb_mode(key, context, in_out)
821
            }
822
        },
823
    }
824
0
}
825
826
#[cfg(test)]
827
mod tests {
828
    use super::*;
829
    use crate::test::from_hex;
830
831
    #[cfg(feature = "fips")]
832
    mod fips;
833
834
    #[test]
835
    fn test_debug() {
836
        {
837
            let aes_128_key_bytes = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
838
            let cipher_key = UnboundCipherKey::new(&AES_128, aes_128_key_bytes.as_slice()).unwrap();
839
            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }", format!("{cipher_key:?}"));
840
        }
841
842
        {
843
            let aes_256_key_bytes =
844
                from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")
845
                    .unwrap();
846
            let cipher_key = UnboundCipherKey::new(&AES_256, aes_256_key_bytes.as_slice()).unwrap();
847
            assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes256, key_len: 32, block_len: 16 } }", format!("{cipher_key:?}"));
848
        }
849
850
        {
851
            let key_bytes = &[0u8; 16];
852
            let key = PaddedBlockEncryptingKey::cbc_pkcs7(
853
                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
854
            )
855
            .unwrap();
856
            assert_eq!("PaddedBlockEncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
857
            let mut data = vec![0u8; 16];
858
            let context = key.encrypt(&mut data).unwrap();
859
            assert_eq!("Iv128", format!("{context:?}"));
860
            let key = PaddedBlockDecryptingKey::cbc_pkcs7(
861
                UnboundCipherKey::new(&AES_128, key_bytes).unwrap(),
862
            )
863
            .unwrap();
864
            assert_eq!("PaddedBlockDecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}"));
865
        }
866
867
        {
868
            let key_bytes = &[0u8; 16];
869
            let key =
870
                EncryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
871
            assert_eq!("EncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
872
            let mut data = vec![0u8; 16];
873
            let context = key.encrypt(&mut data).unwrap();
874
            assert_eq!("Iv128", format!("{context:?}"));
875
            let key =
876
                DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap();
877
            assert_eq!("DecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}"));
878
        }
879
    }
880
881
    fn helper_test_cipher_n_bytes(
882
        key: &[u8],
883
        alg: &'static Algorithm,
884
        mode: OperatingMode,
885
        n: usize,
886
    ) {
887
        let mut input: Vec<u8> = Vec::with_capacity(n);
888
        for i in 0..n {
889
            let byte: u8 = i.try_into().unwrap();
890
            input.push(byte);
891
        }
892
893
        let cipher_key = UnboundCipherKey::new(alg, key).unwrap();
894
        let encrypting_key = EncryptingKey::new(cipher_key, mode).unwrap();
895
896
        let mut in_out = input.clone();
897
        let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap();
898
899
        if n > 5 {
900
            // There's no more than a 1 in 2^48 chance that this will fail randomly
901
            assert_ne!(input.as_slice(), in_out);
902
        }
903
904
        let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap();
905
        let decrypting_key = DecryptingKey::new(cipher_key2, mode).unwrap();
906
907
        let plaintext = decrypting_key.decrypt(&mut in_out, decrypt_iv).unwrap();
908
        assert_eq!(input.as_slice(), plaintext);
909
    }
910
911
    #[test]
912
    fn test_aes_128_ctr() {
913
        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
914
        for i in 0..=50 {
915
            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CTR, i);
916
        }
917
    }
918
919
    #[test]
920
    fn test_aes_128_cfb128() {
921
        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
922
        for i in 0..=50 {
923
            helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CFB128, i);
924
        }
925
    }
926
927
    #[test]
928
    fn test_aes_256_cfb128() {
929
        let key =
930
            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
931
        for i in 0..=50 {
932
            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CFB128, i);
933
        }
934
    }
935
936
    #[test]
937
    fn test_aes_256_ctr() {
938
        let key =
939
            from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap();
940
        for i in 0..=50 {
941
            helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CTR, i);
942
        }
943
    }
944
945
    #[test]
946
    fn test_aes_128_ecb() {
947
        let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
948
        _ = key;
949
    }
950
951
    macro_rules! cipher_kat {
952
        ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => {
953
            #[test]
954
            fn $name() {
955
                let key = from_hex($key).unwrap();
956
                let input = from_hex($plaintext).unwrap();
957
                let expected_ciphertext = from_hex($ciphertext).unwrap();
958
                let mut iv = from_hex($iv).unwrap();
959
                let iv = {
960
                    let slice = iv.as_mut_slice();
961
                    let mut iv = [0u8; $iv.len() / 2];
962
                    {
963
                        let x = iv.as_mut_slice();
964
                        x.copy_from_slice(slice);
965
                    }
966
                    iv
967
                };
968
969
                let ec = EncryptionContext::Iv128(FixedLength::from(iv));
970
971
                let alg = $alg;
972
973
                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
974
975
                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
976
977
                let mut in_out = input.clone();
978
979
                let context = encrypting_key.less_safe_encrypt(&mut in_out, ec).unwrap();
980
981
                assert_eq!(expected_ciphertext, in_out);
982
983
                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
984
                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
985
986
                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
987
                assert_eq!(input.as_slice(), plaintext);
988
            }
989
        };
990
        ($name:ident, $alg:expr, $mode:expr, $key:literal, $plaintext:literal, $ciphertext:literal) => {
991
            #[test]
992
            fn $name() {
993
                let key = from_hex($key).unwrap();
994
                let input = from_hex($plaintext).unwrap();
995
                let expected_ciphertext = from_hex($ciphertext).unwrap();
996
997
                let alg = $alg;
998
999
                let unbound_key = UnboundCipherKey::new(alg, &key).unwrap();
1000
1001
                let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap();
1002
1003
                let mut in_out = input.clone();
1004
1005
                let context = encrypting_key
1006
                    .less_safe_encrypt(&mut in_out, EncryptionContext::None)
1007
                    .unwrap();
1008
1009
                assert_eq!(expected_ciphertext, in_out);
1010
1011
                let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap();
1012
                let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap();
1013
1014
                let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap();
1015
                assert_eq!(input.as_slice(), plaintext);
1016
            }
1017
        };
1018
    }
1019
1020
    cipher_kat!(
1021
        test_iv_aes_128_ctr_16_bytes,
1022
        &AES_128,
1023
        OperatingMode::CTR,
1024
        "000102030405060708090a0b0c0d0e0f",
1025
        "00000000000000000000000000000000",
1026
        "00112233445566778899aabbccddeeff",
1027
        "c6b01904c3da3df5e7d62bd96d153686"
1028
    );
1029
1030
    cipher_kat!(
1031
        test_iv_aes_256_ctr_15_bytes,
1032
        &AES_256,
1033
        OperatingMode::CTR,
1034
        "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
1035
        "00000000000000000000000000000000",
1036
        "00112233445566778899aabbccddee",
1037
        "f28122856e1cf9a7216a30d111f399"
1038
    );
1039
1040
    cipher_kat!(
1041
        test_openssl_aes_128_ctr_15_bytes,
1042
        &AES_128,
1043
        OperatingMode::CTR,
1044
        "244828580821c1652582c76e34d299f5",
1045
        "093145d5af233f46072a5eb5adc11aa1",
1046
        "3ee38cec171e6cf466bf0df98aa0e1",
1047
        "bd7d928f60e3422d96b3f8cd614eb2"
1048
    );
1049
1050
    cipher_kat!(
1051
        test_openssl_aes_256_ctr_15_bytes,
1052
        &AES_256,
1053
        OperatingMode::CTR,
1054
        "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d",
1055
        "f028ecb053f801102d11fccc9d303a27",
1056
        "eca7285d19f3c20e295378460e8729",
1057
        "b5098e5e788de6ac2f2098eb2fc6f8"
1058
    );
1059
1060
    cipher_kat!(
1061
        test_sp800_38a_cfb128_aes128,
1062
        &AES_128,
1063
        OperatingMode::CFB128,
1064
        "2b7e151628aed2a6abf7158809cf4f3c",
1065
        "000102030405060708090a0b0c0d0e0f",
1066
        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1067
        "3b3fd92eb72dad20333449f8e83cfb4ac8a64537a0b3a93fcde3cdad9f1ce58b26751f67a3cbb140b1808cf187a4f4dfc04b05357c5d1c0eeac4c66f9ff7f2e6"
1068
    );
1069
1070
    cipher_kat!(
1071
        test_sp800_38a_cfb128_aes256,
1072
        &AES_256,
1073
        OperatingMode::CFB128,
1074
        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1075
        "000102030405060708090a0b0c0d0e0f",
1076
        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1077
        "dc7e84bfda79164b7ecd8486985d386039ffed143b28b1c832113c6331e5407bdf10132415e54b92a13ed0a8267ae2f975a385741ab9cef82031623d55b1e471"
1078
    );
1079
1080
    cipher_kat!(
1081
        test_sp800_38a_ecb_aes128,
1082
        &AES_128,
1083
        OperatingMode::ECB,
1084
        "2b7e151628aed2a6abf7158809cf4f3c",
1085
        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1086
        "3ad77bb40d7a3660a89ecaf32466ef97f5d3d58503b9699de785895a96fdbaaf43b1cd7f598ece23881b00e3ed0306887b0c785e27e8ad3f8223207104725dd4"
1087
    );
1088
1089
    cipher_kat!(
1090
        test_sp800_38a_ecb_aes256,
1091
        &AES_256,
1092
        OperatingMode::ECB,
1093
        "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
1094
        "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
1095
        "f3eed1bdb5d2a03c064b5a7e3db181f8591ccb10d410ed26dc5ba74a31362870b6ed21b99ca6f4f9f153e7b1beafed1d23304b7a39f9f3ff067d8d8f9e24ecc7"
1096
    );
1097
}