crypto: Add a higher level simple and threadsafe SAS object.

This commit is contained in:
Damir Jelić 2020-07-24 15:49:00 +02:00
parent de94b903d6
commit 9ac1417292
2 changed files with 306 additions and 9 deletions

View file

@ -26,6 +26,7 @@ use crate::{Account, Device};
#[allow(dead_code)]
mod sas;
#[derive(Clone)]
struct SasIds {
account: Account,
other_device: Device,

View file

@ -26,7 +26,7 @@ use matrix_sdk_common::events::{
HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString,
VerificationMethod,
},
ToDeviceEvent,
AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent,
};
use matrix_sdk_common::identifiers::{DeviceId, UserId};
use matrix_sdk_common::uuid::Uuid;
@ -34,6 +34,230 @@ use matrix_sdk_common::uuid::Uuid;
use super::{get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds};
use crate::{Account, Device};
#[derive(Clone)]
struct Sas {
inner: Arc<Mutex<InnerSas>>,
account: Account,
other_device: Device,
}
impl Sas {
fn user_id(&self) -> &UserId {
self.account.user_id()
}
fn device_id(&self) -> &DeviceId {
self.account.device_id()
}
fn start(account: Account, other_device: Device) -> (Sas, StartEventContent) {
let (inner, content) = InnerSas::start(account.clone(), other_device.clone());
let sas = Sas {
inner: Arc::new(Mutex::new(inner)),
account,
other_device,
};
(sas, content)
}
fn from_start_event(
account: Account,
other_device: Device,
event: &ToDeviceEvent<StartEventContent>,
) -> Sas {
let inner = InnerSas::from_start_event(account.clone(), other_device.clone(), event);
Sas {
inner: Arc::new(Mutex::new(inner)),
account,
other_device,
}
}
fn accept(&self) -> Option<AcceptEventContent> {
self.inner.lock().unwrap().accept()
}
fn confirm(&self) -> Option<MacEventContent> {
let mut guard = self.inner.lock().unwrap();
let sas: InnerSas = (*guard).clone();
let (sas, content) = sas.confirm();
*guard = sas;
content
}
fn can_be_presented(&self) -> bool {
self.inner.lock().unwrap().can_be_presented()
}
fn is_done(&self) -> bool {
self.inner.lock().unwrap().is_done()
}
fn emoji(&self) -> Option<Vec<(&'static str, &'static str)>> {
self.inner.lock().unwrap().emoji()
}
fn decimals(&self) -> Option<(u32, u32, u32)> {
self.inner.lock().unwrap().decimals()
}
fn receive_event(&self, event: &mut AnyToDeviceEvent) -> Option<AnyToDeviceEventContent> {
let mut guard = self.inner.lock().unwrap();
let sas: InnerSas = (*guard).clone();
let (sas, content) = sas.receive_event(event);
*guard = sas;
content
}
fn verified_devices(&self) -> Option<Arc<Vec<Box<DeviceId>>>> {
self.inner.lock().unwrap().verified_devices()
}
}
#[derive(Clone)]
enum InnerSas {
Created(SasState<Created>),
Started(SasState<Started>),
Accepted(SasState<Accepted>),
KeyRecieved(SasState<KeyReceived>),
Confirmed(SasState<Confirmed>),
MacReceived(SasState<MacReceived>),
Done(SasState<Done>),
}
impl InnerSas {
fn start(account: Account, other_device: Device) -> (InnerSas, StartEventContent) {
let sas = SasState::<Created>::new(account, other_device);
let content = sas.get_start_event();
(InnerSas::Created(sas), content)
}
fn from_start_event(
account: Account,
other_device: Device,
event: &ToDeviceEvent<StartEventContent>,
) -> InnerSas {
InnerSas::Started(SasState::<Started>::from_start_event(
account,
other_device,
event,
))
}
fn accept(&self) -> Option<AcceptEventContent> {
if let InnerSas::Started(s) = self {
Some(s.get_accept_content())
} else {
None
}
}
fn confirm(self) -> (InnerSas, Option<MacEventContent>) {
match self {
InnerSas::KeyRecieved(s) => {
let sas = s.confirm();
let content = sas.get_mac_event_content();
(InnerSas::Confirmed(sas), Some(content))
}
InnerSas::MacReceived(s) => {
let sas = s.confirm();
let content = sas.get_mac_event_content();
(InnerSas::Done(sas), Some(content))
}
_ => (self, None),
}
}
fn receive_event(
self,
event: &mut AnyToDeviceEvent,
) -> (InnerSas, Option<AnyToDeviceEventContent>) {
match event {
AnyToDeviceEvent::KeyVerificationAccept(e) => {
if let InnerSas::Created(s) = self {
let sas = s.into_accepted(e);
let content = sas.get_key_content();
(
InnerSas::Accepted(sas),
Some(AnyToDeviceEventContent::KeyVerificationKey(content)),
)
} else {
(self, None)
}
}
AnyToDeviceEvent::KeyVerificationKey(e) => match self {
InnerSas::Accepted(s) => (InnerSas::KeyRecieved(s.into_key_received(e)), None),
InnerSas::Started(s) => {
let sas = s.into_key_received(e);
let content = sas.get_key_content();
(
InnerSas::KeyRecieved(sas),
Some(AnyToDeviceEventContent::KeyVerificationKey(content)),
)
}
_ => (self, None),
},
AnyToDeviceEvent::KeyVerificationMac(e) => match self {
InnerSas::KeyRecieved(s) => (InnerSas::MacReceived(s.into_mac_received(e)), None),
InnerSas::Confirmed(s) => (InnerSas::Done(s.into_done(e)), None),
_ => (self, None),
},
_ => (self, None),
}
}
fn can_be_presented(&self) -> bool {
match self {
InnerSas::KeyRecieved(_) => true,
InnerSas::MacReceived(_) => true,
_ => false,
}
}
fn is_done(&self) -> bool {
if let InnerSas::Done(_) = self {
true
} else {
false
}
}
fn emoji(&self) -> Option<Vec<(&'static str, &'static str)>> {
match self {
InnerSas::KeyRecieved(s) => Some(s.get_emoji()),
InnerSas::MacReceived(s) => Some(s.get_emoji()),
_ => None,
}
}
fn decimals(&self) -> Option<(u32, u32, u32)> {
match self {
InnerSas::KeyRecieved(s) => Some(s.get_decimal()),
InnerSas::MacReceived(s) => Some(s.get_decimal()),
_ => None,
}
}
fn verified_devices(&self) -> Option<Arc<Vec<Box<DeviceId>>>> {
if let InnerSas::Done(s) = self {
Some(s.verified_devices())
} else {
None
}
}
fn verified_master_keys(&self) -> Option<Arc<Vec<String>>> {
if let InnerSas::Done(s) = self {
Some(s.verified_master_keys())
} else {
None
}
}
}
/// Struct containing the protocols that were agreed to be used for the SAS
/// flow.
#[derive(Clone, Debug)]
@ -65,7 +289,8 @@ impl From<AcceptEventContent> for AcceptedProtocols {
///
/// This is the generic struc holding common data between the different states
/// and the specific state.
struct SasState<S> {
#[derive(Clone)]
struct SasState<S: Clone> {
/// The Olm SAS struct.
inner: Arc<Mutex<OlmSas>>,
/// Struct holding the identities that are doing the SAS dance.
@ -80,17 +305,20 @@ struct SasState<S> {
}
/// The initial SAS state.
#[derive(Clone)]
struct Created {
protocol_definitions: MSasV1ContentOptions,
}
/// The initial SAS state if the other side started the SAS verification.
#[derive(Clone)]
struct Started {
protocol_definitions: MSasV1Content,
}
/// The SAS state we're going to be in after the other side accepted our
/// verification start event.
#[derive(Clone)]
struct Accepted {
accepted_protocols: Arc<AcceptedProtocols>,
commitment: String,
@ -100,6 +328,7 @@ struct Accepted {
/// other participant.
///
/// From now on we can show the short auth string to the user.
#[derive(Clone)]
struct KeyReceived {
we_started: bool,
accepted_protocols: Arc<AcceptedProtocols>,
@ -108,6 +337,7 @@ struct KeyReceived {
/// The SAS state we're going to be in after the user has confirmed that the
/// short auth string matches. We still need to receive a MAC event from the
/// other side.
#[derive(Clone)]
struct Confirmed {
accepted_protocols: Arc<AcceptedProtocols>,
}
@ -115,6 +345,7 @@ struct Confirmed {
/// The SAS state we're going to be in after we receive a MAC event from the
/// other side. Our own user still needs to confirm that the short auth string
/// matches.
#[derive(Clone)]
struct MacReceived {
we_started: bool,
verified_devices: Arc<Vec<Box<DeviceId>>>,
@ -125,12 +356,13 @@ struct MacReceived {
///
/// We can now mark the device in our verified devices lits as verified and sign
/// the master keys in the verified devices list.
#[derive(Clone)]
struct Done {
verified_devices: Arc<Vec<Box<DeviceId>>>,
verified_master_keys: Arc<Vec<String>>,
}
impl<S> SasState<S> {
impl<S: Clone> SasState<S> {
/// Get our own user id.
pub fn user_id(&self) -> &UserId {
&self.ids.account.user_id()
@ -530,13 +762,13 @@ impl SasState<Done> {
}
/// Get the list of verified devices.
fn verified_devices(&self) -> &[Box<DeviceId>] {
&self.state.verified_devices
fn verified_devices(&self) -> Arc<Vec<Box<DeviceId>>> {
self.state.verified_devices.clone()
}
/// Get the list of verified master keys.
fn verified_master_keys(&self) -> &[String] {
&self.state.verified_master_keys
fn verified_master_keys(&self) -> Arc<Vec<String>> {
self.state.verified_master_keys.clone()
}
}
@ -545,10 +777,12 @@ mod test {
use std::convert::TryFrom;
use crate::{Account, Device};
use matrix_sdk_common::events::{EventContent, ToDeviceEvent};
use matrix_sdk_common::events::{
AnyToDeviceEvent, AnyToDeviceEventContent, EventContent, ToDeviceEvent,
};
use matrix_sdk_common::identifiers::{DeviceId, UserId};
use super::{Accepted, Created, SasState, Started};
use super::{Accepted, Created, Sas, SasState, Started};
fn alice_id() -> UserId {
UserId::try_from("@alice:example.org").unwrap()
@ -573,6 +807,21 @@ mod test {
}
}
fn wrap_any_to_device_content(
sender: &UserId,
content: AnyToDeviceEventContent,
) -> AnyToDeviceEvent {
match content {
AnyToDeviceEventContent::KeyVerificationKey(c) => {
AnyToDeviceEvent::KeyVerificationKey(ToDeviceEvent {
sender: sender.clone(),
content: c,
})
}
_ => unreachable!(),
}
}
async fn get_sas_pair() -> (SasState<Created>, SasState<Started>) {
let alice = Account::new(&alice_id(), &alice_device_id());
let alice_device = Device::from_account(&alice).await;
@ -655,4 +904,51 @@ mod test {
assert!(bob.verified_devices().contains(&alice.device_id().into()));
assert!(alice.verified_devices().contains(&bob.device_id().into()));
}
#[tokio::test]
async fn sas_wrapper_full() {
let alice = Account::new(&alice_id(), &alice_device_id());
let alice_device = Device::from_account(&alice).await;
let bob = Account::new(&bob_id(), &bob_device_id());
let bob_device = Device::from_account(&bob).await;
let (alice, content) = Sas::start(alice, bob_device);
let mut event = wrap_to_device_event(alice.user_id(), content);
let bob = Sas::from_start_event(bob, alice_device, &mut event);
let event = wrap_to_device_event(bob.user_id(), bob.accept().unwrap());
let content = alice.receive_event(&mut AnyToDeviceEvent::KeyVerificationAccept(event));
assert!(!alice.can_be_presented());
assert!(!bob.can_be_presented());
let mut event = wrap_any_to_device_content(alice.user_id(), content.unwrap());
let mut event =
wrap_any_to_device_content(bob.user_id(), bob.receive_event(&mut event).unwrap());
assert!(bob.can_be_presented());
alice.receive_event(&mut event);
assert!(alice.can_be_presented());
assert_eq!(alice.emoji().unwrap(), bob.emoji().unwrap());
assert_eq!(alice.decimals().unwrap(), bob.decimals().unwrap());
let event = wrap_to_device_event(alice.user_id(), alice.confirm().unwrap());
bob.receive_event(&mut AnyToDeviceEvent::KeyVerificationMac(event));
let event = wrap_to_device_event(bob.user_id(), bob.confirm().unwrap());
alice.receive_event(&mut AnyToDeviceEvent::KeyVerificationMac(event));
assert!(alice
.verified_devices()
.unwrap()
.contains(&bob.device_id().into()));
assert!(bob
.verified_devices()
.unwrap()
.contains(&alice.device_id().into()));
}
}