crypto: Implement the whole SAS flow.
parent
b1ae5534a1
commit
bb9adea5de
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::{Account, Device};
|
use crate::{Account, Device};
|
||||||
|
@ -24,7 +25,7 @@ use matrix_sdk_common::events::{
|
||||||
key::verification::{
|
key::verification::{
|
||||||
accept::AcceptEventContent,
|
accept::AcceptEventContent,
|
||||||
key::KeyEventContent,
|
key::KeyEventContent,
|
||||||
mac::{MacEvent, MacEventContent},
|
mac::MacEventContent,
|
||||||
start::{MSasV1Content, MSasV1ContentOptions, StartEventContent},
|
start::{MSasV1Content, MSasV1ContentOptions, StartEventContent},
|
||||||
HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString,
|
HashAlgorithm, KeyAgreementProtocol, MessageAuthenticationCode, ShortAuthenticationString,
|
||||||
VerificationMethod,
|
VerificationMethod,
|
||||||
|
@ -70,6 +71,10 @@ impl<S> Sas<S> {
|
||||||
pub fn user_id(&self) -> &UserId {
|
pub fn user_id(&self) -> &UserId {
|
||||||
&self.ids.account.user_id()
|
&self.ids.account.user_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn device_id(&self) -> &DeviceId {
|
||||||
|
&self.ids.account.device_id()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_emoji(index: u8) -> (&'static str, &'static str) {
|
fn get_emoji(index: u8) -> (&'static str, &'static str) {
|
||||||
|
@ -298,6 +303,57 @@ struct KeyReceived {
|
||||||
accepted_protocols: AcceptedProtocols,
|
accepted_protocols: AcceptedProtocols,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_mac_event(
|
||||||
|
sas: &OlmSas,
|
||||||
|
ids: &SasIds,
|
||||||
|
flow_id: &str,
|
||||||
|
event: &ToDeviceEvent<MacEventContent>,
|
||||||
|
) -> (Vec<Box<DeviceId>>, Vec<String>) {
|
||||||
|
let mut verified_devices: Vec<Box<DeviceId>> = Vec::new();
|
||||||
|
|
||||||
|
let info = extra_mac_info_receive(&ids, flow_id);
|
||||||
|
|
||||||
|
let mut keys = event.content.mac.keys().cloned().collect::<Vec<String>>();
|
||||||
|
keys.sort();
|
||||||
|
let keys = sas
|
||||||
|
.calculate_mac(&keys.join(","), &format!("{}KEYIDS", &info))
|
||||||
|
.expect("Can't calculate SAS MAC");
|
||||||
|
|
||||||
|
if keys != event.content.keys {
|
||||||
|
panic!("Keys mac mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key_id, key_mac) in &event.content.mac {
|
||||||
|
let split: Vec<&str> = key_id.splitn(2, ":").collect();
|
||||||
|
|
||||||
|
if split.len() != 2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let algorithm: KeyAlgorithm = if let Ok(a) = split[0].try_into() {
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = split[1];
|
||||||
|
let device_key_id = AlgorithmAndDeviceId(algorithm, id.into());
|
||||||
|
|
||||||
|
if let Some(key) = ids.other_device.keys().get(&device_key_id) {
|
||||||
|
if key_mac
|
||||||
|
== &sas
|
||||||
|
.calculate_mac(key, &format!("{}{}", info, key_id))
|
||||||
|
.expect("Can't calculate SAS MAC")
|
||||||
|
{
|
||||||
|
verified_devices.push(ids.other_device.device_id().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO add an else branch for the master key here
|
||||||
|
}
|
||||||
|
|
||||||
|
(verified_devices, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
impl Sas<KeyReceived> {
|
impl Sas<KeyReceived> {
|
||||||
fn get_key_content(&self) -> KeyEventContent {
|
fn get_key_content(&self) -> KeyEventContent {
|
||||||
KeyEventContent {
|
KeyEventContent {
|
||||||
|
@ -368,19 +424,36 @@ impl Sas<KeyReceived> {
|
||||||
.map(|b| b as u32)
|
.map(|b| b as u32)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let first = (bytes[0] << 5 | bytes[1] >> 3) + 1000;
|
let first = bytes[0] << 5 | bytes[1] >> 3;
|
||||||
let second = ((bytes[1] & 0x7) << 10 | bytes[2] << 2 | bytes[3] >> 6) + 1000;
|
let second = (bytes[1] & 0x7) << 10 | bytes[2] << 2 | bytes[3] >> 6;
|
||||||
let third = ((bytes[3] & 0x3F) << 7 | bytes[4] >> 1) + 1000;
|
let third = (bytes[3] & 0x3F) << 7 | bytes[4] >> 1;
|
||||||
|
|
||||||
(first, second, third)
|
(first + 1000, second + 1000, third + 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_mac_received(self, event: &MacEvent) -> Sas<MacReceived> {
|
fn into_mac_received(self, event: &ToDeviceEvent<MacEventContent>) -> Sas<MacReceived> {
|
||||||
todo!()
|
let (devices, master_keys) =
|
||||||
|
receive_mac_event(&self.inner, &self.ids, &self.verification_flow_id, event);
|
||||||
|
Sas {
|
||||||
|
inner: self.inner,
|
||||||
|
verification_flow_id: self.verification_flow_id,
|
||||||
|
ids: self.ids,
|
||||||
|
state: MacReceived {
|
||||||
|
verified_devices: devices,
|
||||||
|
verified_master_keys: master_keys,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm(self) -> Sas<Confirmed> {
|
fn confirm(self) -> Sas<Confirmed> {
|
||||||
todo!()
|
Sas {
|
||||||
|
inner: self.inner,
|
||||||
|
verification_flow_id: self.verification_flow_id,
|
||||||
|
ids: self.ids,
|
||||||
|
state: Confirmed {
|
||||||
|
accepted_protocols: self.state.accepted_protocols,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,72 +461,118 @@ struct Confirmed {
|
||||||
accepted_protocols: AcceptedProtocols,
|
accepted_protocols: AcceptedProtocols,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sas<Confirmed> {
|
fn extra_mac_info_send(ids: &SasIds, flow_id: &str) -> String {
|
||||||
fn into_done(self, event: &MacEvent) -> Sas<Done> {
|
format!(
|
||||||
todo!()
|
"MATRIX_KEY_VERIFICATION_MAC{first_user}{first_device}\
|
||||||
}
|
{second_user}{second_device}{transaction_id}",
|
||||||
|
first_user = ids.account.user_id(),
|
||||||
|
first_device = ids.account.device_id(),
|
||||||
|
second_user = ids.other_device.user_id(),
|
||||||
|
second_device = ids.other_device.device_id(),
|
||||||
|
transaction_id = flow_id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_mac_info(&self) -> String {
|
fn extra_mac_info_receive(ids: &SasIds, flow_id: &str) -> String {
|
||||||
format!(
|
format!(
|
||||||
"MATRIX_KEY_VERIFICATION_MAC{first_user}{first_device}\
|
"MATRIX_KEY_VERIFICATION_MAC{first_user}{first_device}\
|
||||||
{second_user}{second_device}{transaction_id}",
|
{second_user}{second_device}{transaction_id}",
|
||||||
first_user = self.ids.account.user_id(),
|
first_user = ids.other_device.user_id(),
|
||||||
first_device = self.ids.account.device_id(),
|
first_device = ids.other_device.device_id(),
|
||||||
second_user = self.ids.other_device.user_id(),
|
second_user = ids.account.user_id(),
|
||||||
second_device = self.ids.other_device.device_id(),
|
second_device = ids.account.device_id(),
|
||||||
transaction_id = self.verification_flow_id,
|
transaction_id = flow_id,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mac_content(sas: &OlmSas, ids: &SasIds, flow_id: &str) -> MacEventContent {
|
||||||
|
let mut mac: BTreeMap<String, String> = BTreeMap::new();
|
||||||
|
|
||||||
|
let key_id = AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, ids.account.device_id().into());
|
||||||
|
let key = ids.account.identity_keys().ed25519();
|
||||||
|
let info = extra_mac_info_send(ids, flow_id);
|
||||||
|
|
||||||
|
mac.insert(
|
||||||
|
key_id.to_string(),
|
||||||
|
sas.calculate_mac(key, &format!("{}{}", info, key_id))
|
||||||
|
.expect("Can't calculate SAS MAC"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO Add the cross signing master key here if we trust/have it.
|
||||||
|
|
||||||
|
let mut keys = mac.keys().cloned().collect::<Vec<String>>();
|
||||||
|
keys.sort();
|
||||||
|
let keys = sas
|
||||||
|
.calculate_mac(&keys.join(","), &format!("{}KEYIDS", &info))
|
||||||
|
.expect("Can't calculate SAS MAC");
|
||||||
|
|
||||||
|
MacEventContent {
|
||||||
|
transaction_id: flow_id.to_owned(),
|
||||||
|
keys,
|
||||||
|
mac,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sas<Confirmed> {
|
||||||
|
fn into_done(self, event: &ToDeviceEvent<MacEventContent>) -> Sas<Done> {
|
||||||
|
let (devices, master_keys) =
|
||||||
|
receive_mac_event(&self.inner, &self.ids, &self.verification_flow_id, event);
|
||||||
|
|
||||||
|
Sas {
|
||||||
|
inner: self.inner,
|
||||||
|
verification_flow_id: self.verification_flow_id,
|
||||||
|
ids: self.ids,
|
||||||
|
|
||||||
|
state: Done {
|
||||||
|
verified_devices: devices,
|
||||||
|
verified_master_keys: master_keys,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mac_event_content(&self) -> MacEventContent {
|
fn get_mac_event_content(&self) -> MacEventContent {
|
||||||
let mut mac: BTreeMap<String, String> = BTreeMap::new();
|
get_mac_content(&self.inner, &self.ids, &self.verification_flow_id)
|
||||||
|
|
||||||
let info = self.get_mac_info();
|
|
||||||
|
|
||||||
let key_id =
|
|
||||||
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.ids.account.device_id().into());
|
|
||||||
let key = self.ids.account.identity_keys().ed25519();
|
|
||||||
|
|
||||||
mac.insert(
|
|
||||||
key_id.to_string(),
|
|
||||||
self.inner
|
|
||||||
.calculate_mac(key, &format!("{}{}", info, key_id))
|
|
||||||
.expect("Can't calculate SAS MAC"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO Add the cross signing master key here if we trust/have it.
|
|
||||||
|
|
||||||
let mut keys = mac.keys().cloned().collect::<Vec<String>>();
|
|
||||||
keys.sort();
|
|
||||||
let keys = self
|
|
||||||
.inner
|
|
||||||
.calculate_mac(&keys.join(","), &format!("{}KEYIDS", &info))
|
|
||||||
.expect("Can't calculate SAS MAC");
|
|
||||||
|
|
||||||
MacEventContent {
|
|
||||||
transaction_id: self.verification_flow_id.clone(),
|
|
||||||
keys,
|
|
||||||
mac,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MacReceived {
|
struct MacReceived {
|
||||||
verified_devices: Vec<String>,
|
verified_devices: Vec<Box<DeviceId>>,
|
||||||
verified_master_keys: Vec<String>,
|
verified_master_keys: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sas<MacReceived> {
|
impl Sas<MacReceived> {
|
||||||
fn confirm(self) -> Sas<Done> {
|
fn confirm(self) -> Sas<Done> {
|
||||||
todo!()
|
Sas {
|
||||||
|
inner: self.inner,
|
||||||
|
verification_flow_id: self.verification_flow_id,
|
||||||
|
ids: self.ids,
|
||||||
|
state: Done {
|
||||||
|
verified_devices: self.state.verified_devices,
|
||||||
|
verified_master_keys: self.state.verified_master_keys,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Done {
|
struct Done {
|
||||||
verified_devices: Vec<String>,
|
verified_devices: Vec<Box<DeviceId>>,
|
||||||
verified_master_keys: Vec<String>,
|
verified_master_keys: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Sas<Done> {
|
||||||
|
fn get_mac_event_content(&self) -> MacEventContent {
|
||||||
|
get_mac_content(&self.inner, &self.ids, &self.verification_flow_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verified_devices(&self) -> &Vec<Box<DeviceId>> {
|
||||||
|
&self.state.verified_devices
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verified_master_keys(&self) -> &[String] {
|
||||||
|
&self.state.verified_master_keys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -536,4 +655,37 @@ mod test {
|
||||||
assert_eq!(alice.get_decimal(), bob.get_decimal());
|
assert_eq!(alice.get_decimal(), bob.get_decimal());
|
||||||
assert_eq!(alice.get_emoji(), bob.get_emoji());
|
assert_eq!(alice.get_emoji(), bob.get_emoji());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn sas_mac() {
|
||||||
|
let (alice, bob) = get_sas_pair().await;
|
||||||
|
|
||||||
|
let event = wrap_to_device_event(bob.user_id(), bob.get_accept_content());
|
||||||
|
|
||||||
|
let alice: Sas<Accepted> = alice.into_accepted(&event);
|
||||||
|
let mut event = wrap_to_device_event(alice.user_id(), alice.get_key_content());
|
||||||
|
|
||||||
|
let bob = bob.into_key_received(&mut event);
|
||||||
|
|
||||||
|
let mut event = wrap_to_device_event(bob.user_id(), bob.get_key_content());
|
||||||
|
|
||||||
|
let alice = alice.into_key_received(&mut event);
|
||||||
|
|
||||||
|
assert_eq!(alice.get_decimal(), bob.get_decimal());
|
||||||
|
assert_eq!(alice.get_emoji(), bob.get_emoji());
|
||||||
|
|
||||||
|
let bob = bob.confirm();
|
||||||
|
|
||||||
|
let event = wrap_to_device_event(bob.user_id(), bob.get_mac_event_content());
|
||||||
|
|
||||||
|
let alice = alice.into_mac_received(&event);
|
||||||
|
// assert!(!alice.get_emoji().is_empty());
|
||||||
|
let alice = alice.confirm();
|
||||||
|
|
||||||
|
let event = wrap_to_device_event(alice.user_id(), alice.get_mac_event_content());
|
||||||
|
let bob = bob.into_done(&event);
|
||||||
|
|
||||||
|
assert!(bob.verified_devices().contains(&alice.device_id().into()));
|
||||||
|
assert!(alice.verified_devices().contains(&bob.device_id().into()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue