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> {}