refactor(crypto): Use vodozemac types for the cross signing and device keys

This commit is contained in:
Damir Jelić
2022-03-28 13:09:35 +02:00
parent 4b58017951
commit 23040a1bf7
31 changed files with 500 additions and 283 deletions

View File

@@ -28,3 +28,7 @@ ruma-identifiers = "0.22.0"
ruma-serde = "0.6.0"
rqrr = { version = "0.4.0", optional = true }
thiserror = "1.0.25"
[dependencies.vodozemac]
git = "https://github.com/matrix-org/vodozemac"
rev = "16fb254aa3325c61ef949314c81c35f91d0664e1"

View File

@@ -42,6 +42,9 @@ pub enum DecodingError {
/// The QR code data uses an invalid or unsupported version.
#[error("the QR code contains an invalid or unsupported version: {0}")]
Version(u8),
/// The QR code data doesn't contain valid ed25519 keys.
#[error("the QR code contains invalid ed25519 keys: {0}")]
Keys(#[from] vodozemac::KeyError),
}
/// Error type describing errors that happen while QR data is being encoded.
@@ -51,9 +54,6 @@ pub enum EncodingError {
/// doesn't fit into a QR code.
#[error(transparent)]
Qr(#[from] qrcode::types::QrError),
/// Error decoding the identity keys as base64.
#[error(transparent)]
Base64(#[from] base64::DecodeError),
/// Error encoding the given flow id, the flow id is too large.
#[error("The verification flow id length can't be converted into a u16: {0}")]
FlowId(#[from] std::num::TryFromIntError),

View File

@@ -187,11 +187,23 @@ mod test {
let data = b"MATRIX\
\x02\x00\x00\x0f\
test:localhost\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
kS /\x92i\x1e6\xcd'g\xf9#\x11\xd8\x8a\xa2\xf61\x05\x1b6\xef\xfc\xa4%\x80\x1a\x0c\xd2\xe8\x04\
\xbdR|\xf8n\x07\xa4\x1f\xb4\xcc3\x0eBT\xe7[~\xfd\x87\xd06B\xdfoVv%\x9b\x86\xae\xbcM\
SECRETISLONGENOUGH";
let result = QrVerificationData::from_bytes(data);
assert!(matches!(result, Err(DecodingError::Identifier(_))))
}
#[test]
fn decode_invalid_keys() {
let data = b"MATRIX\
\x02\x00\x00\x0f\
!test:localhost\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
SECRETISLONGENOUGH";
let result = QrVerificationData::from_bytes(data);
assert!(matches!(result, Err(DecodingError::Keys(_))))
}
}

View File

@@ -23,12 +23,13 @@ use image::{DynamicImage, GenericImage, GenericImageView, ImageBuffer, Luma};
use qrcode::QrCode;
use ruma_identifiers::EventId;
use ruma_serde::Base64;
use vodozemac::Ed25519PublicKey;
#[cfg(feature = "decode_image")]
use crate::utils::decode_qr;
use crate::{
error::{DecodingError, EncodingError},
utils::{base_64_encode, to_bytes, to_qr_code, HEADER, MAX_MODE, MIN_SECRET_LEN, VERSION},
utils::{to_bytes, to_qr_code, HEADER, MAX_MODE, MIN_SECRET_LEN, VERSION},
};
/// An enum representing the different modes a QR verification can be in.
@@ -150,8 +151,8 @@ impl QrVerificationData {
/// let data = b"MATRIX\
/// \x02\x02\x00\x07\
/// FLOW_ID\
/// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
/// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
/// kS /\x92i\x1e6\xcd'g\xf9#\x11\xd8\x8a\xa2\xf61\x05\x1b6\xef\xfc\xa4%\x80\x1a\x0c\xd2\xe8\x04\
/// \xbdR|\xf8n\x07\xa4\x1f\xb4\xcc3\x0eBT\xe7[~\xfd\x87\xd06B\xdfoVv%\x9b\x86\xae\xbcM\
/// SHARED_SECRET";
///
/// let result = QrVerificationData::from_bytes(data)?;
@@ -178,8 +179,8 @@ impl QrVerificationData {
/// let data = b"MATRIX\
/// \x02\x02\x00\x07\
/// FLOW_ID\
/// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
/// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
/// kS /\x92i\x1e6\xcd'g\xf9#\x11\xd8\x8a\xa2\xf61\x05\x1b6\xef\xfc\xa4%\x80\x1a\x0c\xd2\xe8\x04\
/// \xbdR|\xf8n\x07\xa4\x1f\xb4\xcc3\x0eBT\xe7[~\xfd\x87\xd06B\xdfoVv%\x9b\x86\xae\xbcM\
/// SHARED_SECRET";
///
/// let result = QrVerificationData::from_bytes(data)?;
@@ -208,8 +209,8 @@ impl QrVerificationData {
/// let data = b"MATRIX\
/// \x02\x02\x00\x07\
/// FLOW_ID\
/// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
/// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
/// kS /\x92i\x1e6\xcd'g\xf9#\x11\xd8\x8a\xa2\xf61\x05\x1b6\xef\xfc\xa4%\x80\x1a\x0c\xd2\xe8\x04\
/// \xbdR|\xf8n\x07\xa4\x1f\xb4\xcc3\x0eBT\xe7[~\xfd\x87\xd06B\xdfoVv%\x9b\x86\xae\xbcM\
/// SHARED_SECRET";
///
/// let result = QrVerificationData::from_bytes(data)?;
@@ -289,6 +290,9 @@ impl QrVerificationData {
return Err(DecodingError::SharedSecret(shared_secret.len()));
}
let first_key = Ed25519PublicKey::from_slice(&first_key)?;
let second_key = Ed25519PublicKey::from_slice(&second_key)?;
QrVerificationData::new(mode, flow_id, first_key, second_key, shared_secret)
}
@@ -306,12 +310,10 @@ impl QrVerificationData {
fn new(
mode: u8,
flow_id: Vec<u8>,
first_key: [u8; 32],
second_key: [u8; 32],
first_key: Ed25519PublicKey,
second_key: Ed25519PublicKey,
shared_secret: Vec<u8>,
) -> Result<Self, DecodingError> {
let first_key = base_64_encode(&first_key);
let second_key = base_64_encode(&second_key);
let flow_id = String::from_utf8(flow_id)?;
let shared_secret = Base64::new(shared_secret);
@@ -343,20 +345,20 @@ impl QrVerificationData {
}
/// Get the first key of this `QrVerificationData`.
pub fn first_key(&self) -> &str {
pub fn first_key(&self) -> Ed25519PublicKey {
match self {
QrVerificationData::Verification(v) => &v.first_master_key,
QrVerificationData::SelfVerification(v) => &v.master_key,
QrVerificationData::SelfVerificationNoMasterKey(v) => &v.device_key,
QrVerificationData::Verification(v) => v.first_master_key,
QrVerificationData::SelfVerification(v) => v.master_key,
QrVerificationData::SelfVerificationNoMasterKey(v) => v.device_key,
}
}
/// Get the second key of this `QrVerificationData`.
pub fn second_key(&self) -> &str {
pub fn second_key(&self) -> Ed25519PublicKey {
match self {
QrVerificationData::Verification(v) => &v.second_master_key,
QrVerificationData::SelfVerification(v) => &v.device_key,
QrVerificationData::SelfVerificationNoMasterKey(v) => &v.master_key,
QrVerificationData::Verification(v) => v.second_master_key,
QrVerificationData::SelfVerification(v) => v.device_key,
QrVerificationData::SelfVerificationNoMasterKey(v) => v.master_key,
}
}
@@ -377,8 +379,8 @@ impl QrVerificationData {
#[derive(Clone, Debug, PartialEq)]
pub struct VerificationData {
event_id: Box<EventId>,
first_master_key: String,
second_master_key: String,
first_master_key: Ed25519PublicKey,
second_master_key: Ed25519PublicKey,
shared_secret: Base64,
}
@@ -400,8 +402,8 @@ impl VerificationData {
/// needs to be at least 8 bytes long.
pub fn new(
event_id: Box<EventId>,
first_key: String,
second_key: String,
first_key: Ed25519PublicKey,
second_key: Ed25519PublicKey,
shared_secret: Base64,
) -> Self {
Self { event_id, first_master_key: first_key, second_master_key: second_key, shared_secret }
@@ -420,8 +422,8 @@ impl VerificationData {
/// let data = b"MATRIX\
/// \x02\x00\x00\x0f\
/// $test:localhost\
/// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
/// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
/// kS /\x92i\x1e6\xcd'g\xf9#\x11\xd8\x8a\xa2\xf61\x05\x1b6\xef\xfc\xa4%\x80\x1a\x0c\xd2\xe8\x04\
/// \xbdR|\xf8n\x07\xa4\x1f\xb4\xcc3\x0eBT\xe7[~\xfd\x87\xd06B\xdfoVv%\x9b\x86\xae\xbcM\
/// SHARED_SECRET";
///
/// let result = QrVerificationData::from_bytes(data)?;
@@ -438,8 +440,8 @@ impl VerificationData {
to_bytes(
Self::QR_MODE,
self.event_id.as_str(),
&self.first_master_key,
&self.second_master_key,
self.first_master_key,
self.second_master_key,
&self.shared_secret,
)
}
@@ -455,8 +457,8 @@ impl VerificationData {
to_qr_code(
Self::QR_MODE,
self.event_id.as_str(),
&self.first_master_key,
&self.second_master_key,
self.first_master_key,
self.second_master_key,
&self.shared_secret,
)
}
@@ -476,8 +478,8 @@ impl From<VerificationData> for QrVerificationData {
#[derive(Clone, Debug, PartialEq)]
pub struct SelfVerificationData {
transaction_id: String,
master_key: String,
device_key: String,
master_key: Ed25519PublicKey,
device_key: Ed25519PublicKey,
shared_secret: Base64,
}
@@ -503,8 +505,8 @@ impl SelfVerificationData {
/// needs to be at least 8 bytes long.
pub fn new(
transaction_id: String,
master_key: String,
device_key: String,
master_key: Ed25519PublicKey,
device_key: Ed25519PublicKey,
shared_secret: Base64,
) -> Self {
Self { transaction_id, master_key, device_key, shared_secret }
@@ -523,8 +525,8 @@ impl SelfVerificationData {
/// let data = b"MATRIX\
/// \x02\x01\x00\x06\
/// FLOWID\
/// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
/// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
/// kS /\x92i\x1e6\xcd'g\xf9#\x11\xd8\x8a\xa2\xf61\x05\x1b6\xef\xfc\xa4%\x80\x1a\x0c\xd2\xe8\x04\
/// \xbdR|\xf8n\x07\xa4\x1f\xb4\xcc3\x0eBT\xe7[~\xfd\x87\xd06B\xdfoVv%\x9b\x86\xae\xbcM\
/// SHARED_SECRET";
///
/// let result = QrVerificationData::from_bytes(data)?;
@@ -541,8 +543,8 @@ impl SelfVerificationData {
to_bytes(
Self::QR_MODE,
&self.transaction_id,
&self.master_key,
&self.device_key,
self.master_key,
self.device_key,
&self.shared_secret,
)
}
@@ -558,8 +560,8 @@ impl SelfVerificationData {
to_qr_code(
Self::QR_MODE,
&self.transaction_id,
&self.master_key,
&self.device_key,
self.master_key,
self.device_key,
&self.shared_secret,
)
}
@@ -579,8 +581,8 @@ impl From<SelfVerificationData> for QrVerificationData {
#[derive(Clone, Debug, PartialEq)]
pub struct SelfVerificationNoMasterKey {
transaction_id: String,
device_key: String,
master_key: String,
device_key: Ed25519PublicKey,
master_key: Ed25519PublicKey,
shared_secret: Base64,
}
@@ -606,8 +608,8 @@ impl SelfVerificationNoMasterKey {
/// needs to be at least 8 bytes long.
pub fn new(
transaction_id: String,
device_key: String,
master_key: String,
device_key: Ed25519PublicKey,
master_key: Ed25519PublicKey,
shared_secret: Base64,
) -> Self {
Self { transaction_id, device_key, master_key, shared_secret }
@@ -626,8 +628,8 @@ impl SelfVerificationNoMasterKey {
/// let data = b"MATRIX\
/// \x02\x02\x00\x06\
/// FLOWID\
/// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
/// BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\
/// kS /\x92i\x1e6\xcd'g\xf9#\x11\xd8\x8a\xa2\xf61\x05\x1b6\xef\xfc\xa4%\x80\x1a\x0c\xd2\xe8\x04\
/// \xbdR|\xf8n\x07\xa4\x1f\xb4\xcc3\x0eBT\xe7[~\xfd\x87\xd06B\xdfoVv%\x9b\x86\xae\xbcM\
/// SHARED_SECRET";
///
/// let result = QrVerificationData::from_bytes(data)?;
@@ -644,8 +646,8 @@ impl SelfVerificationNoMasterKey {
to_bytes(
Self::QR_MODE,
&self.transaction_id,
&self.device_key,
&self.master_key,
self.device_key,
self.master_key,
&self.shared_secret,
)
}
@@ -661,8 +663,8 @@ impl SelfVerificationNoMasterKey {
to_qr_code(
Self::QR_MODE,
&self.transaction_id,
&self.device_key,
&self.master_key,
self.device_key,
self.master_key,
&self.shared_secret,
)
}

View File

@@ -14,11 +14,11 @@
use std::convert::TryInto;
use base64::{decode_config, encode_config, STANDARD_NO_PAD};
#[cfg(feature = "decode_image")]
use image::{GenericImage, GenericImageView, Luma};
use qrcode::{bits::Bits, EcLevel, QrCode, Version};
use ruma_serde::Base64;
use vodozemac::Ed25519PublicKey;
#[cfg(feature = "decode_image")]
use crate::error::DecodingError;
@@ -29,35 +29,24 @@ pub(crate) const VERSION: u8 = 0x2;
pub(crate) const MAX_MODE: u8 = 0x2;
pub(crate) const MIN_SECRET_LEN: usize = 8;
pub(crate) fn base_64_encode(data: &[u8]) -> String {
encode_config(data, STANDARD_NO_PAD)
}
pub(crate) fn base64_decode(data: &str) -> Result<Vec<u8>, base64::DecodeError> {
decode_config(data, STANDARD_NO_PAD)
}
pub(crate) fn to_bytes(
mode: u8,
flow_id: &str,
first_key: &str,
second_key: &str,
first_key: Ed25519PublicKey,
second_key: Ed25519PublicKey,
shared_secret: &Base64,
) -> Result<Vec<u8>, EncodingError> {
let flow_id_len: u16 = flow_id.len().try_into()?;
let flow_id_len = flow_id_len.to_be_bytes();
let first_key = base64_decode(first_key)?;
let second_key = base64_decode(second_key)?;
let data = [
HEADER,
&[VERSION],
&[mode],
flow_id_len.as_ref(),
flow_id.as_bytes(),
&first_key,
&second_key,
first_key.as_bytes(),
second_key.as_bytes(),
shared_secret.as_bytes(),
]
.concat();
@@ -68,8 +57,8 @@ pub(crate) fn to_bytes(
pub(crate) fn to_qr_code(
mode: u8,
flow_id: &str,
first_key: &str,
second_key: &str,
first_key: Ed25519PublicKey,
second_key: Ed25519PublicKey,
shared_secret: &Base64,
) -> Result<QrCode, EncodingError> {
let data = to_bytes(mode, flow_id, first_key, second_key, shared_secret)?;

View File

@@ -951,7 +951,7 @@ mod test {
},
room_id,
to_device::DeviceIdOrAllDevices,
user_id, DeviceId, DeviceKeyAlgorithm, RoomId, UserId,
user_id, DeviceId, RoomId, UserId,
};
use super::{GossipMachine, KeyForwardDecision};
@@ -1277,7 +1277,7 @@ mod test {
.mark_shared_with(
bob_device.user_id(),
bob_device.device_id(),
bob_device.get_key(DeviceKeyAlgorithm::Curve25519).unwrap(),
bob_device.curve25519_key().unwrap(),
)
.await;
assert!(machine.should_share_key(&bob_device, &inbound).await.is_ok());
@@ -1315,7 +1315,7 @@ mod test {
.mark_shared_with(
own_device.user_id(),
own_device.device_id(),
own_device.get_key(DeviceKeyAlgorithm::Curve25519).unwrap(),
own_device.curve25519_key().unwrap(),
)
.await;
@@ -1378,7 +1378,7 @@ mod test {
.mark_shared_with(
alice_device.user_id(),
alice_device.device_id(),
alice_device.get_key(DeviceKeyAlgorithm::Curve25519).unwrap(),
alice_device.curve25519_key().unwrap(),
)
.await;
@@ -1582,7 +1582,7 @@ mod test {
.mark_shared_with(
alice_device.user_id(),
alice_device.device_id(),
alice_device.get_key(DeviceKeyAlgorithm::Curve25519).unwrap(),
alice_device.curve25519_key().unwrap(),
)
.await;

View File

@@ -36,7 +36,7 @@ use ruma::{
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::{json, Value};
use tracing::warn;
use vodozemac::Ed25519PublicKey;
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
use super::{atomic_bool_deserializer, atomic_bool_serializer};
use crate::{
@@ -44,7 +44,7 @@ use crate::{
identities::{ReadOnlyOwnUserIdentity, ReadOnlyUserIdentities},
olm::{InboundGroupSession, Session, VerifyJson},
store::{Changes, CryptoStore, DeviceChanges, Result as StoreResult},
types::{device_keys::DeviceKeys, one_time_keys::SignedKey},
types::{DeviceKey, DeviceKeys, SignedKey},
verification::VerificationMachine,
OutgoingVerificationRequest, Sas, ToDeviceRequest, VerificationRequest,
};
@@ -177,8 +177,8 @@ impl Device {
/// Get the Olm sessions that belong to this device.
pub(crate) async fn get_sessions(&self) -> StoreResult<Option<Arc<Mutex<Vec<Session>>>>> {
if let Some(k) = self.get_key(DeviceKeyAlgorithm::Curve25519) {
self.verification_machine.store.get_sessions(k).await
if let Some(k) = self.curve25519_key() {
self.verification_machine.store.get_sessions(&k.to_base64()).await
} else {
Ok(None)
}
@@ -399,12 +399,34 @@ impl ReadOnlyDevice {
}
/// Get the key of the given key algorithm belonging to this device.
pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&String> {
pub fn get_key(&self, algorithm: DeviceKeyAlgorithm) -> Option<&DeviceKey> {
self.inner.keys.get(&DeviceKeyId::from_parts(algorithm, self.device_id()))
}
/// Get the Curve25519 key of the given device.
pub fn curve25519_key(&self) -> Option<Curve25519PublicKey> {
self.get_key(DeviceKeyAlgorithm::Curve25519).and_then(|k| {
if let DeviceKey::Curve25519(k) = k {
Some(*k)
} else {
None
}
})
}
/// Get the Ed25519 key of the given device.
pub fn ed25519_key(&self) -> Option<Ed25519PublicKey> {
self.get_key(DeviceKeyAlgorithm::Ed25519).and_then(|k| {
if let DeviceKey::Ed25519(k) = k {
Some(*k)
} else {
None
}
})
}
/// Get a map containing all the device keys.
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, String> {
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, DeviceKey> {
&self.inner.keys
}
@@ -490,7 +512,7 @@ impl ReadOnlyDevice {
store: &dyn CryptoStore,
content: AnyToDeviceEventContent,
) -> OlmResult<(Session, ToDeviceRoomEncryptedEventContent)> {
let sender_key = if let Some(k) = self.get_key(DeviceKeyAlgorithm::Curve25519) {
let sender_key = if let Some(k) = self.curve25519_key() {
k
} else {
warn!(
@@ -502,7 +524,7 @@ impl ReadOnlyDevice {
return Err(EventError::MissingSenderKey.into());
};
let session = if let Some(s) = store.get_sessions(sender_key).await? {
let session = if let Some(s) = store.get_sessions(&sender_key.to_base64()).await? {
let sessions = s.lock().await;
sessions.get(0).cloned()
} else {
@@ -535,10 +557,7 @@ impl ReadOnlyDevice {
}
pub(crate) fn is_signed_by_device(&self, json: &mut Value) -> Result<(), SignatureError> {
let key =
self.get_key(DeviceKeyAlgorithm::Ed25519).ok_or(SignatureError::MissingSigningKey)?;
let key = Ed25519PublicKey::from_base64(key)?;
let key = self.ed25519_key().ok_or(SignatureError::MissingSigningKey)?;
key.verify_json(
self.user_id(),
@@ -618,7 +637,7 @@ pub(crate) mod testing {
#![allow(dead_code)]
use serde_json::json;
use crate::{identities::ReadOnlyDevice, types::device_keys::DeviceKeys};
use crate::{identities::ReadOnlyDevice, types::DeviceKeys};
/// Generate default DeviceKeys for tests
pub fn device_keys() -> DeviceKeys {
@@ -655,7 +674,8 @@ pub(crate) mod testing {
#[cfg(test)]
pub(crate) mod test {
use ruma::{user_id, DeviceKeyAlgorithm};
use ruma::user_id;
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
use super::testing::{device_keys, get_device};
use crate::identities::LocalTrust;
@@ -673,12 +693,13 @@ pub(crate) mod test {
assert_eq!(LocalTrust::Unset, device.local_trust_state());
assert_eq!("Alice's mobile phone", device.display_name().unwrap());
assert_eq!(
device.get_key(DeviceKeyAlgorithm::Curve25519).unwrap(),
"xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc"
device.curve25519_key().unwrap(),
Curve25519PublicKey::from_base64("xfgbLIC5WAl1OIkpOzoxpCe8FsRDT6nch7NQsOb15nc")
.unwrap(),
);
assert_eq!(
device.get_key(DeviceKeyAlgorithm::Ed25519).unwrap(),
"2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4"
device.ed25519_key().unwrap(),
Ed25519PublicKey::from_base64("2/5LWJMow5zhJqakV88SIc7q/1pa8fmkfgAzx72w9G4").unwrap(),
);
}

View File

@@ -35,7 +35,7 @@ use crate::{
olm::PrivateCrossSigningIdentity,
requests::KeysQueryRequest,
store::{Changes, DeviceChanges, IdentityChanges, Result as StoreResult, Store},
types::{cross_signing_key::CrossSigningKey, device_keys::DeviceKeys},
types::{CrossSigningKey, DeviceKeys},
LocalTrust,
};

View File

@@ -40,7 +40,7 @@ use crate::{
error::SignatureError,
olm::VerifyJson,
store::{Changes, IdentityChanges},
types::{cross_signing_key::CrossSigningKey, device_keys::DeviceKeys},
types::{CrossSigningKey, DeviceKeys, SigningKey},
verification::VerificationMachine,
CryptoStoreError, OutgoingVerificationRequest, ReadOnlyDevice, VerificationRequest,
};
@@ -393,7 +393,7 @@ impl MasterPubkey {
}
/// Get the keys map of containing the master keys.
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, String> {
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, SigningKey> {
&self.0.keys
}
@@ -412,16 +412,20 @@ impl MasterPubkey {
/// # Arguments
///
/// * `key_id` - The id of the key that should be fetched.
pub fn get_key(&self, key_id: &DeviceKeyId) -> Option<&str> {
self.0.keys.get(key_id).map(|k| k.as_str())
pub fn get_key(&self, key_id: &DeviceKeyId) -> Option<&SigningKey> {
self.0.keys.get(key_id)
}
/// Get the first available master key.
///
/// There's usually only a single master key so this will usually fetch the
/// only key.
pub fn get_first_key(&self) -> Option<&str> {
self.0.keys.values().map(|k| k.as_str()).next()
pub fn get_first_key(&self) -> Option<Ed25519PublicKey> {
if let Some(SigningKey::Ed25519(k)) = self.0.keys.values().next() {
Some(*k)
} else {
None
}
}
/// Check if the given cross signing sub-key is signed by the master key.
@@ -450,19 +454,22 @@ impl MasterPubkey {
return Err(SignatureError::UserIdMismatch);
}
let key = Ed25519PublicKey::from_base64(key)?;
key.verify_json(
&self.0.user_id,
key_id,
&mut to_value(subkey.cross_signing_key()).map_err(|_| SignatureError::NotAnObject)?,
)
if let SigningKey::Ed25519(key) = key {
key.verify_json(
&self.0.user_id,
key_id,
&mut to_value(subkey.cross_signing_key())
.map_err(|_| SignatureError::NotAnObject)?,
)
} else {
Err(SignatureError::UnsupportedAlgorithm)
}
}
}
impl<'a> IntoIterator for &'a MasterPubkey {
type Item = (&'a Box<DeviceKeyId>, &'a String);
type IntoIter = Iter<'a, Box<DeviceKeyId>, String>;
type Item = (&'a Box<DeviceKeyId>, &'a SigningKey);
type IntoIter = Iter<'a, Box<DeviceKeyId>, SigningKey>;
fn into_iter(self) -> Self::IntoIter {
self.keys().iter()
@@ -476,7 +483,7 @@ impl UserSigningPubkey {
}
/// Get the keys map of containing the user signing keys.
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, String> {
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, SigningKey> {
&self.0.keys
}
@@ -497,18 +504,21 @@ impl UserSigningPubkey {
// TODO check that the usage is OK.
let key = Ed25519PublicKey::from_base64(key)?;
key.verify_json(
&self.0.user_id,
key_id.as_str().try_into()?,
&mut to_value(&master_key.0).map_err(|_| SignatureError::NotAnObject)?,
)
if let SigningKey::Ed25519(key) = key {
key.verify_json(
&self.0.user_id,
key_id.as_str().try_into()?,
&mut to_value(&master_key.0).map_err(|_| SignatureError::NotAnObject)?,
)
} else {
Err(SignatureError::UnsupportedAlgorithm)
}
}
}
impl<'a> IntoIterator for &'a UserSigningPubkey {
type Item = (&'a Box<DeviceKeyId>, &'a String);
type IntoIter = Iter<'a, Box<DeviceKeyId>, String>;
type Item = (&'a Box<DeviceKeyId>, &'a SigningKey);
type IntoIter = Iter<'a, Box<DeviceKeyId>, SigningKey>;
fn into_iter(self) -> Self::IntoIter {
self.keys().iter()
@@ -522,7 +532,7 @@ impl SelfSigningPubkey {
}
/// Get the keys map of containing the self signing keys.
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, String> {
pub fn keys(&self) -> &BTreeMap<Box<DeviceKeyId>, SigningKey> {
&self.0.keys
}
@@ -532,8 +542,11 @@ impl SelfSigningPubkey {
let mut device = to_value(device_keys)?;
let key = Ed25519PublicKey::from_base64(key)?;
key.verify_json(&self.0.user_id, key_id.as_str().try_into()?, &mut device)
if let SigningKey::Ed25519(key) = key {
key.verify_json(&self.0.user_id, key_id.as_str().try_into()?, &mut device)
} else {
Err(SignatureError::UnsupportedAlgorithm)
}
}
/// Check if the given device is signed by this self signing key.
@@ -550,8 +563,8 @@ impl SelfSigningPubkey {
}
impl<'a> IntoIterator for &'a SelfSigningPubkey {
type Item = (&'a Box<DeviceKeyId>, &'a String);
type IntoIter = Iter<'a, Box<DeviceKeyId>, String>;
type Item = (&'a Box<DeviceKeyId>, &'a SigningKey);
type IntoIter = Iter<'a, Box<DeviceKeyId>, SigningKey>;
fn into_iter(self) -> Self::IntoIter {
self.keys().iter()
@@ -908,7 +921,7 @@ pub(crate) mod testing {
manager::testing::{other_key_query, own_key_query},
ReadOnlyDevice,
},
types::cross_signing_key::CrossSigningKey,
types::CrossSigningKey,
};
/// Generate test devices from KeyQueryResponse
@@ -971,7 +984,7 @@ pub(crate) mod test {
identities::{manager::testing::own_key_query, Device},
olm::{PrivateCrossSigningIdentity, ReadOnlyAccount},
store::MemoryStore,
types::cross_signing_key::CrossSigningKey,
types::CrossSigningKey,
verification::VerificationMachine,
};

View File

@@ -1031,9 +1031,7 @@ impl OlmMachine {
) -> StoreResult<EncryptionInfo> {
let verification_state = if let Some(device) =
self.get_device(sender, device_id).await?.filter(|d| {
d.get_key(DeviceKeyAlgorithm::Curve25519)
.map(|k| k == session.sender_key())
.unwrap_or(false)
d.curve25519_key().map(|k| k.to_base64() == session.sender_key()).unwrap_or(false)
}) {
if (self.user_id() == device.user_id() && self.device_id() == device.device_id())
|| device.verified()
@@ -1488,6 +1486,7 @@ impl OlmMachine {
.await
.and_then(|m| m.get_first_key().map(|k| k.to_owned()))
.ok_or(crate::SignatureError::MissingSigningKey)?
.to_base64()
.into();
let device_key_id = DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &master_key);

View File

@@ -39,7 +39,7 @@ use ruma::{
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UInt, UserId,
};
use serde::{Deserialize, Serialize};
use serde_json::{json, value::RawValue as RawJsonValue, Value};
use serde_json::{value::RawValue as RawJsonValue, Value};
use sha2::{Digest, Sha256};
use tracing::{debug, info, trace, warn};
use vodozemac::{
@@ -56,11 +56,7 @@ use crate::{
identities::{MasterPubkey, ReadOnlyDevice},
requests::UploadSigningKeysRequest,
store::{Changes, Store},
types::{
cross_signing_key::CrossSigningKey,
device_keys::DeviceKeys,
one_time_keys::{OneTimeKey, SignedKey},
},
types::{CrossSigningKey, DeviceKeys, OneTimeKey, SignedKey},
utilities::encode,
CryptoStoreError, OlmError, SignatureError,
};
@@ -724,11 +720,11 @@ impl ReadOnlyAccount {
let keys = BTreeMap::from([
(
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Curve25519, &self.device_id),
identity_keys.curve25519.to_base64(),
identity_keys.curve25519.into(),
),
(
DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, &self.device_id),
identity_keys.ed25519.to_base64(),
identity_keys.ed25519.into(),
),
]);
@@ -748,12 +744,8 @@ impl ReadOnlyAccount {
// Create a copy of the device keys containing only fields that will
// get signed.
let json_device_keys = json!({
"user_id": device_keys.user_id,
"device_id": device_keys.device_id,
"algorithms": device_keys.algorithms,
"keys": device_keys.keys,
});
let json_device_keys =
serde_json::to_value(&device_keys).expect("Can't serialize device keys");
device_keys
.signatures
@@ -799,7 +791,8 @@ impl ReadOnlyAccount {
master_key: MasterPubkey,
) -> Result<SignatureUploadRequest, SignatureError> {
let public_key =
master_key.get_first_key().ok_or(SignatureError::MissingSigningKey)?.into();
master_key.get_first_key().ok_or(SignatureError::MissingSigningKey)?.to_base64().into();
let mut cross_signing_key: CrossSigningKey = master_key.as_ref().clone();
cross_signing_key.signatures.clear();
self.sign_cross_signing_key(&mut cross_signing_key).await?;
@@ -937,7 +930,7 @@ impl ReadOnlyAccount {
our_identity_keys: self.identity_keys.clone(),
inner: Arc::new(Mutex::new(session)),
session_id: session_id.into(),
sender_key: identity_key.to_base64().into(),
sender_key: identity_key,
created_using_fallback_key: fallback_used,
creation_time: Arc::new(now),
last_use_time: Arc::new(now),
@@ -992,14 +985,13 @@ impl ReadOnlyAccount {
)
})?;
let identity_key = device.get_key(DeviceKeyAlgorithm::Curve25519).ok_or_else(|| {
let identity_key = device.curve25519_key().ok_or_else(|| {
SessionCreationError::DeviceMissingCurveKey(
device.user_id().to_owned(),
device.device_id().into(),
)
})?;
let identity_key = Curve25519PublicKey::from_base64(identity_key)?;
let is_fallback = one_time_key.fallback();
let one_time_key = one_time_key.key();
@@ -1034,7 +1026,7 @@ impl ReadOnlyAccount {
our_identity_keys: self.identity_keys.clone(),
inner: Arc::new(Mutex::new(result.session)),
session_id: session_id.into(),
sender_key: their_identity_key.to_base64().into(),
sender_key: their_identity_key,
created_using_fallback_key: false,
creation_time: Arc::new(now),
last_use_time: Arc::new(now),
@@ -1144,16 +1136,7 @@ impl ReadOnlyAccount {
let our_device = ReadOnlyDevice::from_account(self).await;
let other_session = other
.create_inbound_session(
our_device
.keys()
.get(&DeviceKeyId::from_parts(
DeviceKeyAlgorithm::Curve25519,
our_device.device_id(),
))
.unwrap(),
&prekey,
)
.create_inbound_session(&our_device.curve25519_key().unwrap().to_base64(), &prekey)
.await
.unwrap();

View File

@@ -37,11 +37,12 @@ use ruma::{
room_key::ToDeviceRoomKeyEventContent,
AnyToDeviceEventContent,
},
DeviceId, DeviceKeyAlgorithm, EventEncryptionAlgorithm, RoomId, TransactionId, UserId,
DeviceId, EventEncryptionAlgorithm, RoomId, TransactionId, UserId,
};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tracing::{debug, error, info};
use vodozemac::Curve25519PublicKey;
pub use vodozemac::{
megolm::{GroupSession, GroupSessionPickle, MegolmMessage, SessionKey},
olm::IdentityKeys,
@@ -138,7 +139,7 @@ pub type ShareInfoSet = BTreeMap<Box<UserId>, BTreeMap<Box<DeviceId>, ShareInfo>
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ShareInfo {
/// The sender key of the device that was used to encrypt the room key.
pub sender_key: String,
pub sender_key: Curve25519PublicKey,
/// The message index that the device received.
pub message_index: u32,
}
@@ -380,7 +381,7 @@ impl OutboundGroupSession {
// Check if we shared the session.
let shared_state = self.shared_with_set.get(device.user_id()).and_then(|d| {
d.get(device.device_id()).map(|s| {
if Some(&s.sender_key) == device.get_key(DeviceKeyAlgorithm::Curve25519) {
if Some(s.sender_key) == device.curve25519_key() {
ShareState::Shared(s.message_index)
} else {
ShareState::SharedButChangedSenderKey
@@ -401,8 +402,7 @@ impl OutboundGroupSession {
share_info.get(device.user_id()).and_then(|d| {
d.get(device.device_id()).map(|info| {
if Some(&info.sender_key) == device.get_key(DeviceKeyAlgorithm::Curve25519)
{
if Some(info.sender_key) == device.curve25519_key() {
ShareState::Shared(info.message_index)
} else {
ShareState::SharedButChangedSenderKey
@@ -422,25 +422,27 @@ impl OutboundGroupSession {
&self,
user_id: &UserId,
device_id: &DeviceId,
sender_key: &str,
sender_key: Curve25519PublicKey,
index: u32,
) {
self.shared_with_set.entry(user_id.to_owned()).or_insert_with(DashMap::new).insert(
device_id.to_owned(),
ShareInfo { sender_key: sender_key.to_owned(), message_index: index },
);
self.shared_with_set
.entry(user_id.to_owned())
.or_insert_with(DashMap::new)
.insert(device_id.to_owned(), ShareInfo { sender_key, message_index: index });
}
/// Mark the session as shared with the given user/device pair, starting
/// from the current index.
#[cfg(test)]
pub async fn mark_shared_with(&self, user_id: &UserId, device_id: &DeviceId, sender_key: &str) {
pub async fn mark_shared_with(
&self,
user_id: &UserId,
device_id: &DeviceId,
sender_key: Curve25519PublicKey,
) {
self.shared_with_set.entry(user_id.to_owned()).or_insert_with(DashMap::new).insert(
device_id.to_owned(),
ShareInfo {
sender_key: sender_key.to_owned(),
message_index: self.message_index().await,
},
ShareInfo { sender_key, message_index: self.message_index().await },
);
}

View File

@@ -23,11 +23,14 @@ use ruma::{
},
AnyToDeviceEventContent, EventContent,
},
DeviceId, DeviceKeyAlgorithm, UserId,
DeviceId, UserId,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use vodozemac::olm::{DecryptionError, OlmMessage, Session as InnerSession, SessionPickle};
use vodozemac::{
olm::{DecryptionError, OlmMessage, Session as InnerSession, SessionPickle},
Curve25519PublicKey,
};
use super::{deserialize_instant, serialize_instant, IdentityKeys};
use crate::{
@@ -50,7 +53,7 @@ pub struct Session {
/// Our sessionId
pub session_id: Arc<str>,
/// The Key of the sender
pub sender_key: Arc<str>,
pub sender_key: Curve25519PublicKey,
/// Has this been created using the fallback key
pub created_using_fallback_key: bool,
/// When the session was created
@@ -85,8 +88,8 @@ impl Session {
}
/// Get the sender key that was used to establish this Session.
pub fn sender_key(&self) -> &str {
&self.sender_key
pub fn sender_key(&self) -> Curve25519PublicKey {
self.sender_key
}
/// Encrypt the given plaintext as a OlmMessage.
@@ -117,9 +120,8 @@ impl Session {
recipient_device: &ReadOnlyDevice,
content: AnyToDeviceEventContent,
) -> OlmResult<ToDeviceRoomEncryptedEventContent> {
let recipient_signing_key = recipient_device
.get_key(DeviceKeyAlgorithm::Ed25519)
.ok_or(EventError::MissingSigningKey)?;
let recipient_signing_key =
recipient_device.ed25519_key().ok_or(EventError::MissingSigningKey)?;
let event_type = content.event_type();
@@ -131,7 +133,7 @@ impl Session {
},
"recipient": recipient_device.user_id(),
"recipient_keys": {
"ed25519": recipient_signing_key,
"ed25519": recipient_signing_key.to_base64(),
},
"type": event_type,
"content": content,
@@ -144,7 +146,7 @@ impl Session {
let ciphertext = CiphertextInfo::new(ciphertext.1, (message_type as u32).into());
let mut content = BTreeMap::new();
content.insert((*self.sender_key).to_owned(), ciphertext);
content.insert(self.sender_key.to_base64(), ciphertext);
Ok(EncryptedEventScheme::OlmV1Curve25519AesSha2(OlmV1Curve25519AesSha2Content::new(
content,
@@ -169,7 +171,7 @@ impl Session {
PickledSession {
pickle,
sender_key: self.sender_key.to_string(),
sender_key: self.sender_key,
created_using_fallback_key: self.created_using_fallback_key,
creation_time: *self.creation_time,
last_use_time: *self.last_use_time,
@@ -209,7 +211,7 @@ impl Session {
inner: Arc::new(Mutex::new(session)),
session_id: session_id.into(),
created_using_fallback_key: pickle.created_using_fallback_key,
sender_key: pickle.sender_key.into(),
sender_key: pickle.sender_key,
creation_time: Arc::new(pickle.creation_time),
last_use_time: Arc::new(pickle.last_use_time),
}
@@ -232,7 +234,7 @@ pub struct PickledSession {
/// The pickle string holding the Olm Session.
pub pickle: SessionPickle,
/// The curve25519 key of the other user that we share this session with.
pub sender_key: String,
pub sender_key: Curve25519PublicKey,
/// Was the session created using a fallback key.
#[serde(default)]
pub created_using_fallback_key: bool,

View File

@@ -36,7 +36,7 @@ use crate::{
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
requests::UploadSigningKeysRequest,
store::SecretImportError,
types::device_keys::DeviceKeys,
types::DeviceKeys,
OwnUserIdentity, ReadOnlyAccount, ReadOnlyDevice, ReadOnlyOwnUserIdentity,
ReadOnlyUserIdentity,
};
@@ -404,6 +404,7 @@ impl PrivateCrossSigningIdentity {
.master_key()
.get_first_key()
.ok_or(SignatureError::MissingSigningKey)?
.to_base64()
.into(),
master_key.to_raw(),
);

View File

@@ -26,10 +26,7 @@ use vodozemac::{Ed25519PublicKey, Ed25519SecretKey, Ed25519Signature, KeyError};
use crate::{
error::SignatureError,
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
types::{
cross_signing_key::{CrossSigningKey, CrossSigningKeySignatures},
device_keys::DeviceKeys,
},
types::{CrossSigningKey, CrossSigningKeySignatures, DeviceKeys},
utilities::{encode, DecodeError},
ReadOnlyUserIdentity,
};
@@ -324,7 +321,7 @@ impl Signing {
DeviceKeyAlgorithm::Ed25519,
&Box::<DeviceId>::from(self.public_key().to_base64()),
),
self.inner.public_key().to_base64(),
self.inner.public_key().into(),
)]);
CrossSigningKey::new(user_id, vec![usage], keys, BTreeMap::new())

View File

@@ -36,7 +36,7 @@ use ruma::{
};
use serde::{Deserialize, Serialize};
use crate::types::cross_signing_key::CrossSigningKey;
use crate::types::CrossSigningKey;
/// Customized version of
/// `ruma_client_api::to_device::send_event_to_device::v3::Request`

View File

@@ -194,8 +194,8 @@ impl SessionManager {
"Device doesn't support any of our 1-to-1 E2EE \
algorithms, can't establish an Olm session"
);
} else if let Some(sender_key) = device.get_key(DeviceKeyAlgorithm::Curve25519) {
let sessions = self.store.get_sessions(sender_key).await?;
} else if let Some(sender_key) = device.curve25519_key() {
let sessions = self.store.get_sessions(&sender_key.to_base64()).await?;
let is_missing = if let Some(sessions) = sessions {
sessions.lock().await.is_empty()
@@ -452,7 +452,7 @@ mod test {
assert!(!manager.users_for_key_claim.contains_key(bob.user_id()));
assert!(!manager.is_device_wedged(&bob_device));
manager.mark_device_as_wedged(bob_device.user_id(), curve_key).await.unwrap();
manager.mark_device_as_wedged(bob_device.user_id(), &curve_key.to_base64()).await.unwrap();
assert!(manager.is_device_wedged(&bob_device));
assert!(manager.users_for_key_claim.contains_key(bob.user_id()));

View File

@@ -47,7 +47,7 @@ impl SessionStore {
pub async fn add(&self, session: Session) -> bool {
let sessions_lock = self
.entries
.entry(session.sender_key.to_string())
.entry(session.sender_key.to_base64())
.or_insert_with(|| Arc::new(Mutex::new(Vec::new())));
let mut sessions = sessions_lock.lock().await;
@@ -205,7 +205,7 @@ mod test {
assert!(store.add(session.clone()).await);
assert!(!store.add(session.clone()).await);
let sessions = store.get(&session.sender_key).unwrap();
let sessions = store.get(&session.sender_key.to_base64()).unwrap();
let sessions = sessions.lock().await;
let loaded_session = &sessions[0];
@@ -218,9 +218,9 @@ mod test {
let (_, session) = get_account_and_session().await;
let store = SessionStore::new();
store.set_for_sender(&session.sender_key, vec![session.clone()]);
store.set_for_sender(&session.sender_key.to_base64(), vec![session.clone()]);
let sessions = store.get(&session.sender_key).unwrap();
let sessions = store.get(&session.sender_key.to_base64()).unwrap();
let sessions = sessions.lock().await;
let loaded_session = &sessions[0];

View File

@@ -130,7 +130,7 @@ macro_rules! cryptostore_integration_tests {
store.save_changes(changes).await.unwrap();
let sessions =
store.get_sessions(&session.sender_key).await.expect("Can't load sessions").unwrap();
store.get_sessions(&session.sender_key.to_base64()).await.expect("Can't load sessions").unwrap();
let loaded_session = sessions.lock().await.get(0).cloned().unwrap();
assert_eq!(&session, &loaded_session);
@@ -141,7 +141,7 @@ macro_rules! cryptostore_integration_tests {
let store_name = "add_and_save_session".to_owned();
let store = get_store(store_name.clone(), None).await;
let (account, session) = get_account_and_session().await;
let sender_key = session.sender_key.to_owned();
let sender_key = session.sender_key.to_base64();
let session_id = session.session_id().to_owned();
store.save_account(account.clone()).await.expect("Can't save account");

View File

@@ -327,7 +327,7 @@ mod test {
store.save_sessions(vec![session.clone()]).await;
let sessions = store.get_sessions(&session.sender_key).await.unwrap().unwrap();
let sessions = store.get_sessions(&session.sender_key.to_base64()).await.unwrap().unwrap();
let sessions = sessions.lock().await;
let loaded_session = &sessions[0];

View File

@@ -59,12 +59,13 @@ pub use memorystore::MemoryStore;
pub use pickle_key::{EncryptedPickleKey, PickleKey};
use ruma::{
events::secret::request::SecretName, identifiers::Error as IdentifierValidationError, DeviceId,
DeviceKeyAlgorithm, RoomId, TransactionId, UserId,
RoomId, TransactionId, UserId,
};
use serde::{Deserialize, Serialize};
use serde_json::Error as SerdeError;
use thiserror::Error;
use tracing::{info, warn};
use vodozemac::Curve25519PublicKey;
use zeroize::Zeroize;
use crate::{
@@ -376,11 +377,13 @@ impl Store {
user_id: &UserId,
curve_key: &str,
) -> Result<Option<Device>> {
self.get_user_devices(user_id).await.map(|d| {
d.devices().find(|d| {
d.get_key(DeviceKeyAlgorithm::Curve25519).map_or(false, |k| k == curve_key)
})
})
if let Ok(curve_key) = Curve25519PublicKey::from_base64(curve_key) {
self.get_user_devices(user_id)
.await
.map(|d| d.devices().find(|d| d.curve25519_key().map_or(false, |k| k == curve_key)))
} else {
Ok(None)
}
}
/// Get all devices associated with the given `user_id`

View File

@@ -21,15 +21,17 @@
use std::collections::BTreeMap;
use ruma::{encryption::KeyUsage, serde::Raw, DeviceKeyId, UserId};
use ruma::{encryption::KeyUsage, serde::Raw, DeviceKeyAlgorithm, DeviceKeyId, UserId};
use serde::{Deserialize, Serialize};
use serde_json::{value::to_raw_value, Value};
use vodozemac::Ed25519PublicKey;
/// Signatures for a `CrossSigningKey` object.
pub type CrossSigningKeySignatures = BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>;
/// A cross signing key.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(try_from = "CrossSigningKeyHelper", into = "CrossSigningKeyHelper")]
pub struct CrossSigningKey {
/// The ID of the user the key belongs to.
pub user_id: Box<UserId>,
@@ -40,7 +42,7 @@ pub struct CrossSigningKey {
/// The public key.
///
/// The object must have exactly one property.
pub keys: BTreeMap<Box<DeviceKeyId>, String>,
pub keys: BTreeMap<Box<DeviceKeyId>, SigningKey>,
/// Signatures of the key.
///
@@ -58,7 +60,7 @@ impl CrossSigningKey {
pub fn new(
user_id: Box<UserId>,
usage: Vec<KeyUsage>,
keys: BTreeMap<Box<DeviceKeyId>, String>,
keys: BTreeMap<Box<DeviceKeyId>, SigningKey>,
signatures: CrossSigningKeySignatures,
) -> Self {
Self { user_id, usage, keys, signatures, other: BTreeMap::new() }
@@ -70,6 +72,88 @@ impl CrossSigningKey {
}
}
/// An enum over the different key types a cross-signing key can have.
///
/// Currently cross signing keys support an ed25519 keypair. The keys transport
/// format is a base64 encoded string, any unknown key type will be left as such
/// a string.
#[derive(Clone, Debug, PartialEq)]
pub enum SigningKey {
Ed25519(Ed25519PublicKey),
Other(String),
}
impl SigningKey {
/// Convert the `SigningKey` into a base64 encoded string.
pub fn to_base64(&self) -> String {
match self {
SigningKey::Ed25519(k) => k.to_base64(),
SigningKey::Other(k) => k.to_owned(),
}
}
}
impl From<Ed25519PublicKey> for SigningKey {
fn from(val: Ed25519PublicKey) -> Self {
SigningKey::Ed25519(val)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
struct CrossSigningKeyHelper {
pub user_id: Box<UserId>,
pub usage: Vec<KeyUsage>,
pub keys: BTreeMap<Box<DeviceKeyId>, String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub signatures: CrossSigningKeySignatures,
#[serde(flatten)]
other: BTreeMap<String, Value>,
}
impl TryFrom<CrossSigningKeyHelper> for CrossSigningKey {
type Error = vodozemac::KeyError;
fn try_from(value: CrossSigningKeyHelper) -> Result<Self, Self::Error> {
let keys: Result<BTreeMap<Box<DeviceKeyId>, SigningKey>, vodozemac::KeyError> = value
.keys
.into_iter()
.map(|(k, v)| {
let key = match k.algorithm() {
DeviceKeyAlgorithm::Ed25519 => {
SigningKey::Ed25519(Ed25519PublicKey::from_base64(&v)?)
}
_ => SigningKey::Other(v),
};
Ok((k, key))
})
.collect();
Ok(Self {
user_id: value.user_id,
usage: value.usage,
keys: keys?,
signatures: value.signatures,
other: value.other,
})
}
}
impl From<CrossSigningKey> for CrossSigningKeyHelper {
fn from(value: CrossSigningKey) -> Self {
let keys: BTreeMap<Box<DeviceKeyId>, String> =
value.keys.into_iter().map(|(k, v)| (k, v.to_base64())).collect();
Self {
user_id: value.user_id,
usage: value.usage,
keys,
signatures: value.signatures,
other: value.other,
}
}
}
#[cfg(test)]
mod test {
use ruma::user_id;

View File

@@ -20,12 +20,16 @@
use std::collections::BTreeMap;
use ruma::{serde::Raw, DeviceId, DeviceKeyId, EventEncryptionAlgorithm, UserId};
use ruma::{
serde::Raw, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId,
};
use serde::{Deserialize, Serialize};
use serde_json::{value::to_raw_value, Value};
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey};
/// Identity keys for a device.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(try_from = "DeviceKeyHelper", into = "DeviceKeyHelper")]
pub struct DeviceKeys {
/// The ID of the user the device belongs to.
///
@@ -41,7 +45,7 @@ pub struct DeviceKeys {
pub algorithms: Vec<EventEncryptionAlgorithm>,
/// Public identity keys.
pub keys: BTreeMap<Box<DeviceKeyId>, String>,
pub keys: BTreeMap<Box<DeviceKeyId>, DeviceKey>,
/// Signatures for the device key object.
pub signatures: BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>,
@@ -62,7 +66,7 @@ impl DeviceKeys {
user_id: Box<UserId>,
device_id: Box<DeviceId>,
algorithms: Vec<EventEncryptionAlgorithm>,
keys: BTreeMap<Box<DeviceKeyId>, String>,
keys: BTreeMap<Box<DeviceKeyId>, DeviceKey>,
signatures: BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>,
) -> Self {
Self {
@@ -105,6 +109,108 @@ impl UnsignedDeviceInfo {
}
}
/// An enum over the different key types a device can have.
///
/// Currently devices have a curve25519 and ed25519 keypair. The keys transport
/// format is a base64 encoded string, any unknown key type will be left as such
/// a string.
#[derive(Clone, Debug, PartialEq)]
pub enum DeviceKey {
/// The curve25519 device key.
Curve25519(Curve25519PublicKey),
/// The ed25519 device key.
Ed25519(Ed25519PublicKey),
/// An unknown device key.
Other(String),
}
impl DeviceKey {
/// Convert the `DeviceKey` into a base64 encoded string.
pub fn to_base64(&self) -> String {
match self {
DeviceKey::Curve25519(k) => k.to_base64(),
DeviceKey::Ed25519(k) => k.to_base64(),
DeviceKey::Other(k) => k.to_owned(),
}
}
}
impl From<Curve25519PublicKey> for DeviceKey {
fn from(val: Curve25519PublicKey) -> Self {
DeviceKey::Curve25519(val)
}
}
impl From<Ed25519PublicKey> for DeviceKey {
fn from(val: Ed25519PublicKey) -> Self {
DeviceKey::Ed25519(val)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
struct DeviceKeyHelper {
pub user_id: Box<UserId>,
pub device_id: Box<DeviceId>,
pub algorithms: Vec<EventEncryptionAlgorithm>,
pub keys: BTreeMap<Box<DeviceKeyId>, String>,
pub signatures: BTreeMap<Box<UserId>, BTreeMap<Box<DeviceKeyId>, String>>,
#[serde(default, skip_serializing_if = "UnsignedDeviceInfo::is_empty")]
pub unsigned: UnsignedDeviceInfo,
#[serde(flatten)]
other: BTreeMap<String, Value>,
}
impl TryFrom<DeviceKeyHelper> for DeviceKeys {
type Error = vodozemac::KeyError;
fn try_from(value: DeviceKeyHelper) -> Result<Self, Self::Error> {
let keys: Result<BTreeMap<Box<DeviceKeyId>, DeviceKey>, vodozemac::KeyError> = value
.keys
.into_iter()
.map(|(k, v)| {
let key = match k.algorithm() {
DeviceKeyAlgorithm::Ed25519 => {
DeviceKey::Ed25519(Ed25519PublicKey::from_base64(&v)?)
}
DeviceKeyAlgorithm::Curve25519 => {
DeviceKey::Curve25519(Curve25519PublicKey::from_base64(&v)?)
}
_ => DeviceKey::Other(v),
};
Ok((k, key))
})
.collect();
Ok(Self {
user_id: value.user_id,
device_id: value.device_id,
algorithms: value.algorithms,
keys: keys?,
signatures: value.signatures,
unsigned: value.unsigned,
other: value.other,
})
}
}
impl From<DeviceKeys> for DeviceKeyHelper {
fn from(value: DeviceKeys) -> Self {
let keys: BTreeMap<Box<DeviceKeyId>, String> =
value.keys.into_iter().map(|(k, v)| (k, v.to_base64())).collect();
Self {
user_id: value.user_id,
device_id: value.device_id,
algorithms: value.algorithms,
keys,
signatures: value.signatures,
unsigned: value.unsigned,
other: value.other,
}
}
}
#[cfg(test)]
mod test {
use ruma::{device_id, user_id};

View File

@@ -12,6 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod cross_signing_key;
pub mod device_keys;
pub mod one_time_keys;
//! Module containing customized types modeling Matrix keys.
//!
//! These types were mostly taken from the Ruma project. The types differ in two
//! important ways to the Ruma types of the same name:
//!
//! 1. They are using vodozemac types so we directly deserialize into a
//! vodozemac curve25519 or ed25519 key.
//! 2. They support lossless serialization cycles in a canonical JSON supported
//! way, meaning the white-space and field order won't be preserved but the
//! data will.
mod cross_signing_key;
mod device_keys;
mod one_time_keys;
pub use cross_signing_key::*;
pub use device_keys::*;
pub use one_time_keys::*;

View File

@@ -18,16 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//! Module containing customized types modeling Matrix one-time keys.
//!
//! These types were mostly taken from the Ruma project. The types differ in two
//! important ways to the Ruma types of the same name:
//!
//! 1. They are using vodozemac types so we directly deserialize into a
//! vodozemac curve25519 key.
//! 2. They support lossless serialization cycles in a canonical JSON supported
//! way, meaning the white-space and field order won't be preserved.
use std::collections::BTreeMap;
use ruma::{serde::Raw, DeviceKeyId, UserId};

View File

@@ -34,10 +34,11 @@ use ruma::{
AnyMessageEventContent, AnyToDeviceEventContent,
},
serde::Base64,
DeviceId, DeviceKeyAlgorithm, RoomId, TransactionId, UserId,
DeviceId, RoomId, TransactionId, UserId,
};
use thiserror::Error;
use tracing::trace;
use vodozemac::Ed25519PublicKey;
use super::{
event_enums::{CancelContent, DoneContent, OutgoingContent, OwnedStartContent, StartContent},
@@ -441,8 +442,8 @@ impl QrVerification {
pub(crate) fn new_self(
flow_id: FlowId,
own_master_key: String,
other_device_key: String,
own_master_key: Ed25519PublicKey,
other_device_key: Ed25519PublicKey,
identities: IdentitiesBeingVerified,
we_started: bool,
request_handle: Option<RequestHandle>,
@@ -463,7 +464,7 @@ impl QrVerification {
pub(crate) fn new_self_no_master(
store: VerificationStore,
flow_id: FlowId,
own_master_key: String,
own_master_key: Ed25519PublicKey,
identities: IdentitiesBeingVerified,
we_started: bool,
request_handle: Option<RequestHandle>,
@@ -472,7 +473,7 @@ impl QrVerification {
let inner: QrVerificationData = SelfVerificationNoMasterKey::new(
flow_id.as_str().to_owned(),
store.account.identity_keys().ed25519.to_base64(),
store.account.identity_keys().ed25519,
own_master_key,
secret,
)
@@ -483,8 +484,8 @@ impl QrVerification {
pub(crate) fn new_cross(
flow_id: FlowId,
own_master_key: String,
other_master_key: String,
own_master_key: Ed25519PublicKey,
other_master_key: Ed25519PublicKey,
identities: IdentitiesBeingVerified,
we_started: bool,
request_handle: Option<RequestHandle>,
@@ -541,8 +542,8 @@ impl QrVerification {
if key != master_key {
Err(ScanError::KeyMismatch {
expected: master_key.to_owned(),
found: qr_code.first_key().to_owned(),
expected: master_key.to_base64(),
found: qr_code.first_key().to_base64(),
})
} else {
Ok(())
@@ -558,24 +559,24 @@ impl QrVerification {
}
QrVerificationData::SelfVerification(_) => {
check_master_key(qr_code.first_key(), &other_identity)?;
if qr_code.second_key() != store.account.identity_keys().ed25519.to_base64() {
if qr_code.second_key() != store.account.identity_keys().ed25519 {
return Err(ScanError::KeyMismatch {
expected: store.account.identity_keys().ed25519.to_base64(),
found: qr_code.second_key().to_owned(),
found: qr_code.second_key().to_base64(),
});
}
(other_device, Some(other_identity))
}
QrVerificationData::SelfVerificationNoMasterKey(_) => {
let device_key =
other_device.get_key(DeviceKeyAlgorithm::Ed25519).ok_or_else(|| {
ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone())
})?;
let device_key = other_device.ed25519_key().ok_or_else(|| {
ScanError::MissingDeviceKeys(other_user_id.clone(), other_device_id.clone())
})?;
if qr_code.first_key() != device_key {
return Err(ScanError::KeyMismatch {
expected: device_key.to_owned(),
found: qr_code.first_key().to_owned(),
expected: device_key.to_base64(),
found: qr_code.first_key().to_base64(),
});
}
check_master_key(qr_code.second_key(), &other_identity)?;
@@ -834,7 +835,7 @@ mod test {
let private_identity = PrivateCrossSigningIdentity::new(user_id().to_owned()).await;
let flow_id = FlowId::ToDevice("test_transaction".into());
let device_key = account.identity_keys.ed25519.to_base64();
let device_key = account.identity_keys.ed25519;
let master_key = private_identity.master_public_key().await.unwrap();
let master_key = master_key.get_first_key().unwrap().to_owned();
@@ -850,26 +851,26 @@ mod test {
let verification = QrVerification::new_self_no_master(
store.clone(),
flow_id.clone(),
master_key.clone(),
master_key,
identities.clone(),
false,
None,
);
assert_eq!(verification.inner.first_key(), &device_key);
assert_eq!(verification.inner.second_key(), &master_key);
assert_eq!(verification.inner.first_key(), device_key);
assert_eq!(verification.inner.second_key(), master_key);
let verification = QrVerification::new_self(
flow_id,
master_key.clone(),
device_key.clone(),
master_key,
device_key,
identities.clone(),
false,
None,
);
assert_eq!(verification.inner.first_key(), &master_key);
assert_eq!(verification.inner.second_key(), &device_key);
assert_eq!(verification.inner.first_key(), master_key);
assert_eq!(verification.inner.second_key(), device_key);
let bob_identity =
PrivateCrossSigningIdentity::new(user_id!("@bob:example").to_owned()).await;
@@ -879,17 +880,11 @@ mod test {
let flow_id =
FlowId::InRoom(room_id!("!test:example").to_owned(), event_id!("$EVENTID").to_owned());
let verification = QrVerification::new_cross(
flow_id,
master_key.clone(),
bob_master_key.clone(),
identities,
false,
None,
);
let verification =
QrVerification::new_cross(flow_id, master_key, bob_master_key, identities, false, None);
assert_eq!(verification.inner.first_key(), &master_key);
assert_eq!(verification.inner.second_key(), &bob_master_key);
assert_eq!(verification.inner.first_key(), master_key);
assert_eq!(verification.inner.second_key(), bob_master_key);
}
#[async_test]
@@ -929,7 +924,7 @@ mod test {
let alice_verification = QrVerification::new_self_no_master(
store,
flow_id.clone(),
master_key.clone(),
master_key,
identities,
false,
None,

View File

@@ -20,8 +20,6 @@ use std::{
#[cfg(feature = "qrcode")]
use matrix_qrcode::QrVerificationData;
use matrix_sdk_common::{instant::Instant, util::milli_seconds_since_unix_epoch};
#[cfg(feature = "qrcode")]
use ruma::DeviceKeyAlgorithm;
use ruma::{
events::{
key::verification::{
@@ -1004,9 +1002,7 @@ impl RequestState<Ready> {
ReadOnlyUserIdentities::Own(i) => {
if let Some(master_key) = i.master_key().get_first_key() {
if identites.can_sign_devices().await {
if let Some(device_key) =
identites.other_device().get_key(DeviceKeyAlgorithm::Ed25519)
{
if let Some(device_key) = identites.other_device().ed25519_key() {
Some(QrVerification::new_self(
self.flow_id.as_ref().to_owned(),
master_key.to_owned(),

View File

@@ -229,7 +229,7 @@ pub fn receive_mac_event(
if let Some(key) = ids.other_device.keys().get(&key_id) {
let calculated_mac = Base64::parse(
sas.calculate_mac_invalid_base64(key, &format!("{}{}", info, key_id)),
sas.calculate_mac_invalid_base64(&key.to_base64(), &format!("{}{}", info, key_id)),
)
.expect("Can't base64-decode SAS MAC");
@@ -243,9 +243,10 @@ pub fn receive_mac_event(
if let Some(key) = identity.master_key().get_key(&key_id) {
// TODO we should check that the master key signs the device,
// this way we know the master key also trusts the device
let calculated_mac = Base64::parse(
sas.calculate_mac_invalid_base64(key, &format!("{}{}", info, key_id)),
)
let calculated_mac = Base64::parse(sas.calculate_mac_invalid_base64(
&key.to_base64(),
&format!("{}{}", info, key_id),
))
.expect("Can't base64-decode SAS MAC");
if *key_mac == calculated_mac {
@@ -318,11 +319,12 @@ pub fn get_mac_content(sas: &EstablishedSas, ids: &SasIds, flow_id: &FlowId) ->
if let Some(own_identity) = &ids.own_identity {
if own_identity.is_verified() {
if let Some(key) = own_identity.master_key().get_first_key() {
let key_id = format!("{}:{}", DeviceKeyAlgorithm::Ed25519, &key);
let key_id = format!("{}:{}", DeviceKeyAlgorithm::Ed25519, key.to_base64());
let calculated_mac = Base64::parse(
sas.calculate_mac_invalid_base64(key, &format!("{}{}", info, &key_id)),
)
let calculated_mac = Base64::parse(sas.calculate_mac_invalid_base64(
&key.to_base64(),
&format!("{}{}", info, &key_id),
))
.expect("Can't base64-decode SAS Master key MAC");
mac.insert(key_id, calculated_mac);

View File

@@ -324,11 +324,11 @@ impl IndexeddbStore {
let sessions = tx.object_store(KEYS::SESSION)?;
for session in &changes.sessions {
let sender_key = session.sender_key();
let sender_key = session.sender_key().to_base64();
let session_id = session.session_id();
let pickle = session.pickle().await;
let key = (sender_key, session_id).encode();
let key = (&sender_key, session_id).encode();
sessions.put_key_val(&key, &self.serialize_value(&pickle)?)?;
}

View File

@@ -193,7 +193,7 @@ impl EncodeSecureKey for ReadOnlyDevice {
impl EncodeKey for Session {
fn encode(&self) -> Vec<u8> {
let sender_key = self.sender_key();
let sender_key = self.sender_key().to_base64();
let session_id = self.session_id();
[sender_key.as_bytes(), &[Self::SEPARATOR], session_id.as_bytes(), &[Self::SEPARATOR]]
@@ -249,7 +249,8 @@ impl EncodeSecureKey for (&RoomId, &str, &str) {
impl EncodeSecureKey for Session {
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
let sender_key = store_cipher.hash_key(table_name, self.sender_key().as_bytes());
let sender_key =
store_cipher.hash_key(table_name, self.sender_key().to_base64().as_bytes());
let session_id = store_cipher.hash_key(table_name, self.session_id().as_bytes());
[sender_key.as_slice(), &[Self::SEPARATOR], session_id.as_slice(), &[Self::SEPARATOR]]

View File

@@ -379,7 +379,7 @@ impl UserIdentity {
/// // matches what we expect, for this we fetch the first public key we
/// // can find, there's currently only a single key allowed so this is
/// // fine.
/// if user.master_key().get_first_key() == Some("MyMasterKey") {
/// if user.master_key().get_first_key().map(|k| k.to_base64()) == Some("MyMasterKey".to_string()) {
/// println!(
/// "Master keys match for user {}, marking the user as verified",
/// user.user_id().as_str(),