1#![allow(unused_assignments)]
25
26use std::fmt;
27
28use hmac::Hmac;
29pub use hmac::digest::MacError;
30use pbkdf2::pbkdf2;
31use rand::{
32 Rng,
33 distr::{Alphanumeric, SampleString},
34 rng,
35};
36use ruma::{
37 UInt,
38 events::{
39 GlobalAccountDataEventContent, GlobalAccountDataEventType,
40 secret::request::SecretName,
41 secret_storage::{
42 key::{
43 PassPhrase, SecretStorageEncryptionAlgorithm, SecretStorageKeyEventContent,
44 SecretStorageV1AesHmacSha2Properties,
45 },
46 secret::{
47 AesHmacSha2EncryptedData as RumaAesHmacSha2EncryptedData, SecretEncryptedData,
48 },
49 },
50 },
51 serde::{Base64, JsonCastable},
52};
53use serde::{Deserialize, Serialize, de::Error};
54use sha2::Sha512;
55use subtle::ConstantTimeEq;
56use thiserror::Error;
57use zeroize::{Zeroize, ZeroizeOnDrop};
58
59use crate::ciphers::{AesHmacSha2Key, HmacSha256Mac, IV_SIZE, KEY_SIZE, MAC_SIZE};
60
61#[derive(Debug, Error)]
69pub enum DecodeError {
70 #[error("The decoded secret storage key has an invalid prefix: expected {0:?}, got {1:?}")]
72 Prefix([u8; 2], [u8; 2]),
73 #[error("The parity byte of the secret storage key doesn't match: expected {0:?}, got {1:?}")]
75 Parity(u8, u8),
76 #[error(transparent)]
78 Base58(#[from] bs58::decode::Error),
79 #[error(transparent)]
81 Base64(#[from] vodozemac::Base64DecodeError),
82 #[error("The Base58 decoded key has an invalid length, expected {0}, got {1}")]
84 KeyLength(usize, usize),
85 #[error("The MAC check for the secret storage key failed")]
87 Mac(#[from] MacError),
88 #[error(
91 "The MAC of for the secret storage MAC check has an incorrect length, \
92 expected: {0}, got: {1}"
93 )]
94 MacLength(usize, usize),
95 #[error(
98 "The IV of for the secret storage key MAC check has an incorrect length, \
99 expected: {0}, got: {1}"
100 )]
101 IvLength(usize, usize),
102 #[error("The secret storage key is using an unsupported secret encryption algorithm: {0}")]
108 UnsupportedAlgorithm(String),
109 #[error(
112 "The passphrase-based secret storage key has an excessively high KDF iteration count: {0}"
113 )]
114 KdfIterationCount(UInt),
115}
116
117#[derive(Zeroize, ZeroizeOnDrop)]
154pub struct SecretStorageKey {
155 #[zeroize(skip)]
159 storage_key_info: SecretStorageKeyEventContent,
160 secret_key: Box<[u8; 32]>,
162}
163
164#[cfg(not(tarpaulin_include))]
165impl fmt::Debug for SecretStorageKey {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 f.debug_struct("SecretStorageKey")
168 .field("storage_key_info", &self.storage_key_info)
169 .finish_non_exhaustive()
170 }
171}
172
173#[derive(Clone, Debug, Deserialize, Serialize)]
175#[serde(into = "RumaAesHmacSha2EncryptedData", try_from = "RumaAesHmacSha2EncryptedData")]
176pub struct AesHmacSha2EncryptedData {
177 pub iv: [u8; IV_SIZE],
179 pub ciphertext: Base64,
181 pub mac: [u8; MAC_SIZE],
184}
185
186impl TryFrom<RumaAesHmacSha2EncryptedData> for AesHmacSha2EncryptedData {
187 type Error = serde_json::Error;
188
189 fn try_from(value: RumaAesHmacSha2EncryptedData) -> Result<Self, Self::Error> {
190 let RumaAesHmacSha2EncryptedData { iv, ciphertext, mac, .. } = value;
191 let iv_length = iv.as_bytes().len();
192 let mac_length = mac.as_bytes().len();
193
194 if iv_length != IV_SIZE {
195 Err(serde_json::Error::custom(format!(
196 "Invalid initialization vector length, expected length {IV_SIZE}, got: {iv_length}",
197 )))
198 } else if mac_length != MAC_SIZE {
199 Err(serde_json::Error::custom(format!(
200 "Invalid message authentication tag length, expected length {MAC_SIZE}, got: {mac_length}",
201 )))
202 } else {
203 let mut mac_array = [0u8; MAC_SIZE];
204 let mut iv_array = [0u8; IV_SIZE];
205
206 mac_array.copy_from_slice(mac.as_bytes());
207 iv_array.copy_from_slice(iv.as_bytes());
208
209 Ok(Self { iv: iv_array, ciphertext, mac: mac_array })
210 }
211 }
212}
213
214impl From<AesHmacSha2EncryptedData> for RumaAesHmacSha2EncryptedData {
215 fn from(value: AesHmacSha2EncryptedData) -> Self {
216 Self::new(Base64::new(value.iv.to_vec()), value.ciphertext, Base64::new(value.mac.to_vec()))
217 }
218}
219
220impl JsonCastable<SecretEncryptedData> for AesHmacSha2EncryptedData {}
221impl JsonCastable<AesHmacSha2EncryptedData> for SecretEncryptedData {}
222
223impl SecretStorageKey {
224 const ZERO_MESSAGE: &'static [u8; 32] = &[0u8; 32];
225 const PREFIX: [u8; 2] = [0x8b, 0x01];
226 const PREFIX_PARITY: u8 = Self::PREFIX[0] ^ Self::PREFIX[1];
227 const DEFAULT_KEY_ID_LEN: usize = 32;
228 #[cfg(not(test))]
229 const DEFAULT_PBKDF_ITERATIONS: u32 = 500_000;
230 #[cfg(test)]
231 const DEFAULT_PBKDF_ITERATIONS: u32 = 10;
232
233 const DECODED_BASE58_KEY_LEN: usize = 2 + 32 + 1;
236
237 fn parity_byte(bytes: &[u8]) -> u8 {
242 bytes.iter().fold(Self::PREFIX_PARITY, |acc, x| acc ^ x)
243 }
244
245 fn check_zero_message(&self) -> Result<(), DecodeError> {
258 match &self.storage_key_info.algorithm {
259 SecretStorageEncryptionAlgorithm::V1AesHmacSha2(properties) => {
260 let (Some(iv), Some(mac)) = (&properties.iv, &properties.mac) else {
261 return Ok(());
267 };
268
269 let iv = iv.as_bytes();
270 let iv_length = iv.len();
271
272 if iv_length != IV_SIZE {
273 return Err(DecodeError::IvLength(IV_SIZE, iv_length));
274 }
275
276 let mut iv_array = [0u8; 16];
277 iv_array.copy_from_slice(iv);
278
279 let key = AesHmacSha2Key::from_secret_storage_key(&self.secret_key, "");
287 let ciphertext = key.apply_keystream(Self::ZERO_MESSAGE.to_vec(), &iv_array);
288 let expected_mac = HmacSha256Mac::from_slice(mac.as_bytes())
289 .ok_or_else(|| DecodeError::MacLength(MAC_SIZE, mac.as_bytes().len()))?;
290
291 key.verify_mac(&ciphertext, expected_mac.as_bytes())?;
292
293 Ok(())
294 }
295 custom => Err(DecodeError::UnsupportedAlgorithm(custom.algorithm().to_owned())),
296 }
297 }
298
299 fn create_event_content(key_id: String, key: &[u8; KEY_SIZE]) -> SecretStorageKeyEventContent {
300 let key = AesHmacSha2Key::from_secret_storage_key(key, "");
301
302 let (ciphertext, iv) = key.encrypt(Self::ZERO_MESSAGE.to_vec());
303 let iv = Base64::new(iv.to_vec());
304 let mac = Base64::new(key.create_mac_tag(&ciphertext).as_bytes().to_vec());
305
306 SecretStorageKeyEventContent::new(
307 key_id,
308 SecretStorageEncryptionAlgorithm::V1AesHmacSha2(
309 SecretStorageV1AesHmacSha2Properties::new(Some(iv), Some(mac)),
310 ),
311 )
312 }
313
314 pub fn new() -> Self {
316 let mut key = Box::new([0u8; KEY_SIZE]);
317 let mut rng = rng();
318 rng.fill_bytes(key.as_mut_slice());
319
320 let key_id = Alphanumeric.sample_string(&mut rng, Self::DEFAULT_KEY_ID_LEN);
321
322 Self::from_bytes(key_id, key)
323 }
324
325 pub fn new_from_passphrase(passphrase: &str) -> Self {
332 let mut key = Box::new([0u8; 32]);
333 let mut rng = rng();
334 let salt = Alphanumeric.sample_string(&mut rng, Self::DEFAULT_KEY_ID_LEN);
335
336 pbkdf2::<Hmac<Sha512>>(
337 passphrase.as_bytes(),
338 salt.as_bytes(),
339 Self::DEFAULT_PBKDF_ITERATIONS,
340 key.as_mut_slice(),
341 )
342 .expect(
343 "We should be able to expand a passphrase of any length due to \
344 HMAC being able to be initialized with any input size",
345 );
346
347 let key_id = Alphanumeric.sample_string(&mut rng, Self::DEFAULT_KEY_ID_LEN);
348 let mut key = Self::from_bytes(key_id, key);
349
350 key.storage_key_info.passphrase =
351 Some(PassPhrase::new(salt, Self::DEFAULT_PBKDF_ITERATIONS.into()));
352
353 key
354 }
355
356 pub(crate) fn from_bytes(key_id: String, key: Box<[u8; KEY_SIZE]>) -> Self {
357 let storage_key_info = Self::create_event_content(key_id, &key);
358
359 Self { storage_key_info, secret_key: key }
360 }
361
362 pub fn from_account_data(
372 input: &str,
373 content: SecretStorageKeyEventContent,
374 ) -> Result<Self, DecodeError> {
375 let key = if let Some(passphrase_info) = &content.passphrase {
376 match Self::from_passphrase(input, &content, passphrase_info) {
379 Ok(key) => key,
380 Err(e) => Self::from_base58(input, &content).map_err(|_| e)?,
383 }
384 } else {
385 Self::from_base58(input, &content)?
387 };
388
389 Ok(key)
390 }
391
392 fn from_passphrase(
393 passphrase: &str,
394 key_info: &SecretStorageKeyEventContent,
395 passphrase_info: &PassPhrase,
396 ) -> Result<Self, DecodeError> {
397 let mut key = Box::new([0u8; 32]);
398 pbkdf2::<Hmac<Sha512>>(
399 passphrase.as_bytes(),
400 passphrase_info.salt.as_bytes(),
401 passphrase_info
402 .iterations
403 .try_into()
404 .map_err(|_| DecodeError::KdfIterationCount(passphrase_info.iterations))?,
405 key.as_mut_slice(),
406 )
407 .expect(
408 "We should be able to expand a passphrase of any length due to \
409 HMAC being able to be initialized with any input size",
410 );
411
412 let key = Self { storage_key_info: key_info.to_owned(), secret_key: key };
413 key.check_zero_message()?;
414
415 Ok(key)
416 }
417
418 fn parse_base58_key(value: &str) -> Result<Box<[u8; 32]>, DecodeError> {
423 let value: String = value.chars().filter(|c| !c.is_whitespace()).collect();
430
431 let mut decoded = bs58::decode(value).with_alphabet(bs58::Alphabet::BITCOIN).into_vec()?;
432
433 let mut prefix = [0u8; 2];
434 let mut key = Box::new([0u8; 32]);
435
436 let decoded_len = decoded.len();
437
438 if decoded_len != Self::DECODED_BASE58_KEY_LEN {
439 Err(DecodeError::KeyLength(Self::DECODED_BASE58_KEY_LEN, decoded_len))
440 } else {
441 prefix.copy_from_slice(&decoded[0..2]);
442 key.copy_from_slice(&decoded[2..34]);
443 let expected_parity = decoded[34];
444
445 decoded.zeroize();
446
447 let parity = Self::parity_byte(key.as_ref());
448
449 let unexpected_choice = prefix.ct_ne(&Self::PREFIX);
450 let unexpected_parity = expected_parity.ct_ne(&parity);
451
452 if unexpected_choice.into() {
453 Err(DecodeError::Prefix(Self::PREFIX, prefix))
454 } else if unexpected_parity.into() {
455 Err(DecodeError::Parity(expected_parity, parity))
456 } else {
457 Ok(key)
458 }
459 }
460 }
461
462 fn from_base58(
464 value: &str,
465 key_info: &SecretStorageKeyEventContent,
466 ) -> Result<Self, DecodeError> {
467 let secret_key = Self::parse_base58_key(value)?;
468 let key = Self { storage_key_info: key_info.to_owned(), secret_key };
469 key.check_zero_message()?;
470
471 Ok(key)
472 }
473
474 pub fn to_base58(&self) -> String {
483 const DISPLAY_CHUNK_SIZE: usize = 4;
484
485 let mut bytes = Box::new([0u8; Self::DECODED_BASE58_KEY_LEN]);
486
487 bytes[0..2].copy_from_slice(Self::PREFIX.as_slice());
489 bytes[2..34].copy_from_slice(self.secret_key.as_slice());
490
491 bytes[34] = Self::parity_byte(self.secret_key.as_slice());
495
496 let base_58 =
499 bs58::encode(bytes.as_slice()).with_alphabet(bs58::Alphabet::BITCOIN).into_string();
500
501 bytes.zeroize();
502
503 base_58
506 .chars()
507 .collect::<Vec<char>>()
508 .chunks(DISPLAY_CHUNK_SIZE)
509 .map(|c| c.iter().collect::<String>())
510 .collect::<Vec<_>>()
511 .join(" ")
512 }
513
514 pub fn encrypt(
535 &self,
536 plaintext: Vec<u8>,
537 secret_name: &SecretName,
538 ) -> AesHmacSha2EncryptedData {
539 let key = AesHmacSha2Key::from_secret_storage_key(&self.secret_key, secret_name.as_str());
540
541 let (ciphertext, iv) = key.encrypt(plaintext);
542 let mac = key.create_mac_tag(&ciphertext).into_bytes();
543 let ciphertext = Base64::new(ciphertext);
544
545 AesHmacSha2EncryptedData { iv, ciphertext, mac }
546 }
547
548 pub fn decrypt(
551 &self,
552 data: &AesHmacSha2EncryptedData,
553 secret_name: &SecretName,
554 ) -> Result<Vec<u8>, MacError> {
555 let key = AesHmacSha2Key::from_secret_storage_key(&self.secret_key, secret_name.as_str());
556 let ciphertext = data.ciphertext.to_owned().into_inner();
557
558 key.verify_mac(&ciphertext, &data.mac)?;
559
560 let plaintext = key.decrypt(ciphertext, &data.iv);
561
562 Ok(plaintext)
563 }
564
565 pub fn event_content(&self) -> &SecretStorageKeyEventContent {
573 &self.storage_key_info
574 }
575
576 pub fn key_id(&self) -> &str {
578 &self.storage_key_info.key_id
579 }
580
581 pub fn event_type(&self) -> GlobalAccountDataEventType {
590 self.event_content().event_type()
591 }
592}
593
594impl Default for SecretStorageKey {
595 fn default() -> Self {
596 Self::new()
597 }
598}
599
600#[cfg(test)]
601mod test {
602 use assert_matches::assert_matches;
603 use ruma::events::EventContentFromType;
604 use serde_json::{json, value::to_raw_value};
605 use strass::assert_let;
606
607 use super::*;
608
609 const SECRET_STORAGE_KEY: &[u8; 32] = &[0u8; 32];
610
611 #[test]
612 fn encrypting() {
613 let secret = "It's a secret to everybody";
614 let secret_name = SecretName::from("secret_message");
615
616 let key = SecretStorageKey::from_bytes(
617 "key_id".to_owned(),
618 Box::new(SECRET_STORAGE_KEY.to_owned()),
619 );
620
621 let encrypted = key.encrypt(secret.as_bytes().to_vec(), &secret_name);
622 let decrypted = key
623 .decrypt(&encrypted, &secret_name)
624 .expect("We should be able to decrypt the message we just encrypted");
625
626 assert_eq!(
627 secret.as_bytes(),
628 decrypted,
629 "Encryption roundtrip should result in the same plaintext"
630 );
631 }
632
633 #[test]
634 fn from_passphrase_roundtrip() {
635 let passphrase = "It's a secret to everybody";
636 let secret = "Foobar";
637 let secret_name = SecretName::from("secret_message");
638
639 let key = SecretStorageKey::new_from_passphrase("It's a secret to everybody");
640
641 let encrypted = key.encrypt(secret.as_bytes().to_vec(), &secret_name);
642 let content = to_raw_value(key.event_content())
643 .expect("We should be able to serialize the secret storage key event content");
644
645 let content = SecretStorageKeyEventContent::from_parts(
646 &key.event_type().to_string(),
647 &content,
648 )
649 .expect(
650 "We should be able to parse our, just serialized, secret storage key event content",
651 );
652
653 let key = SecretStorageKey::from_account_data(passphrase, content)
654 .expect("We should be able to restore our secret storage key");
655
656 let decrypted = key.decrypt(&encrypted, &secret_name).expect(
657 "We should be able to decrypt the message using the restored secret storage key",
658 );
659
660 assert_eq!(
661 secret.as_bytes(),
662 decrypted,
663 "The encryption roundtrip should produce the same plaintext"
664 );
665 }
666
667 #[test]
668 fn from_base58_roundtrip() {
669 let secret = "Foobar";
670 let secret_name = SecretName::from("secret_message");
671
672 let key = SecretStorageKey::new();
673
674 let encrypted = key.encrypt(secret.as_bytes().to_vec(), &secret_name);
675 let content = to_raw_value(key.event_content())
676 .expect("We should be able to serialize the secret storage key event content");
677
678 let content = SecretStorageKeyEventContent::from_parts(
679 &key.event_type().to_string(),
680 &content,
681 )
682 .expect(
683 "We should be able to parse our, just serialized, secret storage key event content",
684 );
685
686 let base58_key = key.to_base58();
687
688 let key = SecretStorageKey::from_account_data(&base58_key, content)
689 .expect("We should be able to restore our secret storage key");
690
691 let decrypted = key.decrypt(&encrypted, &secret_name).expect(
692 "We should be able to decrypt the message using the restored secret storage key",
693 );
694
695 assert_eq!(
696 secret.as_bytes(),
697 decrypted,
698 "The encryption roundtrip should produce the same plaintext"
699 );
700 }
701
702 #[test]
703 fn from_account_data_and_passphrase() {
704 let json = to_raw_value(&json!({
705 "algorithm":"m.secret_storage.v1.aes-hmac-sha2",
706 "iv":"gH2iNpiETFhApvW6/FFEJQ",
707 "mac":"9Lw12m5SKDipNghdQXKjgpfdj1/K7HFI2brO+UWAGoM",
708 "passphrase":{
709 "algorithm":"m.pbkdf2",
710 "salt":"IuLnH7S85YtZmkkBJKwNUKxWF42g9O1H",
711 "iterations":10
712 }
713 }))
714 .unwrap();
715
716 let content = SecretStorageKeyEventContent::from_parts(
717 "m.secret_storage.key.DZkbKc0RtKSq0z8V61w6KBmJCK6OCiIu",
718 &json,
719 )
720 .expect("We should be able to deserialize our static secret storage key");
721
722 SecretStorageKey::from_account_data("It's a secret to everybody", content)
723 .expect("We should be able to restore the secret storage key");
724 }
725
726 #[test]
727 fn from_account_data_and_base58() {
728 let base58_key = "EsTj 3yST y93F SLpB jJsz eAXc 2XzA ygD3 w69H fGaN TKBj jXEd";
729 let key_id = "bmur2d9ypPUH1msSwCxQOJkuKRmJI55e";
730
731 let json = to_raw_value(&json!({
732 "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
733 "iv": "xv5b6/p3ExEw++wTyfSHEg==",
734 "mac": "ujBBbXahnTAMkmPUX2/0+VTfUh63pGyVRuBcDMgmJC8="
735 }))
736 .unwrap();
737
738 let content = SecretStorageKeyEventContent::from_parts(
739 &format!("m.secret_storage.key.{key_id}"),
740 &json,
741 )
742 .expect("We should be able to deserialize our static secret storage key");
743
744 let key = SecretStorageKey::from_account_data(base58_key, content)
745 .expect("We should be able to restore the secret storage key");
746
747 assert_eq!(key_id, key.key_id(), "The key should correctly remember the key ID");
748 }
749
750 #[test]
751 fn invalid_key() {
752 let key = SecretStorageKey::new_from_passphrase("It's a secret to everybody");
753
754 let content = to_raw_value(key.event_content())
755 .expect("We should be able to serialize the secret storage key event content");
756
757 let content = SecretStorageKeyEventContent::from_parts(
758 &key.event_type().to_string(),
759 &content,
760 )
761 .expect(
762 "We should be able to parse our, just serialized, secret storage key event content",
763 );
764
765 assert_matches!(
766 SecretStorageKey::from_account_data("It's a secret to nobody", content.to_owned()),
767 Err(DecodeError::Mac(_)),
768 "Using the wrong passphrase should throw a MAC error"
769 );
770
771 let key = SecretStorageKey::new();
772 let base58_key = key.to_base58();
773
774 assert_matches!(
775 SecretStorageKey::from_account_data(&base58_key, content),
776 Err(DecodeError::Mac(_)),
777 "Using the wrong base58 key should throw a MAC error"
778 );
779 }
780
781 #[test]
785 fn accepts_any_passphrase_if_mac_and_iv_are_missing() {
786 let mut content = SecretStorageKeyEventContent::new(
787 "my_new_key_id".to_owned(),
788 SecretStorageEncryptionAlgorithm::V1AesHmacSha2(
789 SecretStorageV1AesHmacSha2Properties::new(None, None),
790 ),
791 );
792 content.passphrase =
793 Some(PassPhrase::new("salty goodness".to_owned(), UInt::new_saturating(100)));
794
795 SecretStorageKey::from_account_data("It's a secret to nobody", content)
796 .expect("Should accept any passphrase");
797 }
798
799 #[test]
800 fn base58_parsing() {
801 const DECODED_KEY: [u8; 32] = [
802 159, 189, 70, 187, 52, 81, 113, 198, 246, 2, 44, 154, 37, 213, 104, 27, 165, 78, 236,
803 106, 108, 73, 83, 243, 173, 192, 185, 110, 157, 145, 173, 163,
804 ];
805
806 let key = "EsT pRvZTnjck8 YrhRAtw XLS84Nr2r9S9LGAWDaExVAPBvLRK ";
807 let parsed_key = SecretStorageKey::parse_base58_key(key)
808 .expect("Whitespace in the Base58 encoded key should not matter");
809
810 assert_eq!(
811 parsed_key.as_slice(),
812 DECODED_KEY,
813 "Decoding the key should produce the correct bytes"
814 );
815
816 let key = "EsTpRvZTnjck8YrhRAtwXLS84Nr2r9S9LGAWDaExVAPBvLRk";
817 assert_matches!(
818 SecretStorageKey::parse_base58_key(key),
819 Err(DecodeError::Parity(..)),
820 "We should detect an invalid parity byte"
821 );
822
823 let key = "AATpRvZTnjck8YrhRAtwXLS84Nr2r9S9LGAWDaExVAPBvLRk";
824 assert_matches!(
825 SecretStorageKey::parse_base58_key(key),
826 Err(DecodeError::Prefix(..)),
827 "We should detect an invalid prefix"
828 );
829
830 let key = "AATpRvZTnjck8YrhRAtwXLS84Nr2r9S9";
831 assert_matches!(
832 SecretStorageKey::parse_base58_key(key),
833 Err(DecodeError::KeyLength(..)),
834 "We should detect if the key isn't of the correct length"
835 );
836
837 let key = "AATpRvZTnjck8YrhRAtwXLS84Nr0OIl";
838 assert_matches!(
839 SecretStorageKey::parse_base58_key(key),
840 Err(DecodeError::Base58(..)),
841 "We should detect if the key isn't Base58"
842 );
843 }
844
845 #[test]
846 fn encrypted_data_decoding() {
847 let json = json!({
848 "iv": "bdfCwu+ECYgZ/jWTkGrQ/A==",
849 "ciphertext": "lCRSSA1lChONEXj/8RyogsgAa8ouQwYDnLr4XBCheRikrZykLRzPCx3doCE=",
850 "mac": "NXeV1dZaOe2JLvQ6Hh6tFto7AgFFdaQnY0l9pruwdtE="
851 });
852
853 let content: RumaAesHmacSha2EncryptedData = serde_json::from_value(json)
854 .expect("We should be able to deserialize our static JSON content");
855
856 let encrypted_data: AesHmacSha2EncryptedData = content.try_into()
857 .expect("We should be able to convert a valid SecretEncryptedData to a AesHmacSha2EncryptedData struct");
858
859 assert_eq!(
860 encrypted_data.mac,
861 [
862 53, 119, 149, 213, 214, 90, 57, 237, 137, 46, 244, 58, 30, 30, 173, 22, 218, 59, 2,
863 1, 69, 117, 164, 39, 99, 73, 125, 166, 187, 176, 118, 209
864 ]
865 );
866 assert_eq!(
867 encrypted_data.iv,
868 [109, 215, 194, 194, 239, 132, 9, 136, 25, 254, 53, 147, 144, 106, 208, 252]
869 );
870
871 let secret_encrypted_data: RumaAesHmacSha2EncryptedData = encrypted_data.to_owned().into();
872
873 assert_let!(
874 RumaAesHmacSha2EncryptedData { iv, ciphertext, mac, .. } = secret_encrypted_data
875 );
876 assert_eq!(mac.as_bytes(), encrypted_data.mac.as_slice());
877 assert_eq!(iv.as_bytes(), encrypted_data.iv.as_slice());
878 assert_eq!(ciphertext, encrypted_data.ciphertext);
879
880 let invalid_mac_json = json!({
881 "iv": "bdfCwu+ECYgZ/jWTkGrQ/A==",
882 "ciphertext": "lCRSSA1lChONEXj/8RyogsgAa8ouQwYDnLr4XBCheRikrZykLRzPCx3doCE=",
883 "mac": "NXeV1dZaOe2JLvQ6Hh6tFtgFFdaQnY0l9pruwdtE"
884 });
885
886 let content: RumaAesHmacSha2EncryptedData = serde_json::from_value(invalid_mac_json)
887 .expect("We should be able to deserialize our static JSON content");
888
889 let encrypted_data: Result<AesHmacSha2EncryptedData, _> = content.try_into();
890 encrypted_data.expect_err(
891 "We should be able to detect if a SecretEncryptedData content has an invalid MAC",
892 );
893
894 let invalid_iv_json = json!({
895 "iv": "bdfCwu+gZ/jWTkGrQ/A",
896 "ciphertext": "lCRSSA1lChONEXj/8RyogsgAa8ouQwYDnLr4XBCheRikrZykLRzPCx3doCE=",
897 "mac": "NXeV1dZaOe2JLvQ6Hh6tFto7AgFFdaQnY0l9pruwdtE="
898 });
899
900 let content: RumaAesHmacSha2EncryptedData = serde_json::from_value(invalid_iv_json)
901 .expect("We should be able to deserialize our static JSON content");
902
903 let encrypted_data: Result<AesHmacSha2EncryptedData, _> = content.try_into();
904 encrypted_data.expect_err(
905 "We should be able to detect if a SecretEncryptedData content has an invalid IV",
906 );
907 }
908
909 #[test]
910 fn invalid_key_info() {
911 let base58_key = "EsTj 3yST y93F SLpB jJsz eAXc 2XzA ygD3 w69H fGaN TKBj jXEd";
912
913 let content = SecretStorageKeyEventContent::new(
914 "bmur2d9ypPUH1msSwCxQOJkuKRmJI55e".to_owned(),
915 SecretStorageEncryptionAlgorithm::V1AesHmacSha2(
916 SecretStorageV1AesHmacSha2Properties::new(
917 Some(Base64::new(vec![0u8; 14])),
918 Some(Base64::new(vec![0u8; 32])),
919 ),
920 ),
921 );
922
923 assert_matches!(
924 SecretStorageKey::from_account_data(base58_key, content),
925 Err(DecodeError::IvLength(..)),
926 "We should correctly detect an invalid IV"
927 );
928
929 let content = SecretStorageKeyEventContent::new(
930 "bmur2d9ypPUH1msSwCxQOJkuKRmJI55e".to_owned(),
931 SecretStorageEncryptionAlgorithm::V1AesHmacSha2(
932 SecretStorageV1AesHmacSha2Properties::new(
933 Some(Base64::new(vec![0u8; 16])),
934 Some(Base64::new(vec![0u8; 10])),
935 ),
936 ),
937 );
938
939 assert_matches!(
940 SecretStorageKey::from_account_data(base58_key, content),
941 Err(DecodeError::MacLength(..)),
942 "We should correctly detect an invalid MAC"
943 );
944
945 let json = to_raw_value(&json!({
946 "algorithm": "m.secret_storage.custom",
947 "iv": "xv5b6/p3ExEw++wTyfSHEg==",
948 "mac": "ujBBbXahnTAMkmPUX2/0+VTfUh63pGyVRuBcDMgmJC8="
949 }))
950 .unwrap();
951
952 let content = SecretStorageKeyEventContent::from_parts(
953 "m.secret_storage.key.bmur2d9ypPUH1msSwCxQOJkuKRmJI55e",
954 &json,
955 )
956 .expect("We should be able to deserialize our static secret storage key");
957
958 assert_matches!(
959 SecretStorageKey::from_account_data(base58_key, content),
960 Err(DecodeError::UnsupportedAlgorithm(..)),
961 "We should correctly detect a unsupported algorithm"
962 );
963 }
964}