crypto: Add a method to check it the SAS flow supports emoji

master
Damir Jelić 2021-05-12 14:30:36 +02:00
parent 9863bc4a1c
commit 4f7902d6f0
4 changed files with 155 additions and 69 deletions

View File

@ -121,6 +121,12 @@ impl Sas {
self.inner.decimals()
}
/// Does this verification flow support emoji for the short authentication
/// string.
pub fn supports_emoji(&self) -> bool {
self.inner.supports_emoji()
}
/// Is the verification process done.
pub fn is_done(&self) -> bool {
self.inner.is_done()

View File

@ -18,7 +18,10 @@ use std::time::Instant;
use std::sync::Arc;
use matrix_sdk_common::{
events::{key::verification::cancel::CancelCode, AnyMessageEvent, AnyToDeviceEvent},
events::{
key::verification::{cancel::CancelCode, ShortAuthenticationString},
AnyMessageEvent, AnyToDeviceEvent,
},
identifiers::{EventId, RoomId},
};
@ -61,6 +64,37 @@ impl InnerSas {
(InnerSas::Created(sas), content)
}
pub fn supports_emoji(&self) -> bool {
match self {
InnerSas::Created(_) => false,
InnerSas::Started(s) => s
.state
.accepted_protocols
.short_auth_string
.contains(&ShortAuthenticationString::Emoji),
InnerSas::Accepted(s) => s
.state
.accepted_protocols
.short_auth_string
.contains(&ShortAuthenticationString::Emoji),
InnerSas::KeyRecieved(s) => s
.state
.accepted_protocols
.short_auth_string
.contains(&ShortAuthenticationString::Emoji),
InnerSas::Confirmed(_) => false,
InnerSas::MacReceived(s) => s
.state
.accepted_protocols
.short_auth_string
.contains(&ShortAuthenticationString::Emoji),
InnerSas::WaitingForDone(_) => false,
InnerSas::WaitingForDoneUnconfirmed(_) => false,
InnerSas::Done(_) => false,
InnerSas::Canceled(_) => false,
}
}
pub fn start_in_room(
event_id: EventId,
room_id: RoomId,

View File

@ -110,6 +110,12 @@ impl Sas {
&self.flow_id
}
/// Does this verification flow support displaying emoji for the short
/// authentication string.
pub fn supports_emoji(&self) -> bool {
self.inner.lock().unwrap().supports_emoji()
}
#[cfg(test)]
#[allow(dead_code)]
pub(crate) fn set_creation_time(&self, time: Instant) {

View File

@ -98,12 +98,12 @@ impl FlowId {
/// Struct containing the protocols that were agreed to be used for the SAS
/// flow.
#[derive(Clone, Debug)]
struct AcceptedProtocols {
method: VerificationMethod,
key_agreement_protocol: KeyAgreementProtocol,
hash: HashAlgorithm,
message_auth_code: MessageAuthenticationCode,
short_auth_string: Vec<ShortAuthenticationString>,
pub struct AcceptedProtocols {
pub method: VerificationMethod,
pub key_agreement_protocol: KeyAgreementProtocol,
pub hash: HashAlgorithm,
pub message_auth_code: MessageAuthenticationCode,
pub short_auth_string: Vec<ShortAuthenticationString>,
}
impl TryFrom<AcceptV1Content> for AcceptedProtocols {
@ -133,6 +133,53 @@ impl TryFrom<AcceptV1Content> for AcceptedProtocols {
}
}
impl TryFrom<&MSasV1Content> for AcceptedProtocols {
type Error = CancelCode;
fn try_from(method_content: &MSasV1Content) -> Result<Self, Self::Error> {
if !method_content
.key_agreement_protocols
.contains(&KeyAgreementProtocol::Curve25519HkdfSha256)
|| !method_content
.message_authentication_codes
.contains(&MessageAuthenticationCode::HkdfHmacSha256)
|| !method_content.hashes.contains(&HashAlgorithm::Sha256)
|| (!method_content
.short_authentication_string
.contains(&ShortAuthenticationString::Decimal)
&& !method_content
.short_authentication_string
.contains(&ShortAuthenticationString::Emoji))
{
Err(CancelCode::UnknownMethod)
} else {
let mut short_auth_string = vec![];
if method_content
.short_authentication_string
.contains(&ShortAuthenticationString::Decimal)
{
short_auth_string.push(ShortAuthenticationString::Decimal)
}
if method_content
.short_authentication_string
.contains(&ShortAuthenticationString::Emoji)
{
short_auth_string.push(ShortAuthenticationString::Emoji);
}
Ok(Self {
method: VerificationMethod::MSasV1,
hash: HashAlgorithm::Sha256,
key_agreement_protocol: KeyAgreementProtocol::Curve25519HkdfSha256,
message_auth_code: MessageAuthenticationCode::HkdfHmacSha256,
short_auth_string,
})
}
}
}
#[cfg(not(tarpaulin_include))]
impl Default for AcceptedProtocols {
fn default() -> Self {
@ -176,7 +223,7 @@ pub struct SasState<S: Clone> {
pub verification_flow_id: Arc<FlowId>,
/// The SAS state we're in.
state: Arc<S>,
pub state: Arc<S>,
}
#[cfg(not(tarpaulin_include))]
@ -200,14 +247,14 @@ pub struct Created {
#[derive(Clone, Debug)]
pub struct Started {
commitment: String,
protocol_definitions: MSasV1Content,
pub accepted_protocols: Arc<AcceptedProtocols>,
}
/// The SAS state we're going to be in after the other side accepted our
/// verification start event.
#[derive(Clone, Debug)]
pub struct Accepted {
accepted_protocols: Arc<AcceptedProtocols>,
pub accepted_protocols: Arc<AcceptedProtocols>,
start_content: Arc<StartContent>,
commitment: String,
}
@ -220,7 +267,7 @@ pub struct Accepted {
pub struct KeyReceived {
their_pubkey: String,
we_started: bool,
accepted_protocols: Arc<AcceptedProtocols>,
pub accepted_protocols: Arc<AcceptedProtocols>,
}
/// The SAS state we're going to be in after the user has confirmed that the
@ -228,7 +275,7 @@ pub struct KeyReceived {
/// other side.
#[derive(Clone, Debug)]
pub struct Confirmed {
accepted_protocols: Arc<AcceptedProtocols>,
pub accepted_protocols: Arc<AcceptedProtocols>,
}
/// The SAS state we're going to be in after we receive a MAC event from the
@ -240,6 +287,7 @@ pub struct MacReceived {
their_pubkey: String,
verified_devices: Arc<[ReadOnlyDevice]>,
verified_master_keys: Arc<[UserIdentities]>,
pub accepted_protocols: Arc<AcceptedProtocols>,
}
/// The SAS state we're going to be in after we receive a MAC event in a DM. DMs
@ -490,6 +538,22 @@ impl SasState<Started> {
other_identity: Option<UserIdentities>,
content: &StartContent,
) -> Result<SasState<Started>, SasState<Canceled>> {
let canceled = || SasState {
inner: Arc::new(Mutex::new(OlmSas::new())),
creation_time: Arc::new(Instant::now()),
last_event_time: Arc::new(Instant::now()),
ids: SasIds {
account: account.clone(),
other_device: other_device.clone(),
other_identity: other_identity.clone(),
},
verification_flow_id: content.flow_id().into(),
state: Arc::new(Canceled::new(CancelCode::UnknownMethod)),
};
if let StartMethod::MSasV1(method_content) = content.method() {
let sas = OlmSas::new();
@ -501,60 +565,31 @@ impl SasState<Started> {
pubkey, content, commitment
);
let sas = SasState {
inner: Arc::new(Mutex::new(sas)),
if let Ok(accepted_protocols) = AcceptedProtocols::try_from(method_content) {
Ok(SasState {
inner: Arc::new(Mutex::new(sas)),
ids: SasIds {
account,
other_device,
other_identity,
},
ids: SasIds {
account,
other_device,
other_identity,
},
creation_time: Arc::new(Instant::now()),
last_event_time: Arc::new(Instant::now()),
creation_time: Arc::new(Instant::now()),
last_event_time: Arc::new(Instant::now()),
verification_flow_id: content.flow_id().into(),
verification_flow_id: content.flow_id().into(),
state: Arc::new(Started {
protocol_definitions: method_content.clone(),
commitment,
}),
};
if !method_content
.key_agreement_protocols
.contains(&KeyAgreementProtocol::Curve25519HkdfSha256)
|| !method_content
.message_authentication_codes
.contains(&MessageAuthenticationCode::HkdfHmacSha256)
|| !method_content.hashes.contains(&HashAlgorithm::Sha256)
|| (!method_content
.short_authentication_string
.contains(&ShortAuthenticationString::Decimal)
&& !method_content
.short_authentication_string
.contains(&ShortAuthenticationString::Emoji))
{
Err(sas.cancel(CancelCode::UnknownMethod))
state: Arc::new(Started {
accepted_protocols: accepted_protocols.into(),
commitment,
}),
})
} else {
Ok(sas)
Err(canceled())
}
} else {
Err(SasState {
inner: Arc::new(Mutex::new(OlmSas::new())),
creation_time: Arc::new(Instant::now()),
last_event_time: Arc::new(Instant::now()),
ids: SasIds {
account,
other_device,
other_identity,
},
verification_flow_id: content.flow_id().into(),
state: Arc::new(Canceled::new(CancelCode::UnknownMethod)),
})
Err(canceled())
}
}
@ -566,18 +601,24 @@ impl SasState<Started> {
/// been started because of a
/// m.key.verification.request -> m.key.verification.ready flow.
pub fn as_content(&self) -> AcceptContent {
let accepted_protocols = AcceptedProtocols::default();
let method = AcceptMethod::MSasV1(
AcceptV1ContentInit {
commitment: self.state.commitment.clone(),
hash: accepted_protocols.hash,
key_agreement_protocol: accepted_protocols.key_agreement_protocol,
message_authentication_code: accepted_protocols.message_auth_code,
hash: self.state.accepted_protocols.hash.clone(),
key_agreement_protocol: self
.state
.accepted_protocols
.key_agreement_protocol
.clone(),
message_authentication_code: self
.state
.accepted_protocols
.message_auth_code
.clone(),
short_authentication_string: self
.state
.protocol_definitions
.short_authentication_string
.accepted_protocols
.short_auth_string
.clone(),
}
.into(),
@ -620,8 +661,6 @@ impl SasState<Started> {
self.check_event(&sender, &content.flow_id().as_str())
.map_err(|c| self.clone().cancel(c))?;
let accepted_protocols = AcceptedProtocols::default();
let their_pubkey = content.public_key().to_owned();
self.inner
@ -639,7 +678,7 @@ impl SasState<Started> {
state: Arc::new(KeyReceived {
we_started: false,
their_pubkey,
accepted_protocols: Arc::new(accepted_protocols),
accepted_protocols: self.state.accepted_protocols.clone(),
}),
})
}
@ -823,6 +862,7 @@ impl SasState<KeyReceived> {
their_pubkey: self.state.their_pubkey.clone(),
verified_devices: devices.into(),
verified_master_keys: master_keys.into(),
accepted_protocols: self.state.accepted_protocols.clone(),
}),
})
}