crypto: Add a test for verification request flows.

master
Damir Jelić 2020-12-18 18:23:42 +01:00
parent ec863a928d
commit 55436c6514
3 changed files with 176 additions and 2 deletions

View File

@ -2172,8 +2172,8 @@ impl Client {
let encrypt = move || -> Result<()> { let encrypt = move || -> Result<()> {
let export: String = encrypt_key_export(&keys, &passphrase, 500_000)?; let export: String = encrypt_key_export(&keys, &passphrase, 500_000)?;
let mut file = std::fs::File::create(path).unwrap(); let mut file = std::fs::File::create(path)?;
file.write_all(&export.into_bytes()).unwrap(); file.write_all(&export.into_bytes())?;
Ok(()) Ok(())
}; };
@ -2237,6 +2237,7 @@ impl Client {
}; };
let task = tokio::task::spawn_blocking(decrypt); let task = tokio::task::spawn_blocking(decrypt);
// TODO remove this unwrap.
let import = task.await.expect("Task join error").unwrap(); let import = task.await.expect("Task join error").unwrap();
Ok(olm.import_keys(import).await?) Ok(olm.import_keys(import).await?)

View File

@ -224,6 +224,10 @@ impl VerificationMachine {
// Since these are room events we will get events that we send out on // Since these are room events we will get events that we send out on
// our own as well. // our own as well.
if m.sender() == self.account.user_id() { if m.sender() == self.account.user_id() {
if let AnySyncMessageEvent::KeyVerificationReady(_e) = m {
// TODO if there is a verification request, go into passive
// mode since another device is handling this request.
}
return Ok(()); return Ok(());
} }
@ -250,6 +254,14 @@ impl VerificationMachine {
} }
} }
} }
AnySyncMessageEvent::KeyVerificationReady(e) => {
if let Some(request) = self.requests.get(&e.content.relation.event_id) {
if &e.sender == request.other_user() {
// TODO remove this unwrap.
request.receive_ready(&e.sender, &e.content).unwrap();
}
}
}
AnySyncMessageEvent::KeyVerificationStart(e) => { AnySyncMessageEvent::KeyVerificationStart(e) => {
info!( info!(
"Received a new verification start event from {} {}", "Received a new verification start event from {} {}",

View File

@ -43,12 +43,63 @@ const SUPPORTED_METHODS: &[VerificationMethod] = &[VerificationMethod::MSasV1];
pub struct VerificationRequest { pub struct VerificationRequest {
inner: Arc<Mutex<InnerRequest>>, inner: Arc<Mutex<InnerRequest>>,
account: ReadOnlyAccount, account: ReadOnlyAccount,
other_user_id: Arc<UserId>,
private_cross_signing_identity: PrivateCrossSigningIdentity, private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>, store: Arc<Box<dyn CryptoStore>>,
room_id: Arc<RoomId>, room_id: Arc<RoomId>,
} }
impl VerificationRequest { impl VerificationRequest {
/// TODO
pub fn new(
account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity,
store: Arc<Box<dyn CryptoStore>>,
room_id: Arc<RoomId>,
other_user: &UserId,
) -> Self {
let inner = Mutex::new(InnerRequest::Created(RequestState::new(
account.user_id(),
account.device_id(),
other_user,
)))
.into();
Self {
inner,
account,
private_cross_signing_identity,
store,
other_user_id: other_user.clone().into(),
room_id,
}
}
/// TODO
pub fn request(&self) -> Option<KeyVerificationRequestEventContent> {
match &*self.inner.lock().unwrap() {
InnerRequest::Created(c) => Some(c.as_content()),
_ => None,
}
}
/// The id of the other user that is participating in this verification
/// request.
pub fn other_user(&self) -> &UserId {
&self.other_user_id
}
/// Mark the request as sent.
pub fn mark_as_sent(&self, response: &RoomMessageResponse) {
let mut inner = self.inner.lock().unwrap();
match &*inner {
InnerRequest::Created(c) => {
*inner = InnerRequest::Sent(c.clone().into_sent(response));
}
_ => (),
}
}
pub(crate) fn from_request_event( pub(crate) fn from_request_event(
account: ReadOnlyAccount, account: ReadOnlyAccount,
private_cross_signing_identity: PrivateCrossSigningIdentity, private_cross_signing_identity: PrivateCrossSigningIdentity,
@ -69,6 +120,7 @@ impl VerificationRequest {
), ),
))), ))),
account, account,
other_user_id: sender.clone().into(),
private_cross_signing_identity, private_cross_signing_identity,
store, store,
room_id: room_id.clone().into(), room_id: room_id.clone().into(),
@ -85,6 +137,28 @@ impl VerificationRequest {
self.inner.lock().unwrap().accept() self.inner.lock().unwrap().accept()
} }
pub(crate) fn receive_ready(
&self,
sender: &UserId,
content: &ReadyEventContent,
) -> Result<(), ()> {
let mut inner = self.inner.lock().unwrap();
match &*inner {
InnerRequest::Sent(s) => {
*inner = InnerRequest::Ready(s.clone().into_ready(sender, content));
}
_ => (),
}
Ok(())
}
/// Is the verification request ready to start a verification flow.
pub fn is_ready(&self) -> bool {
matches!(&*self.inner.lock().unwrap(), InnerRequest::Ready(_))
}
pub(crate) fn into_started_sas( pub(crate) fn into_started_sas(
self, self,
event: &SyncMessageEvent<StartEventContent>, event: &SyncMessageEvent<StartEventContent>,
@ -171,6 +245,15 @@ struct RequestState<S: Clone> {
struct Created {} struct Created {}
impl RequestState<Created> { impl RequestState<Created> {
fn new(own_user_id: &UserId, own_device_id: &DeviceId, other_user: &UserId) -> Self {
Self {
own_user_id: own_user_id.clone(),
own_device_id: own_device_id.into(),
other_user_id: other_user.clone(),
state: Created {},
}
}
fn as_content(&self) -> KeyVerificationRequestEventContent { fn as_content(&self) -> KeyVerificationRequestEventContent {
KeyVerificationRequestEventContent { KeyVerificationRequestEventContent {
body: format!( body: format!(
@ -343,3 +426,81 @@ struct Passive {
/// unique id identifying this verification flow. /// unique id identifying this verification flow.
pub flow_id: EventId, pub flow_id: EventId,
} }
#[cfg(test)]
mod test {
use std::convert::TryFrom;
use matrix_sdk_common::{
api::r0::message::send_message_event::Response as RoomMessageResponse,
identifiers::{event_id, room_id, DeviceIdBox, UserId},
};
use matrix_sdk_test::async_test;
use crate::{
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
store::{CryptoStore, MemoryStore},
};
use super::VerificationRequest;
fn alice_id() -> UserId {
UserId::try_from("@alice:example.org").unwrap()
}
fn alice_device_id() -> DeviceIdBox {
"JLAFKJWSCS".into()
}
fn bob_id() -> UserId {
UserId::try_from("@bob:example.org").unwrap()
}
fn bob_device_id() -> DeviceIdBox {
"BOBDEVCIE".into()
}
#[async_test]
async fn test_request_accepting() {
let event_id = event_id!("$1234localhost");
let room_id = room_id!("!test:localhost");
let alice = ReadOnlyAccount::new(&alice_id(), &alice_device_id());
let alice_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new());
let alice_identity = PrivateCrossSigningIdentity::empty(alice_id());
let bob = ReadOnlyAccount::new(&bob_id(), &bob_device_id());
let bob_store: Box<dyn CryptoStore> = Box::new(MemoryStore::new());
let bob_identity = PrivateCrossSigningIdentity::empty(alice_id());
let bob_request = VerificationRequest::new(
bob,
bob_identity,
bob_store.into(),
room_id.clone().into(),
&alice_id(),
);
let content = bob_request.request().unwrap();
let alice_request = VerificationRequest::from_request_event(
alice,
alice_identity,
alice_store.into(),
&room_id,
&bob_id(),
&event_id,
&content,
);
let content = alice_request.accept().unwrap();
let response = RoomMessageResponse::new(event_id);
bob_request.mark_as_sent(&response);
bob_request.receive_ready(&alice_id(), &content).unwrap();
assert!(bob_request.is_ready());
assert!(alice_request.is_ready());
}
}