Skip to main content

aes_kw/
kw.rs

1use core::ops::{Add, Rem};
2
3use crate::{Error, IV_LEN, IvLen, ctx::Ctx, error::IntegrityCheckFailed};
4use aes::cipher::{
5    Array, Block, BlockCipherDecrypt, BlockCipherEncrypt,
6    array::ArraySize,
7    common::{InnerInit, InnerUser},
8    typenum::{Mod, NonZero, Sum, U16, Zero},
9};
10
11/// Default Initial Value for AES-KW as defined in RFC3394 ยง 2.2.3.1.
12///
13/// <https://datatracker.ietf.org/doc/html/rfc3394#section-2.2.3.1>
14///
15/// ```text
16/// The default initial value (IV) is defined to be the hexadecimal
17/// constant:
18///
19///     A[0] = IV = A6A6A6A6A6A6A6A6
20///
21/// The use of a constant as the IV supports a strong integrity check on
22/// the key data during the period that it is wrapped.  If unwrapping
23/// produces A[0] = A6A6A6A6A6A6A6A6, then the chance that the key data
24/// is corrupt is 2^-64.  If unwrapping produces A[0] any other value,
25/// then the unwrap must return an error and not return any key data.
26/// ```
27const IV: [u8; IV_LEN] = [0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6];
28
29/// Type alias representing wrapped key roughly equivalent to `[u8; N + IV_LEN]`.
30pub type KwWrappedKey<N> = Array<u8, Sum<N, IvLen>>;
31
32/// AES Key Wrapper (KW), as defined in [RFC 3394].
33///
34/// [RFC 3394]: https://www.rfc-editor.org/rfc/rfc3394.txt
35#[derive(Debug, Clone, Copy, PartialEq)]
36pub struct AesKw<C> {
37    cipher: C,
38}
39
40impl<C> InnerUser for AesKw<C> {
41    type Inner = C;
42}
43
44impl<C> InnerInit for AesKw<C> {
45    #[inline]
46    fn inner_init(cipher: Self::Inner) -> Self {
47        AesKw { cipher }
48    }
49}
50
51impl<C: BlockCipherEncrypt<BlockSize = U16>> AesKw<C> {
52    /// Wrap key into `buf` assuming that it has correct length.
53    fn wrap_key_trusted(&self, key: &[u8], buf: &mut [u8]) {
54        let blocks_len = key.len() / IV_LEN;
55
56        // 1) Initialize variables
57
58        // Set A to the IV
59        let block = &mut Block::<C>::default();
60        block[..IV_LEN].copy_from_slice(&IV);
61
62        // 2) Calculate intermediate values
63        buf[IV_LEN..].copy_from_slice(key);
64
65        self.cipher.encrypt_with_backend(Ctx {
66            blocks_len,
67            block,
68            buf,
69        });
70
71        // 3) Output the results
72        buf[..IV_LEN].copy_from_slice(&block[..IV_LEN]);
73    }
74
75    /// Wrap `key` and write result to `buf`.
76    ///
77    /// Returns slice which points to `buf` and contains wrapped data.
78    ///
79    /// Length of `data` must be multiple of [`IV_LEN`] and bigger than zero.
80    /// Length of `buf` must be bigger or equal to `data.len() + IV_LEN`.
81    #[inline]
82    pub fn wrap_key<'a>(&self, key: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> {
83        let blocks_rem = key.len() % IV_LEN;
84        if blocks_rem != 0 {
85            return Err(Error::InvalidDataSize);
86        }
87
88        let expected_len = key.len() + IV_LEN;
89        let buf = buf
90            .get_mut(..expected_len)
91            .ok_or(Error::InvalidOutputSize { expected_len })?;
92
93        self.wrap_key_trusted(key, buf);
94
95        Ok(buf)
96    }
97
98    /// Wrap fixed-size key `key` and return wrapped key.
99    ///
100    /// This method is roughly equivalent to:
101    /// ```ignore
102    /// const fn check_key_size(n: usize) -> usize {
103    ///     assert!(n != 0 && n % IV_LEN == 0);
104    ///     0
105    /// }
106    ///
107    /// pub fn wrap_fixed_key<const N: usize>(
108    ///     &self,
109    ///     key: &[u8; N],
110    /// ) -> [u8; N + IV_LEN]
111    /// where
112    ///     [(); check_key_size(N)]: Sized,
113    /// { ... }
114    /// ```
115    /// but uses [`hybrid_array::Array`][Array] instead of built-in arrays
116    /// to work around current limitations of the const generics system.
117    #[inline]
118    pub fn wrap_fixed_key<N>(&self, key: &Array<u8, N>) -> KwWrappedKey<N>
119    where
120        N: ArraySize + NonZero + Add<IvLen> + Rem<IvLen>,
121        Sum<N, IvLen>: ArraySize,
122        Mod<N, IvLen>: Zero,
123    {
124        let mut buf = KwWrappedKey::<N>::default();
125        self.wrap_key_trusted(key, &mut buf);
126        buf
127    }
128}
129
130impl<C: BlockCipherDecrypt<BlockSize = U16>> AesKw<C> {
131    /// Unwrap key into `buf` assuming that it has correct length.
132    fn unwrap_key_trusted<'a>(
133        &self,
134        wkey: &[u8],
135        buf: &'a mut [u8],
136    ) -> Result<&'a [u8], IntegrityCheckFailed> {
137        let blocks_len = buf.len() / IV_LEN;
138
139        // 1) Initialize variables
140
141        let block = &mut Block::<C>::default();
142        block[..IV_LEN].copy_from_slice(&wkey[..IV_LEN]);
143
144        //   for i = 1 to n: R[i] = C[i]
145        buf.copy_from_slice(&wkey[IV_LEN..]);
146
147        // 2) Calculate intermediate values
148
149        self.cipher.decrypt_with_backend(Ctx {
150            blocks_len,
151            block,
152            buf,
153        });
154
155        // 3) Output the results
156
157        let expected_iv = u64::from_ne_bytes(IV);
158        let calc_iv = u64::from_ne_bytes(block[..IV_LEN].try_into().unwrap());
159        if calc_iv == expected_iv {
160            Ok(buf)
161        } else {
162            buf.fill(0);
163            Err(IntegrityCheckFailed)
164        }
165    }
166
167    /// Unwrap `data` and write result to `buf`.
168    ///
169    /// Returns slice which points to `buf` and contains unwrapped data.
170    ///
171    /// Length of `data` must be multiple of [`IV_LEN`] and bigger than zero.
172    /// Length of `buf` must be bigger or equal to `data.len()`.
173    #[inline]
174    pub fn unwrap_key<'a>(&self, wkey: &[u8], buf: &'a mut [u8]) -> Result<&'a [u8], Error> {
175        let blocks_len = wkey.len() / IV_LEN;
176        let blocks_rem = wkey.len() % IV_LEN;
177        if blocks_rem != 0 || blocks_len < 1 {
178            return Err(Error::InvalidDataSize);
179        }
180
181        let blocks_len = blocks_len - 1;
182        let expected_len = blocks_len * IV_LEN;
183        let buf = buf
184            .get_mut(..expected_len)
185            .ok_or(Error::InvalidOutputSize { expected_len })?;
186
187        self.unwrap_key_trusted(wkey, buf)
188            .map_err(|_| Error::IntegrityCheckFailed)?;
189
190        Ok(buf)
191    }
192
193    /// Unwrap key in `data` and return unwrapped key.
194    ///
195    /// This method is roughly equivalent to:
196    /// ```ignore
197    /// const fn check_key_size(n: usize) -> usize {
198    ///     assert!(n != 0 && n % IV_LEN == 0);
199    ///     0
200    /// }
201    ///
202    /// fn unwrap_fixed_key<const N: usize>(
203    ///     &self,
204    ///     data: &[u8; N + IV_LEN],
205    /// ) -> [u8; N]
206    /// where
207    ///     [(); check_key_size(N)]: Sized,
208    /// { ... }
209    /// ```
210    /// but uses [`hybrid_array::Array`][Array] instead of built-in arrays
211    /// to work around current limitations of the const generics system.
212    #[inline]
213    pub fn unwrap_fixed_key<N>(
214        &self,
215        wkey: &KwWrappedKey<N>,
216    ) -> Result<Array<u8, N>, IntegrityCheckFailed>
217    where
218        N: ArraySize + NonZero + Add<IvLen> + Rem<IvLen>,
219        Sum<N, IvLen>: ArraySize,
220        Mod<N, IvLen>: Zero,
221    {
222        let mut buf = Array::<u8, N>::default();
223        self.unwrap_key_trusted(wkey, &mut buf)?;
224        Ok(buf)
225    }
226}
227
228#[cfg(feature = "zeroize")]
229impl<C: zeroize::ZeroizeOnDrop> zeroize::ZeroizeOnDrop for AesKw<C> {}