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