Coverage Report

Created: 2026-07-13 08:11

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