1use matrix_sdk_base::crypto::types::SecretsBundle;
16use matrix_sdk_common::deserialized_responses::PrivOwnedStr;
17use oauth2::{
18 EndUserVerificationUrl, StandardDeviceAuthorizationResponse, VerificationUriComplete,
19};
20use ruma::serde::{JsonObject, StringEnum};
21use serde::{Deserialize, Serialize};
22use url::Url;
23use vodozemac::Curve25519PublicKey;
24
25#[cfg(doc)]
26use super::QRCodeLoginError::SecureChannel;
27use super::secure_channel::ChannelVariant;
28
29#[derive(Debug, Serialize, Deserialize)]
32#[serde(tag = "type")]
33pub enum QrAuthMessage {
34 #[serde(rename = "m.login.protocols")]
37 LoginProtocols(LoginProtocolsMessage),
38
39 #[serde(rename = "m.login.protocol")]
42 LoginProtocol {
43 #[serde(flatten)]
46 protocol: LoginProtocolData,
47 device_id: String,
49 },
50
51 #[serde(rename = "m.login.protocol_accepted")]
54 LoginProtocolAccepted,
55
56 #[serde(rename = "m.login.success")]
59 LoginSuccess,
60
61 #[serde(rename = "m.login.declined")]
65 LoginDeclined,
66
67 #[serde(rename = "m.login.failure")]
70 LoginFailure {
71 reason: LoginFailureReason,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
82 homeserver: Option<String>,
83 },
84
85 #[serde(rename = "m.login.secrets")]
90 LoginSecrets(SecretsBundle),
91}
92
93#[derive(Debug, Serialize, Deserialize)]
99#[serde(untagged)]
100pub enum LoginProtocolsMessage {
101 Msc4388 {
102 protocols: Vec<LoginProtocolType>,
104 base_url: Url,
106 },
107 Msc4108 {
108 protocols: Vec<LoginProtocolType>,
110 homeserver: Url,
116 },
117}
118
119impl QrAuthMessage {
120 pub(super) fn authorization_grant_login_protocol(
124 device_authorization_grant: AuthorizationGrant,
125 device_id: Curve25519PublicKey,
126 channel_variant: ChannelVariant,
127 ) -> QrAuthMessage {
128 QrAuthMessage::LoginProtocol {
129 device_id: device_id.to_base64(),
130 protocol: LoginProtocolData::device_authorization_grant(
131 device_authorization_grant,
132 channel_variant,
133 ),
134 }
135 }
136}
137
138#[derive(Debug, Clone, Deserialize)]
156#[serde(try_from = "LoginProtocolDataHelper")]
157#[non_exhaustive]
158pub enum LoginProtocolData {
159 Msc4108DeviceAuthorizationGrant(AuthorizationGrant),
167 Msc4388DeviceAuthorizationGrant(AuthorizationGrant),
175 Unknown(UnknownLoginProtocol),
177}
178
179impl LoginProtocolData {
180 pub fn new(protocol: LoginProtocolType, data: JsonObject) -> Result<Self, serde_json::Error> {
201 LoginProtocolDataHelper { protocol, other: data }.try_into()
202 }
203
204 pub(super) fn device_authorization_grant(
208 device_authorization_grant: AuthorizationGrant,
209 channel_variant: ChannelVariant,
210 ) -> Self {
211 match channel_variant {
212 ChannelVariant::Msc4108 => {
213 Self::Msc4108DeviceAuthorizationGrant(device_authorization_grant)
214 }
215 #[cfg(feature = "unstable-msc4388")]
216 ChannelVariant::Msc4388 => {
217 Self::Msc4388DeviceAuthorizationGrant(device_authorization_grant)
218 }
219 }
220 }
221
222 pub fn protocol(&self) -> LoginProtocolType {
224 match self {
225 Self::Msc4108DeviceAuthorizationGrant(_) | Self::Msc4388DeviceAuthorizationGrant(_) => {
226 LoginProtocolType::DeviceAuthorizationGrant
227 }
228 Self::Unknown(c) => c.protocol.clone(),
229 }
230 }
231}
232
233#[derive(Debug, Clone)]
236pub struct UnknownLoginProtocol {
237 protocol: LoginProtocolType,
239
240 other: JsonObject,
242}
243
244impl UnknownLoginProtocol {
245 pub fn protocol(&self) -> &LoginProtocolType {
247 &self.protocol
248 }
249
250 pub fn data(&self) -> &JsonObject {
254 &self.other
255 }
256}
257
258#[derive(Deserialize)]
259struct LoginProtocolDataHelper {
260 protocol: LoginProtocolType,
261 #[serde(flatten)]
262 other: JsonObject,
263}
264
265#[derive(Serialize)]
266struct LoginProtocolDataSerHelper<'a, T: Serialize> {
267 protocol: LoginProtocolType,
268 #[serde(flatten)]
269 data: &'a T,
270}
271
272#[derive(Serialize)]
273struct Msc4108DeviceAuthorizationGrantSerHelper<'a> {
274 device_authorization_grant: &'a AuthorizationGrant,
275}
276
277impl Serialize for LoginProtocolData {
278 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
279 where
280 S: serde::Serializer,
281 {
282 let protocol = self.protocol();
283
284 match self {
285 Self::Msc4108DeviceAuthorizationGrant(device_authorization_grant) => {
286 LoginProtocolDataSerHelper {
287 protocol,
288 data: &Msc4108DeviceAuthorizationGrantSerHelper { device_authorization_grant },
289 }
290 .serialize(serializer)
291 }
292 Self::Msc4388DeviceAuthorizationGrant(device_authorization_grant) => {
293 LoginProtocolDataSerHelper { protocol, data: device_authorization_grant }
294 .serialize(serializer)
295 }
296 Self::Unknown(c) => {
297 LoginProtocolDataSerHelper { protocol, data: &c.other }.serialize(serializer)
298 }
299 }
300 }
301}
302
303impl TryFrom<LoginProtocolDataHelper> for LoginProtocolData {
304 type Error = serde_json::Error;
305
306 fn try_from(mut value: LoginProtocolDataHelper) -> Result<Self, Self::Error> {
307 Ok(match value.protocol {
308 LoginProtocolType::DeviceAuthorizationGrant => {
309 let invalid_data = |error: serde_json::Error| {
310 serde::de::Error::custom(format_args!(
311 "invalid device_authorization_grant: {error}"
312 ))
313 };
314
315 if let Some(device_authorization_grant) =
319 value.other.remove("device_authorization_grant")
320 {
321 Self::Msc4108DeviceAuthorizationGrant(
322 serde_json::from_value(device_authorization_grant).map_err(invalid_data)?,
323 )
324 } else {
325 Self::Msc4388DeviceAuthorizationGrant(
326 serde_json::from_value(value.other.into()).map_err(invalid_data)?,
327 )
328 }
329 }
330 _ => {
331 const RESERVED_FIELDS: &[&str] = &["type", "protocol", "device_id"];
334
335 if let Some(field) =
336 RESERVED_FIELDS.iter().find(|field| value.other.contains_key(**field))
337 {
338 return Err(serde::de::Error::custom(format_args!(
339 "the `{field}` field is reserved and can't be part of the login \
340 protocol data"
341 )));
342 }
343
344 Self::Unknown(UnknownLoginProtocol { protocol: value.protocol, other: value.other })
345 }
346 })
347 }
348}
349
350impl From<&StandardDeviceAuthorizationResponse> for AuthorizationGrant {
351 fn from(value: &StandardDeviceAuthorizationResponse) -> Self {
352 Self {
353 verification_uri: value.verification_uri().clone(),
354 verification_uri_complete: value.verification_uri_complete().cloned(),
355 }
356 }
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct AuthorizationGrant {
362 pub verification_uri: EndUserVerificationUrl,
364
365 #[serde(default, skip_serializing_if = "Option::is_none")]
370 pub verification_uri_complete: Option<VerificationUriComplete>,
371}
372
373#[derive(Clone, StringEnum)]
375#[ruma_enum(rename_all = "snake_case")]
376pub enum LoginFailureReason {
377 AuthorizationExpired,
379 DeviceAlreadyExists,
382 DeviceNotFound,
385 UnexpectedMessageReceived,
388 UnsupportedProtocol,
391 UserCancelled,
394 #[doc(hidden)]
395 _Custom(PrivOwnedStr),
396}
397
398#[derive(Clone, StringEnum)]
400#[ruma_enum(rename_all = "snake_case")]
401pub enum LoginProtocolType {
402 DeviceAuthorizationGrant,
404 #[doc(hidden)]
405 _Custom(PrivOwnedStr),
406}
407
408#[cfg(test)]
409mod test {
410 use matrix_sdk_base::crypto::types::BackupSecrets;
411 use serde_json::json;
412 use similar_asserts::assert_eq;
413 use strass::assert_let;
414
415 use super::*;
416
417 #[test]
418 fn test_protocols_serialization_msc_4108() {
419 const HOMESERVER: &str = "https://matrix-client.matrix.org/";
420
421 let json = json!({
422 "type": "m.login.protocols",
423 "protocols": ["device_authorization_grant"],
424 "homeserver": HOMESERVER,
425
426 });
427
428 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
429 assert_let!(
430 QrAuthMessage::LoginProtocols(LoginProtocolsMessage::Msc4108 {
431 protocols,
432 homeserver
433 }) = &message
434 );
435 assert!(protocols.contains(&LoginProtocolType::DeviceAuthorizationGrant));
436 assert_eq!(homeserver.as_str(), HOMESERVER);
437
438 let serialized = serde_json::to_value(&message).unwrap();
439 assert_eq!(json, serialized);
440 }
441
442 #[test]
443 fn test_protocols_serialization_msc_4388() {
444 const HOMESERVER: &str = "https://matrix-client.matrix.org/";
445
446 let json = json!({
447 "type": "m.login.protocols",
448 "protocols": ["device_authorization_grant"],
449 "base_url": HOMESERVER,
450
451 });
452
453 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
454 assert_let!(
455 QrAuthMessage::LoginProtocols(LoginProtocolsMessage::Msc4388 { protocols, base_url }) =
456 &message
457 );
458 assert!(protocols.contains(&LoginProtocolType::DeviceAuthorizationGrant));
459 assert_eq!(base_url.as_str(), HOMESERVER);
460
461 let serialized = serde_json::to_value(&message).unwrap();
462 assert_eq!(json, serialized);
463 }
464
465 #[test]
466 fn test_protocol_serialization_msc_4108() {
467 let json = json!({
468 "type": "m.login.protocol",
469 "protocol": "device_authorization_grant",
470 "device_authorization_grant": {
471 "verification_uri_complete": "https://id.matrix.org/device/abcde",
472 "verification_uri": "https://id.matrix.org/device/abcde?code=ABCDE"
473 },
474 "device_id": "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4"
475 });
476
477 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
478 assert_let!(
479 QrAuthMessage::LoginProtocol {
480 protocol: LoginProtocolData::Msc4108DeviceAuthorizationGrant(
481 device_authorization_grant
482 ),
483 device_id,
484 } = &message
485 );
486 assert_eq!(
487 device_authorization_grant.verification_uri.as_str(),
488 "https://id.matrix.org/device/abcde?code=ABCDE"
489 );
490 assert_eq!(device_id, "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4");
491 let serialized = serde_json::to_value(&message).unwrap();
492 assert_eq!(json, serialized);
493 }
494
495 #[test]
496 fn test_protocol_serialization_msc_4388() {
497 let json = json!({
498 "type": "m.login.protocol",
499 "protocol": "device_authorization_grant",
500 "verification_uri_complete": "https://id.matrix.org/device/abcde",
501 "verification_uri": "https://id.matrix.org/device/abcde?code=ABCDE",
502 "device_id": "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4"
503 });
504
505 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
506 assert_let!(
507 QrAuthMessage::LoginProtocol {
508 protocol: LoginProtocolData::Msc4388DeviceAuthorizationGrant(
509 device_authorization_grant
510 ),
511 device_id,
512 } = &message
513 );
514 assert_eq!(
515 device_authorization_grant.verification_uri.as_str(),
516 "https://id.matrix.org/device/abcde?code=ABCDE"
517 );
518 assert_eq!(device_id, "wjLpTLRqbqBzLs63aYaEv2Boi6cFEbbM/sSRQ2oAKk4");
519 let serialized = serde_json::to_value(&message).unwrap();
520 assert_eq!(json, serialized);
521 }
522
523 #[test]
524 fn test_protocol_serialization_unknown_protocol() {
525 let json = json!({
528 "type": "m.login.protocol",
529 "protocol": "org.example.future_protocol",
530 "org.example.field": {
531 "some": "data"
532 },
533 "device_id": "ABCDEFGH"
534 });
535
536 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
537 assert_let!(
538 QrAuthMessage::LoginProtocol {
539 protocol: LoginProtocolData::Unknown(protocol),
540 device_id
541 } = &message
542 );
543 assert_eq!(protocol.protocol().as_str(), "org.example.future_protocol");
544 assert_eq!(
545 protocol.data(),
546 &JsonObject::from_iter([("org.example.field".to_owned(), json!({ "some": "data" }))])
547 );
548 assert_eq!(device_id, "ABCDEFGH");
549
550 let serialized = serde_json::to_value(&message).unwrap();
553 assert_eq!(json, serialized);
554 }
555
556 #[test]
557 fn test_protocol_deserialization_missing_device_authorization_grant() {
558 let json = json!({
559 "type": "m.login.protocol",
560 "protocol": "device_authorization_grant",
561 "device_id": "ABCDEFGH"
562 });
563
564 let error = serde_json::from_value::<QrAuthMessage>(json).expect_err(
565 "The device authorization grant protocol requires the device authorization grant",
566 );
567 let error = error.to_string();
568 assert!(error.contains("device_authorization_grant"), "{error}");
569 assert!(error.contains("verification_uri"), "{error}");
570 }
571
572 #[test]
573 fn test_protocol_deserialization_invalid_device_authorization_grant() {
574 let json = json!({
575 "type": "m.login.protocol",
576 "protocol": "device_authorization_grant",
577 "device_authorization_grant": {},
578 "device_id": "ABCDEFGH"
579 });
580
581 let error = serde_json::from_value::<QrAuthMessage>(json)
582 .expect_err("The device authorization grant requires the verification_uri field");
583 let error = error.to_string();
584 assert!(error.contains("device_authorization_grant"), "{error}");
585 assert!(error.contains("verification_uri"), "{error}");
586 }
587
588 #[test]
589 fn test_protocol_serialization_drops_unknown_fields_of_known_protocol() {
590 let json = json!({
591 "type": "m.login.protocol",
592 "protocol": "device_authorization_grant",
593 "device_authorization_grant": {
594 "verification_uri": "https://id.matrix.org/device/abcde"
595 },
596 "org.example.hint": "data",
597 "device_id": "ABCDEFGH"
598 });
599
600 let message: QrAuthMessage = serde_json::from_value(json).unwrap();
601 let serialized = serde_json::to_value(&message).unwrap();
602 assert_eq!(
603 serialized,
604 json!({
605 "type": "m.login.protocol",
606 "protocol": "device_authorization_grant",
607 "device_authorization_grant": {
608 "verification_uri": "https://id.matrix.org/device/abcde",
609 },
610 "device_id": "ABCDEFGH"
611 })
612 );
613
614 let json = json!({
615 "type": "m.login.protocol",
616 "protocol": "device_authorization_grant",
617 "verification_uri": "https://id.matrix.org/device/abcde",
618 "org.example.hint": "data",
619 "device_id": "ABCDEFGH"
620 });
621
622 let message: QrAuthMessage = serde_json::from_value(json).unwrap();
623 let serialized = serde_json::to_value(&message).unwrap();
624 assert_eq!(
625 serialized,
626 json!({
627 "type": "m.login.protocol",
628 "protocol": "device_authorization_grant",
629 "verification_uri": "https://id.matrix.org/device/abcde",
630 "device_id": "ABCDEFGH"
631 })
632 );
633 }
634
635 #[test]
636 fn test_login_protocol_data_new() {
637 let grant = json!({
638 "verification_uri": "https://id.matrix.org/device/abcde",
639 });
640 let assert_grant = |device_authorization_grant: AuthorizationGrant| {
641 assert_eq!(
642 device_authorization_grant.verification_uri.as_str(),
643 "https://id.matrix.org/device/abcde"
644 );
645 };
646
647 let data = LoginProtocolData::new(
650 LoginProtocolType::DeviceAuthorizationGrant,
651 JsonObject::from_iter([("device_authorization_grant".to_owned(), grant.clone())]),
652 )
653 .unwrap();
654 assert_let!(
655 LoginProtocolData::Msc4108DeviceAuthorizationGrant(device_authorization_grant) = data
656 );
657 assert_grant(device_authorization_grant);
658
659 let data = LoginProtocolData::new(
662 LoginProtocolType::DeviceAuthorizationGrant,
663 serde_json::from_value(grant.clone()).unwrap(),
664 )
665 .unwrap();
666 assert_let!(
667 LoginProtocolData::Msc4388DeviceAuthorizationGrant(device_authorization_grant) = data
668 );
669 assert_grant(device_authorization_grant);
670
671 LoginProtocolData::new(LoginProtocolType::DeviceAuthorizationGrant, JsonObject::new())
673 .expect_err("The device authorization grant protocol requires its data");
674
675 let other =
678 JsonObject::from_iter([("device_authorization_grant".to_owned(), grant.clone())]);
679 let data =
680 LoginProtocolData::new("org.example.future_protocol".into(), other.clone()).unwrap();
681 assert_let!(LoginProtocolData::Unknown(protocol) = data);
682 assert_eq!(protocol.protocol().as_str(), "org.example.future_protocol");
683 assert_eq!(protocol.data(), &other);
684
685 for field in ["type", "protocol", "device_id"] {
688 let other = JsonObject::from_iter([(field.to_owned(), json!("foo"))]);
689 let error = LoginProtocolData::new("org.example.future_protocol".into(), other)
690 .expect_err("The reserved fields should be rejected");
691 assert!(error.to_string().contains(field), "{error}");
692 }
693
694 let data = LoginProtocolData::new(
697 LoginProtocolType::DeviceAuthorizationGrant,
698 JsonObject::from_iter([
699 ("device_authorization_grant".to_owned(), grant.clone()),
700 ("type".to_owned(), json!("foo")),
701 ("org.example.hint".to_owned(), json!(1)),
702 ]),
703 )
704 .unwrap();
705 assert_let!(LoginProtocolData::Msc4108DeviceAuthorizationGrant(_) = data);
706
707 let data = LoginProtocolData::new(
708 LoginProtocolType::DeviceAuthorizationGrant,
709 JsonObject::from_iter([
710 ("verification_uri".to_owned(), grant["verification_uri"].clone()),
711 ("type".to_owned(), json!("foo")),
712 ("org.example.hint".to_owned(), json!(1)),
713 ]),
714 )
715 .unwrap();
716 assert_let!(LoginProtocolData::Msc4388DeviceAuthorizationGrant(_) = data);
717 }
718
719 #[test]
720 fn test_protocol_accepted_serialization() {
721 let json = json!({
722 "type": "m.login.protocol_accepted",
723 });
724
725 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
726 assert_let!(QrAuthMessage::LoginProtocolAccepted = &message);
727 let serialized = serde_json::to_value(&message).unwrap();
728 assert_eq!(json, serialized);
729 }
730
731 #[test]
732 fn test_login_success() {
733 let json = json!({
734 "type": "m.login.success",
735 });
736
737 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
738 assert_let!(QrAuthMessage::LoginSuccess = &message);
739 let serialized = serde_json::to_value(&message).unwrap();
740 assert_eq!(json, serialized);
741 }
742
743 #[test]
744 fn test_login_declined() {
745 let json = json!({
746 "type": "m.login.declined",
747 });
748
749 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
750 assert_let!(QrAuthMessage::LoginDeclined = &message);
751 let serialized = serde_json::to_value(&message).unwrap();
752 assert_eq!(json, serialized);
753 }
754
755 #[test]
756 fn test_login_failure() {
757 let json = json!({
758 "type": "m.login.failure",
759 "reason": "unsupported_protocol",
760 "homeserver": "https://matrix-client.matrix.org/"
761 });
762
763 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
764 assert_let!(QrAuthMessage::LoginFailure { reason, homeserver } = &message);
765 assert_eq!(reason, &LoginFailureReason::UnsupportedProtocol);
766 assert_eq!(homeserver.as_deref(), Some("https://matrix-client.matrix.org/"));
768 let serialized = serde_json::to_value(&message).unwrap();
769 assert_eq!(json, serialized);
770 }
771
772 #[test]
773 fn test_login_failure_with_server_name() {
774 let json = json!({
776 "type": "m.login.failure",
777 "reason": "unsupported_protocol",
778 "homeserver": "matrix.org"
779 });
780
781 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
782 assert_let!(QrAuthMessage::LoginFailure { reason, homeserver } = &message);
783 assert_eq!(reason, &LoginFailureReason::UnsupportedProtocol);
784 assert_eq!(homeserver.as_deref(), Some("matrix.org"));
785 let serialized = serde_json::to_value(&message).unwrap();
786 assert_eq!(json, serialized);
787 }
788
789 #[test]
790 fn test_login_failure_without_homeserver() {
791 let json = json!({
792 "type": "m.login.failure",
793 "reason": "user_cancelled",
794 });
795
796 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
797 assert_let!(QrAuthMessage::LoginFailure { reason, homeserver } = &message);
798 assert_eq!(reason, &LoginFailureReason::UserCancelled);
799 assert!(homeserver.is_none());
800
801 let serialized = serde_json::to_value(&message).unwrap();
803 assert_eq!(json, serialized);
804
805 let message: QrAuthMessage = serde_json::from_value(json!({
807 "type": "m.login.failure",
808 "reason": "user_cancelled",
809 "homeserver": null,
810 }))
811 .unwrap();
812 assert_let!(QrAuthMessage::LoginFailure { homeserver: None, .. } = &message);
813 }
814
815 #[test]
816 fn test_login_secrets() {
817 let json = json!({
818 "type": "m.login.secrets",
819 "cross_signing": {
820 "master_key": "rTtSv67XGS6k/rg6/yTG/m573cyFTPFRqluFhQY+hSw",
821 "self_signing_key": "4jbPt7jh5D2iyM4U+3IDa+WthgJB87IQN1ATdkau+xk",
822 "user_signing_key": "YkFKtkjcsTxF6UAzIIG/l6Nog/G2RigCRfWj3cjNWeM",
823 },
824 "backup": {
825 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
826 "backup_version": "2",
827 "key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
828 },
829 });
830
831 let message: QrAuthMessage = serde_json::from_value(json.clone()).unwrap();
832 assert_let!(
833 QrAuthMessage::LoginSecrets(SecretsBundle { cross_signing, backup }) = &message
834 );
835 assert_eq!(cross_signing.master_key, "rTtSv67XGS6k/rg6/yTG/m573cyFTPFRqluFhQY+hSw");
836 assert_eq!(cross_signing.self_signing_key, "4jbPt7jh5D2iyM4U+3IDa+WthgJB87IQN1ATdkau+xk");
837 assert_eq!(cross_signing.user_signing_key, "YkFKtkjcsTxF6UAzIIG/l6Nog/G2RigCRfWj3cjNWeM");
838
839 assert_let!(Some(BackupSecrets::MegolmBackupV1Curve25519AesSha2(backup)) = backup);
840 assert_eq!(backup.backup_version, "2");
841 assert_eq!(&backup.key.to_base64(), "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
842
843 let serialized = serde_json::to_value(&message).unwrap();
844 assert_eq!(json, serialized);
845 }
846}