Merge branch 'master' into key_export
commit
5a069a8721
|
@ -1,3 +1,5 @@
|
||||||
|
#![type_length_limit = "1693004"]
|
||||||
|
|
||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
api::r0::sync::sync_events::Response as SyncResponse,
|
api::r0::sync::sync_events::Response as SyncResponse,
|
||||||
events::{
|
events::{
|
||||||
|
|
|
@ -36,7 +36,7 @@ use tracing::{error, info, instrument};
|
||||||
use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore};
|
use matrix_sdk_base::{BaseClient, BaseClientConfig, Room, Session, StateStore};
|
||||||
|
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use matrix_sdk_base::{CryptoStoreError, OutgoingRequests};
|
use matrix_sdk_base::{CryptoStoreError, OutgoingRequests, ToDeviceRequest};
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::{
|
api::r0::{
|
||||||
|
@ -72,8 +72,7 @@ use matrix_sdk_common::{
|
||||||
api::r0::{
|
api::r0::{
|
||||||
keys::{claim_keys, get_keys, upload_keys},
|
keys::{claim_keys, get_keys, upload_keys},
|
||||||
to_device::send_event_to_device::{
|
to_device::send_event_to_device::{
|
||||||
IncomingRequest as OwnedToDeviceRequest, Request as ToDeviceRequest,
|
Request as RumaToDeviceRequest, Response as ToDeviceResponse,
|
||||||
Response as ToDeviceResponse,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
locks::Mutex,
|
locks::Mutex,
|
||||||
|
@ -514,7 +513,8 @@ impl Client {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `registration` - The easiest way to create this request is using the `RegistrationBuilder`.
|
/// * `registration` - The easiest way to create this request is using the `register::Request`
|
||||||
|
/// itself.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -740,7 +740,8 @@ impl Client {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `room_search` - The easiest way to create this request is using the `RoomListFilterBuilder`.
|
/// * `room_search` - The easiest way to create this request is using the
|
||||||
|
/// `get_public_rooms_filtered::Request` itself.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -777,7 +778,8 @@ impl Client {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `room` - The easiest way to create this request is using the `RoomBuilder`.
|
/// * `room` - The easiest way to create this request is using the
|
||||||
|
/// `create_room::Request` itself.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -811,7 +813,7 @@ impl Client {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `request` - The easiest way to create this request is using the
|
/// * `request` - The easiest way to create this request is using the
|
||||||
/// `MessagesRequestBuilder`.
|
/// `get_message_events::Request` itself.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -1098,11 +1100,12 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
async fn send_to_device(&self, request: &OwnedToDeviceRequest) -> Result<ToDeviceResponse> {
|
async fn send_to_device(&self, request: ToDeviceRequest) -> Result<ToDeviceResponse> {
|
||||||
let request = ToDeviceRequest {
|
let txn_id_string = request.txn_id_string();
|
||||||
event_type: request.event_type.clone(),
|
let request = RumaToDeviceRequest {
|
||||||
txn_id: &request.txn_id,
|
event_type: request.event_type,
|
||||||
messages: request.messages.clone(),
|
txn_id: &txn_id_string,
|
||||||
|
messages: request.messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.send(request).await
|
self.send(request).await
|
||||||
|
@ -1227,14 +1230,13 @@ impl Client {
|
||||||
warn!("Error while querying device keys {:?}", e);
|
warn!("Error while querying device keys {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OutgoingRequests::KeysUpload(request) => {
|
OutgoingRequests::KeysUpload(request) => {
|
||||||
if let Err(e) = self.keys_upload(&r.request_id(), request).await {
|
if let Err(e) = self.keys_upload(&r.request_id(), request).await {
|
||||||
warn!("Error while querying device keys {:?}", e);
|
warn!("Error while querying device keys {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutgoingRequests::ToDeviceRequest(request) => {
|
OutgoingRequests::ToDeviceRequest(request) => {
|
||||||
if let Ok(resp) = self.send_to_device(request).await {
|
if let Ok(resp) = self.send_to_device(request.clone()).await {
|
||||||
self.base_client
|
self.base_client
|
||||||
.mark_request_as_sent(&r.request_id(), &resp)
|
.mark_request_as_sent(&r.request_id(), &resp)
|
||||||
.await
|
.await
|
||||||
|
@ -1317,7 +1319,7 @@ impl Client {
|
||||||
.expect("Keys don't need to be uploaded");
|
.expect("Keys don't need to be uploaded");
|
||||||
|
|
||||||
for request in requests.drain(..) {
|
for request in requests.drain(..) {
|
||||||
self.send_to_device(&request).await?;
|
self.send_to_device(request).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl Device {
|
||||||
let (sas, request) = self.inner.start_verification().await?;
|
let (sas, request) = self.inner.start_verification().await?;
|
||||||
let request = ToDeviceRequest {
|
let request = ToDeviceRequest {
|
||||||
event_type: request.event_type,
|
event_type: request.event_type,
|
||||||
txn_id: &request.txn_id,
|
txn_id: &request.txn_id.to_string(),
|
||||||
messages: request.messages,
|
messages: request.messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl Sas {
|
||||||
if let Some(req) = self.inner.accept() {
|
if let Some(req) = self.inner.accept() {
|
||||||
let request = ToDeviceRequest {
|
let request = ToDeviceRequest {
|
||||||
event_type: req.event_type,
|
event_type: req.event_type,
|
||||||
txn_id: &req.txn_id,
|
txn_id: &req.txn_id.to_string(),
|
||||||
messages: req.messages,
|
messages: req.messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ impl Sas {
|
||||||
if let Some(req) = self.inner.confirm().await? {
|
if let Some(req) = self.inner.confirm().await? {
|
||||||
let request = ToDeviceRequest {
|
let request = ToDeviceRequest {
|
||||||
event_type: req.event_type,
|
event_type: req.event_type,
|
||||||
txn_id: &req.txn_id,
|
txn_id: &req.txn_id.to_string(),
|
||||||
messages: req.messages,
|
messages: req.messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,10 +56,10 @@ impl Sas {
|
||||||
|
|
||||||
/// Cancel the interactive verification flow.
|
/// Cancel the interactive verification flow.
|
||||||
pub async fn cancel(&self) -> Result<()> {
|
pub async fn cancel(&self) -> Result<()> {
|
||||||
if let Some((_, req)) = self.inner.cancel() {
|
if let Some(req) = self.inner.cancel() {
|
||||||
let request = ToDeviceRequest {
|
let request = ToDeviceRequest {
|
||||||
event_type: req.event_type,
|
event_type: req.event_type,
|
||||||
txn_id: &req.txn_id,
|
txn_id: &req.txn_id.to_string(),
|
||||||
messages: req.messages,
|
messages: req.messages,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,15 +39,15 @@ use matrix_sdk_common::{
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::keys::claim_keys::Request as KeysClaimRequest,
|
api::r0::keys::claim_keys::Request as KeysClaimRequest,
|
||||||
api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest,
|
|
||||||
events::{room::encrypted::EncryptedEventContent, AnyMessageEventContent},
|
events::{room::encrypted::EncryptedEventContent, AnyMessageEventContent},
|
||||||
identifiers::DeviceId,
|
identifiers::DeviceId,
|
||||||
uuid::Uuid,
|
uuid::Uuid,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use matrix_sdk_crypto::{
|
use matrix_sdk_crypto::{
|
||||||
CryptoStore, CryptoStoreError, Device, IncomingResponse, OlmError, OlmMachine, OutgoingRequest,
|
store::{CryptoStore, CryptoStoreError},
|
||||||
Sas, UserDevices,
|
Device, IncomingResponse, OlmError, OlmMachine, OutgoingRequest, Sas, ToDeviceRequest,
|
||||||
|
UserDevices,
|
||||||
};
|
};
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
@ -1304,7 +1304,7 @@ impl BaseClient {
|
||||||
/// Get a to-device request that will share a group session for a room.
|
/// Get a to-device request that will share a group session for a room.
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||||
pub async fn share_group_session(&self, room_id: &RoomId) -> Result<Vec<OwnedToDeviceRequest>> {
|
pub async fn share_group_session(&self, room_id: &RoomId) -> Result<Vec<ToDeviceRequest>> {
|
||||||
let room = self.get_joined_room(room_id).await.expect("No room found");
|
let room = self.get_joined_room(room_id).await.expect("No room found");
|
||||||
let olm = self.olm.lock().await;
|
let olm = self.olm.lock().await;
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,8 @@ pub use state::{AllRooms, ClientState};
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
||||||
pub use matrix_sdk_crypto::{
|
pub use matrix_sdk_crypto::{
|
||||||
CryptoStoreError, Device, IncomingResponse, LocalTrust, OutgoingRequest, OutgoingRequests,
|
store::CryptoStoreError, Device, IncomingResponse, LocalTrust, OutgoingRequest,
|
||||||
ReadOnlyDevice, Sas, UserDevices,
|
OutgoingRequests, ReadOnlyDevice, Sas, ToDeviceRequest, UserDevices,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "messages")]
|
#[cfg(feature = "messages")]
|
||||||
|
|
|
@ -18,7 +18,7 @@ js_int = "0.1.9"
|
||||||
[dependencies.ruma]
|
[dependencies.ruma]
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
git = "https://github.com/ruma/ruma"
|
git = "https://github.com/ruma/ruma"
|
||||||
rev = "e4cd59e7e5f8ce4c8b90948155c3d031e45f9c54"
|
rev = "409fbcc9d745fb7290327cb7f5defc714229ab30"
|
||||||
features = ["client-api", "unstable-pre-spec"]
|
features = ["client-api", "unstable-pre-spec"]
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
|
|
|
@ -20,13 +20,13 @@ sqlite_cryptostore = ["sqlx"]
|
||||||
docs = ["sqlite_cryptostore"]
|
docs = ["sqlite_cryptostore"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.38"
|
async-trait = "0.1.40"
|
||||||
|
|
||||||
matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" }
|
matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_macros" }
|
||||||
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
|
||||||
|
|
||||||
olm-rs = { git = 'https://gitlab.gnome.org/jhaye/olm-rs/', features = ["serde"]}
|
olm-rs = { git = 'https://gitlab.gnome.org/jhaye/olm-rs/', features = ["serde"]}
|
||||||
serde = { version = "1.0.115", features = ["derive"] }
|
serde = { version = "1.0.115", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.57"
|
serde_json = "1.0.57"
|
||||||
cjson = "0.1.1"
|
cjson = "0.1.1"
|
||||||
zeroize = { version = "1.1.0", features = ["zeroize_derive"] }
|
zeroize = { version = "1.1.0", features = ["zeroize_derive"] }
|
||||||
|
|
|
@ -115,6 +115,16 @@ pub enum EventError {
|
||||||
MissmatchedKeys,
|
MissmatchedKeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum SessionUnpicklingError {
|
||||||
|
/// The underlying Olm session operation returned an error.
|
||||||
|
#[error("can't finish Olm Session operation {0}")]
|
||||||
|
OlmSession(#[from] OlmSessionError),
|
||||||
|
/// The Session timestamp was invalid.
|
||||||
|
#[error("can't load session timestamps")]
|
||||||
|
SessionTimestampError,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum SignatureError {
|
pub enum SignatureError {
|
||||||
#[error("the signature used a unsupported algorithm")]
|
#[error("the signature used a unsupported algorithm")]
|
||||||
|
|
|
@ -24,25 +24,25 @@ use std::{
|
||||||
|
|
||||||
use atomic::Atomic;
|
use atomic::Atomic;
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::{
|
api::r0::keys::SignedKey,
|
||||||
keys::SignedKey, to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest,
|
|
||||||
},
|
|
||||||
encryption::DeviceKeys,
|
encryption::DeviceKeys,
|
||||||
events::{room::encrypted::EncryptedEventContent, EventType},
|
events::{room::encrypted::EncryptedEventContent, EventType},
|
||||||
identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId},
|
identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, UserId},
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use super::{Account, OlmMachine};
|
use crate::{Account, OlmMachine};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{EventError, OlmError, OlmResult, SignatureError},
|
error::{EventError, OlmError, OlmResult, SignatureError},
|
||||||
store::Result as StoreResult,
|
identities::{OwnUserIdentity, UserIdentities},
|
||||||
user_identity::{OwnUserIdentity, UserIdentities},
|
olm::Utility,
|
||||||
|
store::{caches::ReadOnlyUserDevices, Result as StoreResult},
|
||||||
verification::VerificationMachine,
|
verification::VerificationMachine,
|
||||||
verify_json, ReadOnlyUserDevices, Sas,
|
Sas, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A read-only version of a `Device`.
|
/// A read-only version of a `Device`.
|
||||||
|
@ -79,7 +79,7 @@ impl Device {
|
||||||
/// Start a interactive verification with this `Device`
|
/// Start a interactive verification with this `Device`
|
||||||
///
|
///
|
||||||
/// Returns a `Sas` object and to-device request that needs to be sent out.
|
/// Returns a `Sas` object and to-device request that needs to be sent out.
|
||||||
pub async fn start_verification(&self) -> StoreResult<(Sas, OwnedToDeviceRequest)> {
|
pub async fn start_verification(&self) -> StoreResult<(Sas, ToDeviceRequest)> {
|
||||||
self.verification_machine
|
self.verification_machine
|
||||||
.start_sas(self.inner.clone())
|
.start_sas(self.inner.clone())
|
||||||
.await
|
.await
|
||||||
|
@ -230,7 +230,7 @@ impl UserDevices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
/// The local trust state of a device.
|
/// The local trust state of a device.
|
||||||
pub enum LocalTrust {
|
pub enum LocalTrust {
|
||||||
/// The device has been verified and is trusted.
|
/// The device has been verified and is trusted.
|
||||||
|
@ -363,7 +363,9 @@ impl ReadOnlyDevice {
|
||||||
.get_key(DeviceKeyAlgorithm::Ed25519)
|
.get_key(DeviceKeyAlgorithm::Ed25519)
|
||||||
.ok_or(SignatureError::MissingSigningKey)?;
|
.ok_or(SignatureError::MissingSigningKey)?;
|
||||||
|
|
||||||
verify_json(
|
let utility = Utility::new();
|
||||||
|
|
||||||
|
utility.verify_json(
|
||||||
&self.user_id,
|
&self.user_id,
|
||||||
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
|
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
|
||||||
signing_key,
|
signing_key,
|
||||||
|
@ -443,7 +445,7 @@ pub(crate) mod test {
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use crate::device::{LocalTrust, ReadOnlyDevice};
|
use crate::identities::{LocalTrust, ReadOnlyDevice};
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
encryption::DeviceKeys,
|
encryption::DeviceKeys,
|
||||||
identifiers::{user_id, DeviceKeyAlgorithm},
|
identifiers::{user_id, DeviceKeyAlgorithm},
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//! Collection of public identities used in Matrix.
|
||||||
|
//!
|
||||||
|
//! Matrix supports two main types of identities, a per-device identity and a
|
||||||
|
//! per-user identity.
|
||||||
|
//!
|
||||||
|
//! ## Device
|
||||||
|
//!
|
||||||
|
//! Every E2EE capable Matrix client will create a new Olm account and upload
|
||||||
|
//! the public keys of the Olm account to the server. This is represented as a
|
||||||
|
//! `ReadOnlyDevice`.
|
||||||
|
//!
|
||||||
|
//! Devices can have a local trust state which is needs to be saved in our
|
||||||
|
//! `CryptoStore`, to avoid reference cycles a wrapper for the `ReadOnlyDevice`
|
||||||
|
//! exists which adds methods to manipulate the local trust state.
|
||||||
|
//!
|
||||||
|
//! ## User
|
||||||
|
//!
|
||||||
|
//! Cross-signing capable devices will upload 3 additional (master, self-signing,
|
||||||
|
//! user-signing) public keys which represent the user identity owning all the
|
||||||
|
//! devices. This is represented in two ways, as a `UserIdentity` for other
|
||||||
|
//! users and as `OwnUserIdentity` for our own user.
|
||||||
|
//!
|
||||||
|
//! This is done because the server will only give us access to 2 of the 3
|
||||||
|
//! additional public keys for other users, while it will give us access to all
|
||||||
|
//! 3 for our own user.
|
||||||
|
//!
|
||||||
|
//! Both identity sets need to reqularly fetched from the server using the
|
||||||
|
//! `/keys/query` API call.
|
||||||
|
pub(crate) mod device;
|
||||||
|
mod user;
|
||||||
|
|
||||||
|
pub use device::{Device, LocalTrust, ReadOnlyDevice, UserDevices};
|
||||||
|
pub use user::{
|
||||||
|
MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity,
|
||||||
|
UserSigningPubkey,
|
||||||
|
};
|
|
@ -20,6 +20,7 @@ use std::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
|
@ -27,25 +28,25 @@ use matrix_sdk_common::{
|
||||||
identifiers::{DeviceKeyId, UserId},
|
identifiers::{DeviceKeyId, UserId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{error::SignatureError, verify_json, ReadOnlyDevice};
|
use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice};
|
||||||
|
|
||||||
/// Wrapper for a cross signing key marking it as the master key.
|
/// Wrapper for a cross signing key marking it as the master key.
|
||||||
///
|
///
|
||||||
/// Master keys are used to sign other cross signing keys, the self signing and
|
/// Master keys are used to sign other cross signing keys, the self signing and
|
||||||
/// user signing keys of an user will be signed by their master key.
|
/// user signing keys of an user will be signed by their master key.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct MasterPubkey(Arc<CrossSigningKey>);
|
pub struct MasterPubkey(Arc<CrossSigningKey>);
|
||||||
|
|
||||||
/// Wrapper for a cross signing key marking it as a self signing key.
|
/// Wrapper for a cross signing key marking it as a self signing key.
|
||||||
///
|
///
|
||||||
/// Self signing keys are used to sign the user's own devices.
|
/// Self signing keys are used to sign the user's own devices.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct SelfSigningPubkey(Arc<CrossSigningKey>);
|
pub struct SelfSigningPubkey(Arc<CrossSigningKey>);
|
||||||
|
|
||||||
/// Wrapper for a cross signing key marking it as a user signing key.
|
/// Wrapper for a cross signing key marking it as a user signing key.
|
||||||
///
|
///
|
||||||
/// User signing keys are used to sign the master keys of other users.
|
/// User signing keys are used to sign the master keys of other users.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct UserSigningPubkey(Arc<CrossSigningKey>);
|
pub struct UserSigningPubkey(Arc<CrossSigningKey>);
|
||||||
|
|
||||||
impl PartialEq for MasterPubkey {
|
impl PartialEq for MasterPubkey {
|
||||||
|
@ -156,7 +157,8 @@ impl MasterPubkey {
|
||||||
return Err(SignatureError::UserIdMissmatch);
|
return Err(SignatureError::UserIdMissmatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_json(
|
let utility = Utility::new();
|
||||||
|
utility.verify_json(
|
||||||
&self.0.user_id,
|
&self.0.user_id,
|
||||||
&key_id,
|
&key_id,
|
||||||
key,
|
key,
|
||||||
|
@ -190,7 +192,8 @@ impl UserSigningPubkey {
|
||||||
|
|
||||||
// TODO check that the usage is OK.
|
// TODO check that the usage is OK.
|
||||||
|
|
||||||
verify_json(
|
let utility = Utility::new();
|
||||||
|
utility.verify_json(
|
||||||
&self.0.user_id,
|
&self.0.user_id,
|
||||||
&DeviceKeyId::try_from(key_id.as_str())?,
|
&DeviceKeyId::try_from(key_id.as_str())?,
|
||||||
key,
|
key,
|
||||||
|
@ -223,7 +226,8 @@ impl SelfSigningPubkey {
|
||||||
|
|
||||||
// TODO check that the usage is OK.
|
// TODO check that the usage is OK.
|
||||||
|
|
||||||
verify_json(
|
let utility = Utility::new();
|
||||||
|
utility.verify_json(
|
||||||
&self.0.user_id,
|
&self.0.user_id,
|
||||||
&DeviceKeyId::try_from(key_id.as_str())?,
|
&DeviceKeyId::try_from(key_id.as_str())?,
|
||||||
key,
|
key,
|
||||||
|
@ -279,7 +283,7 @@ impl PartialEq for UserIdentities {
|
||||||
/// This is the user identity of a user that isn't our own. Other users will
|
/// This is the user identity of a user that isn't our own. Other users will
|
||||||
/// only contain a master key and a self signing key, meaning that only device
|
/// only contain a master key and a self signing key, meaning that only device
|
||||||
/// signatures can be checked with this identity.
|
/// signatures can be checked with this identity.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct UserIdentity {
|
pub struct UserIdentity {
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
master_key: MasterPubkey,
|
master_key: MasterPubkey,
|
||||||
|
@ -509,10 +513,10 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{Device, ReadOnlyDevice},
|
identities::{Device, ReadOnlyDevice},
|
||||||
machine::test::response_from_file,
|
machine::test::response_from_file,
|
||||||
olm::Account,
|
olm::Account,
|
||||||
store::memorystore::MemoryStore,
|
store::MemoryStore,
|
||||||
verification::VerificationMachine,
|
verification::VerificationMachine,
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,98 +27,22 @@
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(feature = "docs", feature(doc_cfg))]
|
#![cfg_attr(feature = "docs", feature(doc_cfg))]
|
||||||
|
|
||||||
mod device;
|
|
||||||
mod error;
|
mod error;
|
||||||
|
mod identities;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod key_export;
|
mod key_export;
|
||||||
mod machine;
|
mod machine;
|
||||||
pub mod memory_stores;
|
|
||||||
pub mod olm;
|
pub mod olm;
|
||||||
mod requests;
|
mod requests;
|
||||||
mod store;
|
pub mod store;
|
||||||
#[allow(dead_code)]
|
|
||||||
mod user_identity;
|
|
||||||
mod verification;
|
mod verification;
|
||||||
|
|
||||||
pub use device::{Device, LocalTrust, ReadOnlyDevice, UserDevices};
|
|
||||||
pub use error::{MegolmError, OlmError};
|
pub use error::{MegolmError, OlmError};
|
||||||
|
pub use identities::{
|
||||||
|
Device, LocalTrust, OwnUserIdentity, ReadOnlyDevice, UserDevices, UserIdentities, UserIdentity,
|
||||||
|
};
|
||||||
pub use machine::OlmMachine;
|
pub use machine::OlmMachine;
|
||||||
use memory_stores::ReadOnlyUserDevices;
|
|
||||||
pub(crate) use olm::Account;
|
pub(crate) use olm::Account;
|
||||||
pub use olm::EncryptionSettings;
|
pub use olm::EncryptionSettings;
|
||||||
pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests};
|
pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests, ToDeviceRequest};
|
||||||
#[cfg(feature = "sqlite_cryptostore")]
|
|
||||||
pub use store::sqlite::SqliteStore;
|
|
||||||
pub use store::{CryptoStore, CryptoStoreError};
|
|
||||||
pub use verification::Sas;
|
pub use verification::Sas;
|
||||||
|
|
||||||
use error::SignatureError;
|
|
||||||
use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId};
|
|
||||||
use olm_rs::utility::OlmUtility;
|
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
/// Verify a signed JSON object.
|
|
||||||
///
|
|
||||||
/// The object must have a signatures key associated with an object of the
|
|
||||||
/// form `user_id: {key_id: signature}`.
|
|
||||||
///
|
|
||||||
/// Returns Ok if the signature was successfully verified, otherwise an
|
|
||||||
/// SignatureError.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `user_id` - The user who signed the JSON object.
|
|
||||||
///
|
|
||||||
/// * `key_id` - The id of the key that signed the JSON object.
|
|
||||||
///
|
|
||||||
/// * `signing_key` - The public ed25519 key which was used to sign the JSON
|
|
||||||
/// object.
|
|
||||||
///
|
|
||||||
/// * `json` - The JSON object that should be verified.
|
|
||||||
pub(crate) fn verify_json(
|
|
||||||
user_id: &UserId,
|
|
||||||
key_id: &DeviceKeyId,
|
|
||||||
signing_key: &str,
|
|
||||||
json: &mut Value,
|
|
||||||
) -> Result<(), SignatureError> {
|
|
||||||
if key_id.algorithm() != DeviceKeyAlgorithm::Ed25519 {
|
|
||||||
return Err(SignatureError::UnsupportedAlgorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
|
|
||||||
let unsigned = json_object.remove("unsigned");
|
|
||||||
let signatures = json_object.remove("signatures");
|
|
||||||
|
|
||||||
let canonical_json = cjson::to_string(json_object)?;
|
|
||||||
|
|
||||||
if let Some(u) = unsigned {
|
|
||||||
json_object.insert("unsigned".to_string(), u);
|
|
||||||
}
|
|
||||||
|
|
||||||
let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?;
|
|
||||||
let signature_object = signatures
|
|
||||||
.as_object()
|
|
||||||
.ok_or(SignatureError::NoSignatureFound)?;
|
|
||||||
let signature = signature_object
|
|
||||||
.get(user_id.as_str())
|
|
||||||
.ok_or(SignatureError::NoSignatureFound)?;
|
|
||||||
let signature = signature
|
|
||||||
.get(key_id.to_string())
|
|
||||||
.ok_or(SignatureError::NoSignatureFound)?;
|
|
||||||
let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?;
|
|
||||||
|
|
||||||
let utility = OlmUtility::new();
|
|
||||||
|
|
||||||
let ret = if utility
|
|
||||||
.ed25519_verify(signing_key, &canonical_json, signature)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(SignatureError::VerificationError)
|
|
||||||
};
|
|
||||||
|
|
||||||
json_object.insert("signatures".to_string(), signatures);
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
|
@ -34,9 +34,7 @@ use matrix_sdk_common::{
|
||||||
upload_keys,
|
upload_keys,
|
||||||
},
|
},
|
||||||
sync::sync_events::Response as SyncResponse,
|
sync::sync_events::Response as SyncResponse,
|
||||||
to_device::{
|
to_device::DeviceIdOrAllDevices,
|
||||||
send_event_to_device::IncomingRequest as OwnedToDeviceRequest, DeviceIdOrAllDevices,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
encryption::DeviceKeys,
|
encryption::DeviceKeys,
|
||||||
events::{
|
events::{
|
||||||
|
@ -53,20 +51,18 @@ use matrix_sdk_common::{
|
||||||
#[cfg(feature = "sqlite_cryptostore")]
|
#[cfg(feature = "sqlite_cryptostore")]
|
||||||
use super::store::sqlite::SqliteStore;
|
use super::store::sqlite::SqliteStore;
|
||||||
use super::{
|
use super::{
|
||||||
device::{Device, ReadOnlyDevice, UserDevices},
|
|
||||||
error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult},
|
error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult},
|
||||||
|
identities::{
|
||||||
|
Device, MasterPubkey, OwnUserIdentity, ReadOnlyDevice, SelfSigningPubkey, UserDevices,
|
||||||
|
UserIdentities, UserIdentity, UserSigningPubkey,
|
||||||
|
},
|
||||||
olm::{
|
olm::{
|
||||||
Account, EncryptionSettings, GroupSessionKey, IdentityKeys, InboundGroupSession,
|
Account, EncryptionSettings, GroupSessionKey, IdentityKeys, InboundGroupSession,
|
||||||
OlmMessage, OutboundGroupSession,
|
OlmMessage, OutboundGroupSession,
|
||||||
},
|
},
|
||||||
requests::{IncomingResponse, OutgoingRequest},
|
requests::{IncomingResponse, OutgoingRequest, ToDeviceRequest},
|
||||||
store::{memorystore::MemoryStore, Result as StoreResult},
|
store::{CryptoStore, MemoryStore, Result as StoreResult},
|
||||||
user_identity::{
|
|
||||||
MasterPubkey, OwnUserIdentity, SelfSigningPubkey, UserIdentities, UserIdentity,
|
|
||||||
UserSigningPubkey,
|
|
||||||
},
|
|
||||||
verification::{Sas, VerificationMachine},
|
verification::{Sas, VerificationMachine},
|
||||||
CryptoStore,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// State machine implementation of the Olm/Megolm encryption protocol used for
|
/// State machine implementation of the Olm/Megolm encryption protocol used for
|
||||||
|
@ -1127,7 +1123,7 @@ impl OlmMachine {
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
users: impl Iterator<Item = &UserId>,
|
users: impl Iterator<Item = &UserId>,
|
||||||
encryption_settings: impl Into<EncryptionSettings>,
|
encryption_settings: impl Into<EncryptionSettings>,
|
||||||
) -> OlmResult<Vec<OwnedToDeviceRequest>> {
|
) -> OlmResult<Vec<ToDeviceRequest>> {
|
||||||
self.create_outbound_group_session(room_id, encryption_settings.into())
|
self.create_outbound_group_session(room_id, encryption_settings.into())
|
||||||
.await?;
|
.await?;
|
||||||
let session = self.outbound_group_sessions.get(room_id).unwrap();
|
let session = self.outbound_group_sessions.get(room_id).unwrap();
|
||||||
|
@ -1181,9 +1177,9 @@ impl OlmMachine {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
requests.push(OwnedToDeviceRequest {
|
requests.push(ToDeviceRequest {
|
||||||
event_type: EventType::RoomEncrypted,
|
event_type: EventType::RoomEncrypted,
|
||||||
txn_id: Uuid::new_v4().to_string(),
|
txn_id: Uuid::new_v4(),
|
||||||
messages,
|
messages,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1521,9 +1517,7 @@ impl OlmMachine {
|
||||||
let own_identity = self
|
let own_identity = self
|
||||||
.store
|
.store
|
||||||
.get_user_identity(self.user_id())
|
.get_user_identity(self.user_id())
|
||||||
.await
|
.await?
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.map(|i| i.own().cloned())
|
.map(|i| i.own().cloned())
|
||||||
.flatten();
|
.flatten();
|
||||||
let device_owner_identity = self.store.get_user_identity(user_id).await.ok().flatten();
|
let device_owner_identity = self.store.get_user_identity(user_id).await.ok().flatten();
|
||||||
|
@ -1554,15 +1548,13 @@ pub(crate) mod test {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
machine::OlmMachine,
|
machine::OlmMachine,
|
||||||
|
olm::Utility,
|
||||||
verification::test::{outgoing_request_to_event, request_to_event},
|
verification::test::{outgoing_request_to_event, request_to_event},
|
||||||
verify_json, EncryptionSettings, ReadOnlyDevice,
|
EncryptionSettings, ReadOnlyDevice, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::{
|
api::r0::keys::{claim_keys, get_keys, upload_keys, OneTimeKey},
|
||||||
keys::{claim_keys, get_keys, upload_keys, OneTimeKey},
|
|
||||||
to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest,
|
|
||||||
},
|
|
||||||
events::{
|
events::{
|
||||||
room::{
|
room::{
|
||||||
encrypted::EncryptedEventContent,
|
encrypted::EncryptedEventContent,
|
||||||
|
@ -1612,7 +1604,7 @@ pub(crate) mod test {
|
||||||
get_keys::Response::try_from(data).expect("Can't parse the keys upload response")
|
get_keys::Response::try_from(data).expect("Can't parse the keys upload response")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_device_requests_to_content(requests: Vec<OwnedToDeviceRequest>) -> EncryptedEventContent {
|
fn to_device_requests_to_content(requests: Vec<ToDeviceRequest>) -> EncryptedEventContent {
|
||||||
let to_device_request = &requests[0];
|
let to_device_request = &requests[0];
|
||||||
|
|
||||||
let content: Raw<EncryptedEventContent> = serde_json::from_str(
|
let content: Raw<EncryptedEventContent> = serde_json::from_str(
|
||||||
|
@ -1793,7 +1785,8 @@ pub(crate) mod test {
|
||||||
let identity_keys = machine.account.identity_keys();
|
let identity_keys = machine.account.identity_keys();
|
||||||
let ed25519_key = identity_keys.ed25519();
|
let ed25519_key = identity_keys.ed25519();
|
||||||
|
|
||||||
let ret = verify_json(
|
let utility = Utility::new();
|
||||||
|
let ret = utility.verify_json(
|
||||||
&machine.user_id,
|
&machine.user_id,
|
||||||
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
||||||
ed25519_key,
|
ed25519_key,
|
||||||
|
@ -1824,7 +1817,8 @@ pub(crate) mod test {
|
||||||
|
|
||||||
let mut device_keys = machine.account.device_keys().await;
|
let mut device_keys = machine.account.device_keys().await;
|
||||||
|
|
||||||
let ret = verify_json(
|
let utility = Utility::new();
|
||||||
|
let ret = utility.verify_json(
|
||||||
&machine.user_id,
|
&machine.user_id,
|
||||||
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
||||||
"fake_key",
|
"fake_key",
|
||||||
|
@ -1844,7 +1838,8 @@ pub(crate) mod test {
|
||||||
|
|
||||||
let mut one_time_key = one_time_keys.values_mut().next().unwrap();
|
let mut one_time_key = one_time_keys.values_mut().next().unwrap();
|
||||||
|
|
||||||
let ret = verify_json(
|
let utility = Utility::new();
|
||||||
|
let ret = utility.verify_json(
|
||||||
&machine.user_id,
|
&machine.user_id,
|
||||||
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
||||||
ed25519_key,
|
ed25519_key,
|
||||||
|
@ -1866,7 +1861,8 @@ pub(crate) mod test {
|
||||||
.await
|
.await
|
||||||
.expect("Can't prepare initial key upload");
|
.expect("Can't prepare initial key upload");
|
||||||
|
|
||||||
let ret = verify_json(
|
let utility = Utility::new();
|
||||||
|
let ret = utility.verify_json(
|
||||||
&machine.user_id,
|
&machine.user_id,
|
||||||
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
||||||
ed25519_key,
|
ed25519_key,
|
||||||
|
@ -1874,7 +1870,8 @@ pub(crate) mod test {
|
||||||
);
|
);
|
||||||
assert!(ret.is_ok());
|
assert!(ret.is_ok());
|
||||||
|
|
||||||
let ret = verify_json(
|
let utility = Utility::new();
|
||||||
|
let ret = utility.verify_json(
|
||||||
&machine.user_id,
|
&machine.user_id,
|
||||||
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
|
||||||
ed25519_key,
|
ed25519_key,
|
||||||
|
|
|
@ -26,7 +26,8 @@ use matrix_sdk_common::{
|
||||||
api::r0::keys::{OneTimeKey, SignedKey},
|
api::r0::keys::{OneTimeKey, SignedKey},
|
||||||
encryption::DeviceKeys,
|
encryption::DeviceKeys,
|
||||||
identifiers::{
|
identifiers::{
|
||||||
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId,
|
DeviceId, DeviceIdBox, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId,
|
||||||
|
UserId,
|
||||||
},
|
},
|
||||||
instant::Instant,
|
instant::Instant,
|
||||||
locks::Mutex,
|
locks::Mutex,
|
||||||
|
@ -36,6 +37,7 @@ use olm_rs::{
|
||||||
errors::{OlmAccountError, OlmSessionError},
|
errors::{OlmAccountError, OlmSessionError},
|
||||||
PicklingMode,
|
PicklingMode,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
pub use olm_rs::{
|
pub use olm_rs::{
|
||||||
|
@ -45,7 +47,7 @@ pub use olm_rs::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{EncryptionSettings, InboundGroupSession, OutboundGroupSession, Session};
|
use super::{EncryptionSettings, InboundGroupSession, OutboundGroupSession, Session};
|
||||||
use crate::{device::ReadOnlyDevice, error::SessionCreationError};
|
use crate::{error::SessionCreationError, identities::ReadOnlyDevice};
|
||||||
|
|
||||||
/// Account holding identity keys for which sessions can be created.
|
/// Account holding identity keys for which sessions can be created.
|
||||||
///
|
///
|
||||||
|
@ -65,6 +67,42 @@ pub struct Account {
|
||||||
uploaded_signed_key_count: Arc<AtomicI64>,
|
uploaded_signed_key_count: Arc<AtomicI64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A typed representation of a base64 encoded string containing the account
|
||||||
|
/// pickle.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AccountPickle(String);
|
||||||
|
|
||||||
|
impl AccountPickle {
|
||||||
|
/// Get the string representation of the pickle.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for AccountPickle {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A pickled version of an `Account`.
|
||||||
|
///
|
||||||
|
/// Holds all the information that needs to be stored in a database to restore
|
||||||
|
/// an account.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PickledAccount {
|
||||||
|
/// The user id of the account owner.
|
||||||
|
pub user_id: UserId,
|
||||||
|
/// The device id of the account owner.
|
||||||
|
pub device_id: DeviceIdBox,
|
||||||
|
/// The pickled version of the Olm account.
|
||||||
|
pub pickle: AccountPickle,
|
||||||
|
/// Was the account shared.
|
||||||
|
pub shared: bool,
|
||||||
|
/// The number of uploaded one-time keys we have on the server.
|
||||||
|
pub uploaded_signed_key_count: i64,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(tarpaulin_include))]
|
#[cfg(not(tarpaulin_include))]
|
||||||
impl fmt::Debug for Account {
|
impl fmt::Debug for Account {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
@ -240,40 +278,40 @@ impl Account {
|
||||||
///
|
///
|
||||||
/// * `pickle_mode` - The mode that was used to pickle the account, either an
|
/// * `pickle_mode` - The mode that was used to pickle the account, either an
|
||||||
/// unencrypted mode or an encrypted using passphrase.
|
/// unencrypted mode or an encrypted using passphrase.
|
||||||
pub async fn pickle(&self, pickle_mode: PicklingMode) -> String {
|
pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledAccount {
|
||||||
self.inner.lock().await.pickle(pickle_mode)
|
let pickle = AccountPickle(self.inner.lock().await.pickle(pickle_mode));
|
||||||
|
|
||||||
|
PickledAccount {
|
||||||
|
user_id: self.user_id().to_owned(),
|
||||||
|
device_id: self.device_id().to_owned(),
|
||||||
|
pickle,
|
||||||
|
shared: self.shared(),
|
||||||
|
uploaded_signed_key_count: self.uploaded_key_count(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore an account from a previously pickled string.
|
/// Restore an account from a previously pickled one.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `pickle` - The pickled string of the account.
|
/// * `pickle` - The pickled version of the Account.
|
||||||
///
|
///
|
||||||
/// * `pickle_mode` - The mode that was used to pickle the account, either an
|
/// * `pickle_mode` - The mode that was used to pickle the account, either an
|
||||||
/// unencrypted mode or an encrypted using passphrase.
|
/// unencrypted mode or an encrypted using passphrase.
|
||||||
///
|
|
||||||
/// * `shared` - Boolean determining if the account was uploaded to the
|
|
||||||
/// server.
|
|
||||||
#[allow(clippy::ptr_arg)]
|
|
||||||
pub fn from_pickle(
|
pub fn from_pickle(
|
||||||
pickle: String,
|
pickle: PickledAccount,
|
||||||
pickle_mode: PicklingMode,
|
pickle_mode: PicklingMode,
|
||||||
shared: bool,
|
|
||||||
uploaded_signed_key_count: i64,
|
|
||||||
user_id: &UserId,
|
|
||||||
device_id: &DeviceId,
|
|
||||||
) -> Result<Self, OlmAccountError> {
|
) -> Result<Self, OlmAccountError> {
|
||||||
let account = OlmAccount::unpickle(pickle, pickle_mode)?;
|
let account = OlmAccount::unpickle(pickle.pickle.0, pickle_mode)?;
|
||||||
let identity_keys = account.parsed_identity_keys();
|
let identity_keys = account.parsed_identity_keys();
|
||||||
|
|
||||||
Ok(Account {
|
Ok(Account {
|
||||||
user_id: Arc::new(user_id.to_owned()),
|
user_id: Arc::new(pickle.user_id),
|
||||||
device_id: Arc::new(device_id.into()),
|
device_id: Arc::new(pickle.device_id),
|
||||||
inner: Arc::new(Mutex::new(account)),
|
inner: Arc::new(Mutex::new(account)),
|
||||||
identity_keys: Arc::new(identity_keys),
|
identity_keys: Arc::new(identity_keys),
|
||||||
shared: Arc::new(AtomicBool::from(shared)),
|
shared: Arc::new(AtomicBool::from(pickle.shared)),
|
||||||
uploaded_signed_key_count: Arc::new(AtomicI64::new(uploaded_signed_key_count)),
|
uploaded_signed_key_count: Arc::new(AtomicI64::new(pickle.uploaded_signed_key_count)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ use olm_rs::{
|
||||||
errors::OlmGroupSessionError, inbound_group_session::OlmInboundGroupSession,
|
errors::OlmGroupSessionError, inbound_group_session::OlmInboundGroupSession,
|
||||||
outbound_group_session::OlmOutboundGroupSession, PicklingMode,
|
outbound_group_session::OlmOutboundGroupSession, PicklingMode,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
@ -154,8 +154,16 @@ impl InboundGroupSession {
|
||||||
///
|
///
|
||||||
/// * `pickle_mode` - The mode that was used to pickle the group session,
|
/// * `pickle_mode` - The mode that was used to pickle the group session,
|
||||||
/// either an unencrypted mode or an encrypted using passphrase.
|
/// either an unencrypted mode or an encrypted using passphrase.
|
||||||
pub async fn pickle(&self, pickle_mode: PicklingMode) -> String {
|
pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledInboundGroupSession {
|
||||||
self.inner.lock().await.pickle(pickle_mode)
|
let pickle = self.inner.lock().await.pickle(pickle_mode);
|
||||||
|
|
||||||
|
PickledInboundGroupSession {
|
||||||
|
pickle: InboundGroupSessionPickle::from(pickle),
|
||||||
|
sender_key: self.sender_key.to_string(),
|
||||||
|
signing_key: self.signing_key.to_string(),
|
||||||
|
room_id: (&*self.room_id).clone(),
|
||||||
|
forwarding_chains: self.forwarding_chains.lock().await.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore a Session from a previously pickled string.
|
/// Restore a Session from a previously pickled string.
|
||||||
|
@ -165,35 +173,24 @@ impl InboundGroupSession {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `pickle` - The pickled string of the group session session.
|
/// * `pickle` - The pickled version of the `InboundGroupSession`.
|
||||||
///
|
///
|
||||||
/// * `pickle_mode` - The mode that was used to pickle the session, either
|
/// * `pickle_mode` - The mode that was used to pickle the session, either
|
||||||
/// an unencrypted mode or an encrypted using passphrase.
|
/// an unencrypted mode or an encrypted using passphrase.
|
||||||
///
|
|
||||||
/// * `sender_key` - The public curve25519 key of the account that
|
|
||||||
/// sent us the session
|
|
||||||
///
|
|
||||||
/// * `signing_key` - The public ed25519 key of the account that
|
|
||||||
/// sent us the session.
|
|
||||||
///
|
|
||||||
/// * `room_id` - The id of the room that the session is used in.
|
|
||||||
pub fn from_pickle(
|
pub fn from_pickle(
|
||||||
pickle: String,
|
pickle: PickledInboundGroupSession,
|
||||||
pickle_mode: PicklingMode,
|
pickle_mode: PicklingMode,
|
||||||
sender_key: String,
|
|
||||||
signing_key: String,
|
|
||||||
room_id: RoomId,
|
|
||||||
) -> Result<Self, OlmGroupSessionError> {
|
) -> Result<Self, OlmGroupSessionError> {
|
||||||
let session = OlmInboundGroupSession::unpickle(pickle, pickle_mode)?;
|
let session = OlmInboundGroupSession::unpickle(pickle.pickle.0, pickle_mode)?;
|
||||||
let session_id = session.session_id();
|
let session_id = session.session_id();
|
||||||
|
|
||||||
Ok(InboundGroupSession {
|
Ok(InboundGroupSession {
|
||||||
inner: Arc::new(Mutex::new(session)),
|
inner: Arc::new(Mutex::new(session)),
|
||||||
session_id: Arc::new(session_id),
|
session_id: Arc::new(session_id),
|
||||||
sender_key: Arc::new(sender_key),
|
sender_key: Arc::new(pickle.sender_key),
|
||||||
signing_key: Arc::new(signing_key),
|
signing_key: Arc::new(pickle.signing_key),
|
||||||
room_id: Arc::new(room_id),
|
room_id: Arc::new(pickle.room_id),
|
||||||
forwarding_chains: Arc::new(Mutex::new(None)),
|
forwarding_chains: Arc::new(Mutex::new(pickle.forwarding_chains)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +279,42 @@ impl PartialEq for InboundGroupSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A pickled version of an `InboundGroupSession`.
|
||||||
|
///
|
||||||
|
/// Holds all the information that needs to be stored in a database to restore
|
||||||
|
/// an InboundGroupSession.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PickledInboundGroupSession {
|
||||||
|
/// The pickle string holding the InboundGroupSession.
|
||||||
|
pub pickle: InboundGroupSessionPickle,
|
||||||
|
/// The public curve25519 key of the account that sent us the session
|
||||||
|
pub sender_key: String,
|
||||||
|
/// The public ed25519 key of the account that sent us the session.
|
||||||
|
pub signing_key: String,
|
||||||
|
/// The id of the room that the session is used in.
|
||||||
|
pub room_id: RoomId,
|
||||||
|
/// The list of claimed ed25519 that forwarded us this key. Will be None if
|
||||||
|
/// we dirrectly received this session.
|
||||||
|
pub forwarding_chains: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The typed representation of a base64 encoded string of the GroupSession pickle.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct InboundGroupSessionPickle(String);
|
||||||
|
|
||||||
|
impl From<String> for InboundGroupSessionPickle {
|
||||||
|
fn from(pickle_string: String) -> Self {
|
||||||
|
InboundGroupSessionPickle(pickle_string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InboundGroupSessionPickle {
|
||||||
|
/// Get the string representation of the pickle.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Outbound group session.
|
/// Outbound group session.
|
||||||
///
|
///
|
||||||
/// Outbound group sessions are used to exchange room messages between a group
|
/// Outbound group sessions are used to exchange room messages between a group
|
||||||
|
|
|
@ -20,12 +20,17 @@
|
||||||
mod account;
|
mod account;
|
||||||
mod group_sessions;
|
mod group_sessions;
|
||||||
mod session;
|
mod session;
|
||||||
|
mod utility;
|
||||||
|
|
||||||
pub use account::{Account, IdentityKeys};
|
pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount};
|
||||||
pub use group_sessions::{EncryptionSettings, InboundGroupSession};
|
pub use group_sessions::{
|
||||||
|
EncryptionSettings, InboundGroupSession, InboundGroupSessionPickle, PickledInboundGroupSession,
|
||||||
|
};
|
||||||
pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession};
|
pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession};
|
||||||
|
pub use olm_rs::PicklingMode;
|
||||||
pub(crate) use session::OlmMessage;
|
pub(crate) use session::OlmMessage;
|
||||||
pub use session::Session;
|
pub use session::{PickledSession, Session, SessionPickle};
|
||||||
|
pub(crate) use utility::Utility;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test {
|
pub(crate) mod test {
|
||||||
|
|
|
@ -20,15 +20,16 @@ use matrix_sdk_common::{
|
||||||
EventType,
|
EventType,
|
||||||
},
|
},
|
||||||
identifiers::{DeviceId, DeviceKeyAlgorithm, UserId},
|
identifiers::{DeviceId, DeviceKeyAlgorithm, UserId},
|
||||||
instant::Instant,
|
instant::{Duration, Instant},
|
||||||
locks::Mutex,
|
locks::Mutex,
|
||||||
};
|
};
|
||||||
use olm_rs::{errors::OlmSessionError, session::OlmSession, PicklingMode};
|
use olm_rs::{errors::OlmSessionError, session::OlmSession, PicklingMode};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
use super::IdentityKeys;
|
use super::IdentityKeys;
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{EventError, OlmResult},
|
error::{EventError, OlmResult, SessionUnpicklingError},
|
||||||
ReadOnlyDevice,
|
ReadOnlyDevice,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,14 +178,22 @@ impl Session {
|
||||||
///
|
///
|
||||||
/// * `pickle_mode` - The mode that was used to pickle the session, either
|
/// * `pickle_mode` - The mode that was used to pickle the session, either
|
||||||
/// an unencrypted mode or an encrypted using passphrase.
|
/// an unencrypted mode or an encrypted using passphrase.
|
||||||
pub async fn pickle(&self, pickle_mode: PicklingMode) -> String {
|
pub async fn pickle(&self, pickle_mode: PicklingMode) -> PickledSession {
|
||||||
self.inner.lock().await.pickle(pickle_mode)
|
let pickle = self.inner.lock().await.pickle(pickle_mode);
|
||||||
|
|
||||||
|
PickledSession {
|
||||||
|
pickle: SessionPickle::from(pickle),
|
||||||
|
sender_key: self.sender_key.to_string(),
|
||||||
|
// FIXME this should use the duration from the unix epoch.
|
||||||
|
creation_time: self.creation_time.elapsed(),
|
||||||
|
last_use_time: self.last_use_time.elapsed(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore a Session from a previously pickled string.
|
/// Restore a Session from a previously pickled string.
|
||||||
///
|
///
|
||||||
/// Returns the restored Olm Session or a `OlmSessionError` if there was an
|
/// Returns the restored Olm Session or a `SessionUnpicklingError` if there
|
||||||
/// error.
|
/// was an error.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
|
@ -194,40 +203,37 @@ impl Session {
|
||||||
///
|
///
|
||||||
/// * `our_idenity_keys` - An clone of the Arc to our own identity keys.
|
/// * `our_idenity_keys` - An clone of the Arc to our own identity keys.
|
||||||
///
|
///
|
||||||
/// * `pickle` - The pickled string of the session.
|
/// * `pickle` - The pickled version of the `Session`.
|
||||||
///
|
///
|
||||||
/// * `pickle_mode` - The mode that was used to pickle the session, either
|
/// * `pickle_mode` - The mode that was used to pickle the session, either
|
||||||
/// an unencrypted mode or an encrypted using passphrase.
|
/// an unencrypted mode or an encrypted using passphrase.
|
||||||
///
|
|
||||||
/// * `sender_key` - The public curve25519 key of the account that
|
|
||||||
/// established the session with us.
|
|
||||||
///
|
|
||||||
/// * `creation_time` - The timestamp that marks when the session was
|
|
||||||
/// created.
|
|
||||||
///
|
|
||||||
/// * `last_use_time` - The timestamp that marks when the session was
|
|
||||||
/// last used to encrypt or decrypt an Olm message.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn from_pickle(
|
pub fn from_pickle(
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
device_id: Arc<Box<DeviceId>>,
|
device_id: Arc<Box<DeviceId>>,
|
||||||
our_identity_keys: Arc<IdentityKeys>,
|
our_identity_keys: Arc<IdentityKeys>,
|
||||||
pickle: String,
|
pickle: PickledSession,
|
||||||
pickle_mode: PicklingMode,
|
pickle_mode: PicklingMode,
|
||||||
sender_key: String,
|
) -> Result<Self, SessionUnpicklingError> {
|
||||||
creation_time: Instant,
|
let session = OlmSession::unpickle(pickle.pickle.0, pickle_mode)?;
|
||||||
last_use_time: Instant,
|
|
||||||
) -> Result<Self, OlmSessionError> {
|
|
||||||
let session = OlmSession::unpickle(pickle, pickle_mode)?;
|
|
||||||
let session_id = session.session_id();
|
let session_id = session.session_id();
|
||||||
|
|
||||||
|
// FIXME this should use the UNIX epoch.
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
let creation_time = now
|
||||||
|
.checked_sub(pickle.creation_time)
|
||||||
|
.ok_or(SessionUnpicklingError::SessionTimestampError)?;
|
||||||
|
let last_use_time = now
|
||||||
|
.checked_sub(pickle.last_use_time)
|
||||||
|
.ok_or(SessionUnpicklingError::SessionTimestampError)?;
|
||||||
|
|
||||||
Ok(Session {
|
Ok(Session {
|
||||||
user_id,
|
user_id,
|
||||||
device_id,
|
device_id,
|
||||||
our_identity_keys,
|
our_identity_keys,
|
||||||
inner: Arc::new(Mutex::new(session)),
|
inner: Arc::new(Mutex::new(session)),
|
||||||
session_id: Arc::new(session_id),
|
session_id: Arc::new(session_id),
|
||||||
sender_key: Arc::new(sender_key),
|
sender_key: Arc::new(pickle.sender_key),
|
||||||
creation_time: Arc::new(creation_time),
|
creation_time: Arc::new(creation_time),
|
||||||
last_use_time: Arc::new(last_use_time),
|
last_use_time: Arc::new(last_use_time),
|
||||||
})
|
})
|
||||||
|
@ -239,3 +245,36 @@ impl PartialEq for Session {
|
||||||
self.session_id() == other.session_id()
|
self.session_id() == other.session_id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A pickled version of a `Session`.
|
||||||
|
///
|
||||||
|
/// Holds all the information that needs to be stored in a database to restore
|
||||||
|
/// a Session.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
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,
|
||||||
|
/// The relative time elapsed since the session was created.
|
||||||
|
pub creation_time: Duration,
|
||||||
|
/// The relative time elapsed since the session was last used.
|
||||||
|
pub last_use_time: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The typed representation of a base64 encoded string of the Olm Session pickle.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SessionPickle(String);
|
||||||
|
|
||||||
|
impl From<String> for SessionPickle {
|
||||||
|
fn from(picle_string: String) -> Self {
|
||||||
|
SessionPickle(picle_string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionPickle {
|
||||||
|
/// Get the string representation of the pickle.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use olm_rs::utility::OlmUtility;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId};
|
||||||
|
|
||||||
|
use crate::error::SignatureError;
|
||||||
|
|
||||||
|
pub(crate) struct Utility {
|
||||||
|
inner: OlmUtility,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Utility {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: OlmUtility::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify a signed JSON object.
|
||||||
|
///
|
||||||
|
/// The object must have a signatures key associated with an object of the
|
||||||
|
/// form `user_id: {key_id: signature}`.
|
||||||
|
///
|
||||||
|
/// Returns Ok if the signature was successfully verified, otherwise an
|
||||||
|
/// SignatureError.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `user_id` - The user who signed the JSON object.
|
||||||
|
///
|
||||||
|
/// * `key_id` - The id of the key that signed the JSON object.
|
||||||
|
///
|
||||||
|
/// * `signing_key` - The public ed25519 key which was used to sign the JSON
|
||||||
|
/// object.
|
||||||
|
///
|
||||||
|
/// * `json` - The JSON object that should be verified.
|
||||||
|
pub(crate) fn verify_json(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId,
|
||||||
|
key_id: &DeviceKeyId,
|
||||||
|
signing_key: &str,
|
||||||
|
json: &mut Value,
|
||||||
|
) -> Result<(), SignatureError> {
|
||||||
|
if key_id.algorithm() != DeviceKeyAlgorithm::Ed25519 {
|
||||||
|
return Err(SignatureError::UnsupportedAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
|
||||||
|
let unsigned = json_object.remove("unsigned");
|
||||||
|
let signatures = json_object.remove("signatures");
|
||||||
|
|
||||||
|
let canonical_json = cjson::to_string(json_object)?;
|
||||||
|
|
||||||
|
if let Some(u) = unsigned {
|
||||||
|
json_object.insert("unsigned".to_string(), u);
|
||||||
|
}
|
||||||
|
|
||||||
|
let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?;
|
||||||
|
let signature_object = signatures
|
||||||
|
.as_object()
|
||||||
|
.ok_or(SignatureError::NoSignatureFound)?;
|
||||||
|
let signature = signature_object
|
||||||
|
.get(user_id.as_str())
|
||||||
|
.ok_or(SignatureError::NoSignatureFound)?;
|
||||||
|
let signature = signature
|
||||||
|
.get(key_id.to_string())
|
||||||
|
.ok_or(SignatureError::NoSignatureFound)?;
|
||||||
|
let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?;
|
||||||
|
|
||||||
|
let ret = if self
|
||||||
|
.inner
|
||||||
|
.ed25519_verify(signing_key, &canonical_json, signature)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(SignatureError::VerificationError)
|
||||||
|
};
|
||||||
|
|
||||||
|
json_object.insert("signatures".to_string(), signatures);
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::{
|
api::r0::{
|
||||||
|
@ -21,13 +21,41 @@ use matrix_sdk_common::{
|
||||||
get_keys::{IncomingRequest as KeysQueryRequest, Response as KeysQueryResponse},
|
get_keys::{IncomingRequest as KeysQueryRequest, Response as KeysQueryResponse},
|
||||||
upload_keys::{Request as KeysUploadRequest, Response as KeysUploadResponse},
|
upload_keys::{Request as KeysUploadRequest, Response as KeysUploadResponse},
|
||||||
},
|
},
|
||||||
to_device::send_event_to_device::{
|
to_device::{send_event_to_device::Response as ToDeviceResponse, DeviceIdOrAllDevices},
|
||||||
IncomingRequest as ToDeviceRequest, Response as ToDeviceResponse,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
events::EventType,
|
||||||
|
identifiers::UserId,
|
||||||
uuid::Uuid,
|
uuid::Uuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
|
/// Customized version of `ruma_client_api::r0::to_device::send_event_to_device::Request`, using a
|
||||||
|
/// UUID for the transaction ID.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ToDeviceRequest {
|
||||||
|
/// Type of event being sent to each device.
|
||||||
|
pub event_type: EventType,
|
||||||
|
|
||||||
|
/// A request identifier unique to the access token used to send the request.
|
||||||
|
pub txn_id: Uuid,
|
||||||
|
|
||||||
|
/// A map of users to devices to a content for a message event to be
|
||||||
|
/// sent to the user's device. Individual message events can be sent
|
||||||
|
/// to devices, but all events must be of the same type.
|
||||||
|
/// The content's type for this field will be updated in a future
|
||||||
|
/// release, until then you can create a value using
|
||||||
|
/// `serde_json::value::to_raw_value`.
|
||||||
|
pub messages: BTreeMap<UserId, BTreeMap<DeviceIdOrAllDevices, Box<RawJsonValue>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToDeviceRequest {
|
||||||
|
/// Gets the transaction ID as a string.
|
||||||
|
pub fn txn_id_string(&self) -> String {
|
||||||
|
self.txn_id.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Enum over the different outgoing requests we can have.
|
/// Enum over the different outgoing requests we can have.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum OutgoingRequests {
|
pub enum OutgoingRequests {
|
||||||
|
|
|
@ -25,8 +25,8 @@ use matrix_sdk_common::{
|
||||||
locks::Mutex,
|
locks::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use crate::{
|
||||||
device::ReadOnlyDevice,
|
identities::ReadOnlyDevice,
|
||||||
olm::{InboundGroupSession, Session},
|
olm::{InboundGroupSession, Session},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -208,9 +208,9 @@ impl DeviceStore {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::{
|
use crate::{
|
||||||
device::test::get_device,
|
identities::device::test::get_device,
|
||||||
memory_stores::{DeviceStore, GroupSessionStore, SessionStore},
|
|
||||||
olm::{test::get_account_and_session, InboundGroupSession},
|
olm::{test::get_account_and_session, InboundGroupSession},
|
||||||
|
store::caches::{DeviceStore, GroupSessionStore, SessionStore},
|
||||||
};
|
};
|
||||||
use matrix_sdk_common::identifiers::room_id;
|
use matrix_sdk_common::identifiers::room_id;
|
||||||
|
|
|
@ -14,19 +14,20 @@
|
||||||
|
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use dashmap::{DashMap, DashSet};
|
use dashmap::{DashMap, DashSet};
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
identifiers::{DeviceId, RoomId, UserId},
|
identifiers::{DeviceId, RoomId, UserId},
|
||||||
locks::Mutex,
|
locks::Mutex,
|
||||||
};
|
};
|
||||||
|
use matrix_sdk_common_macros::async_trait;
|
||||||
|
|
||||||
use super::{Account, CryptoStore, InboundGroupSession, Result, Session};
|
use super::{
|
||||||
use crate::{
|
caches::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore},
|
||||||
device::ReadOnlyDevice,
|
Account, CryptoStore, InboundGroupSession, Result, Session,
|
||||||
memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore},
|
|
||||||
user_identity::UserIdentities,
|
|
||||||
};
|
};
|
||||||
|
use crate::identities::{ReadOnlyDevice, UserIdentities};
|
||||||
|
|
||||||
|
/// An in-memory only store that will forget all the E2EE key once it's dropped.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MemoryStore {
|
pub struct MemoryStore {
|
||||||
sessions: SessionStore,
|
sessions: SessionStore,
|
||||||
|
@ -37,8 +38,8 @@ pub struct MemoryStore {
|
||||||
identities: Arc<DashMap<UserId, UserIdentities>>,
|
identities: Arc<DashMap<UserId, UserIdentities>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemoryStore {
|
impl Default for MemoryStore {
|
||||||
pub fn new() -> Self {
|
fn default() -> Self {
|
||||||
MemoryStore {
|
MemoryStore {
|
||||||
sessions: SessionStore::new(),
|
sessions: SessionStore::new(),
|
||||||
inbound_group_sessions: GroupSessionStore::new(),
|
inbound_group_sessions: GroupSessionStore::new(),
|
||||||
|
@ -50,6 +51,13 @@ impl MemoryStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MemoryStore {
|
||||||
|
/// Create a new empty `MemoryStore`.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CryptoStore for MemoryStore {
|
impl CryptoStore for MemoryStore {
|
||||||
async fn load_account(&self) -> Result<Option<Account>> {
|
async fn load_account(&self) -> Result<Option<Account>> {
|
||||||
|
@ -153,7 +161,7 @@ impl CryptoStore for MemoryStore {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::{
|
use crate::{
|
||||||
device::test::get_device,
|
identities::device::test::get_device,
|
||||||
olm::{test::get_account_and_session, InboundGroupSession},
|
olm::{test::get_account_and_session, InboundGroupSession},
|
||||||
store::{memorystore::MemoryStore, CryptoStore},
|
store::{memorystore::MemoryStore, CryptoStore},
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,37 +12,73 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::{collections::HashSet, io::Error as IoError, sync::Arc};
|
//! Types and traits to implement the storage layer for the [`OlmMachine`]
|
||||||
|
//!
|
||||||
|
//! The storage layer for the [`OlmMachine`] can be customized using a trait.
|
||||||
|
//! Implementing your own [`CryptoStore`]
|
||||||
|
//!
|
||||||
|
//! An in-memory only store is provided as well as a SQLite based one, depending
|
||||||
|
//! on your needs and targets a custom store may be implemented, e.g. for
|
||||||
|
//! `wasm-unknown-unknown` an indexeddb store would be needed
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use matrix_sdk_crypto::{
|
||||||
|
//! # OlmMachine,
|
||||||
|
//! # store::MemoryStore,
|
||||||
|
//! # };
|
||||||
|
//! # use matrix_sdk_common::identifiers::{user_id, DeviceIdBox};
|
||||||
|
//! # let user_id = user_id!("@example:localhost");
|
||||||
|
//! # let device_id: DeviceIdBox = "TEST".into();
|
||||||
|
//! let store = Box::new(MemoryStore::new());
|
||||||
|
//!
|
||||||
|
//! let machine = OlmMachine::new_with_store(user_id, device_id, store);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! [`OlmMachine`]: /matrix_sdk_crypto/struct.OlmMachine.html
|
||||||
|
//! [`CryptoStore`]: trait.Cryptostore.html
|
||||||
|
|
||||||
|
pub mod caches;
|
||||||
|
mod memorystore;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[cfg(feature = "sqlite_cryptostore")]
|
||||||
|
pub(crate) mod sqlite;
|
||||||
|
|
||||||
|
use caches::ReadOnlyUserDevices;
|
||||||
|
pub use memorystore::MemoryStore;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[cfg(feature = "sqlite_cryptostore")]
|
||||||
|
pub use sqlite::SqliteStore;
|
||||||
|
|
||||||
|
use std::{collections::HashSet, fmt::Debug, io::Error as IoError, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use core::fmt::Debug;
|
|
||||||
use matrix_sdk_common::{
|
|
||||||
identifiers::{DeviceId, RoomId, UserId},
|
|
||||||
locks::Mutex,
|
|
||||||
};
|
|
||||||
use matrix_sdk_common_macros::send_sync;
|
|
||||||
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
|
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
|
||||||
use serde_json::Error as SerdeError;
|
use serde_json::Error as SerdeError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use url::ParseError;
|
use url::ParseError;
|
||||||
|
|
||||||
use super::{
|
#[cfg_attr(feature = "docs", doc(cfg(r#sqlite_cryptostore)))]
|
||||||
device::ReadOnlyDevice,
|
|
||||||
memory_stores::ReadOnlyUserDevices,
|
|
||||||
olm::{Account, InboundGroupSession, Session},
|
|
||||||
user_identity::UserIdentities,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod memorystore;
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[cfg(feature = "sqlite_cryptostore")]
|
|
||||||
pub mod sqlite;
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(feature = "sqlite_cryptostore")]
|
#[cfg(feature = "sqlite_cryptostore")]
|
||||||
use sqlx::Error as SqlxError;
|
use sqlx::Error as SqlxError;
|
||||||
|
|
||||||
|
use matrix_sdk_common::{
|
||||||
|
identifiers::{DeviceId, Error as IdentifierValidationError, RoomId, UserId},
|
||||||
|
locks::Mutex,
|
||||||
|
};
|
||||||
|
use matrix_sdk_common_macros::async_trait;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use matrix_sdk_common_macros::send_sync;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
identities::{ReadOnlyDevice, UserIdentities},
|
||||||
|
olm::{Account, InboundGroupSession, Session},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::SessionUnpicklingError;
|
||||||
|
|
||||||
|
/// A `CryptoStore` specific result type.
|
||||||
|
pub type Result<T> = std::result::Result<T, CryptoStoreError>;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
/// The crypto store's error type.
|
/// The crypto store's error type.
|
||||||
pub enum CryptoStoreError {
|
pub enum CryptoStoreError {
|
||||||
|
@ -75,8 +111,12 @@ pub enum CryptoStoreError {
|
||||||
OlmGroupSession(#[from] OlmGroupSessionError),
|
OlmGroupSession(#[from] OlmGroupSessionError),
|
||||||
|
|
||||||
/// A session time-stamp couldn't be loaded.
|
/// A session time-stamp couldn't be loaded.
|
||||||
#[error("can't load session timestamps")]
|
#[error(transparent)]
|
||||||
SessionTimestampError,
|
SessionUnpickling(#[from] SessionUnpicklingError),
|
||||||
|
|
||||||
|
/// A Matirx identifier failed to be validated.
|
||||||
|
#[error(transparent)]
|
||||||
|
IdentifierValidation(#[from] IdentifierValidationError),
|
||||||
|
|
||||||
/// The store failed to (de)serialize a data type.
|
/// The store failed to (de)serialize a data type.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
@ -87,13 +127,11 @@ pub enum CryptoStoreError {
|
||||||
UrlParse(#[from] ParseError),
|
UrlParse(#[from] ParseError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, CryptoStoreError>;
|
/// Trait abstracting a store that the `OlmMachine` uses to store cryptographic
|
||||||
|
/// keys.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), send_sync)]
|
#[cfg_attr(not(target_arch = "wasm32"), send_sync)]
|
||||||
/// Trait abstracting a store that the `OlmMachine` uses to store cryptographic
|
|
||||||
/// keys.
|
|
||||||
pub trait CryptoStore: Debug {
|
pub trait CryptoStore: Debug {
|
||||||
/// Load an account that was previously stored.
|
/// Load an account that was previously stored.
|
||||||
async fn load_account(&self) -> Result<Option<Account>>;
|
async fn load_account(&self) -> Result<Option<Account>>;
|
||||||
|
|
|
@ -26,24 +26,29 @@ use matrix_sdk_common::{
|
||||||
identifiers::{
|
identifiers::{
|
||||||
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId,
|
DeviceId, DeviceKeyAlgorithm, DeviceKeyId, EventEncryptionAlgorithm, RoomId, UserId,
|
||||||
},
|
},
|
||||||
instant::{Duration, Instant},
|
instant::Duration,
|
||||||
locks::Mutex,
|
locks::Mutex,
|
||||||
};
|
};
|
||||||
use olm_rs::PicklingMode;
|
|
||||||
use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection};
|
use sqlx::{query, query_as, sqlite::SqliteQueryAs, Connect, Executor, SqliteConnection};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use super::{CryptoStore, CryptoStoreError, Result};
|
use super::{
|
||||||
|
caches::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore},
|
||||||
|
CryptoStore, CryptoStoreError, Result,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{LocalTrust, ReadOnlyDevice},
|
identities::{LocalTrust, ReadOnlyDevice, UserIdentities},
|
||||||
memory_stores::{DeviceStore, GroupSessionStore, ReadOnlyUserDevices, SessionStore},
|
olm::{
|
||||||
olm::{Account, IdentityKeys, InboundGroupSession, Session},
|
Account, AccountPickle, IdentityKeys, InboundGroupSession, InboundGroupSessionPickle,
|
||||||
user_identity::UserIdentities,
|
PickledAccount, PickledInboundGroupSession, PickledSession, PicklingMode, Session,
|
||||||
|
SessionPickle,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// SQLite based implementation of a `CryptoStore`.
|
/// SQLite based implementation of a `CryptoStore`.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
#[cfg_attr(feature = "docs", doc(cfg(r#sqlite_cryptostore)))]
|
||||||
pub struct SqliteStore {
|
pub struct SqliteStore {
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
device_id: Arc<Box<DeviceId>>,
|
device_id: Arc<Box<DeviceId>>,
|
||||||
|
@ -336,7 +341,7 @@ impl SqliteStore {
|
||||||
.ok_or(CryptoStoreError::AccountUnset)?;
|
.ok_or(CryptoStoreError::AccountUnset)?;
|
||||||
let mut connection = self.connection.lock().await;
|
let mut connection = self.connection.lock().await;
|
||||||
|
|
||||||
let rows: Vec<(String, String, String, String)> = query_as(
|
let mut rows: Vec<(String, String, String, String)> = query_as(
|
||||||
"SELECT pickle, sender_key, creation_time, last_use_time
|
"SELECT pickle, sender_key, creation_time, last_use_time
|
||||||
FROM sessions WHERE account_id = ? and sender_key = ?",
|
FROM sessions WHERE account_id = ? and sender_key = ?",
|
||||||
)
|
)
|
||||||
|
@ -345,29 +350,27 @@ impl SqliteStore {
|
||||||
.fetch_all(&mut *connection)
|
.fetch_all(&mut *connection)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let now = Instant::now();
|
|
||||||
|
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.iter()
|
.drain(..)
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
let pickle = &row.0;
|
let pickle = row.0;
|
||||||
let sender_key = &row.1;
|
let sender_key = row.1;
|
||||||
let creation_time = now
|
let creation_time = serde_json::from_str::<Duration>(&row.2)?;
|
||||||
.checked_sub(serde_json::from_str::<Duration>(&row.2)?)
|
let last_use_time = serde_json::from_str::<Duration>(&row.3)?;
|
||||||
.ok_or(CryptoStoreError::SessionTimestampError)?;
|
|
||||||
let last_use_time = now
|
let pickle = PickledSession {
|
||||||
.checked_sub(serde_json::from_str::<Duration>(&row.3)?)
|
pickle: SessionPickle::from(pickle),
|
||||||
.ok_or(CryptoStoreError::SessionTimestampError)?;
|
last_use_time,
|
||||||
|
creation_time,
|
||||||
|
sender_key,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Session::from_pickle(
|
Ok(Session::from_pickle(
|
||||||
self.user_id.clone(),
|
self.user_id.clone(),
|
||||||
self.device_id.clone(),
|
self.device_id.clone(),
|
||||||
account_info.identity_keys.clone(),
|
account_info.identity_keys.clone(),
|
||||||
pickle.to_string(),
|
pickle,
|
||||||
self.get_pickle_mode(),
|
self.get_pickle_mode(),
|
||||||
sender_key.to_string(),
|
|
||||||
creation_time,
|
|
||||||
last_use_time,
|
|
||||||
)?)
|
)?)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<Session>>>()?)
|
.collect::<Result<Vec<Session>>>()?)
|
||||||
|
@ -377,7 +380,7 @@ impl SqliteStore {
|
||||||
let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
|
let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?;
|
||||||
let mut connection = self.connection.lock().await;
|
let mut connection = self.connection.lock().await;
|
||||||
|
|
||||||
let rows: Vec<(String, String, String, String)> = query_as(
|
let mut rows: Vec<(String, String, String, String)> = query_as(
|
||||||
"SELECT pickle, sender_key, signing_key, room_id
|
"SELECT pickle, sender_key, signing_key, room_id
|
||||||
FROM inbound_group_sessions WHERE account_id = ?",
|
FROM inbound_group_sessions WHERE account_id = ?",
|
||||||
)
|
)
|
||||||
|
@ -386,19 +389,26 @@ impl SqliteStore {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut group_sessions = rows
|
let mut group_sessions = rows
|
||||||
.iter()
|
.drain(..)
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
let pickle = &row.0;
|
let pickle = row.0;
|
||||||
let sender_key = &row.1;
|
let sender_key = row.1;
|
||||||
let signing_key = &row.2;
|
let signing_key = row.2;
|
||||||
let room_id = &row.3;
|
let room_id = row.3;
|
||||||
|
|
||||||
|
let pickle = PickledInboundGroupSession {
|
||||||
|
pickle: InboundGroupSessionPickle::from(pickle),
|
||||||
|
sender_key,
|
||||||
|
signing_key,
|
||||||
|
room_id: RoomId::try_from(room_id)?,
|
||||||
|
// Fixme we need to store/restore these once we get support
|
||||||
|
// for key requesting/forwarding.
|
||||||
|
forwarding_chains: None,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(InboundGroupSession::from_pickle(
|
Ok(InboundGroupSession::from_pickle(
|
||||||
pickle.to_string(),
|
pickle,
|
||||||
self.get_pickle_mode(),
|
self.get_pickle_mode(),
|
||||||
sender_key.to_string(),
|
|
||||||
signing_key.to_owned(),
|
|
||||||
RoomId::try_from(room_id.as_str()).unwrap(),
|
|
||||||
)?)
|
)?)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<InboundGroupSession>>>()?;
|
.collect::<Result<Vec<InboundGroupSession>>>()?;
|
||||||
|
@ -546,15 +556,13 @@ impl SqliteStore {
|
||||||
|
|
||||||
let signature = row.2;
|
let signature = row.2;
|
||||||
|
|
||||||
if !signatures.contains_key(&user_id) {
|
signatures
|
||||||
let _ = signatures.insert(user_id.clone(), BTreeMap::new());
|
.entry(user_id)
|
||||||
}
|
.or_insert_with(BTreeMap::new)
|
||||||
let user_map = signatures.get_mut(&user_id).unwrap();
|
.insert(
|
||||||
|
DeviceKeyId::from_parts(key_algorithm, device_id.as_str().into()),
|
||||||
user_map.insert(
|
signature.to_owned(),
|
||||||
DeviceKeyId::from_parts(key_algorithm, device_id.as_str().into()),
|
);
|
||||||
signature.to_owned(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let device = ReadOnlyDevice::new(
|
let device = ReadOnlyDevice::new(
|
||||||
|
@ -679,14 +687,15 @@ impl CryptoStore for SqliteStore {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let result = if let Some((id, pickle, shared, uploaded_key_count)) = row {
|
let result = if let Some((id, pickle, shared, uploaded_key_count)) = row {
|
||||||
let account = Account::from_pickle(
|
let pickle = PickledAccount {
|
||||||
pickle,
|
user_id: (&*self.user_id).clone(),
|
||||||
self.get_pickle_mode(),
|
device_id: (&*self.device_id).clone(),
|
||||||
|
pickle: AccountPickle::from(pickle),
|
||||||
shared,
|
shared,
|
||||||
uploaded_key_count,
|
uploaded_signed_key_count: uploaded_key_count,
|
||||||
&self.user_id,
|
};
|
||||||
&self.device_id,
|
|
||||||
)?;
|
let account = Account::from_pickle(pickle, self.get_pickle_mode())?;
|
||||||
|
|
||||||
*self.account_info.lock().unwrap() = Some(AccountInfo {
|
*self.account_info.lock().unwrap() = Some(AccountInfo {
|
||||||
account_id: id,
|
account_id: id,
|
||||||
|
@ -720,18 +729,18 @@ impl CryptoStore for SqliteStore {
|
||||||
shared = excluded.shared
|
shared = excluded.shared
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.bind(&*self.user_id.to_string())
|
.bind(pickle.user_id.as_str())
|
||||||
.bind(&*self.device_id.to_string())
|
.bind(pickle.device_id.as_str())
|
||||||
.bind(&pickle)
|
.bind(pickle.pickle.as_str())
|
||||||
.bind(account.shared())
|
.bind(pickle.shared)
|
||||||
.bind(account.uploaded_key_count())
|
.bind(pickle.uploaded_signed_key_count)
|
||||||
.execute(&mut *connection)
|
.execute(&mut *connection)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let account_id: (i64,) =
|
let account_id: (i64,) =
|
||||||
query_as("SELECT id FROM accounts WHERE user_id = ? and device_id = ?")
|
query_as("SELECT id FROM accounts WHERE user_id = ? and device_id = ?")
|
||||||
.bind(&*self.user_id.to_string())
|
.bind(self.user_id.as_str())
|
||||||
.bind(&*self.device_id.to_string())
|
.bind(self.device_id.as_str())
|
||||||
.fetch_one(&mut *connection)
|
.fetch_one(&mut *connection)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -751,11 +760,12 @@ impl CryptoStore for SqliteStore {
|
||||||
self.lazy_load_sessions(&session.sender_key).await?;
|
self.lazy_load_sessions(&session.sender_key).await?;
|
||||||
self.sessions.add(session.clone()).await;
|
self.sessions.add(session.clone()).await;
|
||||||
|
|
||||||
let session_id = session.session_id();
|
|
||||||
let creation_time = serde_json::to_string(&session.creation_time.elapsed())?;
|
|
||||||
let last_use_time = serde_json::to_string(&session.last_use_time.elapsed())?;
|
|
||||||
let pickle = session.pickle(self.get_pickle_mode()).await;
|
let pickle = session.pickle(self.get_pickle_mode()).await;
|
||||||
|
|
||||||
|
let session_id = session.session_id();
|
||||||
|
let creation_time = serde_json::to_string(&pickle.creation_time)?;
|
||||||
|
let last_use_time = serde_json::to_string(&pickle.last_use_time)?;
|
||||||
|
|
||||||
let mut connection = self.connection.lock().await;
|
let mut connection = self.connection.lock().await;
|
||||||
|
|
||||||
query(
|
query(
|
||||||
|
@ -767,8 +777,8 @@ impl CryptoStore for SqliteStore {
|
||||||
.bind(&account_id)
|
.bind(&account_id)
|
||||||
.bind(&*creation_time)
|
.bind(&*creation_time)
|
||||||
.bind(&*last_use_time)
|
.bind(&*last_use_time)
|
||||||
.bind(&*session.sender_key)
|
.bind(&pickle.sender_key)
|
||||||
.bind(&pickle)
|
.bind(&pickle.pickle.as_str())
|
||||||
.execute(&mut *connection)
|
.execute(&mut *connection)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -786,6 +796,10 @@ impl CryptoStore for SqliteStore {
|
||||||
let mut connection = self.connection.lock().await;
|
let mut connection = self.connection.lock().await;
|
||||||
let session_id = session.session_id();
|
let session_id = session.session_id();
|
||||||
|
|
||||||
|
// FIXME we need to store/restore the forwarding chains.
|
||||||
|
// FIXME this should be converted so it accepts an array of sessions for
|
||||||
|
// the key import feature.
|
||||||
|
|
||||||
query(
|
query(
|
||||||
"INSERT INTO inbound_group_sessions (
|
"INSERT INTO inbound_group_sessions (
|
||||||
session_id, account_id, sender_key, signing_key,
|
session_id, account_id, sender_key, signing_key,
|
||||||
|
@ -797,10 +811,10 @@ impl CryptoStore for SqliteStore {
|
||||||
)
|
)
|
||||||
.bind(session_id)
|
.bind(session_id)
|
||||||
.bind(account_id)
|
.bind(account_id)
|
||||||
.bind(&*session.sender_key)
|
.bind(pickle.sender_key)
|
||||||
.bind(&*session.signing_key)
|
.bind(pickle.signing_key)
|
||||||
.bind(&*session.room_id.to_string())
|
.bind(pickle.room_id.as_str())
|
||||||
.bind(&pickle)
|
.bind(pickle.pickle.as_str())
|
||||||
.execute(&mut *connection)
|
.execute(&mut *connection)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -908,7 +922,7 @@ impl std::fmt::Debug for SqliteStore {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::{
|
use crate::{
|
||||||
device::test::get_device,
|
identities::device::test::get_device,
|
||||||
olm::{Account, GroupSessionKey, InboundGroupSession, Session},
|
olm::{Account, GroupSessionKey, InboundGroupSession, Session},
|
||||||
};
|
};
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
|
@ -921,27 +935,37 @@ mod test {
|
||||||
|
|
||||||
use super::{CryptoStore, SqliteStore};
|
use super::{CryptoStore, SqliteStore};
|
||||||
|
|
||||||
fn example_user_id() -> UserId {
|
fn alice_id() -> UserId {
|
||||||
user_id!("@example:localhost")
|
user_id!("@alice:example.org")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn example_device_id() -> &'static DeviceId {
|
fn alice_device_id() -> Box<DeviceId> {
|
||||||
"DEVICEID".into()
|
"ALICEDEVICE".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bob_id() -> UserId {
|
||||||
|
user_id!("@bob:example.org")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bob_device_id() -> Box<DeviceId> {
|
||||||
|
"BOBDEVICE".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_store(passphrase: Option<&str>) -> (SqliteStore, tempfile::TempDir) {
|
async fn get_store(passphrase: Option<&str>) -> (SqliteStore, tempfile::TempDir) {
|
||||||
let tmpdir = tempdir().unwrap();
|
let tmpdir = tempdir().unwrap();
|
||||||
let tmpdir_path = tmpdir.path().to_str().unwrap();
|
let tmpdir_path = tmpdir.path().to_str().unwrap();
|
||||||
|
|
||||||
let user_id = &example_user_id();
|
|
||||||
let device_id = example_device_id();
|
|
||||||
|
|
||||||
let store = if let Some(passphrase) = passphrase {
|
let store = if let Some(passphrase) = passphrase {
|
||||||
SqliteStore::open_with_passphrase(&user_id, device_id, tmpdir_path, passphrase)
|
SqliteStore::open_with_passphrase(
|
||||||
.await
|
&alice_id(),
|
||||||
.expect("Can't create a passphrase protected store")
|
&alice_device_id(),
|
||||||
|
tmpdir_path,
|
||||||
|
passphrase,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Can't create a passphrase protected store")
|
||||||
} else {
|
} else {
|
||||||
SqliteStore::open(&user_id, device_id, tmpdir_path)
|
SqliteStore::open(&alice_id(), &alice_device_id(), tmpdir_path)
|
||||||
.await
|
.await
|
||||||
.expect("Can't create store")
|
.expect("Can't create store")
|
||||||
};
|
};
|
||||||
|
@ -960,22 +984,6 @@ mod test {
|
||||||
(account, store, dir)
|
(account, store, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alice_id() -> UserId {
|
|
||||||
user_id!("@alice:example.org")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alice_device_id() -> Box<DeviceId> {
|
|
||||||
"ALICEDEVICE".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bob_id() -> UserId {
|
|
||||||
user_id!("@bob:example.org")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bob_device_id() -> Box<DeviceId> {
|
|
||||||
"BOBDEVICE".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_account() -> Account {
|
fn get_account() -> Account {
|
||||||
Account::new(&alice_id(), &alice_device_id())
|
Account::new(&alice_id(), &alice_device_id())
|
||||||
}
|
}
|
||||||
|
@ -1011,7 +1019,7 @@ mod test {
|
||||||
async fn create_store() {
|
async fn create_store() {
|
||||||
let tmpdir = tempdir().unwrap();
|
let tmpdir = tempdir().unwrap();
|
||||||
let tmpdir_path = tmpdir.path().to_str().unwrap();
|
let tmpdir_path = tmpdir.path().to_str().unwrap();
|
||||||
let _ = SqliteStore::open(&example_user_id(), "DEVICEID".into(), tmpdir_path)
|
let _ = SqliteStore::open(&alice_id(), &alice_device_id(), tmpdir_path)
|
||||||
.await
|
.await
|
||||||
.expect("Can't create store");
|
.expect("Can't create store");
|
||||||
}
|
}
|
||||||
|
@ -1138,7 +1146,7 @@ mod test {
|
||||||
|
|
||||||
drop(store);
|
drop(store);
|
||||||
|
|
||||||
let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path())
|
let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path())
|
||||||
.await
|
.await
|
||||||
.expect("Can't create store");
|
.expect("Can't create store");
|
||||||
|
|
||||||
|
@ -1224,7 +1232,7 @@ mod test {
|
||||||
assert!(store.users_for_key_query().contains(device.user_id()));
|
assert!(store.users_for_key_query().contains(device.user_id()));
|
||||||
drop(store);
|
drop(store);
|
||||||
|
|
||||||
let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path())
|
let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path())
|
||||||
.await
|
.await
|
||||||
.expect("Can't create store");
|
.expect("Can't create store");
|
||||||
|
|
||||||
|
@ -1239,7 +1247,7 @@ mod test {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(!store.users_for_key_query().contains(device.user_id()));
|
assert!(!store.users_for_key_query().contains(device.user_id()));
|
||||||
|
|
||||||
let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path())
|
let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path())
|
||||||
.await
|
.await
|
||||||
.expect("Can't create store");
|
.expect("Can't create store");
|
||||||
|
|
||||||
|
@ -1257,7 +1265,7 @@ mod test {
|
||||||
|
|
||||||
drop(store);
|
drop(store);
|
||||||
|
|
||||||
let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path())
|
let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path())
|
||||||
.await
|
.await
|
||||||
.expect("Can't create store");
|
.expect("Can't create store");
|
||||||
|
|
||||||
|
@ -1290,7 +1298,7 @@ mod test {
|
||||||
store.save_devices(&[device.clone()]).await.unwrap();
|
store.save_devices(&[device.clone()]).await.unwrap();
|
||||||
store.delete_device(device.clone()).await.unwrap();
|
store.delete_device(device.clone()).await.unwrap();
|
||||||
|
|
||||||
let store = SqliteStore::open(&example_user_id(), example_device_id(), dir.path())
|
let store = SqliteStore::open(&alice_id(), &alice_device_id(), dir.path())
|
||||||
.await
|
.await
|
||||||
.expect("Can't create store");
|
.expect("Can't create store");
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,17 @@ use dashmap::DashMap;
|
||||||
use tracing::{trace, warn};
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest,
|
|
||||||
events::{AnyToDeviceEvent, AnyToDeviceEventContent},
|
events::{AnyToDeviceEvent, AnyToDeviceEventContent},
|
||||||
identifiers::{DeviceId, UserId},
|
identifiers::{DeviceId, UserId},
|
||||||
uuid::Uuid,
|
uuid::Uuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::sas::{content_to_request, Sas};
|
use super::sas::{content_to_request, Sas};
|
||||||
use crate::{requests::OutgoingRequest, Account, CryptoStore, CryptoStoreError, ReadOnlyDevice};
|
use crate::{
|
||||||
|
requests::{OutgoingRequest, ToDeviceRequest},
|
||||||
|
store::{CryptoStore, CryptoStoreError},
|
||||||
|
Account, ReadOnlyDevice,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct VerificationMachine {
|
pub struct VerificationMachine {
|
||||||
|
@ -49,7 +52,7 @@ impl VerificationMachine {
|
||||||
pub async fn start_sas(
|
pub async fn start_sas(
|
||||||
&self,
|
&self,
|
||||||
device: ReadOnlyDevice,
|
device: ReadOnlyDevice,
|
||||||
) -> Result<(Sas, OwnedToDeviceRequest), CryptoStoreError> {
|
) -> Result<(Sas, ToDeviceRequest), CryptoStoreError> {
|
||||||
let identity = self.store.get_user_identity(device.user_id()).await?;
|
let identity = self.store.get_user_identity(device.user_id()).await?;
|
||||||
|
|
||||||
let (sas, content) = Sas::start(
|
let (sas, content) = Sas::start(
|
||||||
|
@ -59,7 +62,7 @@ impl VerificationMachine {
|
||||||
identity,
|
identity,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (_, request) = content_to_request(
|
let request = content_to_request(
|
||||||
device.user_id(),
|
device.user_id(),
|
||||||
device.device_id(),
|
device.device_id(),
|
||||||
AnyToDeviceEventContent::KeyVerificationStart(content),
|
AnyToDeviceEventContent::KeyVerificationStart(content),
|
||||||
|
@ -82,7 +85,8 @@ impl VerificationMachine {
|
||||||
recipient_device: &DeviceId,
|
recipient_device: &DeviceId,
|
||||||
content: AnyToDeviceEventContent,
|
content: AnyToDeviceEventContent,
|
||||||
) {
|
) {
|
||||||
let (request_id, request) = content_to_request(recipient, recipient_device, content);
|
let request = content_to_request(recipient, recipient_device, content);
|
||||||
|
let request_id = request.txn_id;
|
||||||
|
|
||||||
let request = OutgoingRequest {
|
let request = OutgoingRequest {
|
||||||
request_id,
|
request_id,
|
||||||
|
@ -117,10 +121,10 @@ impl VerificationMachine {
|
||||||
for sas in self.verifications.iter() {
|
for sas in self.verifications.iter() {
|
||||||
if let Some(r) = sas.cancel_if_timed_out() {
|
if let Some(r) = sas.cancel_if_timed_out() {
|
||||||
self.outgoing_to_device_messages.insert(
|
self.outgoing_to_device_messages.insert(
|
||||||
r.0,
|
r.txn_id,
|
||||||
OutgoingRequest {
|
OutgoingRequest {
|
||||||
request_id: r.0,
|
request_id: r.txn_id,
|
||||||
request: Arc::new(r.1.into()),
|
request: Arc::new(r.into()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -193,10 +197,10 @@ impl VerificationMachine {
|
||||||
if !s.mark_device_as_verified().await? {
|
if !s.mark_device_as_verified().await? {
|
||||||
if let Some(r) = s.cancel() {
|
if let Some(r) = s.cancel() {
|
||||||
self.outgoing_to_device_messages.insert(
|
self.outgoing_to_device_messages.insert(
|
||||||
r.0,
|
r.txn_id,
|
||||||
OutgoingRequest {
|
OutgoingRequest {
|
||||||
request_id: r.0,
|
request_id: r.txn_id,
|
||||||
request: Arc::new(r.1.into()),
|
request: Arc::new(r.into()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -229,9 +233,9 @@ mod test {
|
||||||
use super::{Sas, VerificationMachine};
|
use super::{Sas, VerificationMachine};
|
||||||
use crate::{
|
use crate::{
|
||||||
requests::OutgoingRequests,
|
requests::OutgoingRequests,
|
||||||
store::memorystore::MemoryStore,
|
store::{CryptoStore, MemoryStore},
|
||||||
verification::test::{get_content_from_request, wrap_any_to_device_content},
|
verification::test::{get_content_from_request, wrap_any_to_device_content},
|
||||||
Account, CryptoStore, ReadOnlyDevice,
|
Account, ReadOnlyDevice,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn alice_id() -> UserId {
|
fn alice_id() -> UserId {
|
||||||
|
|
|
@ -20,19 +20,15 @@ pub use sas::Sas;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test {
|
pub(crate) mod test {
|
||||||
use crate::requests::{OutgoingRequest, OutgoingRequests};
|
use crate::requests::{OutgoingRequest, OutgoingRequests, ToDeviceRequest};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest,
|
|
||||||
events::{AnyToDeviceEvent, AnyToDeviceEventContent, EventType, ToDeviceEvent},
|
events::{AnyToDeviceEvent, AnyToDeviceEventContent, EventType, ToDeviceEvent},
|
||||||
identifiers::UserId,
|
identifiers::UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn request_to_event(
|
pub(crate) fn request_to_event(sender: &UserId, request: &ToDeviceRequest) -> AnyToDeviceEvent {
|
||||||
sender: &UserId,
|
|
||||||
request: &OwnedToDeviceRequest,
|
|
||||||
) -> AnyToDeviceEvent {
|
|
||||||
let content = get_content_from_request(request);
|
let content = get_content_from_request(request);
|
||||||
wrap_any_to_device_content(sender, content)
|
wrap_any_to_device_content(sender, content)
|
||||||
}
|
}
|
||||||
|
@ -81,9 +77,7 @@ pub(crate) mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_content_from_request(
|
pub(crate) fn get_content_from_request(request: &ToDeviceRequest) -> AnyToDeviceEventContent {
|
||||||
request: &OwnedToDeviceRequest,
|
|
||||||
) -> AnyToDeviceEventContent {
|
|
||||||
let json: Value = serde_json::from_str(
|
let json: Value = serde_json::from_str(
|
||||||
request
|
request
|
||||||
.messages
|
.messages
|
||||||
|
|
|
@ -19,9 +19,7 @@ use tracing::{trace, warn};
|
||||||
use olm_rs::sas::OlmSas;
|
use olm_rs::sas::OlmSas;
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::to_device::{
|
api::r0::to_device::DeviceIdOrAllDevices,
|
||||||
send_event_to_device::IncomingRequest as OwnedToDeviceRequest, DeviceIdOrAllDevices,
|
|
||||||
},
|
|
||||||
events::{
|
events::{
|
||||||
key::verification::{cancel::CancelCode, mac::MacEventContent},
|
key::verification::{cancel::CancelCode, mac::MacEventContent},
|
||||||
AnyToDeviceEventContent, EventType, ToDeviceEvent,
|
AnyToDeviceEventContent, EventType, ToDeviceEvent,
|
||||||
|
@ -30,7 +28,10 @@ use matrix_sdk_common::{
|
||||||
uuid::Uuid,
|
uuid::Uuid,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{user_identity::UserIdentities, Account, ReadOnlyDevice};
|
use crate::{
|
||||||
|
identities::{ReadOnlyDevice, UserIdentities},
|
||||||
|
Account, ToDeviceRequest,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SasIds {
|
pub struct SasIds {
|
||||||
|
@ -464,7 +465,7 @@ pub fn content_to_request(
|
||||||
recipient: &UserId,
|
recipient: &UserId,
|
||||||
recipient_device: &DeviceId,
|
recipient_device: &DeviceId,
|
||||||
content: AnyToDeviceEventContent,
|
content: AnyToDeviceEventContent,
|
||||||
) -> (Uuid, OwnedToDeviceRequest) {
|
) -> ToDeviceRequest {
|
||||||
let mut messages = BTreeMap::new();
|
let mut messages = BTreeMap::new();
|
||||||
let mut user_messages = BTreeMap::new();
|
let mut user_messages = BTreeMap::new();
|
||||||
|
|
||||||
|
@ -483,16 +484,11 @@ pub fn content_to_request(
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let request_id = Uuid::new_v4();
|
ToDeviceRequest {
|
||||||
|
txn_id: Uuid::new_v4(),
|
||||||
(
|
event_type,
|
||||||
request_id,
|
messages,
|
||||||
OwnedToDeviceRequest {
|
}
|
||||||
txn_id: request_id.to_string(),
|
|
||||||
event_type,
|
|
||||||
messages,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -22,7 +22,6 @@ use std::sync::{Arc, Mutex};
|
||||||
use tracing::{info, trace, warn};
|
use tracing::{info, trace, warn};
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::to_device::send_event_to_device::IncomingRequest as OwnedToDeviceRequest,
|
|
||||||
events::{
|
events::{
|
||||||
key::verification::{
|
key::verification::{
|
||||||
accept::AcceptEventContent, cancel::CancelCode, mac::MacEventContent,
|
accept::AcceptEventContent, cancel::CancelCode, mac::MacEventContent,
|
||||||
|
@ -31,12 +30,12 @@ use matrix_sdk_common::{
|
||||||
AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent,
|
AnyToDeviceEvent, AnyToDeviceEventContent, ToDeviceEvent,
|
||||||
},
|
},
|
||||||
identifiers::{DeviceId, UserId},
|
identifiers::{DeviceId, UserId},
|
||||||
uuid::Uuid,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
user_identity::UserIdentities, Account, CryptoStore, CryptoStoreError, LocalTrust,
|
identities::{LocalTrust, ReadOnlyDevice, UserIdentities},
|
||||||
ReadOnlyDevice,
|
store::{CryptoStore, CryptoStoreError},
|
||||||
|
Account, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use helpers::content_to_request;
|
pub use helpers::content_to_request;
|
||||||
|
@ -166,10 +165,10 @@ impl Sas {
|
||||||
///
|
///
|
||||||
/// This does nothing if the verification was already accepted, otherwise it
|
/// This does nothing if the verification was already accepted, otherwise it
|
||||||
/// returns an `AcceptEventContent` that needs to be sent out.
|
/// returns an `AcceptEventContent` that needs to be sent out.
|
||||||
pub fn accept(&self) -> Option<OwnedToDeviceRequest> {
|
pub fn accept(&self) -> Option<ToDeviceRequest> {
|
||||||
self.inner.lock().unwrap().accept().map(|c| {
|
self.inner.lock().unwrap().accept().map(|c| {
|
||||||
let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
|
let content = AnyToDeviceEventContent::KeyVerificationAccept(c);
|
||||||
self.content_to_request(content).1
|
self.content_to_request(content)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +179,7 @@ impl Sas {
|
||||||
/// Does nothing if we're not in a state where we can confirm the short auth
|
/// Does nothing if we're not in a state where we can confirm the short auth
|
||||||
/// string, otherwise returns a `MacEventContent` that needs to be sent to
|
/// string, otherwise returns a `MacEventContent` that needs to be sent to
|
||||||
/// the server.
|
/// the server.
|
||||||
pub async fn confirm(&self) -> Result<Option<OwnedToDeviceRequest>, CryptoStoreError> {
|
pub async fn confirm(&self) -> Result<Option<ToDeviceRequest>, CryptoStoreError> {
|
||||||
let (content, done) = {
|
let (content, done) = {
|
||||||
let mut guard = self.inner.lock().unwrap();
|
let mut guard = self.inner.lock().unwrap();
|
||||||
let sas: InnerSas = (*guard).clone();
|
let sas: InnerSas = (*guard).clone();
|
||||||
|
@ -195,7 +194,7 @@ impl Sas {
|
||||||
// else branch and only after the identity was verified as well. We
|
// else branch and only after the identity was verified as well. We
|
||||||
// dont' want to verify one without the other.
|
// dont' want to verify one without the other.
|
||||||
if !self.mark_device_as_verified().await? {
|
if !self.mark_device_as_verified().await? {
|
||||||
return Ok(self.cancel().map(|r| r.1));
|
return Ok(self.cancel());
|
||||||
} else {
|
} else {
|
||||||
self.mark_identity_as_verified().await?;
|
self.mark_identity_as_verified().await?;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +202,7 @@ impl Sas {
|
||||||
|
|
||||||
Ok(content.map(|c| {
|
Ok(content.map(|c| {
|
||||||
let content = AnyToDeviceEventContent::KeyVerificationMac(c);
|
let content = AnyToDeviceEventContent::KeyVerificationMac(c);
|
||||||
self.content_to_request(content).1
|
self.content_to_request(content)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +327,7 @@ impl Sas {
|
||||||
///
|
///
|
||||||
/// Returns None if the `Sas` object is already in a canceled state,
|
/// Returns None if the `Sas` object is already in a canceled state,
|
||||||
/// otherwise it returns a request that needs to be sent out.
|
/// otherwise it returns a request that needs to be sent out.
|
||||||
pub fn cancel(&self) -> Option<(Uuid, OwnedToDeviceRequest)> {
|
pub fn cancel(&self) -> Option<ToDeviceRequest> {
|
||||||
let mut guard = self.inner.lock().unwrap();
|
let mut guard = self.inner.lock().unwrap();
|
||||||
let sas: InnerSas = (*guard).clone();
|
let sas: InnerSas = (*guard).clone();
|
||||||
let (sas, content) = sas.cancel(CancelCode::User);
|
let (sas, content) = sas.cancel(CancelCode::User);
|
||||||
|
@ -337,7 +336,7 @@ impl Sas {
|
||||||
content.map(|c| self.content_to_request(c))
|
content.map(|c| self.content_to_request(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cancel_if_timed_out(&self) -> Option<(Uuid, OwnedToDeviceRequest)> {
|
pub(crate) fn cancel_if_timed_out(&self) -> Option<ToDeviceRequest> {
|
||||||
if self.is_canceled() || self.is_done() {
|
if self.is_canceled() || self.is_done() {
|
||||||
None
|
None
|
||||||
} else if self.timed_out() {
|
} else if self.timed_out() {
|
||||||
|
@ -408,10 +407,7 @@ impl Sas {
|
||||||
self.inner.lock().unwrap().verified_identities()
|
self.inner.lock().unwrap().verified_identities()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn content_to_request(
|
pub(crate) fn content_to_request(&self, content: AnyToDeviceEventContent) -> ToDeviceRequest {
|
||||||
&self,
|
|
||||||
content: AnyToDeviceEventContent,
|
|
||||||
) -> (Uuid, OwnedToDeviceRequest) {
|
|
||||||
content_to_request(self.other_user_id(), self.other_device_id(), content)
|
content_to_request(self.other_user_id(), self.other_device_id(), content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -658,9 +654,9 @@ mod test {
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
store::memorystore::MemoryStore,
|
store::{CryptoStore, MemoryStore},
|
||||||
verification::test::{get_content_from_request, wrap_any_to_device_content},
|
verification::test::{get_content_from_request, wrap_any_to_device_content},
|
||||||
Account, CryptoStore, ReadOnlyDevice,
|
Account, ReadOnlyDevice,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Accepted, Created, Sas, SasState, Started};
|
use super::{Accepted, Created, Sas, SasState, Started};
|
||||||
|
|
|
@ -43,7 +43,10 @@ use matrix_sdk_common::{
|
||||||
|
|
||||||
use super::helpers::{get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds};
|
use super::helpers::{get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds};
|
||||||
|
|
||||||
use crate::{user_identity::UserIdentities, Account, ReadOnlyDevice};
|
use crate::{
|
||||||
|
identities::{ReadOnlyDevice, UserIdentities},
|
||||||
|
Account,
|
||||||
|
};
|
||||||
|
|
||||||
const KEY_AGREEMENT_PROTOCOLS: &[KeyAgreementProtocol] =
|
const KEY_AGREEMENT_PROTOCOLS: &[KeyAgreementProtocol] =
|
||||||
&[KeyAgreementProtocol::Curve25519HkdfSha256];
|
&[KeyAgreementProtocol::Curve25519HkdfSha256];
|
||||||
|
|
Loading…
Reference in New Issue