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