//
// Copyright 2020 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//

mod support;

use futures_util::FutureExt;
use libsignal_protocol::*;
use rand::rngs::OsRng;
use std::convert::TryFrom;
use support::*;

#[test]
#[allow(clippy::eval_order_dependence)]
fn test_basic_prekey_v3() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let bob_pre_key_pair = KeyPair::generate(&mut csprng);
        let bob_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let bob_signed_pre_key_public = bob_signed_pre_key_pair.public_key.serialize();
        let bob_signed_pre_key_signature = bob_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&bob_signed_pre_key_public, &mut csprng)?;

        let pre_key_id = 31337;
        let signed_pre_key_id = 22;

        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1,                                               // device id
            Some((pre_key_id, bob_pre_key_pair.public_key)), // pre key
            signed_pre_key_id,                               // signed pre key id
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature.to_vec(),
            *bob_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        assert!(alice_store
            .load_session(&bob_address, None)
            .await?
            .is_some());
        assert_eq!(
            alice_store
                .load_session(&bob_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );

        let original_message = "L'homme est condamné à être libre";

        let outgoing_message = encrypt(&mut alice_store, &bob_address, original_message).await?;

        assert_eq!(
            outgoing_message.message_type(),
            CiphertextMessageType::PreKey
        );

        let incoming_message = CiphertextMessage::PreKeySignalMessage(
            PreKeySignalMessage::try_from(outgoing_message.serialize())?,
        );

        bob_store
            .save_pre_key(
                pre_key_id,
                &PreKeyRecord::new(pre_key_id, &bob_pre_key_pair),
                None,
            )
            .await?;
        bob_store
            .save_signed_pre_key(
                signed_pre_key_id,
                &SignedPreKeyRecord::new(
                    signed_pre_key_id,
                    /*timestamp*/ 42,
                    &bob_signed_pre_key_pair,
                    &bob_signed_pre_key_signature,
                ),
                None,
            )
            .await?;

        let ptext = decrypt(&mut bob_store, &alice_address, &incoming_message).await?;

        assert_eq!(
            String::from_utf8(ptext).expect("valid utf8"),
            original_message
        );

        let bobs_response = "Who watches the watchers?";

        assert!(bob_store
            .load_session(&alice_address, None)
            .await?
            .is_some());
        let bobs_session_with_alice = bob_store
            .load_session(&alice_address, None)
            .await?
            .expect("session found");
        assert_eq!(bobs_session_with_alice.session_version()?, 3);
        assert_eq!(bobs_session_with_alice.alice_base_key()?.len(), 32 + 1);

        let bob_outgoing = encrypt(&mut bob_store, &alice_address, bobs_response).await?;

        assert_eq!(bob_outgoing.message_type(), CiphertextMessageType::Whisper);

        let alice_decrypts = decrypt(&mut alice_store, &bob_address, &bob_outgoing).await?;

        assert_eq!(
            String::from_utf8(alice_decrypts).expect("valid utf8"),
            bobs_response
        );

        run_interaction(
            &mut alice_store,
            &alice_address,
            &mut bob_store,
            &bob_address,
        )
        .await?;

        let mut alice_store = support::test_in_memory_protocol_store()?;

        let bob_pre_key_pair = KeyPair::generate(&mut csprng);
        let bob_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let bob_signed_pre_key_public = bob_signed_pre_key_pair.public_key.serialize();
        let bob_signed_pre_key_signature = bob_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&bob_signed_pre_key_public, &mut csprng)?;

        let pre_key_id = 31337;
        let signed_pre_key_id = 22;

        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1,                                                   // device id
            Some((pre_key_id + 1, bob_pre_key_pair.public_key)), // pre key,
            signed_pre_key_id + 1,
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature.to_vec(),
            *bob_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        bob_store
            .save_pre_key(
                pre_key_id + 1,
                &PreKeyRecord::new(pre_key_id + 1, &bob_pre_key_pair),
                None,
            )
            .await?;
        bob_store
            .save_signed_pre_key(
                signed_pre_key_id + 1,
                &SignedPreKeyRecord::new(
                    signed_pre_key_id + 1,
                    /*timestamp*/ 42,
                    &bob_signed_pre_key_pair,
                    &bob_signed_pre_key_signature,
                ),
                None,
            )
            .await?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        let outgoing_message = encrypt(&mut alice_store, &bob_address, original_message).await?;

        assert!(matches!(
            decrypt(&mut bob_store, &alice_address, &outgoing_message)
                .await
                .unwrap_err(),
            SignalProtocolError::UntrustedIdentity(a) if a == alice_address
        ));

        assert!(
            bob_store
                .save_identity(
                    &alice_address,
                    alice_store
                        .get_identity_key_pair(None)
                        .await?
                        .identity_key(),
                    None,
                )
                .await?
        );

        let decrypted = decrypt(&mut bob_store, &alice_address, &outgoing_message).await?;
        assert_eq!(
            String::from_utf8(decrypted).expect("valid utf8"),
            original_message
        );

        // Sign pre-key with wrong key:
        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1,                                               // device id
            Some((pre_key_id, bob_pre_key_pair.public_key)), // pre key
            signed_pre_key_id,
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature.to_vec(),
            *alice_store
                .get_identity_key_pair(None)
                .await?
                .identity_key(),
        )?;

        assert!(process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await
        .is_err());

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
#[ignore = "slow to run locally"]
#[allow(clippy::eval_order_dependence)]
fn chain_jump_over_limit() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let bob_pre_key_pair = KeyPair::generate(&mut csprng);
        let bob_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let bob_signed_pre_key_public = bob_signed_pre_key_pair.public_key.serialize();
        let bob_signed_pre_key_signature = bob_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&bob_signed_pre_key_public, &mut csprng)?;

        let pre_key_id = 31337;
        let signed_pre_key_id = 22;

        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1,                                               // device id
            Some((pre_key_id, bob_pre_key_pair.public_key)), // pre key
            signed_pre_key_id,                               // signed pre key id
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature.to_vec(),
            *bob_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        bob_store
            .save_pre_key(
                pre_key_id,
                &PreKeyRecord::new(pre_key_id, &bob_pre_key_pair),
                None,
            )
            .await?;
        bob_store
            .save_signed_pre_key(
                signed_pre_key_id,
                &SignedPreKeyRecord::new(
                    signed_pre_key_id,
                    /*timestamp*/ 42,
                    &bob_signed_pre_key_pair,
                    &bob_signed_pre_key_signature,
                ),
                None,
            )
            .await?;

        // Same as library consts.rs
        pub const MAX_FORWARD_JUMPS: usize = 25_000;

        for _i in 0..(MAX_FORWARD_JUMPS + 1) {
            let _msg = encrypt(
                &mut alice_store,
                &bob_address,
                "Yet another message for you",
            )
            .await?;
        }

        let too_far = encrypt(&mut alice_store, &bob_address, "Now you have gone too far").await?;

        assert!(decrypt(&mut bob_store, &alice_address, &too_far)
            .await
            .is_err());
        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
#[ignore = "slow to run locally"]
#[allow(clippy::eval_order_dependence)]
fn chain_jump_over_limit_with_self() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let a1_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let a2_address = ProtocolAddress::new("+14151111111".to_owned(), 2);

        let mut a1_store = support::test_in_memory_protocol_store()?;
        let mut a2_store = a1_store.clone(); // same key!

        let a2_pre_key_pair = KeyPair::generate(&mut csprng);
        let a2_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let a2_signed_pre_key_public = a2_signed_pre_key_pair.public_key.serialize();
        let a2_signed_pre_key_signature = a2_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&a2_signed_pre_key_public, &mut csprng)?;

        let pre_key_id = 31337;
        let signed_pre_key_id = 22;

        let a2_pre_key_bundle = PreKeyBundle::new(
            a2_store.get_local_registration_id(None).await?,
            1,                                              // device id
            Some((pre_key_id, a2_pre_key_pair.public_key)), // pre key
            signed_pre_key_id,                              // signed pre key id
            a2_signed_pre_key_pair.public_key,
            a2_signed_pre_key_signature.to_vec(),
            *a2_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        process_prekey_bundle(
            &a2_address,
            &mut a1_store.session_store,
            &mut a1_store.identity_store,
            &a2_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        a2_store
            .save_pre_key(
                pre_key_id,
                &PreKeyRecord::new(pre_key_id, &a2_pre_key_pair),
                None,
            )
            .await?;
        a2_store
            .save_signed_pre_key(
                signed_pre_key_id,
                &SignedPreKeyRecord::new(
                    signed_pre_key_id,
                    /*timestamp*/ 42,
                    &a2_signed_pre_key_pair,
                    &a2_signed_pre_key_signature,
                ),
                None,
            )
            .await?;

        // Same as library consts.rs
        pub const MAX_FORWARD_JUMPS: usize = 25_000;

        for _i in 0..(MAX_FORWARD_JUMPS + 1) {
            let _msg = encrypt(
                &mut a1_store,
                &a2_address,
                "Yet another message for youself",
            )
            .await?;
        }

        let too_far = encrypt(
            &mut a1_store,
            &a2_address,
            "This is the song that never ends",
        )
        .await?;

        let ptext = decrypt(&mut a2_store, &a1_address, &too_far).await?;
        assert_eq!(
            String::from_utf8(ptext).unwrap(),
            "This is the song that never ends"
        );

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
#[allow(clippy::eval_order_dependence)]
fn test_bad_signed_pre_key_signature() -> Result<(), SignalProtocolError> {
    async {
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let bob_store = support::test_in_memory_protocol_store()?;

        let mut csprng = OsRng;
        let bob_pre_key_pair = KeyPair::generate(&mut csprng);
        let bob_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let bob_signed_pre_key_public = bob_signed_pre_key_pair.public_key.serialize();
        let bob_signed_pre_key_signature = bob_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&bob_signed_pre_key_public, &mut csprng)?
            .to_vec();

        let pre_key_id = 31337;
        let signed_pre_key_id = 22;

        for bit in 0..8 * bob_signed_pre_key_signature.len() {
            let mut bad_signature = bob_signed_pre_key_signature.clone();

            bad_signature[bit / 8] ^= 0x01u8 << (bit % 8);

            let bob_pre_key_bundle = PreKeyBundle::new(
                bob_store.get_local_registration_id(None).await?,
                1,
                Some((pre_key_id, bob_pre_key_pair.public_key)),
                signed_pre_key_id,
                bob_signed_pre_key_pair.public_key,
                bad_signature,
                *bob_store.get_identity_key_pair(None).await?.identity_key(),
            )?;

            assert!(process_prekey_bundle(
                &bob_address,
                &mut alice_store.session_store,
                &mut alice_store.identity_store,
                &bob_pre_key_bundle,
                &mut csprng,
                None,
            )
            .await
            .is_err());
        }

        // Finally check that the non-corrupted signature is accepted:

        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1,
            Some((pre_key_id, bob_pre_key_pair.public_key)),
            signed_pre_key_id,
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature,
            *bob_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

// testRepeatBundleMessageV2 cannot be represented

#[test]
#[allow(clippy::eval_order_dependence)]
fn repeat_bundle_message_v3() -> Result<(), SignalProtocolError> {
    async {
        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let mut csprng = OsRng;
        let bob_pre_key_pair = KeyPair::generate(&mut csprng);
        let bob_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let bob_signed_pre_key_public = bob_signed_pre_key_pair.public_key.serialize();
        let bob_signed_pre_key_signature = bob_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&bob_signed_pre_key_public, &mut csprng)?;

        let pre_key_id = 31337;
        let signed_pre_key_id = 22;

        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1,                                               // device id
            Some((pre_key_id, bob_pre_key_pair.public_key)), // pre key
            signed_pre_key_id,                               // signed pre key id
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature.to_vec(),
            *bob_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        assert!(alice_store
            .load_session(&bob_address, None)
            .await?
            .is_some());
        assert_eq!(
            alice_store
                .load_session(&bob_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );

        let original_message = "L'homme est condamné à être libre";

        let outgoing_message1 = encrypt(&mut alice_store, &bob_address, original_message).await?;
        let outgoing_message2 = encrypt(&mut alice_store, &bob_address, original_message).await?;

        assert_eq!(
            outgoing_message1.message_type(),
            CiphertextMessageType::PreKey
        );
        assert_eq!(
            outgoing_message2.message_type(),
            CiphertextMessageType::PreKey
        );

        let incoming_message = CiphertextMessage::PreKeySignalMessage(
            PreKeySignalMessage::try_from(outgoing_message1.serialize())?,
        );

        bob_store
            .save_pre_key(
                pre_key_id,
                &PreKeyRecord::new(pre_key_id, &bob_pre_key_pair),
                None,
            )
            .await?;
        bob_store
            .save_signed_pre_key(
                signed_pre_key_id,
                &SignedPreKeyRecord::new(
                    signed_pre_key_id,
                    /*timestamp*/ 42,
                    &bob_signed_pre_key_pair,
                    &bob_signed_pre_key_signature,
                ),
                None,
            )
            .await?;

        let ptext = decrypt(&mut bob_store, &alice_address, &incoming_message).await?;
        assert_eq!(
            String::from_utf8(ptext).expect("valid utf8"),
            original_message
        );

        let bob_outgoing = encrypt(&mut bob_store, &alice_address, original_message).await?;
        assert_eq!(bob_outgoing.message_type(), CiphertextMessageType::Whisper);
        let alice_decrypts = decrypt(&mut alice_store, &bob_address, &bob_outgoing).await?;
        assert_eq!(
            String::from_utf8(alice_decrypts).expect("valid utf8"),
            original_message
        );

        // The test

        let incoming_message2 = CiphertextMessage::PreKeySignalMessage(
            PreKeySignalMessage::try_from(outgoing_message2.serialize())?,
        );

        let ptext = decrypt(&mut bob_store, &alice_address, &incoming_message2).await?;
        assert_eq!(
            String::from_utf8(ptext).expect("valid utf8"),
            original_message
        );

        let bob_outgoing = encrypt(&mut bob_store, &alice_address, original_message).await?;
        let alice_decrypts = decrypt(&mut alice_store, &bob_address, &bob_outgoing).await?;
        assert_eq!(
            String::from_utf8(alice_decrypts).expect("valid utf8"),
            original_message
        );

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
#[allow(clippy::eval_order_dependence)]
fn bad_message_bundle() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let bob_pre_key_pair = KeyPair::generate(&mut csprng);
        let bob_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let bob_signed_pre_key_public = bob_signed_pre_key_pair.public_key.serialize();
        let bob_signed_pre_key_signature = bob_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&bob_signed_pre_key_public, &mut csprng)?;

        let pre_key_id = 31337;
        let signed_pre_key_id = 22;

        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1, // device id
            Some((pre_key_id, bob_pre_key_pair.public_key)),
            signed_pre_key_id, // signed pre key id
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature.to_vec(),
            *bob_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        bob_store
            .save_pre_key(
                pre_key_id,
                &PreKeyRecord::new(pre_key_id, &bob_pre_key_pair),
                None,
            )
            .await?;
        bob_store
            .save_signed_pre_key(
                signed_pre_key_id,
                &SignedPreKeyRecord::new(
                    signed_pre_key_id,
                    /*timestamp*/ 42,
                    &bob_signed_pre_key_pair,
                    &bob_signed_pre_key_signature,
                ),
                None,
            )
            .await?;

        assert!(alice_store
            .load_session(&bob_address, None)
            .await?
            .is_some());
        assert_eq!(
            alice_store
                .load_session(&bob_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );

        let original_message = "L'homme est condamné à être libre";

        assert!(bob_store.get_pre_key(pre_key_id, None).await.is_ok());
        let outgoing_message = encrypt(&mut alice_store, &bob_address, original_message).await?;

        assert_eq!(
            outgoing_message.message_type(),
            CiphertextMessageType::PreKey
        );

        let outgoing_message = outgoing_message.serialize().to_vec();

        let mut corrupted_message: Vec<u8> = outgoing_message.clone();
        corrupted_message[outgoing_message.len() - 10] ^= 1;

        let incoming_message = CiphertextMessage::PreKeySignalMessage(
            PreKeySignalMessage::try_from(corrupted_message.as_slice())?,
        );

        assert!(decrypt(&mut bob_store, &alice_address, &incoming_message)
            .await
            .is_err());
        assert!(bob_store.get_pre_key(pre_key_id, None).await.is_ok());

        let incoming_message = CiphertextMessage::PreKeySignalMessage(
            PreKeySignalMessage::try_from(outgoing_message.as_slice())?,
        );

        let ptext = decrypt(&mut bob_store, &alice_address, &incoming_message).await?;

        assert_eq!(
            String::from_utf8(ptext).expect("valid utf8"),
            original_message
        );
        assert!(matches!(
            bob_store.get_pre_key(pre_key_id, None).await.unwrap_err(),
            SignalProtocolError::InvalidPreKeyId
        ));

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
#[allow(clippy::eval_order_dependence)]
fn optional_one_time_prekey() -> Result<(), SignalProtocolError> {
    async {
        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let mut csprng = OsRng;
        let bob_signed_pre_key_pair = KeyPair::generate(&mut csprng);

        let bob_signed_pre_key_public = bob_signed_pre_key_pair.public_key.serialize();
        let bob_signed_pre_key_signature = bob_store
            .get_identity_key_pair(None)
            .await?
            .private_key()
            .calculate_signature(&bob_signed_pre_key_public, &mut csprng)?;

        let signed_pre_key_id = 22;

        let bob_pre_key_bundle = PreKeyBundle::new(
            bob_store.get_local_registration_id(None).await?,
            1,                 // device id
            None,              // no pre key
            signed_pre_key_id, // signed pre key id
            bob_signed_pre_key_pair.public_key,
            bob_signed_pre_key_signature.to_vec(),
            *bob_store.get_identity_key_pair(None).await?.identity_key(),
        )?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        assert_eq!(
            alice_store
                .load_session(&bob_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );

        let original_message = "L'homme est condamné à être libre";

        let outgoing_message = encrypt(&mut alice_store, &bob_address, original_message).await?;

        assert_eq!(
            outgoing_message.message_type(),
            CiphertextMessageType::PreKey
        );

        let incoming_message = CiphertextMessage::PreKeySignalMessage(
            PreKeySignalMessage::try_from(outgoing_message.serialize())?,
        );

        bob_store
            .save_signed_pre_key(
                signed_pre_key_id,
                &SignedPreKeyRecord::new(
                    signed_pre_key_id,
                    /*timestamp*/ 42,
                    &bob_signed_pre_key_pair,
                    &bob_signed_pre_key_signature,
                ),
                None,
            )
            .await?;

        let ptext = decrypt(&mut bob_store, &alice_address, &incoming_message).await?;

        assert_eq!(
            String::from_utf8(ptext).expect("valid utf8"),
            original_message
        );

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
fn basic_session_v3() -> Result<(), SignalProtocolError> {
    let (alice_session, bob_session) = initialize_sessions_v3()?;
    run_session_interaction(alice_session, bob_session)?;
    Ok(())
}

#[test]
fn message_key_limits() -> Result<(), SignalProtocolError> {
    async {
        let (alice_session_record, bob_session_record) = initialize_sessions_v3()?;

        let alice_address = ProtocolAddress::new("+14159999999".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14158888888".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        alice_store
            .store_session(&bob_address, &alice_session_record, None)
            .await?;
        bob_store
            .store_session(&alice_address, &bob_session_record, None)
            .await?;

        const MAX_MESSAGE_KEYS: usize = 2000; // same value as in library
        const TOO_MANY_MESSAGES: usize = MAX_MESSAGE_KEYS + 300;

        let mut inflight = Vec::with_capacity(TOO_MANY_MESSAGES);

        for i in 0..TOO_MANY_MESSAGES {
            inflight
                .push(encrypt(&mut alice_store, &bob_address, &format!("It's over {}", i)).await?);
        }

        assert_eq!(
            String::from_utf8(decrypt(&mut bob_store, &alice_address, &inflight[1000]).await?)
                .expect("valid utf8"),
            "It's over 1000"
        );
        assert_eq!(
            String::from_utf8(
                decrypt(
                    &mut bob_store,
                    &alice_address,
                    &inflight[TOO_MANY_MESSAGES - 1]
                )
                .await?
            )
            .expect("valid utf8"),
            format!("It's over {}", TOO_MANY_MESSAGES - 1)
        );

        let err = decrypt(&mut bob_store, &alice_address, &inflight[5])
            .await
            .unwrap_err();
        assert!(matches!(
            err,
            SignalProtocolError::DuplicatedMessage(2300, 5)
        ));
        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[allow(clippy::needless_range_loop)]
fn run_session_interaction(
    alice_session: SessionRecord,
    bob_session: SessionRecord,
) -> Result<(), SignalProtocolError> {
    async {
        use rand::seq::SliceRandom;

        let alice_address = ProtocolAddress::new("+14159999999".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14158888888".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        alice_store
            .store_session(&bob_address, &alice_session, None)
            .await?;
        bob_store
            .store_session(&alice_address, &bob_session, None)
            .await?;

        let alice_plaintext = "This is Alice's message";
        let alice_ciphertext = encrypt(&mut alice_store, &bob_address, alice_plaintext).await?;
        let bob_decrypted = decrypt(&mut bob_store, &alice_address, &alice_ciphertext).await?;
        assert_eq!(
            String::from_utf8(bob_decrypted).expect("valid utf8"),
            alice_plaintext
        );

        let bob_plaintext = "This is Bob's reply";

        let bob_ciphertext = encrypt(&mut bob_store, &alice_address, bob_plaintext).await?;
        let alice_decrypted = decrypt(&mut alice_store, &bob_address, &bob_ciphertext).await?;
        assert_eq!(
            String::from_utf8(alice_decrypted).expect("valid utf8"),
            bob_plaintext
        );

        const ALICE_MESSAGE_COUNT: usize = 50;
        const BOB_MESSAGE_COUNT: usize = 50;

        let mut alice_messages = Vec::with_capacity(ALICE_MESSAGE_COUNT);

        for i in 0..ALICE_MESSAGE_COUNT {
            let ptext = format!("смерть за смерть {}", i);
            let ctext = encrypt(&mut alice_store, &bob_address, &ptext).await?;
            alice_messages.push((ptext, ctext));
        }

        let mut rng = rand::rngs::OsRng;

        alice_messages.shuffle(&mut rng);

        for i in 0..ALICE_MESSAGE_COUNT / 2 {
            let ptext = decrypt(&mut bob_store, &alice_address, &alice_messages[i].1).await?;
            assert_eq!(
                String::from_utf8(ptext).expect("valid utf8"),
                alice_messages[i].0
            );
        }

        let mut bob_messages = Vec::with_capacity(BOB_MESSAGE_COUNT);

        for i in 0..BOB_MESSAGE_COUNT {
            let ptext = format!("Relax in the safety of your own delusions. {}", i);
            let ctext = encrypt(&mut bob_store, &alice_address, &ptext).await?;
            bob_messages.push((ptext, ctext));
        }

        bob_messages.shuffle(&mut rng);

        for i in 0..BOB_MESSAGE_COUNT / 2 {
            let ptext = decrypt(&mut alice_store, &bob_address, &bob_messages[i].1).await?;
            assert_eq!(
                String::from_utf8(ptext).expect("valid utf8"),
                bob_messages[i].0
            );
        }

        for i in ALICE_MESSAGE_COUNT / 2..ALICE_MESSAGE_COUNT {
            let ptext = decrypt(&mut bob_store, &alice_address, &alice_messages[i].1).await?;
            assert_eq!(
                String::from_utf8(ptext).expect("valid utf8"),
                alice_messages[i].0
            );
        }

        for i in BOB_MESSAGE_COUNT / 2..BOB_MESSAGE_COUNT {
            let ptext = decrypt(&mut alice_store, &bob_address, &bob_messages[i].1).await?;
            assert_eq!(
                String::from_utf8(ptext).expect("valid utf8"),
                bob_messages[i].0
            );
        }

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

async fn run_interaction(
    alice_store: &mut InMemSignalProtocolStore,
    alice_address: &ProtocolAddress,
    bob_store: &mut InMemSignalProtocolStore,
    bob_address: &ProtocolAddress,
) -> Result<(), SignalProtocolError> {
    let alice_ptext = "It's rabbit season";

    let alice_message = encrypt(alice_store, bob_address, alice_ptext).await?;
    assert_eq!(alice_message.message_type(), CiphertextMessageType::Whisper);
    assert_eq!(
        String::from_utf8(decrypt(bob_store, alice_address, &alice_message).await?)
            .expect("valid utf8"),
        alice_ptext
    );

    let bob_ptext = "It's duck season";

    let bob_message = encrypt(bob_store, alice_address, bob_ptext).await?;
    assert_eq!(bob_message.message_type(), CiphertextMessageType::Whisper);
    assert_eq!(
        String::from_utf8(decrypt(alice_store, bob_address, &bob_message).await?)
            .expect("valid utf8"),
        bob_ptext
    );

    for i in 0..10 {
        let alice_ptext = format!("A->B message {}", i);
        let alice_message = encrypt(alice_store, bob_address, &alice_ptext).await?;
        assert_eq!(alice_message.message_type(), CiphertextMessageType::Whisper);
        assert_eq!(
            String::from_utf8(decrypt(bob_store, alice_address, &alice_message).await?)
                .expect("valid utf8"),
            alice_ptext
        );
    }

    for i in 0..10 {
        let bob_ptext = format!("B->A message {}", i);
        let bob_message = encrypt(bob_store, alice_address, &bob_ptext).await?;
        assert_eq!(bob_message.message_type(), CiphertextMessageType::Whisper);
        assert_eq!(
            String::from_utf8(decrypt(alice_store, bob_address, &bob_message).await?)
                .expect("valid utf8"),
            bob_ptext
        );
    }

    let mut alice_ooo_messages = vec![];

    for i in 0..10 {
        let alice_ptext = format!("A->B OOO message {}", i);
        let alice_message = encrypt(alice_store, bob_address, &alice_ptext).await?;
        alice_ooo_messages.push((alice_ptext, alice_message));
    }

    for i in 0..10 {
        let alice_ptext = format!("A->B post-OOO message {}", i);
        let alice_message = encrypt(alice_store, bob_address, &alice_ptext).await?;
        assert_eq!(alice_message.message_type(), CiphertextMessageType::Whisper);
        assert_eq!(
            String::from_utf8(decrypt(bob_store, alice_address, &alice_message).await?)
                .expect("valid utf8"),
            alice_ptext
        );
    }

    for i in 0..10 {
        let bob_ptext = format!("B->A message post-OOO {}", i);
        let bob_message = encrypt(bob_store, alice_address, &bob_ptext).await?;
        assert_eq!(bob_message.message_type(), CiphertextMessageType::Whisper);
        assert_eq!(
            String::from_utf8(decrypt(alice_store, bob_address, &bob_message).await?)
                .expect("valid utf8"),
            bob_ptext
        );
    }

    for (ptext, ctext) in alice_ooo_messages {
        assert_eq!(
            String::from_utf8(decrypt(bob_store, alice_address, &ctext).await?)
                .expect("valid utf8"),
            ptext
        );
    }

    Ok(())
}

#[allow(clippy::eval_order_dependence)]
async fn is_session_id_equal(
    alice_store: &dyn ProtocolStore,
    alice_address: &ProtocolAddress,
    bob_store: &dyn ProtocolStore,
    bob_address: &ProtocolAddress,
) -> Result<bool, SignalProtocolError> {
    Ok(alice_store
        .load_session(bob_address, None)
        .await?
        .expect("session found")
        .alice_base_key()?
        == bob_store
            .load_session(alice_address, None)
            .await?
            .expect("session found")
            .alice_base_key()?)
}

#[test]
fn basic_simultaneous_initiate() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let alice_pre_key_bundle = create_pre_key_bundle(&mut alice_store, &mut csprng).await?;
        let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut csprng).await?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        process_prekey_bundle(
            &alice_address,
            &mut bob_store.session_store,
            &mut bob_store.identity_store,
            &alice_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        let message_for_bob = encrypt(&mut alice_store, &bob_address, "hi bob").await?;
        let message_for_alice = encrypt(&mut bob_store, &alice_address, "hi alice").await?;

        assert_eq!(
            message_for_bob.message_type(),
            CiphertextMessageType::PreKey
        );
        assert_eq!(
            message_for_alice.message_type(),
            CiphertextMessageType::PreKey
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let alice_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                message_for_alice.serialize(),
            )?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(alice_plaintext).expect("valid utf8"),
            "hi alice"
        );

        let bob_plaintext = decrypt(
            &mut bob_store,
            &alice_address,
            &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                message_for_bob.serialize(),
            )?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(bob_plaintext).expect("valid utf8"),
            "hi bob"
        );

        assert_eq!(
            alice_store
                .load_session(&bob_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );
        assert_eq!(
            bob_store
                .load_session(&alice_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let alice_response = encrypt(&mut alice_store, &bob_address, "nice to see you").await?;

        assert_eq!(
            alice_response.message_type(),
            CiphertextMessageType::Whisper
        );

        let response_plaintext = decrypt(
            &mut bob_store,
            &alice_address,
            &CiphertextMessage::SignalMessage(SignalMessage::try_from(alice_response.serialize())?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "nice to see you"
        );

        assert!(is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?);

        let bob_response = encrypt(&mut bob_store, &alice_address, "you as well").await?;

        assert_eq!(bob_response.message_type(), CiphertextMessageType::Whisper);

        let response_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::SignalMessage(SignalMessage::try_from(bob_response.serialize())?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "you as well"
        );

        assert!(is_session_id_equal(&bob_store, &bob_address, &alice_store, &alice_address).await?);

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
fn simultaneous_initiate_with_lossage() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let alice_pre_key_bundle = create_pre_key_bundle(&mut alice_store, &mut csprng).await?;
        let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut csprng).await?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        process_prekey_bundle(
            &alice_address,
            &mut bob_store.session_store,
            &mut bob_store.identity_store,
            &alice_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        let message_for_bob = encrypt(&mut alice_store, &bob_address, "hi bob").await?;
        let message_for_alice = encrypt(&mut bob_store, &alice_address, "hi alice").await?;

        assert_eq!(
            message_for_bob.message_type(),
            CiphertextMessageType::PreKey
        );
        assert_eq!(
            message_for_alice.message_type(),
            CiphertextMessageType::PreKey
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let bob_plaintext = decrypt(
            &mut bob_store,
            &alice_address,
            &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                message_for_bob.serialize(),
            )?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(bob_plaintext).expect("valid utf8"),
            "hi bob"
        );

        assert_eq!(
            alice_store
                .load_session(&bob_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );
        assert_eq!(
            bob_store
                .load_session(&alice_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );

        let alice_response = encrypt(&mut alice_store, &bob_address, "nice to see you").await?;

        assert_eq!(alice_response.message_type(), CiphertextMessageType::PreKey);

        let response_plaintext = decrypt(
            &mut bob_store,
            &alice_address,
            &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                alice_response.serialize(),
            )?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "nice to see you"
        );

        assert!(is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?);

        let bob_response = encrypt(&mut bob_store, &alice_address, "you as well").await?;

        assert_eq!(bob_response.message_type(), CiphertextMessageType::Whisper);

        let response_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::SignalMessage(SignalMessage::try_from(bob_response.serialize())?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "you as well"
        );

        assert!(is_session_id_equal(&bob_store, &bob_address, &alice_store, &alice_address).await?);

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
fn simultaneous_initiate_lost_message() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let alice_pre_key_bundle = create_pre_key_bundle(&mut alice_store, &mut csprng).await?;
        let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut csprng).await?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        process_prekey_bundle(
            &alice_address,
            &mut bob_store.session_store,
            &mut bob_store.identity_store,
            &alice_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;

        let message_for_bob = encrypt(&mut alice_store, &bob_address, "hi bob").await?;
        let message_for_alice = encrypt(&mut bob_store, &alice_address, "hi alice").await?;

        assert_eq!(
            message_for_bob.message_type(),
            CiphertextMessageType::PreKey
        );
        assert_eq!(
            message_for_alice.message_type(),
            CiphertextMessageType::PreKey
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let alice_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                message_for_alice.serialize(),
            )?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(alice_plaintext).expect("valid utf8"),
            "hi alice"
        );

        let bob_plaintext = decrypt(
            &mut bob_store,
            &alice_address,
            &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                message_for_bob.serialize(),
            )?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(bob_plaintext).expect("valid utf8"),
            "hi bob"
        );

        assert_eq!(
            alice_store
                .load_session(&bob_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );
        assert_eq!(
            bob_store
                .load_session(&alice_address, None)
                .await?
                .expect("session found")
                .session_version()?,
            3
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let alice_response = encrypt(&mut alice_store, &bob_address, "nice to see you").await?;

        assert_eq!(
            alice_response.message_type(),
            CiphertextMessageType::Whisper
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let bob_response = encrypt(&mut bob_store, &alice_address, "you as well").await?;

        assert_eq!(bob_response.message_type(), CiphertextMessageType::Whisper);

        let response_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::SignalMessage(SignalMessage::try_from(bob_response.serialize())?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "you as well"
        );

        assert!(is_session_id_equal(&bob_store, &bob_address, &alice_store, &alice_address).await?);

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
fn simultaneous_initiate_repeated_messages() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        for _ in 0..15 {
            let alice_pre_key_bundle = create_pre_key_bundle(&mut alice_store, &mut csprng).await?;
            let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut csprng).await?;

            process_prekey_bundle(
                &bob_address,
                &mut alice_store.session_store,
                &mut alice_store.identity_store,
                &bob_pre_key_bundle,
                &mut csprng,
                None,
            )
            .await?;

            process_prekey_bundle(
                &alice_address,
                &mut bob_store.session_store,
                &mut bob_store.identity_store,
                &alice_pre_key_bundle,
                &mut csprng,
                None,
            )
            .await?;

            let message_for_bob = encrypt(&mut alice_store, &bob_address, "hi bob").await?;
            let message_for_alice = encrypt(&mut bob_store, &alice_address, "hi alice").await?;

            assert_eq!(
                message_for_bob.message_type(),
                CiphertextMessageType::PreKey
            );
            assert_eq!(
                message_for_alice.message_type(),
                CiphertextMessageType::PreKey
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );

            let alice_plaintext = decrypt(
                &mut alice_store,
                &bob_address,
                &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                    message_for_alice.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(alice_plaintext).expect("valid utf8"),
                "hi alice"
            );

            let bob_plaintext = decrypt(
                &mut bob_store,
                &alice_address,
                &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                    message_for_bob.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(bob_plaintext).expect("valid utf8"),
                "hi bob"
            );

            assert_eq!(
                alice_store
                    .load_session(&bob_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );
            assert_eq!(
                bob_store
                    .load_session(&alice_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );
        }

        for _ in 0..50 {
            let message_for_bob = encrypt(&mut alice_store, &bob_address, "hi bob").await?;
            let message_for_alice = encrypt(&mut bob_store, &alice_address, "hi alice").await?;

            assert_eq!(
                message_for_bob.message_type(),
                CiphertextMessageType::Whisper
            );
            assert_eq!(
                message_for_alice.message_type(),
                CiphertextMessageType::Whisper
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );

            let alice_plaintext = decrypt(
                &mut alice_store,
                &bob_address,
                &CiphertextMessage::SignalMessage(SignalMessage::try_from(
                    message_for_alice.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(alice_plaintext).expect("valid utf8"),
                "hi alice"
            );

            let bob_plaintext = decrypt(
                &mut bob_store,
                &alice_address,
                &CiphertextMessage::SignalMessage(SignalMessage::try_from(
                    message_for_bob.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(bob_plaintext).expect("valid utf8"),
                "hi bob"
            );

            assert_eq!(
                alice_store
                    .load_session(&bob_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );
            assert_eq!(
                bob_store
                    .load_session(&alice_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );
        }

        let alice_response = encrypt(&mut alice_store, &bob_address, "nice to see you").await?;

        assert_eq!(
            alice_response.message_type(),
            CiphertextMessageType::Whisper
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let bob_response = encrypt(&mut bob_store, &alice_address, "you as well").await?;

        assert_eq!(bob_response.message_type(), CiphertextMessageType::Whisper);

        let response_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::SignalMessage(SignalMessage::try_from(bob_response.serialize())?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "you as well"
        );

        assert!(is_session_id_equal(&bob_store, &bob_address, &alice_store, &alice_address).await?);

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}

#[test]
fn simultaneous_initiate_lost_message_repeated_messages() -> Result<(), SignalProtocolError> {
    async {
        let mut csprng = OsRng;

        let alice_address = ProtocolAddress::new("+14151111111".to_owned(), 1);
        let bob_address = ProtocolAddress::new("+14151111112".to_owned(), 1);

        let mut alice_store = support::test_in_memory_protocol_store()?;
        let mut bob_store = support::test_in_memory_protocol_store()?;

        let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut csprng).await?;

        process_prekey_bundle(
            &bob_address,
            &mut alice_store.session_store,
            &mut alice_store.identity_store,
            &bob_pre_key_bundle,
            &mut csprng,
            None,
        )
        .await?;
        let lost_message_for_bob =
            encrypt(&mut alice_store, &bob_address, "it was so long ago").await?;

        for _ in 0..15 {
            let alice_pre_key_bundle = create_pre_key_bundle(&mut alice_store, &mut csprng).await?;
            let bob_pre_key_bundle = create_pre_key_bundle(&mut bob_store, &mut csprng).await?;

            process_prekey_bundle(
                &bob_address,
                &mut alice_store.session_store,
                &mut alice_store.identity_store,
                &bob_pre_key_bundle,
                &mut csprng,
                None,
            )
            .await?;

            process_prekey_bundle(
                &alice_address,
                &mut bob_store.session_store,
                &mut bob_store.identity_store,
                &alice_pre_key_bundle,
                &mut csprng,
                None,
            )
            .await?;

            let message_for_bob = encrypt(&mut alice_store, &bob_address, "hi bob").await?;
            let message_for_alice = encrypt(&mut bob_store, &alice_address, "hi alice").await?;

            assert_eq!(
                message_for_bob.message_type(),
                CiphertextMessageType::PreKey
            );
            assert_eq!(
                message_for_alice.message_type(),
                CiphertextMessageType::PreKey
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );

            let alice_plaintext = decrypt(
                &mut alice_store,
                &bob_address,
                &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                    message_for_alice.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(alice_plaintext).expect("valid utf8"),
                "hi alice"
            );

            let bob_plaintext = decrypt(
                &mut bob_store,
                &alice_address,
                &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                    message_for_bob.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(bob_plaintext).expect("valid utf8"),
                "hi bob"
            );

            assert_eq!(
                alice_store
                    .load_session(&bob_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );
            assert_eq!(
                bob_store
                    .load_session(&alice_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );
        }

        for _ in 0..50 {
            let message_for_bob = encrypt(&mut alice_store, &bob_address, "hi bob").await?;
            let message_for_alice = encrypt(&mut bob_store, &alice_address, "hi alice").await?;

            assert_eq!(
                message_for_bob.message_type(),
                CiphertextMessageType::Whisper
            );
            assert_eq!(
                message_for_alice.message_type(),
                CiphertextMessageType::Whisper
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );

            let alice_plaintext = decrypt(
                &mut alice_store,
                &bob_address,
                &CiphertextMessage::SignalMessage(SignalMessage::try_from(
                    message_for_alice.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(alice_plaintext).expect("valid utf8"),
                "hi alice"
            );

            let bob_plaintext = decrypt(
                &mut bob_store,
                &alice_address,
                &CiphertextMessage::SignalMessage(SignalMessage::try_from(
                    message_for_bob.serialize(),
                )?),
            )
            .await?;
            assert_eq!(
                String::from_utf8(bob_plaintext).expect("valid utf8"),
                "hi bob"
            );

            assert_eq!(
                alice_store
                    .load_session(&bob_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );
            assert_eq!(
                bob_store
                    .load_session(&alice_address, None)
                    .await?
                    .expect("session found")
                    .session_version()?,
                3
            );

            assert!(
                !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address)
                    .await?
            );
        }

        let alice_response = encrypt(&mut alice_store, &bob_address, "nice to see you").await?;

        assert_eq!(
            alice_response.message_type(),
            CiphertextMessageType::Whisper
        );

        assert!(
            !is_session_id_equal(&alice_store, &alice_address, &bob_store, &bob_address).await?
        );

        let bob_response = encrypt(&mut bob_store, &alice_address, "you as well").await?;

        assert_eq!(bob_response.message_type(), CiphertextMessageType::Whisper);

        let response_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::SignalMessage(SignalMessage::try_from(bob_response.serialize())?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "you as well"
        );

        assert!(is_session_id_equal(&bob_store, &bob_address, &alice_store, &alice_address).await?);

        let blast_from_the_past = decrypt(
            &mut bob_store,
            &alice_address,
            &CiphertextMessage::PreKeySignalMessage(PreKeySignalMessage::try_from(
                lost_message_for_bob.serialize(),
            )?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(blast_from_the_past).expect("valid utf8"),
            "it was so long ago"
        );

        assert!(
            !is_session_id_equal(&bob_store, &bob_address, &alice_store, &alice_address).await?
        );

        let bob_response = encrypt(&mut bob_store, &alice_address, "so it was").await?;

        assert_eq!(bob_response.message_type(), CiphertextMessageType::Whisper);

        let response_plaintext = decrypt(
            &mut alice_store,
            &bob_address,
            &CiphertextMessage::SignalMessage(SignalMessage::try_from(bob_response.serialize())?),
        )
        .await?;
        assert_eq!(
            String::from_utf8(response_plaintext).expect("valid utf8"),
            "so it was"
        );

        assert!(is_session_id_equal(&bob_store, &bob_address, &alice_store, &alice_address).await?);

        Ok(())
    }
    .now_or_never()
    .expect("sync")
}
