1use std::{cmp::max, pin::Pin};
2
3use base64::{engine::general_purpose::STANDARD, Engine};
4use coset::{iana::KeyOperation, CborSerializable, RegisteredLabelWithPrivate};
5use generic_array::GenericArray;
6use rand::Rng;
7#[cfg(test)]
8use rand::SeedableRng;
9#[cfg(test)]
10use rand_chacha::ChaChaRng;
11#[cfg(test)]
12use sha2::Digest;
13use subtle::{Choice, ConstantTimeEq};
14use typenum::U32;
15use zeroize::{Zeroize, ZeroizeOnDrop};
16
17use super::{
18 key_encryptable::CryptoKey,
19 key_id::{KeyId, KEY_ID_SIZE},
20};
21use crate::{cose, CryptoError};
22
23#[derive(ZeroizeOnDrop, Clone)]
27pub struct Aes256CbcKey {
28 pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
30}
31
32impl ConstantTimeEq for Aes256CbcKey {
33 fn ct_eq(&self, other: &Self) -> Choice {
34 self.enc_key.ct_eq(&other.enc_key)
35 }
36}
37
38impl PartialEq for Aes256CbcKey {
39 fn eq(&self, other: &Self) -> bool {
40 self.ct_eq(other).into()
41 }
42}
43
44#[derive(ZeroizeOnDrop, Clone)]
47pub struct Aes256CbcHmacKey {
48 pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
50 pub(crate) mac_key: Pin<Box<GenericArray<u8, U32>>>,
52}
53
54impl ConstantTimeEq for Aes256CbcHmacKey {
55 fn ct_eq(&self, other: &Self) -> Choice {
56 self.enc_key.ct_eq(&other.enc_key) & self.mac_key.ct_eq(&other.mac_key)
57 }
58}
59
60impl PartialEq for Aes256CbcHmacKey {
61 fn eq(&self, other: &Self) -> bool {
62 self.ct_eq(other).into()
63 }
64}
65
66#[derive(Zeroize, Clone)]
71pub struct XChaCha20Poly1305Key {
72 pub(crate) key_id: [u8; KEY_ID_SIZE],
73 pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
74}
75
76impl ConstantTimeEq for XChaCha20Poly1305Key {
77 fn ct_eq(&self, other: &Self) -> Choice {
78 self.enc_key.ct_eq(&other.enc_key) & self.key_id.ct_eq(&other.key_id)
79 }
80}
81
82impl PartialEq for XChaCha20Poly1305Key {
83 fn eq(&self, other: &Self) -> bool {
84 self.ct_eq(other).into()
85 }
86}
87
88#[derive(ZeroizeOnDrop, Clone)]
90pub enum SymmetricCryptoKey {
91 Aes256CbcKey(Aes256CbcKey),
92 Aes256CbcHmacKey(Aes256CbcHmacKey),
93 XChaCha20Poly1305Key(XChaCha20Poly1305Key),
96}
97
98impl SymmetricCryptoKey {
99 const AES256_CBC_KEY_LEN: usize = 32;
101 const AES256_CBC_HMAC_KEY_LEN: usize = 64;
103
104 pub(crate) fn make_aes256_cbc_hmac_key_internal(
110 mut rng: impl rand::RngCore + rand::CryptoRng,
111 ) -> Self {
112 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
113 let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
114
115 rng.fill(enc_key.as_mut_slice());
116 rng.fill(mac_key.as_mut_slice());
117
118 Self::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
119 }
120
121 pub fn make_aes256_cbc_hmac_key() -> Self {
123 let rng = rand::thread_rng();
124 Self::make_aes256_cbc_hmac_key_internal(rng)
125 }
126
127 pub fn make_xchacha20_poly1305_key() -> Self {
129 let mut rng = rand::thread_rng();
130 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
131 rng.fill(enc_key.as_mut_slice());
132 Self::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
133 enc_key,
134 key_id: KeyId::make().into(),
135 })
136 }
137
138 pub fn to_encoded(&self) -> Vec<u8> {
147 let mut encoded_key = self.to_encoded_raw();
148 match self {
149 Self::Aes256CbcKey(_) | Self::Aes256CbcHmacKey(_) => encoded_key,
150 Self::XChaCha20Poly1305Key(_) => {
151 pad_key(&mut encoded_key, Self::AES256_CBC_HMAC_KEY_LEN + 1);
152 encoded_key
153 }
154 }
155 }
156
157 #[cfg(test)]
160 pub fn generate_seeded_for_unit_tests(seed: &str) -> Self {
161 let mut seeded_rng = ChaChaRng::from_seed(sha2::Sha256::digest(seed.as_bytes()).into());
163 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
164 let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
165
166 seeded_rng.fill(enc_key.as_mut_slice());
167 seeded_rng.fill(mac_key.as_mut_slice());
168
169 SymmetricCryptoKey::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
170 }
171
172 pub(crate) fn to_encoded_raw(&self) -> Vec<u8> {
184 match self {
185 Self::Aes256CbcKey(key) => key.enc_key.to_vec(),
186 Self::Aes256CbcHmacKey(key) => {
187 let mut buf = Vec::with_capacity(64);
188 buf.extend_from_slice(&key.enc_key);
189 buf.extend_from_slice(&key.mac_key);
190 buf
191 }
192 Self::XChaCha20Poly1305Key(key) => {
193 let builder = coset::CoseKeyBuilder::new_symmetric_key(key.enc_key.to_vec());
194 let mut cose_key = builder
195 .key_id(key.key_id.to_vec())
196 .add_key_op(KeyOperation::Decrypt)
197 .add_key_op(KeyOperation::Encrypt)
198 .add_key_op(KeyOperation::WrapKey)
199 .add_key_op(KeyOperation::UnwrapKey)
200 .build();
201 cose_key.alg = Some(RegisteredLabelWithPrivate::PrivateUse(
202 cose::XCHACHA20_POLY1305,
203 ));
204 cose_key
205 .to_vec()
206 .expect("cose key serialization should not fail")
207 }
208 }
209 }
210
211 pub fn to_base64(&self) -> String {
212 STANDARD.encode(self.to_encoded())
213 }
214}
215
216impl ConstantTimeEq for SymmetricCryptoKey {
217 fn ct_eq(&self, other: &SymmetricCryptoKey) -> Choice {
221 use SymmetricCryptoKey::*;
222 match (self, other) {
223 (Aes256CbcKey(a), Aes256CbcKey(b)) => a.ct_eq(b),
224 (Aes256CbcKey(_), _) => Choice::from(0),
225
226 (Aes256CbcHmacKey(a), Aes256CbcHmacKey(b)) => a.ct_eq(b),
227 (Aes256CbcHmacKey(_), _) => Choice::from(0),
228
229 (XChaCha20Poly1305Key(a), XChaCha20Poly1305Key(b)) => a.ct_eq(b),
230 (XChaCha20Poly1305Key(_), _) => Choice::from(0),
231 }
232 }
233}
234
235impl PartialEq for SymmetricCryptoKey {
236 fn eq(&self, other: &Self) -> bool {
237 self.ct_eq(other).into()
238 }
239}
240
241impl TryFrom<String> for SymmetricCryptoKey {
242 type Error = CryptoError;
243
244 fn try_from(value: String) -> Result<Self, Self::Error> {
245 let b = STANDARD
246 .decode(value)
247 .map_err(|_| CryptoError::InvalidKey)?;
248 Self::try_from(b)
249 }
250}
251
252impl TryFrom<Vec<u8>> for SymmetricCryptoKey {
253 type Error = CryptoError;
254
255 fn try_from(mut value: Vec<u8>) -> Result<Self, Self::Error> {
256 Self::try_from(value.as_mut_slice())
257 }
258}
259
260impl TryFrom<&mut [u8]> for SymmetricCryptoKey {
261 type Error = CryptoError;
262
263 fn try_from(value: &mut [u8]) -> Result<Self, Self::Error> {
266 let result = if value.len() == Self::AES256_CBC_HMAC_KEY_LEN {
271 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
272 let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
273
274 enc_key.copy_from_slice(&value[..32]);
275 mac_key.copy_from_slice(&value[32..]);
276
277 Ok(Self::Aes256CbcHmacKey(Aes256CbcHmacKey {
278 enc_key,
279 mac_key,
280 }))
281 } else if value.len() == Self::AES256_CBC_KEY_LEN {
282 let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
283
284 enc_key.copy_from_slice(&value[..Self::AES256_CBC_KEY_LEN]);
285
286 Ok(Self::Aes256CbcKey(Aes256CbcKey { enc_key }))
287 } else if value.len() > Self::AES256_CBC_HMAC_KEY_LEN {
288 let unpadded_value = unpad_key(value)?;
289 let cose_key =
290 coset::CoseKey::from_slice(unpadded_value).map_err(|_| CryptoError::InvalidKey)?;
291 SymmetricCryptoKey::try_from(&cose_key)
292 } else {
293 Err(CryptoError::InvalidKeyLen)
294 };
295
296 value.zeroize();
297 result
298 }
299}
300
301impl CryptoKey for SymmetricCryptoKey {}
302
303impl std::fmt::Debug for SymmetricCryptoKey {
305 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306 f.debug_struct("SymmetricCryptoKey")
307 .field(
308 "inner_type",
309 match self {
310 SymmetricCryptoKey::Aes256CbcKey(key) => key,
311 SymmetricCryptoKey::Aes256CbcHmacKey(key) => key,
312 SymmetricCryptoKey::XChaCha20Poly1305Key(key) => key,
313 },
314 )
315 .finish()
316 }
317}
318
319impl std::fmt::Debug for Aes256CbcKey {
320 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321 f.debug_struct("Aes256CbcKey").finish()
322 }
323}
324
325impl std::fmt::Debug for Aes256CbcHmacKey {
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327 f.debug_struct("Aes256CbcHmacKey").finish()
328 }
329}
330
331impl std::fmt::Debug for XChaCha20Poly1305Key {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 f.debug_struct("XChaCha20Poly1305Key")
334 .field("key_id", &self.key_id)
335 .finish()
336 }
337}
338
339fn pad_key(key_bytes: &mut Vec<u8>, min_length: usize) {
350 let pad_bytes = min_length.saturating_sub(key_bytes.len()).max(1);
352 let padded_length = max(min_length, key_bytes.len() + 1);
353 key_bytes.resize(padded_length, pad_bytes as u8);
354}
355
356fn unpad_key(key_bytes: &[u8]) -> Result<&[u8], CryptoError> {
367 let pad_len = *key_bytes.last().ok_or(CryptoError::InvalidKey)? as usize;
368 if pad_len >= key_bytes.len() {
369 return Err(CryptoError::InvalidKey);
370 }
371 Ok(key_bytes[..key_bytes.len() - pad_len].as_ref())
372}
373
374#[cfg(test)]
375pub fn derive_symmetric_key(name: &str) -> Aes256CbcHmacKey {
376 use zeroize::Zeroizing;
377
378 use crate::{derive_shareable_key, generate_random_bytes};
379
380 let secret: Zeroizing<[u8; 16]> = generate_random_bytes();
381 derive_shareable_key(secret, name, None)
382}
383
384#[cfg(test)]
385mod tests {
386 use base64::{engine::general_purpose::STANDARD, Engine};
387 use generic_array::GenericArray;
388 use typenum::U32;
389
390 use super::{derive_symmetric_key, SymmetricCryptoKey};
391 use crate::{
392 keys::symmetric_crypto_key::{pad_key, unpad_key},
393 Aes256CbcHmacKey, Aes256CbcKey, XChaCha20Poly1305Key,
394 };
395
396 #[test]
397 fn test_symmetric_crypto_key() {
398 let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
399 let key2 = SymmetricCryptoKey::try_from(key.to_base64()).unwrap();
400
401 assert_eq!(key, key2);
402
403 let key = "UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ==".to_string();
404 let key2 = SymmetricCryptoKey::try_from(key.clone()).unwrap();
405 assert_eq!(key, key2.to_base64());
406 }
407
408 #[test]
409 fn test_encode_decode_old_symmetric_crypto_key() {
410 let key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
411 let encoded = key.to_encoded();
412 let decoded = SymmetricCryptoKey::try_from(encoded).unwrap();
413 assert_eq!(key, decoded);
414 }
415
416 #[test]
417 fn test_decode_new_symmetric_crypto_key() {
418 let key_b64 = STANDARD.decode("pQEEAlDib+JxbqMBlcd3KTUesbufAzoAARFvBIQDBAUGIFggt79surJXmqhPhYuuqi9ZyPfieebmtw2OsmN5SDrb4yUB").unwrap();
419 let key = SymmetricCryptoKey::try_from(key_b64).unwrap();
420 match key {
421 SymmetricCryptoKey::XChaCha20Poly1305Key(_) => (),
422 _ => panic!("Invalid key type"),
423 }
424 }
425
426 #[test]
427 fn test_encode_xchacha20_poly1305_key() {
428 let key = SymmetricCryptoKey::make_xchacha20_poly1305_key();
429 let encoded = key.to_encoded();
430 let decoded = SymmetricCryptoKey::try_from(encoded).unwrap();
431 assert_eq!(key, decoded);
432 }
433
434 #[test]
435 fn test_pad_unpad_key_63() {
436 let original_key = vec![1u8; 63];
437 let mut key_bytes = original_key.clone();
438 let mut encoded_bytes = vec![1u8; 65];
439 encoded_bytes[63] = 2;
440 encoded_bytes[64] = 2;
441 pad_key(&mut key_bytes, 65);
442 assert_eq!(encoded_bytes, key_bytes);
443 let unpadded_key = unpad_key(&key_bytes).unwrap();
444 assert_eq!(original_key, unpadded_key);
445 }
446
447 #[test]
448 fn test_pad_unpad_key_64() {
449 let original_key = vec![1u8; 64];
450 let mut key_bytes = original_key.clone();
451 let mut encoded_bytes = vec![1u8; 65];
452 encoded_bytes[64] = 1;
453 pad_key(&mut key_bytes, 65);
454 assert_eq!(encoded_bytes, key_bytes);
455 let unpadded_key = unpad_key(&key_bytes).unwrap();
456 assert_eq!(original_key, unpadded_key);
457 }
458
459 #[test]
460 fn test_pad_unpad_key_65() {
461 let original_key = vec![1u8; 65];
462 let mut key_bytes = original_key.clone();
463 let mut encoded_bytes = vec![1u8; 66];
464 encoded_bytes[65] = 1;
465 pad_key(&mut key_bytes, 65);
466 assert_eq!(encoded_bytes, key_bytes);
467 let unpadded_key = unpad_key(&key_bytes).unwrap();
468 assert_eq!(original_key, unpadded_key);
469 }
470
471 #[test]
472 fn test_eq_aes_cbc_hmac() {
473 let key1 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
474 let key2 = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
475 assert_ne!(key1, key2);
476 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
477 assert_eq!(key1, key3);
478 }
479
480 #[test]
481 fn test_eq_aes_cbc() {
482 let key1 = SymmetricCryptoKey::try_from(vec![1u8; 32]).unwrap();
483 let key2 = SymmetricCryptoKey::try_from(vec![2u8; 32]).unwrap();
484 assert_ne!(key1, key2);
485 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
486 assert_eq!(key1, key3);
487 }
488
489 #[test]
490 fn test_eq_xchacha20_poly1305() {
491 let key1 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
492 let key2 = SymmetricCryptoKey::make_xchacha20_poly1305_key();
493 assert_ne!(key1, key2);
494 let key3 = SymmetricCryptoKey::try_from(key1.to_base64()).unwrap();
495 assert_eq!(key1, key3);
496 }
497
498 #[test]
499 fn test_neq_different_key_types() {
500 let key1 = SymmetricCryptoKey::Aes256CbcKey(Aes256CbcKey {
501 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
502 });
503 let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(XChaCha20Poly1305Key {
504 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
505 key_id: [0; 16],
506 });
507 assert_ne!(key1, key2);
508 }
509
510 #[test]
511 fn test_eq_variant_aes256_cbc() {
512 let key1 = Aes256CbcKey {
513 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
514 vec![1u8; 32].as_slice(),
515 )),
516 };
517 let key2 = Aes256CbcKey {
518 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
519 vec![1u8; 32].as_slice(),
520 )),
521 };
522 let key3 = Aes256CbcKey {
523 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
524 vec![2u8; 32].as_slice(),
525 )),
526 };
527 assert_eq!(key1, key2);
528 assert_ne!(key1, key3);
529 }
530
531 #[test]
532 fn test_eq_variant_aes256_cbc_hmac() {
533 let key1 = Aes256CbcHmacKey {
534 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
535 vec![1u8; 32].as_slice(),
536 )),
537 mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
538 vec![2u8; 32].as_slice(),
539 )),
540 };
541 let key2 = Aes256CbcHmacKey {
542 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
543 vec![1u8; 32].as_slice(),
544 )),
545 mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
546 vec![2u8; 32].as_slice(),
547 )),
548 };
549 let key3 = Aes256CbcHmacKey {
550 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
551 vec![3u8; 32].as_slice(),
552 )),
553 mac_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
554 vec![4u8; 32].as_slice(),
555 )),
556 };
557 assert_eq!(key1, key2);
558 assert_ne!(key1, key3);
559 }
560
561 #[test]
562 fn test_eq_variant_xchacha20_poly1305() {
563 let key1 = XChaCha20Poly1305Key {
564 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
565 vec![1u8; 32].as_slice(),
566 )),
567 key_id: [0; 16],
568 };
569 let key2 = XChaCha20Poly1305Key {
570 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
571 vec![1u8; 32].as_slice(),
572 )),
573 key_id: [0; 16],
574 };
575 let key3 = XChaCha20Poly1305Key {
576 enc_key: Box::pin(GenericArray::<u8, U32>::clone_from_slice(
577 vec![2u8; 32].as_slice(),
578 )),
579 key_id: [1; 16],
580 };
581 assert_eq!(key1, key2);
582 assert_ne!(key1, key3);
583 }
584
585 #[test]
586 fn test_neq_different_key_id() {
587 let key1 = XChaCha20Poly1305Key {
588 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
589 key_id: [0; 16],
590 };
591 let key2 = XChaCha20Poly1305Key {
592 enc_key: Box::pin(GenericArray::<u8, U32>::default()),
593 key_id: [1; 16],
594 };
595 assert_ne!(key1, key2);
596
597 let key1 = SymmetricCryptoKey::XChaCha20Poly1305Key(key1);
598 let key2 = SymmetricCryptoKey::XChaCha20Poly1305Key(key2);
599 assert_ne!(key1, key2);
600 }
601}