// // Copyright 2020-2021 Signal Messenger, LLC. // SPDX-License-Identifier: AGPL-3.0-only // mod support; use support::*; use futures_util::FutureExt; use libsignal_protocol::*; use rand::rngs::OsRng; use std::convert::TryFrom; use uuid::Uuid; #[test] fn test_server_cert() -> Result<(), SignalProtocolError> { let mut rng = OsRng; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let serialized = server_cert.serialized()?.to_vec(); let recovered = ServerCertificate::deserialize(&serialized)?; assert!(recovered.validate(&trust_root.public_key)?); let mut cert_data = serialized; let cert_bits = cert_data.len() * 8; for b in 0..cert_bits { cert_data[b / 8] ^= 1u8 << (b % 8); // flip a bit let cert = ServerCertificate::deserialize(&cert_data); cert_data[b / 8] ^= 1u8 << (b % 8); // flip the bit back match cert { Ok(cert) => { assert!(!cert.validate(&trust_root.public_key)?); } Err(e) => match e { SignalProtocolError::InvalidProtobufEncoding | SignalProtocolError::ProtobufDecodingError(_) | SignalProtocolError::BadKeyType(_) | SignalProtocolError::BadKeyLength(_, _) => {} unexpected_err => { panic!("unexpected error {:?}", unexpected_err) } }, } } Ok(()) } #[test] fn test_revoked_server_cert() -> Result<(), SignalProtocolError> { let mut rng = OsRng; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let revoked_id = 0xDEADC357; let server_cert = ServerCertificate::new( revoked_id, server_key.public_key, &trust_root.private_key, &mut rng, )?; let serialized = server_cert.serialized()?.to_vec(); let recovered = ServerCertificate::deserialize(&serialized)?; assert!(!recovered.validate(&trust_root.public_key)?); Ok(()) } #[test] fn test_sender_cert() -> Result<(), SignalProtocolError> { let mut rng = OsRng; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let device_id = 42; let expires = 1605722925; let sender_cert = SenderCertificate::new( "9d0652a3-dcc3-4d11-975f-74d61598733f".to_string(), Some("+14152222222".to_string()), key.public_key, device_id, expires, server_cert, &server_key.private_key, &mut rng, )?; assert!(sender_cert.validate(&trust_root.public_key, expires)?); assert!(!sender_cert.validate(&trust_root.public_key, expires + 1)?); // expired let mut sender_cert_data = sender_cert.serialized()?.to_vec(); let sender_cert_bits = sender_cert_data.len() * 8; for b in 0..sender_cert_bits { sender_cert_data[b / 8] ^= 1u8 << (b % 8); // flip a bit let cert = SenderCertificate::deserialize(&sender_cert_data); sender_cert_data[b / 8] ^= 1u8 << (b % 8); // flip the bit back match cert { Ok(cert) => { assert!(!cert.validate(&trust_root.public_key, expires)?); } Err(e) => match e { SignalProtocolError::InvalidProtobufEncoding | SignalProtocolError::ProtobufDecodingError(_) | SignalProtocolError::BadKeyLength(_, _) | SignalProtocolError::BadKeyType(_) => {} unexpected_err => { panic!("unexpected error {:?}", unexpected_err) } }, } } Ok(()) } #[test] fn test_sealed_sender() -> Result<(), SignalProtocolError> { async { let mut rng = OsRng; let alice_device_id = 23; let bob_device_id = 42; let alice_e164 = "+14151111111".to_owned(); let bob_e164 = "+14151114444".to_owned(); let alice_uuid = "9d0652a3-dcc3-4d11-975f-74d61598733f".to_string(); let bob_uuid = "796abedb-ca4e-4f18-8803-1fde5b921f9f".to_string(); let bob_uuid_address = ProtocolAddress::new(bob_uuid.clone(), bob_device_id); let mut alice_store = support::test_in_memory_protocol_store()?; let mut bob_store = support::test_in_memory_protocol_store()?; let alice_pubkey = *alice_store.get_identity_key_pair(None).await?.public_key(); let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut rng).await?; process_prekey_bundle( &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, &bob_pre_key_bundle, &mut rng, None, ) .await?; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let expires = 1605722925; let sender_cert = SenderCertificate::new( alice_uuid.clone(), Some(alice_e164.clone()), alice_pubkey, alice_device_id, expires, server_cert, &server_key.private_key, &mut rng, )?; let alice_ptext = vec![1, 2, 3, 23, 99]; let alice_ctext = sealed_sender_encrypt( &bob_uuid_address, &sender_cert, &alice_ptext, &mut alice_store.session_store, &mut alice_store.identity_store, None, &mut rng, ) .await?; let bob_ptext = sealed_sender_decrypt( &alice_ctext, &trust_root.public_key, expires - 1, Some(bob_e164.clone()), bob_uuid.clone(), bob_device_id, &mut bob_store.identity_store, &mut bob_store.session_store, &mut bob_store.pre_key_store, &mut bob_store.signed_pre_key_store, None, ) .await?; assert_eq!(bob_ptext.message, alice_ptext); assert_eq!(bob_ptext.sender_uuid, alice_uuid); assert_eq!(bob_ptext.sender_e164, Some(alice_e164)); assert_eq!(bob_ptext.device_id, alice_device_id); // Now test but with an expired cert: let alice_ctext = sealed_sender_encrypt( &bob_uuid_address, &sender_cert, &alice_ptext, &mut alice_store.session_store, &mut alice_store.identity_store, None, &mut rng, ) .await?; let bob_ptext = sealed_sender_decrypt( &alice_ctext, &trust_root.public_key, expires + 11, Some(bob_e164.clone()), bob_uuid.clone(), bob_device_id, &mut bob_store.identity_store, &mut bob_store.session_store, &mut bob_store.pre_key_store, &mut bob_store.signed_pre_key_store, None, ) .await; match bob_ptext { Err(SignalProtocolError::InvalidSealedSenderMessage(_)) => { /* ok */ } Err(err) => { panic!("Unexpected error {}", err) } Ok(_) => { panic!("Shouldn't have decrypted") } } // Now test but try to verify using some other trust root let alice_ctext = sealed_sender_encrypt( &bob_uuid_address, &sender_cert, &alice_ptext, &mut alice_store.session_store, &mut alice_store.identity_store, None, &mut rng, ) .await?; let wrong_trust_root = KeyPair::generate(&mut rng); let bob_ptext = sealed_sender_decrypt( &alice_ctext, &wrong_trust_root.public_key, expires - 1, Some(bob_e164.clone()), bob_uuid.clone(), bob_device_id, &mut bob_store.identity_store, &mut bob_store.session_store, &mut bob_store.pre_key_store, &mut bob_store.signed_pre_key_store, None, ) .await; match bob_ptext { Err(SignalProtocolError::InvalidSealedSenderMessage(_)) => { /* ok */ } Err(err) => { panic!("Unexpected error {}", err) } Ok(_) => { panic!("Shouldn't have decrypted") } } Ok(()) } .now_or_never() .expect("sync") } #[test] fn test_sender_key_in_sealed_sender() -> Result<(), SignalProtocolError> { async { let mut rng = OsRng; let alice_device_id = 23; let bob_device_id = 42; let alice_e164 = "+14151111111".to_owned(); let alice_uuid = "9d0652a3-dcc3-4d11-975f-74d61598733f".to_string(); let bob_uuid = "796abedb-ca4e-4f18-8803-1fde5b921f9f".to_string(); let distribution_id = Uuid::from_u128(0xd1d1d1d1_7000_11eb_b32a_33b8a8a487a6); let alice_uuid_address = ProtocolAddress::new(alice_uuid.clone(), 1); let bob_uuid_address = ProtocolAddress::new(bob_uuid.clone(), bob_device_id); let mut alice_store = support::test_in_memory_protocol_store()?; let mut bob_store = support::test_in_memory_protocol_store()?; let alice_pubkey = *alice_store.get_identity_key_pair(None).await?.public_key(); let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut rng).await?; process_prekey_bundle( &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, &bob_pre_key_bundle, &mut rng, None, ) .await?; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let expires = 1605722925; let sender_cert = SenderCertificate::new( alice_uuid.clone(), Some(alice_e164.clone()), alice_pubkey, alice_device_id, expires, server_cert, &server_key.private_key, &mut rng, )?; let distribution_message = create_sender_key_distribution_message( &alice_uuid_address, distribution_id, &mut alice_store, &mut rng, None, ) .await?; process_sender_key_distribution_message( &alice_uuid_address, &distribution_message, &mut bob_store, None, ) .await?; let alice_message = group_encrypt( &mut alice_store, &alice_uuid_address, distribution_id, "swim camp".as_bytes(), &mut rng, None, ) .await?; let alice_usmc = UnidentifiedSenderMessageContent::new( CiphertextMessageType::SenderKey, sender_cert.clone(), alice_message.serialized().to_vec(), ContentHint::Default, None, )?; let alice_ctext = sealed_sender_encrypt_from_usmc( &bob_uuid_address, &alice_usmc, &mut alice_store.identity_store, None, &mut rng, ) .await?; let bob_usmc = sealed_sender_decrypt_to_usmc(&alice_ctext, &mut bob_store.identity_store, None) .await?; assert!(matches!( bob_usmc.msg_type()?, CiphertextMessageType::SenderKey, )); let bob_plaintext = group_decrypt( bob_usmc.contents()?, &mut bob_store, &alice_uuid_address, None, ) .await?; assert_eq!( String::from_utf8(bob_plaintext).expect("valid UTF-8"), "swim camp" ); Ok(()) } .now_or_never() .expect("sync") } #[test] fn test_sealed_sender_multi_recipient() -> Result<(), SignalProtocolError> { async { let mut rng = OsRng; let alice_device_id = 23; let bob_device_id = 42; let alice_e164 = "+14151111111".to_owned(); let bob_e164 = "+14151114444".to_owned(); let alice_uuid = "9d0652a3-dcc3-4d11-975f-74d61598733f".to_string(); let bob_uuid = "796abedb-ca4e-4f18-8803-1fde5b921f9f".to_string(); let bob_uuid_address = ProtocolAddress::new(bob_uuid.clone(), bob_device_id); let mut alice_store = support::test_in_memory_protocol_store()?; let mut bob_store = support::test_in_memory_protocol_store()?; let alice_pubkey = *alice_store.get_identity_key_pair(None).await?.public_key(); let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut rng).await?; process_prekey_bundle( &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, &bob_pre_key_bundle, &mut rng, None, ) .await?; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let expires = 1605722925; let sender_cert = SenderCertificate::new( alice_uuid.clone(), Some(alice_e164.clone()), alice_pubkey, alice_device_id, expires, server_cert, &server_key.private_key, &mut rng, )?; let alice_ptext = vec![1, 2, 3, 23, 99]; let alice_message = message_encrypt( &alice_ptext, &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, None, ) .await?; let alice_usmc = UnidentifiedSenderMessageContent::new( alice_message.message_type(), sender_cert.clone(), alice_message.serialize().to_vec(), ContentHint::Default, None, )?; let recipients = [&bob_uuid_address]; let alice_ctext = sealed_sender_multi_recipient_encrypt( &recipients, &alice_store .session_store .load_existing_sessions(&recipients)?, &alice_usmc, &mut alice_store.identity_store, None, &mut rng, ) .await?; let [bob_ctext] = <[_; 1]>::try_from(sealed_sender_multi_recipient_fan_out(&alice_ctext)?) .expect("only one recipient"); let bob_ptext = sealed_sender_decrypt( &bob_ctext, &trust_root.public_key, expires - 1, Some(bob_e164.clone()), bob_uuid.clone(), bob_device_id, &mut bob_store.identity_store, &mut bob_store.session_store, &mut bob_store.pre_key_store, &mut bob_store.signed_pre_key_store, None, ) .await?; assert_eq!(bob_ptext.message, alice_ptext); assert_eq!(bob_ptext.sender_uuid, alice_uuid); assert_eq!(bob_ptext.sender_e164, Some(alice_e164)); assert_eq!(bob_ptext.device_id, alice_device_id); // Now test but with an expired cert: let alice_message = message_encrypt( &alice_ptext, &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, None, ) .await?; let alice_usmc = UnidentifiedSenderMessageContent::new( alice_message.message_type(), sender_cert.clone(), alice_message.serialize().to_vec(), ContentHint::Default, None, )?; let recipients = [&bob_uuid_address]; let alice_ctext = sealed_sender_multi_recipient_encrypt( &recipients, &alice_store .session_store .load_existing_sessions(&recipients)?, &alice_usmc, &mut alice_store.identity_store, None, &mut rng, ) .await?; let [bob_ctext] = <[_; 1]>::try_from(sealed_sender_multi_recipient_fan_out(&alice_ctext)?) .expect("only one recipient"); let bob_ptext = sealed_sender_decrypt( &bob_ctext, &trust_root.public_key, expires + 11, Some(bob_e164.clone()), bob_uuid.clone(), bob_device_id, &mut bob_store.identity_store, &mut bob_store.session_store, &mut bob_store.pre_key_store, &mut bob_store.signed_pre_key_store, None, ) .await; match bob_ptext { Err(SignalProtocolError::InvalidSealedSenderMessage(_)) => { /* ok */ } Err(err) => { panic!("Unexpected error {}", err) } Ok(_) => { panic!("Shouldn't have decrypted") } } // Now test but try to verify using some other trust root let alice_message = message_encrypt( &alice_ptext, &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, None, ) .await?; let alice_usmc = UnidentifiedSenderMessageContent::new( alice_message.message_type(), sender_cert.clone(), alice_message.serialize().to_vec(), ContentHint::Default, None, )?; let recipients = [&bob_uuid_address]; let alice_ctext = sealed_sender_multi_recipient_encrypt( &recipients, &alice_store .session_store .load_existing_sessions(&recipients)?, &alice_usmc, &mut alice_store.identity_store, None, &mut rng, ) .await?; let wrong_trust_root = KeyPair::generate(&mut rng); let [bob_ctext] = <[_; 1]>::try_from(sealed_sender_multi_recipient_fan_out(&alice_ctext)?) .expect("only one recipient"); let bob_ptext = sealed_sender_decrypt( &bob_ctext, &wrong_trust_root.public_key, expires - 1, Some(bob_e164.clone()), bob_uuid.clone(), bob_device_id, &mut bob_store.identity_store, &mut bob_store.session_store, &mut bob_store.pre_key_store, &mut bob_store.signed_pre_key_store, None, ) .await; match bob_ptext { Err(SignalProtocolError::InvalidSealedSenderMessage(_)) => { /* ok */ } Err(err) => { panic!("Unexpected error {}", err) } Ok(_) => { panic!("Shouldn't have decrypted") } } Ok(()) } .now_or_never() .expect("sync") } #[test] fn test_sealed_sender_multi_recipient_encrypt_with_archived_session( ) -> Result<(), SignalProtocolError> { async { let mut rng = OsRng; let alice_device_id = 23; let bob_device_id = 42; let alice_e164 = "+14151111111".to_owned(); let alice_uuid = "9d0652a3-dcc3-4d11-975f-74d61598733f".to_string(); let bob_uuid = "796abedb-ca4e-4f18-8803-1fde5b921f9f".to_string(); let bob_uuid_address = ProtocolAddress::new(bob_uuid.clone(), bob_device_id); let mut alice_store = support::test_in_memory_protocol_store()?; let mut bob_store = support::test_in_memory_protocol_store()?; let alice_pubkey = *alice_store.get_identity_key_pair(None).await?.public_key(); let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut rng).await?; process_prekey_bundle( &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, &bob_pre_key_bundle, &mut rng, None, ) .await?; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let expires = 1605722925; let sender_cert = SenderCertificate::new( alice_uuid.clone(), Some(alice_e164.clone()), alice_pubkey, alice_device_id, expires, server_cert, &server_key.private_key, &mut rng, )?; let alice_ptext = vec![1, 2, 3, 23, 99]; let alice_message = message_encrypt( &alice_ptext, &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, None, ) .await?; let alice_usmc = UnidentifiedSenderMessageContent::new( alice_message.message_type(), sender_cert.clone(), alice_message.serialize().to_vec(), ContentHint::Default, None, )?; let recipients = [&bob_uuid_address]; let mut session = alice_store .session_store .load_session(&bob_uuid_address, None) .await? .expect("present"); session.archive_current_state()?; match sealed_sender_multi_recipient_encrypt( &recipients, &[&session], &alice_usmc, &mut alice_store.identity_store, None, &mut rng, ) .await { Ok(_) => panic!("should have failed"), Err(e) => { // Make sure we mention *which* recipient's session failed. let description = e.to_string(); assert!( description.contains(&bob_uuid_address.to_string()), "should mention recipient in message \"{}\"", description ); } } Ok(()) } .now_or_never() .expect("sync") } #[test] fn test_sealed_sender_multi_recipient_encrypt_with_bad_registration_id( ) -> Result<(), SignalProtocolError> { async { let mut rng = OsRng; let alice_device_id = 23; let bob_device_id = 42; let alice_e164 = "+14151111111".to_owned(); let alice_uuid = "9d0652a3-dcc3-4d11-975f-74d61598733f".to_string(); let bob_uuid = "796abedb-ca4e-4f18-8803-1fde5b921f9f".to_string(); let bob_uuid_address = ProtocolAddress::new(bob_uuid.clone(), bob_device_id); let mut alice_store = support::test_in_memory_protocol_store()?; let mut bob_store = InMemSignalProtocolStore::new(IdentityKeyPair::generate(&mut rng), 0x4000)?; let alice_pubkey = *alice_store.get_identity_key_pair(None).await?.public_key(); let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut rng).await?; process_prekey_bundle( &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, &bob_pre_key_bundle, &mut rng, None, ) .await?; let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let expires = 1605722925; let sender_cert = SenderCertificate::new( alice_uuid.clone(), Some(alice_e164.clone()), alice_pubkey, alice_device_id, expires, server_cert, &server_key.private_key, &mut rng, )?; let alice_ptext = vec![1, 2, 3, 23, 99]; let alice_message = message_encrypt( &alice_ptext, &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, None, ) .await?; let alice_usmc = UnidentifiedSenderMessageContent::new( alice_message.message_type(), sender_cert.clone(), alice_message.serialize().to_vec(), ContentHint::Default, None, )?; let recipients = [&bob_uuid_address]; match sealed_sender_multi_recipient_encrypt( &recipients, &alice_store .session_store .load_existing_sessions(&recipients)?, &alice_usmc, &mut alice_store.identity_store, None, &mut rng, ) .await { Ok(_) => panic!("should have failed"), Err(SignalProtocolError::InvalidRegistrationId(address, _id)) => { assert_eq!(address, bob_uuid_address); } Err(e) => panic!("wrong error: {}", e), } Ok(()) } .now_or_never() .expect("sync") } #[test] fn test_decryption_error_in_sealed_sender() -> Result<(), SignalProtocolError> { async { let mut rng = OsRng; let alice_device_id = 23; let bob_device_id = 42; let alice_e164 = "+14151111111".to_owned(); let alice_uuid = "9d0652a3-dcc3-4d11-975f-74d61598733f".to_string(); let bob_uuid = "796abedb-ca4e-4f18-8803-1fde5b921f9f".to_string(); let alice_uuid_address = ProtocolAddress::new(alice_uuid.clone(), 1); let bob_uuid_address = ProtocolAddress::new(bob_uuid.clone(), bob_device_id); let mut alice_store = support::test_in_memory_protocol_store()?; let mut bob_store = support::test_in_memory_protocol_store()?; let alice_pubkey = *alice_store.get_identity_key_pair(None).await?.public_key(); let alice_pre_key_bundle = create_pre_key_bundle(&mut alice_store, &mut rng).await?; process_prekey_bundle( &alice_uuid_address, &mut bob_store.session_store, &mut bob_store.identity_store, &alice_pre_key_bundle, &mut rng, None, ) .await?; // Send one message to establish a session. let bob_first_message = message_encrypt( b"swim camp", &alice_uuid_address, &mut bob_store.session_store, &mut bob_store.identity_store, None, ) .await?; message_decrypt( &bob_first_message, &bob_uuid_address, &mut alice_store.session_store, &mut alice_store.identity_store, &mut alice_store.pre_key_store, &mut alice_store.signed_pre_key_store, &mut rng, None, ) .await?; // Pretend the second message fails to decrypt. let bob_message = message_encrypt( b"space camp", &alice_uuid_address, &mut bob_store.session_store, &mut bob_store.identity_store, None, ) .await?; let original_ratchet_key = match bob_message { CiphertextMessage::PreKeySignalMessage(ref m) => m.message().sender_ratchet_key(), _ => panic!("without ACKs, every message should be a PreKeySignalMessage"), }; // Skip over the part where Bob sends this to Alice and Alice fails to decrypt it, // for whatever reason. let trust_root = KeyPair::generate(&mut rng); let server_key = KeyPair::generate(&mut rng); let server_cert = ServerCertificate::new(1, server_key.public_key, &trust_root.private_key, &mut rng)?; let expires = 1605722925; let sender_cert = SenderCertificate::new( alice_uuid.clone(), Some(alice_e164.clone()), alice_pubkey, alice_device_id, expires, server_cert, &server_key.private_key, &mut rng, )?; let error_message = DecryptionErrorMessage::for_original( bob_message.serialize(), bob_message.message_type(), 408, 5, )?; let error_message_content = PlaintextContent::from(error_message); let error_message_usmc = UnidentifiedSenderMessageContent::new( CiphertextMessageType::Plaintext, sender_cert.clone(), error_message_content.serialized().to_vec(), ContentHint::Default, None, )?; let alice_ctext = sealed_sender_encrypt_from_usmc( &bob_uuid_address, &error_message_usmc, &mut alice_store.identity_store, None, &mut rng, ) .await?; let bob_usmc = sealed_sender_decrypt_to_usmc(&alice_ctext, &mut bob_store.identity_store, None) .await?; assert!(matches!( bob_usmc.msg_type()?, CiphertextMessageType::Plaintext, )); let bob_plaintext = PlaintextContent::try_from(bob_usmc.contents()?)?; let bob_error_message = extract_decryption_error_message_from_serialized_content(bob_plaintext.body()) .expect("present"); assert_eq!(bob_error_message.ratchet_key(), Some(original_ratchet_key)); assert_eq!(bob_error_message.timestamp(), 408); assert_eq!(bob_error_message.device_id(), 5); Ok(()) } .now_or_never() .expect("sync") }