mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-18 13:40:55 -04:00
refactor(crypto): Use vodozemac types for the cross signing and device keys
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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(_))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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()));
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)?)?;
|
||||
}
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user