1use crate::aws_lc::{AES_set_decrypt_key, AES_set_encrypt_key, AES_KEY};
5#[cfg(feature = "legacy-des")]
6use crate::aws_lc::{DES_cblock, DES_key_schedule, DES_set_key};
7use crate::cipher::block::Block;
8use crate::cipher::chacha::ChaCha20Key;
9#[cfg(feature = "legacy-des")]
10use crate::cipher::des::{DesKey, DES_EDE3_KEY_LEN, DES_EDE_KEY_LEN, DES_KEY_LEN};
11use crate::cipher::{AES_128_KEY_LEN, AES_192_KEY_LEN, AES_256_KEY_LEN};
12#[cfg(feature = "legacy-des")]
13use crate::constant_time;
14use crate::error::{KeyRejected, Unspecified};
15use core::mem::{size_of, MaybeUninit};
16use core::ptr::copy_nonoverlapping;
17use std::os::raw::c_uint;
20use zeroize::Zeroize;
21
22pub(crate) enum SymmetricCipherKey {
23 Aes128 {
24 enc_key: AES_KEY,
25 dec_key: AES_KEY,
26 },
27 Aes192 {
28 enc_key: AES_KEY,
29 dec_key: AES_KEY,
30 },
31 Aes256 {
32 enc_key: AES_KEY,
33 dec_key: AES_KEY,
34 },
35 ChaCha20 {
36 raw_key: ChaCha20Key,
37 },
38 #[cfg(feature = "legacy-des")]
39 Des {
40 key: DesKey,
41 },
42 #[cfg(feature = "legacy-des")]
43 DesEde {
44 key: DesKey,
45 },
46 #[cfg(feature = "legacy-des")]
47 DesEde3 {
48 key: DesKey,
49 },
50}
51
52unsafe impl Send for SymmetricCipherKey {}
53
54unsafe impl Sync for SymmetricCipherKey {}
56
57impl Drop for SymmetricCipherKey {
58 fn drop(&mut self) {
59 match self {
61 SymmetricCipherKey::Aes128 { enc_key, dec_key }
62 | SymmetricCipherKey::Aes192 { enc_key, dec_key }
63 | SymmetricCipherKey::Aes256 { enc_key, dec_key } => unsafe {
64 let enc_bytes: &mut [u8; size_of::<AES_KEY>()] = (enc_key as *mut AES_KEY)
65 .cast::<[u8; size_of::<AES_KEY>()]>()
66 .as_mut()
67 .unwrap();
68 enc_bytes.zeroize();
69 let dec_bytes: &mut [u8; size_of::<AES_KEY>()] = (dec_key as *mut AES_KEY)
70 .cast::<[u8; size_of::<AES_KEY>()]>()
71 .as_mut()
72 .unwrap();
73 dec_bytes.zeroize();
74 },
75 SymmetricCipherKey::ChaCha20 { .. } => {}
76 #[cfg(feature = "legacy-des")]
77 SymmetricCipherKey::Des { key }
78 | SymmetricCipherKey::DesEde { key }
79 | SymmetricCipherKey::DesEde3 { key } => unsafe {
80 let key_bytes: &mut [u8; size_of::<DesKey>()] = (key as *mut DesKey)
81 .cast::<[u8; size_of::<DesKey>()]>()
82 .as_mut()
83 .unwrap();
84 key_bytes.zeroize();
85 },
86 }
87 }
88}
89
90impl SymmetricCipherKey {
91 fn aes(key_bytes: &[u8]) -> Result<(AES_KEY, AES_KEY), Unspecified> {
92 let mut enc_key = MaybeUninit::<AES_KEY>::uninit();
93 let mut dec_key = MaybeUninit::<AES_KEY>::uninit();
94 #[allow(clippy::cast_possible_truncation)]
95 if unsafe {
96 0 != AES_set_encrypt_key(
97 key_bytes.as_ptr(),
98 (key_bytes.len() * 8) as c_uint,
99 enc_key.as_mut_ptr(),
100 )
101 } {
102 return Err(Unspecified);
103 }
104
105 #[allow(clippy::cast_possible_truncation)]
106 if unsafe {
107 0 != AES_set_decrypt_key(
108 key_bytes.as_ptr(),
109 (key_bytes.len() * 8) as c_uint,
110 dec_key.as_mut_ptr(),
111 )
112 } {
113 return Err(Unspecified);
114 }
115 unsafe { Ok((enc_key.assume_init(), dec_key.assume_init())) }
116 }
117
118 pub(crate) fn aes128(key_bytes: &[u8]) -> Result<Self, KeyRejected> {
119 if key_bytes.len() != AES_128_KEY_LEN {
120 return Err(KeyRejected::unspecified());
121 }
122 let (enc_key, dec_key) = SymmetricCipherKey::aes(key_bytes)?;
123 Ok(SymmetricCipherKey::Aes128 { enc_key, dec_key })
124 }
125
126 pub(crate) fn aes192(key_bytes: &[u8]) -> Result<Self, KeyRejected> {
127 if key_bytes.len() != AES_192_KEY_LEN {
128 return Err(KeyRejected::unspecified());
129 }
130 let (enc_key, dec_key) = SymmetricCipherKey::aes(key_bytes)?;
131 Ok(SymmetricCipherKey::Aes192 { enc_key, dec_key })
132 }
133
134 pub(crate) fn aes256(key_bytes: &[u8]) -> Result<Self, KeyRejected> {
135 if key_bytes.len() != AES_256_KEY_LEN {
136 return Err(KeyRejected::unspecified());
137 }
138 let (enc_key, dec_key) = SymmetricCipherKey::aes(key_bytes)?;
139 Ok(SymmetricCipherKey::Aes256 { enc_key, dec_key })
140 }
141
142 pub(crate) fn chacha20(key_bytes: &[u8]) -> Result<Self, KeyRejected> {
143 if key_bytes.len() != 32 {
144 return Err(KeyRejected::unspecified());
145 }
146 let mut kb = MaybeUninit::<[u8; 32]>::uninit();
147 unsafe {
148 copy_nonoverlapping(key_bytes.as_ptr(), kb.as_mut_ptr().cast(), 32);
149 Ok(SymmetricCipherKey::ChaCha20 {
150 raw_key: ChaCha20Key(kb.assume_init()),
151 })
152 }
153 }
154
155 #[cfg(feature = "legacy-des")]
162 pub(crate) fn prepare_des(key_bytes: &[u8]) -> Result<[DES_key_schedule; 3], KeyRejected> {
163 if key_bytes.len() != DES_KEY_LEN {
164 return Err(KeyRejected::unspecified());
165 }
166 let k: &[u8; 8] = key_bytes.try_into().expect("length already checked");
167 let ks = Self::des_set_key(k)?;
168 let zero = MaybeUninit::<DES_key_schedule>::zeroed();
169 unsafe { Ok([ks, zero.assume_init(), zero.assume_init()]) }
170 }
171
172 #[cfg(feature = "legacy-des")]
173 pub(crate) fn des(key_bytes: &[u8]) -> Result<Self, KeyRejected> {
174 Ok(SymmetricCipherKey::Des {
175 key: DesKey(Self::prepare_des(key_bytes)?),
176 })
177 }
178
179 #[cfg(feature = "legacy-des")]
186 pub(crate) fn prepare_des_ede(key_bytes: &[u8]) -> Result<[DES_key_schedule; 3], KeyRejected> {
187 if key_bytes.len() != DES_EDE_KEY_LEN {
188 return Err(KeyRejected::unspecified());
189 }
190 let first_key: &[u8; 8] = key_bytes[0..8].try_into().expect("length already checked");
193 let second_key: &[u8; 8] = key_bytes[8..16].try_into().expect("length already checked");
194
195 if constant_time::verify_slices_are_equal(first_key, second_key).is_ok() {
198 return Err(KeyRejected::inconsistent_components());
199 }
200
201 let ks1 = Self::des_set_key(first_key)?;
205 let ks2 = Self::des_set_key(second_key)?;
206 Ok([ks1, ks2, ks1])
207 }
208
209 #[cfg(feature = "legacy-des")]
210 pub(crate) fn des_ede(key_bytes: &[u8]) -> Result<Self, KeyRejected> {
211 Ok(SymmetricCipherKey::DesEde {
212 key: DesKey(Self::prepare_des_ede(key_bytes)?),
213 })
214 }
215
216 #[cfg(feature = "legacy-des")]
217 fn des_set_key(key_bytes: &[u8; 8]) -> Result<DES_key_schedule, KeyRejected> {
218 let mut ks = MaybeUninit::<DES_key_schedule>::uninit();
219 match unsafe { DES_set_key(key_bytes.as_ptr() as *const DES_cblock, ks.as_mut_ptr()) } {
226 0 | -1 => Ok(unsafe { ks.assume_init() }),
227 _ => Err(KeyRejected::unspecified()),
228 }
229 }
230
231 #[cfg(feature = "legacy-des")]
237 pub(crate) fn prepare_des_ede3(key_bytes: &[u8]) -> Result<[DES_key_schedule; 3], KeyRejected> {
238 if key_bytes.len() != DES_EDE3_KEY_LEN {
239 return Err(KeyRejected::unspecified());
240 }
241 let first_key: &[u8; 8] = key_bytes[0..8].try_into().expect("length already checked");
244 let second_key: &[u8; 8] = key_bytes[8..16].try_into().expect("length already checked");
245 let third_key: &[u8; 8] = key_bytes[16..24]
246 .try_into()
247 .expect("length already checked");
248
249 let mut any_equal: u8 = 0;
256 for (a, b) in [
257 (first_key, second_key),
258 (second_key, third_key),
259 (first_key, third_key),
260 ] {
261 any_equal |= u8::from(constant_time::verify_slices_are_equal(a, b).is_ok());
262 any_equal = core::hint::black_box(any_equal);
263 }
264 if any_equal != 0 {
265 return Err(KeyRejected::inconsistent_components());
266 }
267
268 Ok([
269 Self::des_set_key(first_key)?,
270 Self::des_set_key(second_key)?,
271 Self::des_set_key(third_key)?,
272 ])
273 }
274
275 #[cfg(feature = "legacy-des")]
276 pub(crate) fn des_ede3(key_bytes: &[u8]) -> Result<Self, KeyRejected> {
277 Ok(SymmetricCipherKey::DesEde3 {
278 key: DesKey(Self::prepare_des_ede3(key_bytes)?),
279 })
280 }
281
282 #[allow(dead_code)]
283 #[inline]
284 pub(crate) fn encrypt_block(&self, block: Block) -> Block {
285 match self {
286 SymmetricCipherKey::Aes128 { enc_key, .. }
287 | SymmetricCipherKey::Aes192 { enc_key, .. }
288 | SymmetricCipherKey::Aes256 { enc_key, .. } => {
289 super::aes::encrypt_block(enc_key, block)
290 }
291 SymmetricCipherKey::ChaCha20 { .. } => {
292 panic!("Unsupported algorithm!")
293 }
294 #[cfg(feature = "legacy-des")]
295 SymmetricCipherKey::Des { .. }
296 | SymmetricCipherKey::DesEde { .. }
297 | SymmetricCipherKey::DesEde3 { .. } => {
298 panic!("Unsupported algorithm!")
299 }
300 }
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use crate::cipher::block::{Block, BLOCK_LEN};
307 use crate::cipher::key::SymmetricCipherKey;
308 use crate::test::from_hex;
309
310 #[test]
311 fn test_encrypt_block_aes_128() {
312 let key = from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
313 let input = from_hex("00112233445566778899aabbccddeeff").unwrap();
314 let expected_result = from_hex("69c4e0d86a7b0430d8cdb78070b4c55a").unwrap();
315 let input_block: [u8; BLOCK_LEN] = <[u8; BLOCK_LEN]>::try_from(input).unwrap();
316
317 let aes128 = SymmetricCipherKey::aes128(key.as_slice()).unwrap();
318 let result = aes128.encrypt_block(Block::from(input_block));
319
320 assert_eq!(expected_result.as_slice(), result.as_ref());
321 }
322
323 #[test]
324 fn test_encrypt_block_aes_256() {
325 let key =
326 from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap();
327 let input = from_hex("00112233445566778899aabbccddeeff").unwrap();
328 let expected_result = from_hex("8ea2b7ca516745bfeafc49904b496089").unwrap();
329 let input_block: [u8; BLOCK_LEN] = <[u8; BLOCK_LEN]>::try_from(input).unwrap();
330
331 let aes128 = SymmetricCipherKey::aes256(key.as_slice()).unwrap();
332 let result = aes128.encrypt_block(Block::from(input_block));
333
334 assert_eq!(expected_result.as_slice(), result.as_ref());
335 }
336}