aes_kw/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
5    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
6)]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8#![forbid(unsafe_code)]
9#![warn(missing_docs, rust_2018_idioms)]
10
11#[cfg(feature = "alloc")]
12#[macro_use]
13extern crate alloc;
14#[cfg(feature = "std")]
15extern crate std;
16
17mod error;
18
19pub use error::{Error, Result};
20
21use aes::cipher::{
22    generic_array::GenericArray,
23    typenum::{Unsigned, U16, U24, U32},
24    Block, BlockBackend, BlockCipher, BlockClosure, BlockDecrypt, BlockEncrypt, BlockSizeUser,
25    KeyInit,
26};
27
28#[cfg(feature = "alloc")]
29use alloc::vec::Vec;
30
31/// Size of an AES "semiblock" in bytes.
32///
33/// From NIST SP 800-38F § 4.1:
34///
35/// > semiblock: given a block cipher, a bit string whose length is half of the
36/// > block size.
37pub const SEMIBLOCK_SIZE: usize = 8;
38
39/// Maximum length of the AES-KWP input data (2^32 bytes).
40pub const KWP_MAX_LEN: usize = u32::MAX as usize;
41
42/// Size of an AES-KW and AES-KWP initialization vector in bytes.
43pub const IV_LEN: usize = SEMIBLOCK_SIZE;
44
45/// Default Initial Value for AES-KW as defined in RFC3394 § 2.2.3.1.
46///
47/// <https://datatracker.ietf.org/doc/html/rfc3394#section-2.2.3.1>
48///
49/// ```text
50/// The default initial value (IV) is defined to be the hexadecimal
51/// constant:
52///
53///     A[0] = IV = A6A6A6A6A6A6A6A6
54///
55/// The use of a constant as the IV supports a strong integrity check on
56/// the key data during the period that it is wrapped.  If unwrapping
57/// produces A[0] = A6A6A6A6A6A6A6A6, then the chance that the key data
58/// is corrupt is 2^-64.  If unwrapping produces A[0] any other value,
59/// then the unwrap must return an error and not return any key data.
60/// ```
61pub const IV: [u8; IV_LEN] = [0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6];
62
63/// Alternative Initial Value constant prefix for AES-KWP as defined in
64/// RFC3394 § 3.
65///
66/// <https://datatracker.ietf.org/doc/html/rfc5649#section-3>
67///
68/// ```text
69/// The Alternative Initial Value (AIV) required by this specification is
70//  a 32-bit constant concatenated to a 32-bit MLI.  The constant is (in
71//  hexadecimal) A65959A6 and occupies the high-order half of the AIV.
72/// ```
73pub const KWP_IV_PREFIX: [u8; IV_LEN / 2] = [0xA6, 0x59, 0x59, 0xA6];
74
75/// A Key-Encrypting-Key (KEK) that can be used to wrap and unwrap other
76/// keys.
77#[derive(Debug, Clone, Copy, PartialEq)]
78pub struct Kek<Aes>
79where
80    Aes: KeyInit + BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt + BlockDecrypt,
81{
82    /// Initialized cipher
83    cipher: Aes,
84}
85
86/// AES-128 KEK
87pub type KekAes128 = Kek<aes::Aes128>;
88
89/// AES-192 KEK
90pub type KekAes192 = Kek<aes::Aes192>;
91
92/// AES-256 KEK
93pub type KekAes256 = Kek<aes::Aes256>;
94
95impl From<GenericArray<u8, U16>> for KekAes128 {
96    fn from(kek: GenericArray<u8, U16>) -> Self {
97        Kek::new(&kek)
98    }
99}
100
101impl From<GenericArray<u8, U24>> for KekAes192 {
102    fn from(kek: GenericArray<u8, U24>) -> Self {
103        Kek::new(&kek)
104    }
105}
106
107impl From<GenericArray<u8, U32>> for KekAes256 {
108    fn from(kek: GenericArray<u8, U32>) -> Self {
109        Kek::new(&kek)
110    }
111}
112
113impl From<[u8; 16]> for KekAes128 {
114    fn from(kek: [u8; 16]) -> Self {
115        Kek::new(&kek.into())
116    }
117}
118
119impl From<[u8; 24]> for KekAes192 {
120    fn from(kek: [u8; 24]) -> Self {
121        Kek::new(&kek.into())
122    }
123}
124
125impl From<[u8; 32]> for KekAes256 {
126    fn from(kek: [u8; 32]) -> Self {
127        Kek::new(&kek.into())
128    }
129}
130
131impl<Aes> TryFrom<&[u8]> for Kek<Aes>
132where
133    Aes: KeyInit + BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt + BlockDecrypt,
134{
135    type Error = Error;
136
137    fn try_from(value: &[u8]) -> Result<Self> {
138        if value.len() == Aes::KeySize::to_usize() {
139            Ok(Kek::new(GenericArray::from_slice(value)))
140        } else {
141            Err(Error::InvalidKekSize { size: value.len() })
142        }
143    }
144}
145
146impl<Aes> Kek<Aes>
147where
148    Aes: KeyInit + BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt + BlockDecrypt,
149{
150    /// Constructs a new Kek based on the appropriate raw key material.
151    pub fn new(key: &GenericArray<u8, Aes::KeySize>) -> Self {
152        let cipher = Aes::new(key);
153        Kek { cipher }
154    }
155
156    /// AES Key Wrap, as defined in RFC 3394.
157    ///
158    /// The `out` buffer will be overwritten, and must be exactly [`IV_LEN`]
159    /// bytes (i.e. 8 bytes) longer than the length of `data`.
160    pub fn wrap(&self, data: &[u8], out: &mut [u8]) -> Result<()> {
161        if data.len() % SEMIBLOCK_SIZE != 0 {
162            return Err(Error::InvalidDataSize);
163        }
164
165        if out.len() != data.len() + IV_LEN {
166            return Err(Error::InvalidOutputSize {
167                expected: data.len() + IV_LEN,
168            });
169        }
170
171        // 0) Prepare inputs
172
173        // number of 64 bit blocks in the input data
174        let n = data.len() / 8;
175
176        // 1) Initialize variables
177
178        // Set A to the IV
179        let block = &mut Block::<WCtx<'_>>::default();
180        block[..IV_LEN].copy_from_slice(&IV);
181
182        // 2) Calculate intermediate values
183        out[IV_LEN..].copy_from_slice(data);
184
185        self.cipher.encrypt_with_backend(WCtx { n, block, out });
186
187        // 3) Output the results
188        out[..IV_LEN].copy_from_slice(&block[..IV_LEN]);
189
190        Ok(())
191    }
192
193    /// Computes [`Self::wrap`], allocating a [`Vec`] for the return value.
194    #[cfg(feature = "alloc")]
195    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
196    pub fn wrap_vec(&self, data: &[u8]) -> Result<Vec<u8>> {
197        let mut out = vec![0u8; data.len() + IV_LEN];
198        self.wrap(data, &mut out)?;
199        Ok(out)
200    }
201
202    /// AES Key Unwrap, as defined in RFC 3394.
203    ///
204    /// The `out` buffer will be overwritten, and must be exactly [`IV_LEN`]
205    /// bytes (i.e. 8 bytes) shorter than the length of `data`.
206    pub fn unwrap(&self, data: &[u8], out: &mut [u8]) -> Result<()> {
207        if data.len() % SEMIBLOCK_SIZE != 0 {
208            return Err(Error::InvalidDataSize);
209        }
210
211        // 0) Prepare inputs
212
213        let n = (data.len() / SEMIBLOCK_SIZE)
214            .checked_sub(1)
215            .ok_or(Error::InvalidDataSize)?;
216
217        if out.len() != n * SEMIBLOCK_SIZE {
218            return Err(Error::InvalidOutputSize {
219                expected: n * SEMIBLOCK_SIZE,
220            });
221        }
222
223        // 1) Initialize variables
224
225        let block = &mut Block::<WInverseCtx<'_>>::default();
226        block[..IV_LEN].copy_from_slice(&data[..IV_LEN]);
227
228        //   for i = 1 to n: R[i] = C[i]
229        out.copy_from_slice(&data[IV_LEN..]);
230
231        // 2) Calculate intermediate values
232
233        self.cipher
234            .decrypt_with_backend(WInverseCtx { n, block, out });
235
236        // 3) Output the results
237
238        if block[..IV_LEN] == IV[..] {
239            Ok(())
240        } else {
241            Err(Error::IntegrityCheckFailed)
242        }
243    }
244
245    /// Computes [`Self::unwrap`], allocating a [`Vec`] for the return value.
246    #[cfg(feature = "alloc")]
247    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
248    pub fn unwrap_vec(&self, data: &[u8]) -> Result<Vec<u8>> {
249        let out_len = data
250            .len()
251            .checked_sub(IV_LEN)
252            .ok_or(Error::InvalidDataSize)?;
253
254        let mut out = vec![0u8; out_len];
255        self.unwrap(data, &mut out)?;
256        Ok(out)
257    }
258
259    /// AES Key Wrap with Padding, as defined in RFC 5649.
260    ///
261    ///
262    /// The `out` buffer will be overwritten, and must be the smallest
263    /// multiple of [`SEMIBLOCK_SIZE`] (i.e. 8) which is at least [`IV_LEN`]
264    /// bytes (i.e. 8 bytes) longer than the length of `data`.
265    pub fn wrap_with_padding(&self, data: &[u8], out: &mut [u8]) -> Result<()> {
266        if data.len() > KWP_MAX_LEN {
267            return Err(Error::InvalidDataSize);
268        }
269
270        // 0) Prepare inputs
271
272        // number of 64 bit blocks in the input data (padded)
273        let n = (data.len() + SEMIBLOCK_SIZE - 1) / SEMIBLOCK_SIZE;
274
275        if out.len() != n * SEMIBLOCK_SIZE + IV_LEN {
276            return Err(Error::InvalidOutputSize {
277                expected: n * SEMIBLOCK_SIZE + IV_LEN,
278            });
279        }
280
281        // 32-bit MLI equal to the number of bytes in the input data, big endian
282        let mli = (data.len() as u32).to_be_bytes();
283
284        // 2) Wrapping
285
286        // 2.1) Initialize variables
287
288        // Set A to the AIV
289        let block = &mut Block::<WCtx<'_>>::default();
290        block[..IV_LEN / 2].copy_from_slice(&KWP_IV_PREFIX);
291        block[IV_LEN / 2..IV_LEN].copy_from_slice(&mli);
292
293        // If n is 1, the plaintext is encrypted as a single AES block
294        if n == 1 {
295            // 1) Append padding
296
297            // GenericArrays should be zero by default, but zeroize again to be sure
298            for i in data.len()..n * SEMIBLOCK_SIZE {
299                block[IV_LEN + i] = 0;
300            }
301
302            block[IV_LEN..IV_LEN + data.len()].copy_from_slice(data);
303
304            self.cipher.encrypt_block(block);
305            out.copy_from_slice(block);
306        } else {
307            // 1) Append padding
308
309            // Don't trust the caller to provide a zeroized out buffer, zeroize again to be sure
310            for i in data.len()..n * SEMIBLOCK_SIZE {
311                out[IV_LEN + i] = 0;
312            }
313
314            // 2.2) Calculate intermediate values
315            out[IV_LEN..IV_LEN + data.len()].copy_from_slice(data);
316
317            self.cipher.encrypt_with_backend(WCtx { n, block, out });
318
319            // 2.3) Output the results
320            out[..IV_LEN].copy_from_slice(&block[..IV_LEN]);
321        }
322
323        Ok(())
324    }
325
326    /// Computes [`Self::wrap`], allocating a [`Vec`] for the return value.
327    #[cfg(feature = "alloc")]
328    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
329    pub fn wrap_with_padding_vec(&self, data: &[u8]) -> Result<Vec<u8>> {
330        let n = (data.len() + SEMIBLOCK_SIZE - 1) / SEMIBLOCK_SIZE;
331        let mut out = vec![0u8; n * SEMIBLOCK_SIZE + IV_LEN];
332        self.wrap_with_padding(data, &mut out)?;
333        Ok(out)
334    }
335
336    /// AES Key Wrap with Padding, as defined in RFC 5649.
337    ///
338    /// The `out` buffer will be overwritten, and must be exactly [`IV_LEN`]
339    /// bytes (i.e. 8 bytes) shorter than the length of `data`.
340    /// This method returns a slice of `out`, truncated to the appropriate
341    /// length by removing the padding.
342    pub fn unwrap_with_padding<'a>(&self, data: &[u8], out: &'a mut [u8]) -> Result<&'a [u8]> {
343        if data.len() % SEMIBLOCK_SIZE != 0 {
344            return Err(Error::InvalidDataSize);
345        }
346
347        // 0) Prepare inputs
348
349        let n = (data.len() / SEMIBLOCK_SIZE)
350            .checked_sub(1)
351            .ok_or(Error::InvalidDataSize)?;
352
353        if out.len() != n * SEMIBLOCK_SIZE {
354            return Err(Error::InvalidOutputSize {
355                expected: n * SEMIBLOCK_SIZE,
356            });
357        }
358
359        // 1) Key unwrapping
360
361        // 1.1) Initialize variables
362
363        let block = &mut Block::<WInverseCtx<'_>>::default();
364
365        // If n is 1, the plaintext is encrypted as a single AES block
366        if n == 1 {
367            block.copy_from_slice(data);
368
369            self.cipher.decrypt_block(block);
370            out.copy_from_slice(&block[IV_LEN..]);
371        } else {
372            block[..IV_LEN].copy_from_slice(&data[..IV_LEN]);
373
374            //   for i = 1 to n: R[i] = C[i]
375            out.copy_from_slice(&data[IV_LEN..]);
376
377            // 1.2) Calculate intermediate values
378
379            self.cipher
380                .decrypt_with_backend(WInverseCtx { n, block, out });
381        }
382
383        // 2) AIV verification
384
385        // Checks as defined in RFC5649 § 3
386
387        if block[..IV_LEN / 2] != KWP_IV_PREFIX {
388            return Err(Error::IntegrityCheckFailed);
389        }
390
391        let mli = u32::from_be_bytes(block[IV_LEN / 2..IV_LEN].try_into().unwrap()) as usize;
392        if !(SEMIBLOCK_SIZE * (n - 1) < mli && mli <= SEMIBLOCK_SIZE * n) {
393            return Err(Error::IntegrityCheckFailed);
394        }
395
396        let b = SEMIBLOCK_SIZE * n - mli;
397        if !out.iter().rev().take(b).all(|&x| x == 0) {
398            return Err(Error::IntegrityCheckFailed);
399        }
400
401        // 3) Output the results
402
403        Ok(&out[..mli])
404    }
405
406    /// Computes [`Self::unwrap`], allocating a [`Vec`] for the return value.
407    #[cfg(feature = "alloc")]
408    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
409    pub fn unwrap_with_padding_vec(&self, data: &[u8]) -> Result<Vec<u8>> {
410        let out_len = data
411            .len()
412            .checked_sub(IV_LEN)
413            .ok_or(Error::InvalidDataSize)?;
414
415        let mut out = vec![0u8; out_len];
416        let out_len = self.unwrap_with_padding(data, &mut out)?.len();
417        out.truncate(out_len);
418        Ok(out)
419    }
420}
421
422struct WCtx<'a> {
423    n: usize,
424    block: &'a mut Block<Self>,
425    out: &'a mut [u8],
426}
427
428impl<'a> BlockSizeUser for WCtx<'a> {
429    type BlockSize = U16;
430}
431
432/// Very similar to the W(S) function defined by NIST in SP 800-38F,
433/// Section 6.1
434impl<'a> BlockClosure for WCtx<'a> {
435    #[inline(always)]
436    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
437        for j in 0..=5 {
438            for (i, chunk) in self.out.chunks_mut(SEMIBLOCK_SIZE).skip(1).enumerate() {
439                // A | R[i]
440                self.block[IV_LEN..].copy_from_slice(chunk);
441                // B = AES(K, ..)
442                backend.proc_block(self.block.into());
443
444                // A = MSB(64, B) ^ t
445                let t = (self.n * j + (i + 1)) as u64;
446                for (ai, ti) in self.block[..IV_LEN].iter_mut().zip(&t.to_be_bytes()) {
447                    *ai ^= ti;
448                }
449
450                // R[i] = LSB(64, B)
451                chunk.copy_from_slice(&self.block[IV_LEN..]);
452            }
453        }
454    }
455}
456
457struct WInverseCtx<'a> {
458    n: usize,
459    block: &'a mut Block<Self>,
460    out: &'a mut [u8],
461}
462
463impl<'a> BlockSizeUser for WInverseCtx<'a> {
464    type BlockSize = U16;
465}
466
467/// Very similar to the W^-1(S) function defined by NIST in SP 800-38F,
468/// Section 6.1
469impl<'a> BlockClosure for WInverseCtx<'a> {
470    #[inline(always)]
471    fn call<B: BlockBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B) {
472        for j in (0..=5).rev() {
473            for (i, chunk) in self.out.chunks_mut(SEMIBLOCK_SIZE).enumerate().rev() {
474                // A ^ t
475                let t = (self.n * j + (i + 1)) as u64;
476                for (ai, ti) in self.block[..IV_LEN].iter_mut().zip(&t.to_be_bytes()) {
477                    *ai ^= ti;
478                }
479
480                // (A ^ t) | R[i]
481                self.block[IV_LEN..].copy_from_slice(chunk);
482
483                // B = AES-1(K, ..)
484                backend.proc_block(self.block.into());
485
486                // A = MSB(64, B)
487                // already set
488
489                // R[i] = LSB(64, B)
490                chunk.copy_from_slice(&self.block[IV_LEN..]);
491            }
492        }
493    }
494}