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