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}