/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.16.3/src/key_wrap.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 | | //! Key Wrap Algorithms. |
5 | | //! |
6 | | //! # Examples |
7 | | //! ```rust |
8 | | //! # use std::error::Error; |
9 | | //! # fn main() -> Result<(), Box<dyn Error>> { |
10 | | //! use aws_lc_rs::key_wrap::{AesKek, KeyWrapPadded, AES_128}; |
11 | | //! |
12 | | //! const KEY: &[u8] = &[ |
13 | | //! 0xa8, 0xe0, 0x6d, 0xa6, 0x25, 0xa6, 0x5b, 0x25, 0xcf, 0x50, 0x30, 0x82, 0x68, 0x30, 0xb6, |
14 | | //! 0x61, |
15 | | //! ]; |
16 | | //! const PLAINTEXT: &[u8] = &[0x43, 0xac, 0xff, 0x29, 0x31, 0x20, 0xdd, 0x5d]; |
17 | | //! |
18 | | //! let kek = AesKek::new(&AES_128, KEY)?; |
19 | | //! |
20 | | //! let mut output = vec![0u8; PLAINTEXT.len() + 15]; |
21 | | //! |
22 | | //! let ciphertext = kek.wrap_with_padding(PLAINTEXT, &mut output)?; |
23 | | //! |
24 | | //! let kek = AesKek::new(&AES_128, KEY)?; |
25 | | //! |
26 | | //! let mut output = vec![0u8; ciphertext.len()]; |
27 | | //! |
28 | | //! let plaintext = kek.unwrap_with_padding(&*ciphertext, &mut output)?; |
29 | | //! |
30 | | //! assert_eq!(PLAINTEXT, plaintext); |
31 | | //! # Ok(()) |
32 | | //! # } |
33 | | //! ``` |
34 | | |
35 | | use crate::aws_lc::{ |
36 | | AES_set_decrypt_key, AES_set_encrypt_key, AES_unwrap_key, AES_unwrap_key_padded, AES_wrap_key, |
37 | | AES_wrap_key_padded, AES_KEY, |
38 | | }; |
39 | | use crate::error::Unspecified; |
40 | | use crate::fips::indicator_check; |
41 | | use crate::sealed::Sealed; |
42 | | use core::fmt::Debug; |
43 | | use core::mem::MaybeUninit; |
44 | | use core::ptr::null; |
45 | | |
46 | | mod tests; |
47 | | |
48 | | /// The Key Wrapping Algorithm Identifier |
49 | | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
50 | | #[non_exhaustive] |
51 | | pub enum BlockCipherId { |
52 | | /// AES Block Cipher with 128-bit key. |
53 | | Aes128, |
54 | | |
55 | | /// AES Block Cipher with 256-bit key. |
56 | | Aes256, |
57 | | } |
58 | | |
59 | | /// A key wrap block cipher. |
60 | | pub trait BlockCipher: 'static + Debug + Sealed { |
61 | | /// The block cipher identifier. |
62 | | fn id(&self) -> BlockCipherId; |
63 | | |
64 | | /// The key size in bytes to be used with the block cipher. |
65 | | fn key_len(&self) -> usize; |
66 | | } |
67 | | |
68 | | /// An AES Block Cipher |
69 | | pub struct AesBlockCipher { |
70 | | id: BlockCipherId, |
71 | | key_len: usize, |
72 | | } |
73 | | |
74 | | impl BlockCipher for AesBlockCipher { |
75 | | /// Returns the algorithm identifier. |
76 | | #[inline] |
77 | 0 | fn id(&self) -> BlockCipherId { |
78 | 0 | self.id |
79 | 0 | } |
80 | | |
81 | | /// Returns the algorithm key length. |
82 | | #[inline] |
83 | 0 | fn key_len(&self) -> usize { |
84 | 0 | self.key_len |
85 | 0 | } |
86 | | } |
87 | | |
88 | | impl Sealed for AesBlockCipher {} |
89 | | |
90 | | impl Debug for AesBlockCipher { |
91 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
92 | 0 | Debug::fmt(&self.id, f) |
93 | 0 | } |
94 | | } |
95 | | |
96 | | /// AES Block Cipher with 128-bit key. |
97 | | pub const AES_128: AesBlockCipher = AesBlockCipher { |
98 | | id: BlockCipherId::Aes128, |
99 | | key_len: 16, |
100 | | }; |
101 | | |
102 | | /// AES Block Cipher with 256-bit key. |
103 | | pub const AES_256: AesBlockCipher = AesBlockCipher { |
104 | | id: BlockCipherId::Aes256, |
105 | | key_len: 32, |
106 | | }; |
107 | | |
108 | | /// A Key Wrap (KW) algorithm implementation. |
109 | | #[allow(clippy::module_name_repetitions)] |
110 | | pub trait KeyWrap: Sealed { |
111 | | /// Peforms the key wrap encryption algorithm using a block cipher. |
112 | | /// It wraps `plaintext` and writes the corresponding ciphertext to `output`. |
113 | | /// |
114 | | /// # Errors |
115 | | /// * [`Unspecified`]: Any error that has occurred performing the operation. |
116 | | fn wrap<'output>( |
117 | | self, |
118 | | plaintext: &[u8], |
119 | | output: &'output mut [u8], |
120 | | ) -> Result<&'output mut [u8], Unspecified>; |
121 | | |
122 | | /// Peforms the key wrap decryption algorithm using a block cipher. |
123 | | /// It unwraps `ciphertext` and writes the corresponding plaintext to `output`. |
124 | | /// |
125 | | /// # Errors |
126 | | /// * [`Unspecified`]: Any error that has occurred performing the operation. |
127 | | fn unwrap<'output>( |
128 | | self, |
129 | | ciphertext: &[u8], |
130 | | output: &'output mut [u8], |
131 | | ) -> Result<&'output mut [u8], Unspecified>; |
132 | | } |
133 | | |
134 | | /// A Key Wrap with Padding (KWP) algorithm implementation. |
135 | | #[allow(clippy::module_name_repetitions)] |
136 | | pub trait KeyWrapPadded: Sealed { |
137 | | /// Peforms the key wrap padding encryption algorithm using a block cipher. |
138 | | /// It wraps and pads `plaintext` writes the corresponding ciphertext to `output`. |
139 | | /// |
140 | | /// # Errors |
141 | | /// * [`Unspecified`]: Any error that has occurred performing the operation. |
142 | | fn wrap_with_padding<'output>( |
143 | | self, |
144 | | plaintext: &[u8], |
145 | | output: &'output mut [u8], |
146 | | ) -> Result<&'output mut [u8], Unspecified>; |
147 | | |
148 | | /// Peforms the key wrap padding decryption algorithm using a block cipher. |
149 | | /// It unwraps the padded `ciphertext` and writes the corresponding plaintext to `output`. |
150 | | /// |
151 | | /// # Errors |
152 | | /// * [`Unspecified`]: Any error that has occurred performing the operation. |
153 | | fn unwrap_with_padding<'output>( |
154 | | self, |
155 | | ciphertext: &[u8], |
156 | | output: &'output mut [u8], |
157 | | ) -> Result<&'output mut [u8], Unspecified>; |
158 | | } |
159 | | |
160 | | /// AES Key Encryption Key. |
161 | | pub type AesKek = KeyEncryptionKey<AesBlockCipher>; |
162 | | |
163 | | /// The key-encryption key used with the selected cipher algorithn to wrap or unwrap a key. |
164 | | /// |
165 | | /// Implements the NIST SP 800-38F key wrapping algoirthm. |
166 | | /// |
167 | | /// The NIST specification is similar to that of RFC 3394 but with the following caveats: |
168 | | /// * Specifies a maxiumum plaintext length that can be accepted. |
169 | | /// * Allows implementations to specify a subset of valid lengths accepted. |
170 | | /// * Allows for the usage of other 128-bit block ciphers other than AES. |
171 | | pub struct KeyEncryptionKey<Cipher: BlockCipher> { |
172 | | cipher: &'static Cipher, |
173 | | key: Box<[u8]>, |
174 | | } |
175 | | |
176 | | impl<Cipher: BlockCipher> KeyEncryptionKey<Cipher> { |
177 | | /// Construct a new Key Encryption Key. |
178 | | /// |
179 | | /// # Errors |
180 | | /// * [`Unspecified`]: Any error that occurs constructing the key encryption key. |
181 | 0 | pub fn new(cipher: &'static Cipher, key: &[u8]) -> Result<Self, Unspecified> { |
182 | 0 | if key.len() != cipher.key_len() { |
183 | 0 | return Err(Unspecified); |
184 | 0 | } |
185 | | |
186 | 0 | let key = Vec::from(key).into_boxed_slice(); |
187 | | |
188 | 0 | Ok(Self { cipher, key }) |
189 | 0 | } |
190 | | |
191 | | /// Returns the block cipher algorithm identifier configured for the key. |
192 | | #[must_use] |
193 | 0 | pub fn block_cipher_id(&self) -> BlockCipherId { |
194 | 0 | self.cipher.id() |
195 | 0 | } |
196 | | } |
197 | | |
198 | | impl<Cipher: BlockCipher> Sealed for KeyEncryptionKey<Cipher> {} |
199 | | |
200 | | impl KeyWrap for KeyEncryptionKey<AesBlockCipher> { |
201 | | /// Peforms the key wrap encryption algorithm using `KeyEncryptionKey`'s configured block cipher. |
202 | | /// It wraps `plaintext` and writes the corresponding ciphertext to `output`. |
203 | | /// |
204 | | /// # Validation |
205 | | /// * `plaintext.len()` must be a multiple of eight |
206 | | /// * `output.len() >= (input.len() + 8)` |
207 | | /// |
208 | | /// # Errors |
209 | | /// * [`Unspecified`]: An error occurred either due to `output` being insufficiently sized, `input` exceeding |
210 | | /// the allowed input size, or for other unspecified reasons. |
211 | 0 | fn wrap<'output>( |
212 | 0 | self, |
213 | 0 | plaintext: &[u8], |
214 | 0 | output: &'output mut [u8], |
215 | 0 | ) -> Result<&'output mut [u8], Unspecified> { |
216 | 0 | if output.len() < plaintext.len() + 8 { |
217 | 0 | return Err(Unspecified); |
218 | 0 | } |
219 | | |
220 | 0 | let mut aes_key = MaybeUninit::<AES_KEY>::uninit(); |
221 | | |
222 | 0 | let key_bits: u32 = (self.key.len() * 8).try_into().map_err(|_| Unspecified)?; |
223 | | |
224 | 0 | if 0 != unsafe { AES_set_encrypt_key(self.key.as_ptr(), key_bits, aes_key.as_mut_ptr()) } { |
225 | 0 | return Err(Unspecified); |
226 | 0 | } |
227 | | |
228 | 0 | let aes_key = unsafe { aes_key.assume_init() }; |
229 | | |
230 | | // AWS-LC validates the following: |
231 | | // * in_len <= INT_MAX - 8 |
232 | | // * in_len >= 16 |
233 | | // * in_len % 8 == 0 |
234 | 0 | let out_len = indicator_check!(unsafe { |
235 | 0 | AES_wrap_key( |
236 | 0 | &aes_key, |
237 | 0 | null(), |
238 | 0 | output.as_mut_ptr(), |
239 | 0 | plaintext.as_ptr(), |
240 | 0 | plaintext.len(), |
241 | | ) |
242 | | }); |
243 | | |
244 | 0 | if out_len == -1 { |
245 | 0 | return Err(Unspecified); |
246 | 0 | } |
247 | | |
248 | 0 | let out_len: usize = out_len.try_into().map_err(|_| Unspecified)?; |
249 | | |
250 | 0 | debug_assert_eq!(out_len, plaintext.len() + 8); |
251 | | |
252 | 0 | Ok(&mut output[..out_len]) |
253 | 0 | } |
254 | | |
255 | | /// Peforms the key wrap decryption algorithm using `KeyEncryptionKey`'s configured block cipher. |
256 | | /// It unwraps `ciphertext` and writes the corresponding plaintext to `output`. |
257 | | /// |
258 | | /// # Validation |
259 | | /// * `ciphertext.len()` must be a multiple of 8 |
260 | | /// * `output.len() >= (input.len() - 8)` |
261 | | /// |
262 | | /// # Errors |
263 | | /// * [`Unspecified`]: An error occurred either due to `output` being insufficiently sized, `input` exceeding |
264 | | /// the allowed input size, or for other unspecified reasons. |
265 | 0 | fn unwrap<'output>( |
266 | 0 | self, |
267 | 0 | ciphertext: &[u8], |
268 | 0 | output: &'output mut [u8], |
269 | 0 | ) -> Result<&'output mut [u8], Unspecified> { |
270 | 0 | if output.len() < ciphertext.len() - 8 { |
271 | 0 | return Err(Unspecified); |
272 | 0 | } |
273 | | |
274 | 0 | let mut aes_key = MaybeUninit::<AES_KEY>::uninit(); |
275 | | |
276 | | if 0 != unsafe { |
277 | 0 | AES_set_decrypt_key( |
278 | 0 | self.key.as_ptr(), |
279 | 0 | (self.key.len() * 8).try_into().map_err(|_| Unspecified)?, |
280 | 0 | aes_key.as_mut_ptr(), |
281 | | ) |
282 | | } { |
283 | 0 | return Err(Unspecified); |
284 | 0 | } |
285 | | |
286 | 0 | let aes_key = unsafe { aes_key.assume_init() }; |
287 | | |
288 | | // AWS-LC validates the following: |
289 | | // * in_len < INT_MAX |
290 | | // * in_len > 24 |
291 | | // * in_len % 8 == 0 |
292 | 0 | let out_len = indicator_check!(unsafe { |
293 | 0 | AES_unwrap_key( |
294 | 0 | &aes_key, |
295 | 0 | null(), |
296 | 0 | output.as_mut_ptr(), |
297 | 0 | ciphertext.as_ptr(), |
298 | 0 | ciphertext.len(), |
299 | | ) |
300 | | }); |
301 | | |
302 | 0 | if out_len == -1 { |
303 | 0 | return Err(Unspecified); |
304 | 0 | } |
305 | | |
306 | 0 | let out_len: usize = out_len.try_into().map_err(|_| Unspecified)?; |
307 | | |
308 | 0 | debug_assert_eq!(out_len, ciphertext.len() - 8); |
309 | | |
310 | 0 | Ok(&mut output[..out_len]) |
311 | 0 | } |
312 | | } |
313 | | |
314 | | impl KeyWrapPadded for KeyEncryptionKey<AesBlockCipher> { |
315 | | /// Peforms the key wrap padding encryption algorithm using `KeyEncryptionKey`'s configured block cipher. |
316 | | /// It wraps and pads `plaintext` writes the corresponding ciphertext to `output`. |
317 | | /// |
318 | | /// # Validation |
319 | | /// * `output.len() >= (input.len() + 15)` |
320 | | /// |
321 | | /// # Errors |
322 | | /// * [`Unspecified`]: An error occurred either due to `output` being insufficiently sized, `input` exceeding |
323 | | /// the allowed input size, or for other unspecified reasons. |
324 | 0 | fn wrap_with_padding<'output>( |
325 | 0 | self, |
326 | 0 | plaintext: &[u8], |
327 | 0 | output: &'output mut [u8], |
328 | 0 | ) -> Result<&'output mut [u8], Unspecified> { |
329 | 0 | let mut aes_key = MaybeUninit::<AES_KEY>::uninit(); |
330 | | |
331 | 0 | let key_bits: u32 = (self.key.len() * 8).try_into().map_err(|_| Unspecified)?; |
332 | | |
333 | 0 | if 0 != unsafe { AES_set_encrypt_key(self.key.as_ptr(), key_bits, aes_key.as_mut_ptr()) } { |
334 | 0 | return Err(Unspecified); |
335 | 0 | } |
336 | | |
337 | 0 | let aes_key = unsafe { aes_key.assume_init() }; |
338 | | |
339 | 0 | let mut out_len: usize = 0; |
340 | | |
341 | | // AWS-LC validates the following: |
342 | | // * in_len != 0 |
343 | | // * in_len <= INT_MAX |
344 | | // * max_out >= required_padding + 8 |
345 | 0 | if 1 != indicator_check!(unsafe { |
346 | 0 | AES_wrap_key_padded( |
347 | 0 | &aes_key, |
348 | 0 | output.as_mut_ptr(), |
349 | 0 | &mut out_len, |
350 | 0 | output.len(), |
351 | 0 | plaintext.as_ptr(), |
352 | 0 | plaintext.len(), |
353 | 0 | ) |
354 | 0 | }) { |
355 | 0 | return Err(Unspecified); |
356 | 0 | } |
357 | | |
358 | 0 | Ok(&mut output[..out_len]) |
359 | 0 | } |
360 | | |
361 | | /// Peforms the key wrap padding decryption algorithm using `KeyEncryptionKey`'s configured block cipher. |
362 | | /// It unwraps the padded `ciphertext` and writes the corresponding plaintext to `output`. |
363 | | /// |
364 | | /// # Sizing `output` |
365 | | /// `output.len() >= input.len()`. |
366 | | /// |
367 | | /// # Errors |
368 | | /// * [`Unspecified`]: An error occurred either due to `output` being insufficiently sized, `input` exceeding |
369 | | /// the allowed input size, or for other unspecified reasons. |
370 | 0 | fn unwrap_with_padding<'output>( |
371 | 0 | self, |
372 | 0 | ciphertext: &[u8], |
373 | 0 | output: &'output mut [u8], |
374 | 0 | ) -> Result<&'output mut [u8], Unspecified> { |
375 | 0 | let mut aes_key = MaybeUninit::<AES_KEY>::uninit(); |
376 | | |
377 | | if 0 != unsafe { |
378 | 0 | AES_set_decrypt_key( |
379 | 0 | self.key.as_ptr(), |
380 | 0 | (self.key.len() * 8).try_into().map_err(|_| Unspecified)?, |
381 | 0 | aes_key.as_mut_ptr(), |
382 | | ) |
383 | | } { |
384 | 0 | return Err(Unspecified); |
385 | 0 | } |
386 | | |
387 | 0 | let aes_key = unsafe { aes_key.assume_init() }; |
388 | | |
389 | 0 | let mut out_len: usize = 0; |
390 | | |
391 | | // AWS-LC validates the following: |
392 | | // * in_len >= AES_BLOCK_SIZE |
393 | | // * max_out >= in_len - 8 |
394 | 0 | if 1 != indicator_check!(unsafe { |
395 | 0 | AES_unwrap_key_padded( |
396 | 0 | &aes_key, |
397 | 0 | output.as_mut_ptr(), |
398 | 0 | &mut out_len, |
399 | 0 | output.len(), |
400 | 0 | ciphertext.as_ptr(), |
401 | 0 | ciphertext.len(), |
402 | 0 | ) |
403 | 0 | }) { |
404 | 0 | return Err(Unspecified); |
405 | 0 | } |
406 | | |
407 | 0 | Ok(&mut output[..out_len]) |
408 | 0 | } |
409 | | } |
410 | | |
411 | | impl<Cipher: BlockCipher> Debug for KeyEncryptionKey<Cipher> { |
412 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
413 | 0 | f.debug_struct("KeyEncryptionKey") |
414 | 0 | .field("cipher", &self.cipher) |
415 | 0 | .finish_non_exhaustive() |
416 | 0 | } |
417 | | } |