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