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 fn id(&self) -> BlockCipherId {
78 self.id
79 }
80
81 /// Returns the algorithm key length.
82 #[inline]
83 fn key_len(&self) -> usize {
84 self.key_len
85 }
86}
87
88impl Sealed for AesBlockCipher {}
89
90impl Debug for AesBlockCipher {
91 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
92 Debug::fmt(&self.id, f)
93 }
94}
95
96/// AES Block Cipher with 128-bit key.
97pub const AES_128: AesBlockCipher = AesBlockCipher {
98 id: BlockCipherId::Aes128,
99 key_len: 16,
100};
101
102/// AES Block Cipher with 256-bit key.
103pub 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)]
110pub 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)]
136pub 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.
161pub 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.
171pub struct KeyEncryptionKey<Cipher: BlockCipher> {
172 cipher: &'static Cipher,
173 key: Box<[u8]>,
174}
175
176impl<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 pub fn new(cipher: &'static Cipher, key: &[u8]) -> Result<Self, Unspecified> {
182 if key.len() != cipher.key_len() {
183 return Err(Unspecified);
184 }
185
186 let key = Vec::from(key).into_boxed_slice();
187
188 Ok(Self { cipher, key })
189 }
190
191 /// Returns the block cipher algorithm identifier configured for the key.
192 #[must_use]
193 pub fn block_cipher_id(&self) -> BlockCipherId {
194 self.cipher.id()
195 }
196}
197
198impl<Cipher: BlockCipher> Sealed for KeyEncryptionKey<Cipher> {}
199
200impl 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 fn wrap<'output>(
212 self,
213 plaintext: &[u8],
214 output: &'output mut [u8],
215 ) -> Result<&'output mut [u8], Unspecified> {
216 if output.len() < plaintext.len() + 8 {
217 return Err(Unspecified);
218 }
219
220 let mut aes_key = MaybeUninit::<AES_KEY>::uninit();
221
222 let key_bits: u32 = (self.key.len() * 8).try_into().map_err(|_| Unspecified)?;
223
224 if 0 != unsafe { AES_set_encrypt_key(self.key.as_ptr(), key_bits, aes_key.as_mut_ptr()) } {
225 return Err(Unspecified);
226 }
227
228 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 let out_len = indicator_check!(unsafe {
235 AES_wrap_key(
236 &aes_key,
237 null(),
238 output.as_mut_ptr(),
239 plaintext.as_ptr(),
240 plaintext.len(),
241 )
242 });
243
244 if out_len == -1 {
245 return Err(Unspecified);
246 }
247
248 let out_len: usize = out_len.try_into().map_err(|_| Unspecified)?;
249
250 debug_assert_eq!(out_len, plaintext.len() + 8);
251
252 Ok(&mut output[..out_len])
253 }
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 fn unwrap<'output>(
266 self,
267 ciphertext: &[u8],
268 output: &'output mut [u8],
269 ) -> Result<&'output mut [u8], Unspecified> {
270 if output.len() < ciphertext.len() - 8 {
271 return Err(Unspecified);
272 }
273
274 let mut aes_key = MaybeUninit::<AES_KEY>::uninit();
275
276 if 0 != unsafe {
277 AES_set_decrypt_key(
278 self.key.as_ptr(),
279 (self.key.len() * 8).try_into().map_err(|_| Unspecified)?,
280 aes_key.as_mut_ptr(),
281 )
282 } {
283 return Err(Unspecified);
284 }
285
286 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 let out_len = indicator_check!(unsafe {
293 AES_unwrap_key(
294 &aes_key,
295 null(),
296 output.as_mut_ptr(),
297 ciphertext.as_ptr(),
298 ciphertext.len(),
299 )
300 });
301
302 if out_len == -1 {
303 return Err(Unspecified);
304 }
305
306 let out_len: usize = out_len.try_into().map_err(|_| Unspecified)?;
307
308 debug_assert_eq!(out_len, ciphertext.len() - 8);
309
310 Ok(&mut output[..out_len])
311 }
312}
313
314impl 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 fn wrap_with_padding<'output>(
325 self,
326 plaintext: &[u8],
327 output: &'output mut [u8],
328 ) -> Result<&'output mut [u8], Unspecified> {
329 let mut aes_key = MaybeUninit::<AES_KEY>::uninit();
330
331 let key_bits: u32 = (self.key.len() * 8).try_into().map_err(|_| Unspecified)?;
332
333 if 0 != unsafe { AES_set_encrypt_key(self.key.as_ptr(), key_bits, aes_key.as_mut_ptr()) } {
334 return Err(Unspecified);
335 }
336
337 let aes_key = unsafe { aes_key.assume_init() };
338
339 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 if 1 != indicator_check!(unsafe {
346 AES_wrap_key_padded(
347 &aes_key,
348 output.as_mut_ptr(),
349 &mut out_len,
350 output.len(),
351 plaintext.as_ptr(),
352 plaintext.len(),
353 )
354 }) {
355 return Err(Unspecified);
356 }
357
358 Ok(&mut output[..out_len])
359 }
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 fn unwrap_with_padding<'output>(
371 self,
372 ciphertext: &[u8],
373 output: &'output mut [u8],
374 ) -> Result<&'output mut [u8], Unspecified> {
375 let mut aes_key = MaybeUninit::<AES_KEY>::uninit();
376
377 if 0 != unsafe {
378 AES_set_decrypt_key(
379 self.key.as_ptr(),
380 (self.key.len() * 8).try_into().map_err(|_| Unspecified)?,
381 aes_key.as_mut_ptr(),
382 )
383 } {
384 return Err(Unspecified);
385 }
386
387 let aes_key = unsafe { aes_key.assume_init() };
388
389 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 if 1 != indicator_check!(unsafe {
395 AES_unwrap_key_padded(
396 &aes_key,
397 output.as_mut_ptr(),
398 &mut out_len,
399 output.len(),
400 ciphertext.as_ptr(),
401 ciphertext.len(),
402 )
403 }) {
404 return Err(Unspecified);
405 }
406
407 Ok(&mut output[..out_len])
408 }
409}
410
411impl<Cipher: BlockCipher> Debug for KeyEncryptionKey<Cipher> {
412 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
413 f.debug_struct("KeyEncryptionKey")
414 .field("cipher", &self.cipher)
415 .finish_non_exhaustive()
416 }
417}