Add happy path test for secure-join

This commit is contained in:
Floris Bruynooghe
2020-09-05 23:01:34 +02:00
parent c6339c4ae4
commit ce01e1652f
3 changed files with 140 additions and 20 deletions

View File

@@ -1166,7 +1166,7 @@ pub async fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result<ChatId
}
/// Create a normal chat with a single user. To create group chats,
/// see dc_create_group_chat().
/// see [Chat::create_group_chat].
///
/// If a chat already exists, this ID is returned, otherwise a new chat is created;
/// this new chat may already contain messages, eg. from the deaddrop, to get the

View File

@@ -1093,6 +1093,7 @@ fn encrypted_and_signed(
mod tests {
use super::*;
use crate::chat;
use crate::peerstate::Peerstate;
use crate::test_utils::TestContext;
@@ -1306,4 +1307,111 @@ mod tests {
"vc-contact-confirm-received"
);
}
#[async_std::test]
async fn test_secure_join() {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
let chatid = chat::create_group_chat(&alice.ctx, VerifiedStatus::Verified, "the chat")
.await
.unwrap();
// Generate QR-code, secure-join implied by chatid
let qr = dc_get_securejoin_qr(&alice.ctx, chatid).await.unwrap();
// Bob scans QR-code, sends vg-request; blocks on ongoing process
let joiner = {
let qr = qr.clone();
let ctx = bob.ctx.clone();
async_std::task::spawn(async move { dc_join_securejoin(&ctx, &qr).await })
};
let sent = bob.pop_sent_msg().await;
assert_eq!(sent.recipient(), "alice@example.com".parse().unwrap());
let msg = alice.parse_msg(&sent).await;
assert!(!msg.was_encrypted());
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-request");
assert!(msg.get(HeaderDef::SecureJoinInvitenumber).is_some());
// Alice receives vg-request, sends vg-auth-required
alice.recv_msg(&sent).await;
let sent = alice.pop_sent_msg().await;
let msg = bob.parse_msg(&sent).await;
assert!(msg.was_encrypted());
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-auth-required");
// Bob receives vg-auth-required, sends vg-request-with-auth
bob.recv_msg(&sent).await;
let sent = bob.pop_sent_msg().await;
let msg = alice.parse_msg(&sent).await;
assert!(msg.was_encrypted());
assert_eq!(
msg.get(HeaderDef::SecureJoin).unwrap(),
"vg-request-with-auth"
);
assert!(msg.get(HeaderDef::SecureJoinAuth).is_some());
let bob_fp = SignedPublicKey::load_self(&bob.ctx)
.await
.unwrap()
.fingerprint();
assert_eq!(
*msg.get(HeaderDef::SecureJoinFingerprint).unwrap(),
bob_fp.hex()
);
// Alice should not yet have Bob verified
let contact_bob_id =
Contact::lookup_id_by_addr(&alice.ctx, "bob@example.net", Origin::Unknown).await;
let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id)
.await
.unwrap();
assert_eq!(
contact_bob.is_verified(&alice.ctx).await,
VerifiedStatus::Unverified
);
// Alice receives vg-request-with-auth, sends vg-member-added
alice.recv_msg(&sent).await;
assert_eq!(
contact_bob.is_verified(&alice.ctx).await,
VerifiedStatus::BidirectVerified
);
let sent = alice.pop_sent_msg().await;
let msg = bob.parse_msg(&sent).await;
assert!(msg.was_encrypted());
assert_eq!(msg.get(HeaderDef::SecureJoin).unwrap(), "vg-member-added");
// Bob should not yet have Alice verified
let contact_alice_id =
Contact::lookup_id_by_addr(&bob.ctx, "alice@example.com", Origin::Unknown).await;
let contact_alice = Contact::load_from_db(&bob.ctx, contact_alice_id)
.await
.unwrap();
assert_eq!(
contact_bob.is_verified(&bob.ctx).await,
VerifiedStatus::Unverified
);
// Bob receives vg-member-added, sends vg-member-added-received
bob.recv_msg(&sent).await;
assert_eq!(
contact_alice.is_verified(&bob.ctx).await,
VerifiedStatus::BidirectVerified
);
let sent = bob.pop_sent_msg().await;
let msg = alice.parse_msg(&sent).await;
assert!(msg.was_encrypted());
assert_eq!(
msg.get(HeaderDef::SecureJoin).unwrap(),
"vg-member-added-received"
);
let bob_chatid = joiner.await;
let bob_chat = Chat::load_from_db(&bob.ctx, bob_chatid).await.unwrap();
assert!(bob_chat.is_verified());
}
}

View File

@@ -2,10 +2,11 @@
//!
//! This module is only compiled for test runs.
use std::cell::RefCell;
use std::str::FromStr;
use std::time::{Duration, Instant};
use async_std::path::PathBuf;
use async_std::sync::RwLock;
use tempfile::{tempdir, TempDir};
use crate::chat::ChatId;
@@ -22,11 +23,12 @@ use crate::param::{Param, Params};
///
/// The temporary directory can be used to store the SQLite database,
/// see e.g. [test_context] which does this.
#[derive(Debug)]
pub(crate) struct TestContext {
pub ctx: Context,
pub dir: TempDir,
/// Counter for fake IMAP UIDs in [recv_msg], for private use in that function only.
recv_idx: RefCell<u32>,
recv_idx: RwLock<u32>,
}
impl TestContext {
@@ -49,7 +51,7 @@ impl TestContext {
Self {
ctx,
dir,
recv_idx: RefCell::new(0),
recv_idx: RwLock::new(0),
}
}
@@ -112,26 +114,36 @@ impl TestContext {
///
/// Panics if there is no message or on any error.
pub async fn pop_sent_msg(&self) -> SentMessage {
let (rowid, foreign_id, raw_params) = self
.ctx
.sql
.query_row(
r#"
let start = Instant::now();
let (rowid, foreign_id, raw_params) = loop {
let row = self
.ctx
.sql
.query_row(
r#"
SELECT id, foreign_id, param
FROM jobs
WHERE action=?
ORDER BY desired_timestamp;
"#,
paramsv![Action::SendMsgToSmtp],
|row| {
let id: i64 = row.get(0)?;
let foreign_id: i64 = row.get(1)?;
let param: String = row.get(2)?;
Ok((id, foreign_id, param))
},
)
.await
.expect("no sent message found in jobs table");
paramsv![Action::SendMsgToSmtp],
|row| {
let id: i64 = row.get(0)?;
let foreign_id: i64 = row.get(1)?;
let param: String = row.get(2)?;
Ok((id, foreign_id, param))
},
)
.await;
if let Ok(row) = row {
break row;
}
if start.elapsed() < Duration::from_secs(3) {
async_std::task::sleep(Duration::from_millis(100)).await;
} else {
panic!("no sent message found in jobs table");
}
};
let id = ChatId::new(foreign_id as u32);
let params = Params::from_str(&raw_params).unwrap();
let blob_path = params
@@ -169,7 +181,7 @@ impl TestContext {
///
/// Receives a message using the `dc_receive_imf()` pipeline.
pub async fn recv_msg(&self, msg: &SentMessage) {
let mut idx = self.recv_idx.borrow_mut();
let mut idx = self.recv_idx.write().await;
*idx += 1;
dc_receive_imf(&self.ctx, msg.payload().as_bytes(), "INBOX", *idx, false)
.await