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}