/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.14.1/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 | | unsafe { |
289 | 0 | ConstPointer::new_static(match (self, algorithm.id) { |
290 | 0 | (OperatingMode::CBC, AlgorithmId::Aes128) => EVP_aes_128_cbc(), |
291 | 0 | (OperatingMode::CTR, AlgorithmId::Aes128) => EVP_aes_128_ctr(), |
292 | 0 | (OperatingMode::CFB128, AlgorithmId::Aes128) => EVP_aes_128_cfb128(), |
293 | 0 | (OperatingMode::ECB, AlgorithmId::Aes128) => EVP_aes_128_ecb(), |
294 | 0 | (OperatingMode::CBC, AlgorithmId::Aes192) => EVP_aes_192_cbc(), |
295 | 0 | (OperatingMode::CTR, AlgorithmId::Aes192) => EVP_aes_192_ctr(), |
296 | 0 | (OperatingMode::CFB128, AlgorithmId::Aes192) => EVP_aes_192_cfb128(), |
297 | 0 | (OperatingMode::ECB, AlgorithmId::Aes192) => EVP_aes_192_ecb(), |
298 | 0 | (OperatingMode::CBC, AlgorithmId::Aes256) => EVP_aes_256_cbc(), |
299 | 0 | (OperatingMode::CTR, AlgorithmId::Aes256) => EVP_aes_256_ctr(), |
300 | 0 | (OperatingMode::CFB128, AlgorithmId::Aes256) => EVP_aes_256_cfb128(), |
301 | 0 | (OperatingMode::ECB, AlgorithmId::Aes256) => EVP_aes_256_ecb(), |
302 | | }) |
303 | 0 | .unwrap() |
304 | | } |
305 | 0 | } |
306 | | } |
307 | | |
308 | | macro_rules! define_cipher_context { |
309 | | ($name:ident, $other:ident) => { |
310 | | /// The contextual data used to encrypt or decrypt data. |
311 | | #[non_exhaustive] |
312 | | pub enum $name { |
313 | | /// A 128-bit Initialization Vector. |
314 | | Iv128(FixedLength<IV_LEN_128_BIT>), |
315 | | |
316 | | /// No Cipher Context |
317 | | None, |
318 | | } |
319 | | |
320 | | impl<'a> TryFrom<&'a $name> for &'a [u8] { |
321 | | type Error = Unspecified; |
322 | | |
323 | 0 | fn try_from(value: &'a $name) -> Result<Self, Unspecified> { |
324 | 0 | match value { |
325 | 0 | $name::Iv128(iv) => Ok(iv.as_ref()), |
326 | 0 | _ => Err(Unspecified), |
327 | | } |
328 | 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 |
329 | | } |
330 | | |
331 | | impl Debug for $name { |
332 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
333 | 0 | match self { |
334 | 0 | Self::Iv128(_) => write!(f, "Iv128"), |
335 | 0 | Self::None => write!(f, "None"), |
336 | | } |
337 | 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 |
338 | | } |
339 | | |
340 | | impl From<$other> for $name { |
341 | 0 | fn from(value: $other) -> Self { |
342 | 0 | match value { |
343 | 0 | $other::Iv128(iv) => $name::Iv128(iv), |
344 | 0 | $other::None => $name::None, |
345 | | } |
346 | 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 |
347 | | } |
348 | | }; |
349 | | } |
350 | | |
351 | | define_cipher_context!(EncryptionContext, DecryptionContext); |
352 | | define_cipher_context!(DecryptionContext, EncryptionContext); |
353 | | |
354 | | #[non_exhaustive] |
355 | | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
356 | | /// Cipher algorithm identifier. |
357 | | pub enum AlgorithmId { |
358 | | /// AES 128-bit |
359 | | Aes128, |
360 | | |
361 | | /// AES 256-bit |
362 | | Aes256, |
363 | | |
364 | | /// AES 192-bit |
365 | | Aes192, |
366 | | } |
367 | | |
368 | | /// A cipher algorithm. |
369 | | #[derive(Debug, PartialEq, Eq)] |
370 | | pub struct Algorithm { |
371 | | id: AlgorithmId, |
372 | | key_len: usize, |
373 | | block_len: usize, |
374 | | } |
375 | | |
376 | | /// AES 128-bit cipher |
377 | | pub static AES_128: Algorithm = Algorithm { |
378 | | id: AlgorithmId::Aes128, |
379 | | key_len: AES_128_KEY_LEN, |
380 | | block_len: AES_BLOCK_LEN, |
381 | | }; |
382 | | |
383 | | /// AES 192-bit cipher |
384 | | pub static AES_192: Algorithm = Algorithm { |
385 | | id: AlgorithmId::Aes192, |
386 | | key_len: AES_192_KEY_LEN, |
387 | | block_len: AES_BLOCK_LEN, |
388 | | }; |
389 | | |
390 | | /// AES 256-bit cipher |
391 | | pub static AES_256: Algorithm = Algorithm { |
392 | | id: AlgorithmId::Aes256, |
393 | | key_len: AES_256_KEY_LEN, |
394 | | block_len: AES_BLOCK_LEN, |
395 | | }; |
396 | | |
397 | | impl Algorithm { |
398 | 0 | fn id(&self) -> &AlgorithmId { |
399 | 0 | &self.id |
400 | 0 | } |
401 | | |
402 | | /// The block length of this cipher algorithm. |
403 | | #[must_use] |
404 | 0 | pub const fn block_len(&self) -> usize { |
405 | 0 | self.block_len |
406 | 0 | } |
407 | | |
408 | 0 | fn new_encryption_context( |
409 | 0 | &self, |
410 | 0 | mode: OperatingMode, |
411 | 0 | ) -> Result<EncryptionContext, Unspecified> { |
412 | 0 | match self.id { |
413 | | // TODO: Hopefully support CFB1, and CFB8 |
414 | 0 | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode { |
415 | | OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => { |
416 | 0 | Ok(EncryptionContext::Iv128(FixedLength::new()?)) |
417 | | } |
418 | 0 | OperatingMode::ECB => Ok(EncryptionContext::None), |
419 | | }, |
420 | | } |
421 | 0 | } |
422 | | |
423 | 0 | fn is_valid_encryption_context(&self, mode: OperatingMode, input: &EncryptionContext) -> bool { |
424 | 0 | match self.id { |
425 | | // TODO: Hopefully support CFB1, and CFB8 |
426 | 0 | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode { |
427 | | OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => { |
428 | 0 | matches!(input, EncryptionContext::Iv128(_)) |
429 | | } |
430 | | OperatingMode::ECB => { |
431 | 0 | matches!(input, EncryptionContext::None) |
432 | | } |
433 | | }, |
434 | | } |
435 | 0 | } |
436 | | |
437 | 0 | fn is_valid_decryption_context(&self, mode: OperatingMode, input: &DecryptionContext) -> bool { |
438 | | // TODO: Hopefully support CFB1, and CFB8 |
439 | 0 | match self.id { |
440 | 0 | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => match mode { |
441 | | OperatingMode::CBC | OperatingMode::CTR | OperatingMode::CFB128 => { |
442 | 0 | matches!(input, DecryptionContext::Iv128(_)) |
443 | | } |
444 | | OperatingMode::ECB => { |
445 | 0 | matches!(input, DecryptionContext::None) |
446 | | } |
447 | | }, |
448 | | } |
449 | 0 | } |
450 | | } |
451 | | |
452 | | #[allow(clippy::missing_fields_in_debug)] |
453 | | impl Debug for UnboundCipherKey { |
454 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
455 | 0 | f.debug_struct("UnboundCipherKey") |
456 | 0 | .field("algorithm", &self.algorithm) |
457 | 0 | .finish() |
458 | 0 | } |
459 | | } |
460 | | |
461 | | impl From<hkdf::Okm<'_, &'static Algorithm>> for UnboundCipherKey { |
462 | 0 | fn from(okm: hkdf::Okm<&'static Algorithm>) -> Self { |
463 | 0 | let mut key_bytes = [0; MAX_CIPHER_KEY_LEN]; |
464 | 0 | let key_bytes = &mut key_bytes[..okm.len().key_len]; |
465 | 0 | let algorithm = *okm.len(); |
466 | 0 | okm.fill(key_bytes).unwrap(); |
467 | 0 | Self::new(algorithm, key_bytes).unwrap() |
468 | 0 | } |
469 | | } |
470 | | |
471 | | impl KeyType for &'static Algorithm { |
472 | 0 | fn len(&self) -> usize { |
473 | 0 | self.key_len |
474 | 0 | } |
475 | | } |
476 | | |
477 | | /// A key bound to a particular cipher algorithm. |
478 | | pub struct UnboundCipherKey { |
479 | | algorithm: &'static Algorithm, |
480 | | key_bytes: Buffer<'static, &'static [u8]>, |
481 | | } |
482 | | |
483 | | impl UnboundCipherKey { |
484 | | /// Constructs an [`UnboundCipherKey`]. |
485 | | /// |
486 | | /// # Errors |
487 | | /// |
488 | | /// * [`Unspecified`] if `key_bytes.len()` does not match the length required by `algorithm`. |
489 | 0 | pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self, Unspecified> { |
490 | 0 | let key_bytes = Buffer::new(key_bytes.to_vec()); |
491 | 0 | Ok(UnboundCipherKey { |
492 | 0 | algorithm, |
493 | 0 | key_bytes, |
494 | 0 | }) |
495 | 0 | } |
496 | | |
497 | | #[inline] |
498 | | #[must_use] |
499 | | /// Returns the algorithm associated with this key. |
500 | 0 | pub fn algorithm(&self) -> &'static Algorithm { |
501 | 0 | self.algorithm |
502 | 0 | } |
503 | | } |
504 | | |
505 | | impl TryInto<SymmetricCipherKey> for UnboundCipherKey { |
506 | | type Error = Unspecified; |
507 | | |
508 | 0 | fn try_into(self) -> Result<SymmetricCipherKey, Self::Error> { |
509 | 0 | match self.algorithm.id() { |
510 | 0 | AlgorithmId::Aes128 => SymmetricCipherKey::aes128(self.key_bytes.as_ref()), |
511 | 0 | AlgorithmId::Aes192 => SymmetricCipherKey::aes192(self.key_bytes.as_ref()), |
512 | 0 | AlgorithmId::Aes256 => SymmetricCipherKey::aes256(self.key_bytes.as_ref()), |
513 | | } |
514 | 0 | } |
515 | | } |
516 | | |
517 | | /// A cipher encryption key that does not perform block padding. |
518 | | pub struct EncryptingKey { |
519 | | algorithm: &'static Algorithm, |
520 | | key: SymmetricCipherKey, |
521 | | mode: OperatingMode, |
522 | | } |
523 | | |
524 | | impl EncryptingKey { |
525 | | /// Constructs an `EncryptingKey` operating in counter (CTR) mode using the provided key. |
526 | | /// |
527 | | // # FIPS |
528 | | // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms: |
529 | | // * `AES_128` |
530 | | // * `AES_256` |
531 | | // |
532 | | /// # Errors |
533 | | /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`. |
534 | 0 | pub fn ctr(key: UnboundCipherKey) -> Result<Self, Unspecified> { |
535 | 0 | Self::new(key, OperatingMode::CTR) |
536 | 0 | } |
537 | | |
538 | | /// Constructs an `EncryptingKey` operating in cipher feedback 128-bit mode (CFB128) using the provided key. |
539 | | /// |
540 | | // # FIPS |
541 | | // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms: |
542 | | // * `AES_128` |
543 | | // * `AES_256` |
544 | | // |
545 | | /// # Errors |
546 | | /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`. |
547 | 0 | pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> { |
548 | 0 | Self::new(key, OperatingMode::CFB128) |
549 | 0 | } |
550 | | |
551 | | /// Constructs an `EncryptingKey` operating in electronic code book mode (ECB) using the provided key. |
552 | | /// |
553 | | /// # ☠️ ️️️DANGER ☠️ |
554 | | /// Offered for computability purposes only. This is an extremely dangerous mode, and |
555 | | /// very likely not what you want to use. |
556 | | /// |
557 | | // # FIPS |
558 | | // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms: |
559 | | // * `AES_128` |
560 | | // * `AES_256` |
561 | | // |
562 | | /// # Errors |
563 | | /// * [`Unspecified`]: Returned if there is an error constructing the `EncryptingKey`. |
564 | 0 | pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> { |
565 | 0 | Self::new(key, OperatingMode::ECB) |
566 | 0 | } |
567 | | |
568 | | #[allow(clippy::unnecessary_wraps)] |
569 | 0 | fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> { |
570 | 0 | let algorithm = key.algorithm(); |
571 | 0 | let key = key.try_into()?; |
572 | 0 | Ok(Self { |
573 | 0 | algorithm, |
574 | 0 | key, |
575 | 0 | mode, |
576 | 0 | }) |
577 | 0 | } |
578 | | |
579 | | /// Returns the cipher algorithm. |
580 | | #[must_use] |
581 | 0 | pub fn algorithm(&self) -> &Algorithm { |
582 | 0 | self.algorithm |
583 | 0 | } |
584 | | |
585 | | /// Returns the cipher operating mode. |
586 | | #[must_use] |
587 | 0 | pub fn mode(&self) -> OperatingMode { |
588 | 0 | self.mode |
589 | 0 | } |
590 | | |
591 | | /// Encrypts the data provided in `in_out` in-place. |
592 | | /// Returns a [`DecryptionContext`] with the randomly generated IV that was used to encrypt |
593 | | /// the data provided. |
594 | | /// |
595 | | /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple |
596 | | /// of the block length. |
597 | | /// |
598 | | /// # Errors |
599 | | /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, |
600 | | /// and `in_out.len()` is not. Otherwise, returned if encryption fails. |
601 | 0 | pub fn encrypt(&self, in_out: &mut [u8]) -> Result<DecryptionContext, Unspecified> { |
602 | 0 | let context = self.algorithm.new_encryption_context(self.mode)?; |
603 | 0 | self.less_safe_encrypt(in_out, context) |
604 | 0 | } |
605 | | |
606 | | /// Encrypts the data provided in `in_out` in-place using the provided `EncryptionContext`. |
607 | | /// This is considered "less safe" because the caller could potentially construct |
608 | | /// a `EncryptionContext` from a previously used IV (initialization vector). |
609 | | /// Returns a [`DecryptionContext`] produced from the provided `EncryptionContext`. |
610 | | /// |
611 | | /// If `EncryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple |
612 | | /// of the block length. |
613 | | /// |
614 | | /// # Errors |
615 | | /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, |
616 | | /// and `in_out.len()` is not. Otherwise returned if encryption fails. |
617 | 0 | pub fn less_safe_encrypt( |
618 | 0 | &self, |
619 | 0 | in_out: &mut [u8], |
620 | 0 | context: EncryptionContext, |
621 | 0 | ) -> Result<DecryptionContext, Unspecified> { |
622 | 0 | if !self |
623 | 0 | .algorithm() |
624 | 0 | .is_valid_encryption_context(self.mode, &context) |
625 | | { |
626 | 0 | return Err(Unspecified); |
627 | 0 | } |
628 | 0 | encrypt(self.algorithm(), &self.key, self.mode, in_out, context) |
629 | 0 | } |
630 | | } |
631 | | |
632 | | impl Debug for EncryptingKey { |
633 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
634 | 0 | f.debug_struct("EncryptingKey") |
635 | 0 | .field("algorithm", self.algorithm) |
636 | 0 | .field("mode", &self.mode) |
637 | 0 | .finish_non_exhaustive() |
638 | 0 | } |
639 | | } |
640 | | |
641 | | /// A cipher decryption key that does not perform block padding. |
642 | | pub struct DecryptingKey { |
643 | | algorithm: &'static Algorithm, |
644 | | key: SymmetricCipherKey, |
645 | | mode: OperatingMode, |
646 | | } |
647 | | |
648 | | impl DecryptingKey { |
649 | | /// Constructs a cipher decrypting key operating in counter (CTR) mode using the provided key and context. |
650 | | /// |
651 | | // # FIPS |
652 | | // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms: |
653 | | // * `AES_128` |
654 | | // * `AES_256` |
655 | | // |
656 | | /// # Errors |
657 | | /// * [`Unspecified`]: Returned if there is an error during decryption. |
658 | 0 | pub fn ctr(key: UnboundCipherKey) -> Result<DecryptingKey, Unspecified> { |
659 | 0 | Self::new(key, OperatingMode::CTR) |
660 | 0 | } |
661 | | |
662 | | /// Constructs a cipher decrypting key operating in cipher feedback 128-bit mode (CFB128) using the provided key and context. |
663 | | /// |
664 | | // # FIPS |
665 | | // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms: |
666 | | // * `AES_128` |
667 | | // * `AES_256` |
668 | | // |
669 | | /// # Errors |
670 | | /// * [`Unspecified`]: Returned if there is an error during decryption. |
671 | 0 | pub fn cfb128(key: UnboundCipherKey) -> Result<Self, Unspecified> { |
672 | 0 | Self::new(key, OperatingMode::CFB128) |
673 | 0 | } |
674 | | |
675 | | /// Constructs an `DecryptingKey` operating in electronic code book (ECB) mode using the provided key. |
676 | | /// |
677 | | /// # ☠️ ️️️DANGER ☠️ |
678 | | /// Offered for computability purposes only. This is an extremely dangerous mode, and |
679 | | /// very likely not what you want to use. |
680 | | /// |
681 | | // # FIPS |
682 | | // Use this function with an `UnboundCipherKey` constructed with one of the following algorithms: |
683 | | // * `AES_128` |
684 | | // * `AES_256` |
685 | | // |
686 | | /// # Errors |
687 | | /// * [`Unspecified`]: Returned if there is an error constructing the `DecryptingKey`. |
688 | 0 | pub fn ecb(key: UnboundCipherKey) -> Result<Self, Unspecified> { |
689 | 0 | Self::new(key, OperatingMode::ECB) |
690 | 0 | } |
691 | | |
692 | | #[allow(clippy::unnecessary_wraps)] |
693 | 0 | fn new(key: UnboundCipherKey, mode: OperatingMode) -> Result<Self, Unspecified> { |
694 | 0 | let algorithm = key.algorithm(); |
695 | 0 | let key = key.try_into()?; |
696 | 0 | Ok(Self { |
697 | 0 | algorithm, |
698 | 0 | key, |
699 | 0 | mode, |
700 | 0 | }) |
701 | 0 | } |
702 | | |
703 | | /// Returns the cipher algorithm. |
704 | | #[must_use] |
705 | 0 | pub fn algorithm(&self) -> &Algorithm { |
706 | 0 | self.algorithm |
707 | 0 | } |
708 | | |
709 | | /// Returns the cipher operating mode. |
710 | | #[must_use] |
711 | 0 | pub fn mode(&self) -> OperatingMode { |
712 | 0 | self.mode |
713 | 0 | } |
714 | | |
715 | | /// Decrypts the data provided in `in_out` in-place. |
716 | | /// Returns a references to the decrypted data. |
717 | | /// |
718 | | /// If `DecryptingKey` is operating in `OperatingMode::ECB`, then `in_out.len()` must be a multiple |
719 | | /// of the block length. |
720 | | /// |
721 | | /// # Errors |
722 | | /// * [`Unspecified`]: Returned if cipher mode requires input to be a multiple of the block length, |
723 | | /// and `in_out.len()` is not. Also returned if decryption fails. |
724 | 0 | pub fn decrypt<'in_out>( |
725 | 0 | &self, |
726 | 0 | in_out: &'in_out mut [u8], |
727 | 0 | context: DecryptionContext, |
728 | 0 | ) -> Result<&'in_out mut [u8], Unspecified> { |
729 | 0 | decrypt(self.algorithm, &self.key, self.mode, in_out, context) |
730 | 0 | } |
731 | | } |
732 | | |
733 | | impl Debug for DecryptingKey { |
734 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
735 | 0 | f.debug_struct("DecryptingKey") |
736 | 0 | .field("algorithm", &self.algorithm) |
737 | 0 | .field("mode", &self.mode) |
738 | 0 | .finish_non_exhaustive() |
739 | 0 | } |
740 | | } |
741 | | |
742 | 0 | fn encrypt( |
743 | 0 | algorithm: &Algorithm, |
744 | 0 | key: &SymmetricCipherKey, |
745 | 0 | mode: OperatingMode, |
746 | 0 | in_out: &mut [u8], |
747 | 0 | context: EncryptionContext, |
748 | 0 | ) -> Result<DecryptionContext, Unspecified> { |
749 | 0 | let block_len = algorithm.block_len(); |
750 | | |
751 | 0 | match mode { |
752 | | OperatingMode::CBC | OperatingMode::ECB => { |
753 | 0 | if in_out.len() % block_len != 0 { |
754 | 0 | return Err(Unspecified); |
755 | 0 | } |
756 | | } |
757 | 0 | _ => {} |
758 | | } |
759 | | |
760 | 0 | match mode { |
761 | 0 | OperatingMode::CBC => match algorithm.id() { |
762 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
763 | 0 | aes::encrypt_cbc_mode(key, context, in_out) |
764 | | } |
765 | | }, |
766 | 0 | OperatingMode::CTR => match algorithm.id() { |
767 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
768 | 0 | aes::encrypt_ctr_mode(key, context, in_out) |
769 | | } |
770 | | }, |
771 | | // TODO: Hopefully support CFB1, and CFB8 |
772 | 0 | OperatingMode::CFB128 => match algorithm.id() { |
773 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
774 | 0 | aes::encrypt_cfb_mode(key, mode, context, in_out) |
775 | | } |
776 | | }, |
777 | 0 | OperatingMode::ECB => match algorithm.id() { |
778 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
779 | 0 | aes::encrypt_ecb_mode(key, context, in_out) |
780 | | } |
781 | | }, |
782 | | } |
783 | 0 | } |
784 | | |
785 | 0 | fn decrypt<'in_out>( |
786 | 0 | algorithm: &'static Algorithm, |
787 | 0 | key: &SymmetricCipherKey, |
788 | 0 | mode: OperatingMode, |
789 | 0 | in_out: &'in_out mut [u8], |
790 | 0 | context: DecryptionContext, |
791 | 0 | ) -> Result<&'in_out mut [u8], Unspecified> { |
792 | 0 | let block_len = algorithm.block_len(); |
793 | | |
794 | 0 | match mode { |
795 | | OperatingMode::CBC | OperatingMode::ECB => { |
796 | 0 | if in_out.len() % block_len != 0 { |
797 | 0 | return Err(Unspecified); |
798 | 0 | } |
799 | | } |
800 | 0 | _ => {} |
801 | | } |
802 | | |
803 | 0 | match mode { |
804 | 0 | OperatingMode::CBC => match algorithm.id() { |
805 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
806 | 0 | aes::decrypt_cbc_mode(key, context, in_out) |
807 | | } |
808 | | }, |
809 | 0 | OperatingMode::CTR => match algorithm.id() { |
810 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
811 | 0 | aes::decrypt_ctr_mode(key, context, in_out) |
812 | | } |
813 | | }, |
814 | | // TODO: Hopefully support CFB1, and CFB8 |
815 | 0 | OperatingMode::CFB128 => match algorithm.id() { |
816 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
817 | 0 | aes::decrypt_cfb_mode(key, mode, context, in_out) |
818 | | } |
819 | | }, |
820 | 0 | OperatingMode::ECB => match algorithm.id() { |
821 | | AlgorithmId::Aes128 | AlgorithmId::Aes192 | AlgorithmId::Aes256 => { |
822 | 0 | aes::decrypt_ecb_mode(key, context, in_out) |
823 | | } |
824 | | }, |
825 | | } |
826 | 0 | } |
827 | | |
828 | | #[cfg(test)] |
829 | | mod tests { |
830 | | use super::*; |
831 | | use crate::test::from_hex; |
832 | | |
833 | | #[cfg(feature = "fips")] |
834 | | mod fips; |
835 | | |
836 | | #[test] |
837 | | fn test_debug() { |
838 | | { |
839 | | let aes_128_key_bytes = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); |
840 | | let cipher_key = UnboundCipherKey::new(&AES_128, aes_128_key_bytes.as_slice()).unwrap(); |
841 | | assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 } }", format!("{cipher_key:?}")); |
842 | | } |
843 | | |
844 | | { |
845 | | let aes_256_key_bytes = |
846 | | from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f") |
847 | | .unwrap(); |
848 | | let cipher_key = UnboundCipherKey::new(&AES_256, aes_256_key_bytes.as_slice()).unwrap(); |
849 | | assert_eq!("UnboundCipherKey { algorithm: Algorithm { id: Aes256, key_len: 32, block_len: 16 } }", format!("{cipher_key:?}")); |
850 | | } |
851 | | |
852 | | { |
853 | | let key_bytes = &[0u8; 16]; |
854 | | let key = PaddedBlockEncryptingKey::cbc_pkcs7( |
855 | | UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), |
856 | | ) |
857 | | .unwrap(); |
858 | | assert_eq!("PaddedBlockEncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}")); |
859 | | let mut data = vec![0u8; 16]; |
860 | | let context = key.encrypt(&mut data).unwrap(); |
861 | | assert_eq!("Iv128", format!("{context:?}")); |
862 | | let key = PaddedBlockDecryptingKey::cbc_pkcs7( |
863 | | UnboundCipherKey::new(&AES_128, key_bytes).unwrap(), |
864 | | ) |
865 | | .unwrap(); |
866 | | assert_eq!("PaddedBlockDecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CBC, padding: PKCS7, .. }", format!("{key:?}")); |
867 | | } |
868 | | |
869 | | { |
870 | | let key_bytes = &[0u8; 16]; |
871 | | let key = |
872 | | EncryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap(); |
873 | | assert_eq!("EncryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}")); |
874 | | let mut data = vec![0u8; 16]; |
875 | | let context = key.encrypt(&mut data).unwrap(); |
876 | | assert_eq!("Iv128", format!("{context:?}")); |
877 | | let key = |
878 | | DecryptingKey::ctr(UnboundCipherKey::new(&AES_128, key_bytes).unwrap()).unwrap(); |
879 | | assert_eq!("DecryptingKey { algorithm: Algorithm { id: Aes128, key_len: 16, block_len: 16 }, mode: CTR, .. }", format!("{key:?}")); |
880 | | } |
881 | | } |
882 | | |
883 | | fn helper_test_cipher_n_bytes( |
884 | | key: &[u8], |
885 | | alg: &'static Algorithm, |
886 | | mode: OperatingMode, |
887 | | n: usize, |
888 | | ) { |
889 | | let mut input: Vec<u8> = Vec::with_capacity(n); |
890 | | for i in 0..n { |
891 | | let byte: u8 = i.try_into().unwrap(); |
892 | | input.push(byte); |
893 | | } |
894 | | |
895 | | let cipher_key = UnboundCipherKey::new(alg, key).unwrap(); |
896 | | let encrypting_key = EncryptingKey::new(cipher_key, mode).unwrap(); |
897 | | |
898 | | let mut in_out = input.clone(); |
899 | | let decrypt_iv = encrypting_key.encrypt(&mut in_out).unwrap(); |
900 | | |
901 | | if n > 5 { |
902 | | // There's no more than a 1 in 2^48 chance that this will fail randomly |
903 | | assert_ne!(input.as_slice(), in_out); |
904 | | } |
905 | | |
906 | | let cipher_key2 = UnboundCipherKey::new(alg, key).unwrap(); |
907 | | let decrypting_key = DecryptingKey::new(cipher_key2, mode).unwrap(); |
908 | | |
909 | | let plaintext = decrypting_key.decrypt(&mut in_out, decrypt_iv).unwrap(); |
910 | | assert_eq!(input.as_slice(), plaintext); |
911 | | } |
912 | | |
913 | | #[test] |
914 | | fn test_aes_128_ctr() { |
915 | | let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); |
916 | | for i in 0..=50 { |
917 | | helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CTR, i); |
918 | | } |
919 | | } |
920 | | |
921 | | #[test] |
922 | | fn test_aes_128_cfb128() { |
923 | | let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); |
924 | | for i in 0..=50 { |
925 | | helper_test_cipher_n_bytes(key.as_slice(), &AES_128, OperatingMode::CFB128, i); |
926 | | } |
927 | | } |
928 | | |
929 | | #[test] |
930 | | fn test_aes_256_cfb128() { |
931 | | let key = |
932 | | from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); |
933 | | for i in 0..=50 { |
934 | | helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CFB128, i); |
935 | | } |
936 | | } |
937 | | |
938 | | #[test] |
939 | | fn test_aes_256_ctr() { |
940 | | let key = |
941 | | from_hex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(); |
942 | | for i in 0..=50 { |
943 | | helper_test_cipher_n_bytes(key.as_slice(), &AES_256, OperatingMode::CTR, i); |
944 | | } |
945 | | } |
946 | | |
947 | | #[test] |
948 | | fn test_aes_128_ecb() { |
949 | | let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap(); |
950 | | _ = key; |
951 | | } |
952 | | |
953 | | macro_rules! cipher_kat { |
954 | | ($name:ident, $alg:expr, $mode:expr, $key:literal, $iv: literal, $plaintext:literal, $ciphertext:literal) => { |
955 | | #[test] |
956 | | fn $name() { |
957 | | let key = from_hex($key).unwrap(); |
958 | | let input = from_hex($plaintext).unwrap(); |
959 | | let expected_ciphertext = from_hex($ciphertext).unwrap(); |
960 | | let mut iv = from_hex($iv).unwrap(); |
961 | | let iv = { |
962 | | let slice = iv.as_mut_slice(); |
963 | | let mut iv = [0u8; $iv.len() / 2]; |
964 | | { |
965 | | let x = iv.as_mut_slice(); |
966 | | x.copy_from_slice(slice); |
967 | | } |
968 | | iv |
969 | | }; |
970 | | |
971 | | let ec = EncryptionContext::Iv128(FixedLength::from(iv)); |
972 | | |
973 | | let alg = $alg; |
974 | | |
975 | | let unbound_key = UnboundCipherKey::new(alg, &key).unwrap(); |
976 | | |
977 | | let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap(); |
978 | | |
979 | | let mut in_out = input.clone(); |
980 | | |
981 | | let context = encrypting_key.less_safe_encrypt(&mut in_out, ec).unwrap(); |
982 | | |
983 | | assert_eq!(expected_ciphertext, in_out); |
984 | | |
985 | | let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap(); |
986 | | let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap(); |
987 | | |
988 | | let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); |
989 | | assert_eq!(input.as_slice(), plaintext); |
990 | | } |
991 | | }; |
992 | | ($name:ident, $alg:expr, $mode:expr, $key:literal, $plaintext:literal, $ciphertext:literal) => { |
993 | | #[test] |
994 | | fn $name() { |
995 | | let key = from_hex($key).unwrap(); |
996 | | let input = from_hex($plaintext).unwrap(); |
997 | | let expected_ciphertext = from_hex($ciphertext).unwrap(); |
998 | | |
999 | | let alg = $alg; |
1000 | | |
1001 | | let unbound_key = UnboundCipherKey::new(alg, &key).unwrap(); |
1002 | | |
1003 | | let encrypting_key = EncryptingKey::new(unbound_key, $mode).unwrap(); |
1004 | | |
1005 | | let mut in_out = input.clone(); |
1006 | | |
1007 | | let context = encrypting_key |
1008 | | .less_safe_encrypt(&mut in_out, EncryptionContext::None) |
1009 | | .unwrap(); |
1010 | | |
1011 | | assert_eq!(expected_ciphertext, in_out); |
1012 | | |
1013 | | let unbound_key2 = UnboundCipherKey::new(alg, &key).unwrap(); |
1014 | | let decrypting_key = DecryptingKey::new(unbound_key2, $mode).unwrap(); |
1015 | | |
1016 | | let plaintext = decrypting_key.decrypt(&mut in_out, context).unwrap(); |
1017 | | assert_eq!(input.as_slice(), plaintext); |
1018 | | } |
1019 | | }; |
1020 | | } |
1021 | | |
1022 | | cipher_kat!( |
1023 | | test_iv_aes_128_ctr_16_bytes, |
1024 | | &AES_128, |
1025 | | OperatingMode::CTR, |
1026 | | "000102030405060708090a0b0c0d0e0f", |
1027 | | "00000000000000000000000000000000", |
1028 | | "00112233445566778899aabbccddeeff", |
1029 | | "c6b01904c3da3df5e7d62bd96d153686" |
1030 | | ); |
1031 | | |
1032 | | cipher_kat!( |
1033 | | test_iv_aes_256_ctr_15_bytes, |
1034 | | &AES_256, |
1035 | | OperatingMode::CTR, |
1036 | | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", |
1037 | | "00000000000000000000000000000000", |
1038 | | "00112233445566778899aabbccddee", |
1039 | | "f28122856e1cf9a7216a30d111f399" |
1040 | | ); |
1041 | | |
1042 | | cipher_kat!( |
1043 | | test_openssl_aes_128_ctr_15_bytes, |
1044 | | &AES_128, |
1045 | | OperatingMode::CTR, |
1046 | | "244828580821c1652582c76e34d299f5", |
1047 | | "093145d5af233f46072a5eb5adc11aa1", |
1048 | | "3ee38cec171e6cf466bf0df98aa0e1", |
1049 | | "bd7d928f60e3422d96b3f8cd614eb2" |
1050 | | ); |
1051 | | |
1052 | | cipher_kat!( |
1053 | | test_openssl_aes_256_ctr_15_bytes, |
1054 | | &AES_256, |
1055 | | OperatingMode::CTR, |
1056 | | "0857db8240ea459bdf660b4cced66d1f2d3734ff2de7b81e92740e65e7cc6a1d", |
1057 | | "f028ecb053f801102d11fccc9d303a27", |
1058 | | "eca7285d19f3c20e295378460e8729", |
1059 | | "b5098e5e788de6ac2f2098eb2fc6f8" |
1060 | | ); |
1061 | | |
1062 | | cipher_kat!( |
1063 | | test_sp800_38a_cfb128_aes128, |
1064 | | &AES_128, |
1065 | | OperatingMode::CFB128, |
1066 | | "2b7e151628aed2a6abf7158809cf4f3c", |
1067 | | "000102030405060708090a0b0c0d0e0f", |
1068 | | "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", |
1069 | | "3b3fd92eb72dad20333449f8e83cfb4ac8a64537a0b3a93fcde3cdad9f1ce58b26751f67a3cbb140b1808cf187a4f4dfc04b05357c5d1c0eeac4c66f9ff7f2e6" |
1070 | | ); |
1071 | | |
1072 | | cipher_kat!( |
1073 | | test_sp800_38a_cfb128_aes256, |
1074 | | &AES_256, |
1075 | | OperatingMode::CFB128, |
1076 | | "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", |
1077 | | "000102030405060708090a0b0c0d0e0f", |
1078 | | "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", |
1079 | | "dc7e84bfda79164b7ecd8486985d386039ffed143b28b1c832113c6331e5407bdf10132415e54b92a13ed0a8267ae2f975a385741ab9cef82031623d55b1e471" |
1080 | | ); |
1081 | | |
1082 | | cipher_kat!( |
1083 | | test_sp800_38a_ecb_aes128, |
1084 | | &AES_128, |
1085 | | OperatingMode::ECB, |
1086 | | "2b7e151628aed2a6abf7158809cf4f3c", |
1087 | | "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", |
1088 | | "3ad77bb40d7a3660a89ecaf32466ef97f5d3d58503b9699de785895a96fdbaaf43b1cd7f598ece23881b00e3ed0306887b0c785e27e8ad3f8223207104725dd4" |
1089 | | ); |
1090 | | |
1091 | | cipher_kat!( |
1092 | | test_sp800_38a_ecb_aes256, |
1093 | | &AES_256, |
1094 | | OperatingMode::ECB, |
1095 | | "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", |
1096 | | "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", |
1097 | | "f3eed1bdb5d2a03c064b5a7e3db181f8591ccb10d410ed26dc5ba74a31362870b6ed21b99ca6f4f9f153e7b1beafed1d23304b7a39f9f3ff067d8d8f9e24ecc7" |
1098 | | ); |
1099 | | } |