Merge branch 'crypto-improvements' into new-state-store
commit
d4ebe8cc83
|
@ -0,0 +1,103 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
style:
|
||||||
|
name: Check style
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
components: rustfmt
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Cargo fmt
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Run clippy
|
||||||
|
needs: [style]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
components: clippy
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Clippy
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --all-targets -- -D warnings
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: ${{ matrix.name }}
|
||||||
|
needs: [clippy]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
name:
|
||||||
|
- linux / stable
|
||||||
|
- linux / beta
|
||||||
|
- macOS / stable
|
||||||
|
- windows / stable-x86_64-msvc
|
||||||
|
|
||||||
|
include:
|
||||||
|
- name: linux / stable
|
||||||
|
|
||||||
|
- name: linux / beta
|
||||||
|
rust: beta
|
||||||
|
|
||||||
|
- name: macOS / stable
|
||||||
|
os: macOS-latest
|
||||||
|
|
||||||
|
- name: windows / stable-x86_64-msvc
|
||||||
|
os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust || 'stable' }}
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
|
@ -0,0 +1,39 @@
|
||||||
|
name: Code coverage
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
code_coverage:
|
||||||
|
name: Code Coverage
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install stable toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
profile: minimal
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Install tarpaulin
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: install
|
||||||
|
args: cargo-tarpaulin -f
|
||||||
|
|
||||||
|
- name: Run tarpaulin
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: tarpaulin
|
||||||
|
args: --ignore-config --exclude-files "matrix_sdk/examples/*,matrix_sdk_common,matrix_sdk_test" --out Xml
|
||||||
|
|
||||||
|
- name: Upload to codecov.io
|
||||||
|
uses: codecov/codecov-action@v1
|
|
@ -1,4 +1,4 @@
|
||||||
[![Build Status](https://img.shields.io/travis/matrix-org/matrix-rust-sdk.svg?style=flat-square)](https://travis-ci.org/matrix-org/matrix-rust-sdk)
|
![Build Status](https://img.shields.io/github/workflow/status/matrix-org/matrix-rust-sdk/CI?style=flat-square)
|
||||||
[![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-rust-sdk/master.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-rust-sdk)
|
[![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-rust-sdk/master.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-rust-sdk)
|
||||||
[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0)
|
[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0)
|
||||||
[![#matrix-rust-sdk](https://img.shields.io/badge/matrix-%23matrix--rust--sdk-blue?style=flat-square)](https://matrix.to/#/#matrix-rust-sdk:matrix.org)
|
[![#matrix-rust-sdk](https://img.shields.io/badge/matrix-%23matrix--rust--sdk-blue?style=flat-square)](https://matrix.to/#/#matrix-rust-sdk:matrix.org)
|
||||||
|
|
|
@ -76,6 +76,7 @@ use matrix_sdk_common::{
|
||||||
join_room_by_id, join_room_by_id_or_alias, kick_user, leave_room, Invite3pid,
|
join_room_by_id, join_room_by_id_or_alias, kick_user, leave_room, Invite3pid,
|
||||||
},
|
},
|
||||||
message::{get_message_events, send_message_event},
|
message::{get_message_events, send_message_event},
|
||||||
|
profile::{get_avatar_url, get_display_name, set_avatar_url, set_display_name},
|
||||||
read_marker::set_read_marker,
|
read_marker::set_read_marker,
|
||||||
receipt::create_receipt,
|
receipt::create_receipt,
|
||||||
room::create_room,
|
room::create_room,
|
||||||
|
@ -430,11 +431,121 @@ impl Client {
|
||||||
session.as_ref().cloned().map(|s| s.user_id)
|
session.as_ref().cloned().map(|s| s.user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetches the display name of the owner of the client.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```no_run
|
||||||
|
/// # use futures::executor::block_on;
|
||||||
|
/// # use matrix_sdk::Client;
|
||||||
|
/// # use url::Url;
|
||||||
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
||||||
|
/// # block_on(async {
|
||||||
|
/// let user = "example";
|
||||||
|
/// let client = Client::new(homeserver).unwrap();
|
||||||
|
/// client.login(user, "password", None, None).await.unwrap();
|
||||||
|
///
|
||||||
|
/// if let Some(name) = client.display_name().await.unwrap() {
|
||||||
|
/// println!("Logged in as user '{}' with display name '{}'", user, name);
|
||||||
|
/// }
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
pub async fn display_name(&self) -> Result<Option<String>> {
|
||||||
|
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
|
||||||
|
let request = get_display_name::Request::new(&user_id);
|
||||||
|
let response = self.send(request).await?;
|
||||||
|
Ok(response.displayname)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the display name of the owner of the client.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```no_run
|
||||||
|
/// # use futures::executor::block_on;
|
||||||
|
/// # use matrix_sdk::Client;
|
||||||
|
/// # use url::Url;
|
||||||
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
||||||
|
/// # block_on(async {
|
||||||
|
/// let user = "example";
|
||||||
|
/// let client = Client::new(homeserver).unwrap();
|
||||||
|
/// client.login(user, "password", None, None).await.unwrap();
|
||||||
|
///
|
||||||
|
/// client.set_display_name(Some("Alice")).await.expect("Failed setting display name");
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
pub async fn set_display_name(&self, name: Option<&str>) -> Result<()> {
|
||||||
|
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
|
||||||
|
let request = set_display_name::Request::new(&user_id, name);
|
||||||
|
self.send(request).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the mxc avatar url of the owner of the client, if set.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```no_run
|
||||||
|
/// # use futures::executor::block_on;
|
||||||
|
/// # use matrix_sdk::Client;
|
||||||
|
/// # use url::Url;
|
||||||
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
||||||
|
/// # block_on(async {
|
||||||
|
/// # let user = "example";
|
||||||
|
/// let client = Client::new(homeserver).unwrap();
|
||||||
|
/// client.login(user, "password", None, None).await.unwrap();
|
||||||
|
///
|
||||||
|
/// if let Some(url) = client.avatar_url().await.unwrap() {
|
||||||
|
/// println!("Your avatar's mxc url is {}", url);
|
||||||
|
/// }
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
pub async fn avatar_url(&self) -> Result<Option<String>> {
|
||||||
|
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
|
||||||
|
let request = get_avatar_url::Request::new(&user_id);
|
||||||
|
let response = self.send(request).await?;
|
||||||
|
Ok(response.avatar_url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the mxc avatar url of the client's owner. The avatar gets unset if `url` is `None`.
|
||||||
|
pub async fn set_avatar_url(&self, url: Option<&str>) -> Result<()> {
|
||||||
|
let user_id = self.user_id().await.ok_or(Error::AuthenticationRequired)?;
|
||||||
|
let request = set_avatar_url::Request::new(&user_id, url);
|
||||||
|
self.send(request).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Upload and set the owning client's avatar.
|
||||||
|
///
|
||||||
|
/// The will upload the data produced by the reader to the homeserver's content repository, and
|
||||||
|
/// set the user's avatar to the mxc url for the uploaded file.
|
||||||
|
///
|
||||||
|
/// This is a convenience method for calling [`upload()`](#method.upload), followed by
|
||||||
|
/// [`set_avatar_url()`](#method.set_avatar_url).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```no_run
|
||||||
|
/// # use std::{path::Path, fs::File, io::Read};
|
||||||
|
/// # use futures::executor::block_on;
|
||||||
|
/// # use matrix_sdk::Client;
|
||||||
|
/// # use url::Url;
|
||||||
|
/// # block_on(async {
|
||||||
|
/// # let homeserver = Url::parse("http://locahost:8080").unwrap();
|
||||||
|
/// # let client = Client::new(homeserver).unwrap();
|
||||||
|
/// let path = Path::new("/home/example/selfie.jpg");
|
||||||
|
/// let mut image = File::open(&path).unwrap();
|
||||||
|
///
|
||||||
|
/// client.upload_avatar(&mime::IMAGE_JPEG, &mut image).await.expect("Can't set avatar");
|
||||||
|
/// # })
|
||||||
|
/// ```
|
||||||
|
pub async fn upload_avatar<R: Read>(&self, content_type: &Mime, reader: &mut R) -> Result<()> {
|
||||||
|
let upload_response = self.upload(content_type, reader).await?;
|
||||||
|
self.set_avatar_url(Some(&upload_response.content_uri))
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
///// Add `EventEmitter` to `Client`.
|
///// Add `EventEmitter` to `Client`.
|
||||||
/////
|
/////
|
||||||
///// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
|
///// The methods of `EventEmitter` are called when the respective `RoomEvents` occur.
|
||||||
//pub async fn add_event_emitter(&mut self, emitter: Box<dyn EventEmitter>) {
|
//pub async fn add_event_emitter(&mut self, emitter: Box<dyn EventEmitter>) {
|
||||||
// self.base_client.add_event_emitter(emitter).await;
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// /// Returns the joined rooms this client knows about.
|
// /// Returns the joined rooms this client knows about.
|
||||||
|
@ -803,7 +914,7 @@ impl Client {
|
||||||
since: Option<&str>,
|
since: Option<&str>,
|
||||||
server: Option<&ServerName>,
|
server: Option<&ServerName>,
|
||||||
) -> Result<get_public_rooms::Response> {
|
) -> Result<get_public_rooms::Response> {
|
||||||
let limit = limit.map(|n| UInt::from(n));
|
let limit = limit.map(UInt::from);
|
||||||
|
|
||||||
let request = assign!(get_public_rooms::Request::new(), {
|
let request = assign!(get_public_rooms::Request::new(), {
|
||||||
limit,
|
limit,
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub use ruma::{
|
||||||
AuthScheme, EndpointError, OutgoingRequest,
|
AuthScheme, EndpointError, OutgoingRequest,
|
||||||
},
|
},
|
||||||
directory, encryption, events, identifiers, presence, push,
|
directory, encryption, events, identifiers, presence, push,
|
||||||
serde::Raw,
|
serde::{CanonicalJsonValue, Raw},
|
||||||
thirdparty, Outgoing,
|
thirdparty, Outgoing,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ use aes_ctr::{
|
||||||
use base64::DecodeError;
|
use base64::DecodeError;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use super::{decode, decode_url_safe, encode, encode_url_safe};
|
use crate::utilities::{decode, decode_url_safe, encode, encode_url_safe};
|
||||||
|
|
||||||
const IV_SIZE: usize = 16;
|
const IV_SIZE: usize = 16;
|
||||||
const KEY_SIZE: usize = 32;
|
const KEY_SIZE: usize = 32;
|
||||||
|
|
|
@ -27,8 +27,10 @@ use hmac::{Hmac, Mac, NewMac};
|
||||||
use pbkdf2::pbkdf2;
|
use pbkdf2::pbkdf2;
|
||||||
use sha2::{Sha256, Sha512};
|
use sha2::{Sha256, Sha512};
|
||||||
|
|
||||||
use super::{decode, encode, DecodeError};
|
use crate::{
|
||||||
use crate::olm::ExportedRoomKey;
|
olm::ExportedRoomKey,
|
||||||
|
utilities::{decode, encode, DecodeError},
|
||||||
|
};
|
||||||
|
|
||||||
const SALT_SIZE: usize = 16;
|
const SALT_SIZE: usize = 16;
|
||||||
const IV_SIZE: usize = 16;
|
const IV_SIZE: usize = 16;
|
||||||
|
|
|
@ -3,21 +3,3 @@ mod key_export;
|
||||||
|
|
||||||
pub use attachments::{AttachmentDecryptor, AttachmentEncryptor, DecryptorError};
|
pub use attachments::{AttachmentDecryptor, AttachmentEncryptor, DecryptorError};
|
||||||
pub use key_export::{decrypt_key_export, encrypt_key_export};
|
pub use key_export::{decrypt_key_export, encrypt_key_export};
|
||||||
|
|
||||||
use base64::{decode_config, encode_config, DecodeError, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
|
|
||||||
|
|
||||||
fn decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
|
|
||||||
decode_config(input, STANDARD_NO_PAD)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode_url_safe(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
|
|
||||||
decode_config(input, URL_SAFE_NO_PAD)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode(input: impl AsRef<[u8]>) -> String {
|
|
||||||
encode_config(input, STANDARD_NO_PAD)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode_url_safe(input: impl AsRef<[u8]>) -> String {
|
|
||||||
encode_config(input, URL_SAFE_NO_PAD)
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ pub mod olm;
|
||||||
mod requests;
|
mod requests;
|
||||||
mod session_manager;
|
mod session_manager;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
mod utilities;
|
||||||
mod verification;
|
mod verification;
|
||||||
|
|
||||||
pub use error::{MegolmError, OlmError};
|
pub use error::{MegolmError, OlmError};
|
||||||
|
|
|
@ -54,10 +54,10 @@ use olm_rs::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{EventError, OlmResult, SessionCreationError},
|
error::{EventError, OlmResult, SessionCreationError},
|
||||||
file_encryption::encode,
|
|
||||||
identities::ReadOnlyDevice,
|
identities::ReadOnlyDevice,
|
||||||
requests::UploadSigningKeysRequest,
|
requests::UploadSigningKeysRequest,
|
||||||
store::{Changes, Store},
|
store::{Changes, Store},
|
||||||
|
utilities::encode,
|
||||||
OlmError,
|
OlmError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ impl Account {
|
||||||
return Err(OlmError::SessionWedged(user_id, sender_key));
|
return Err(OlmError::SessionWedged(user_id, sender_key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Decrypted a to-device event {:?}", event);
|
debug!("Decrypted a to-device event {:?}", event);
|
||||||
|
|
|
@ -138,7 +138,7 @@ mod test {
|
||||||
use crate::ReadOnlyAccount;
|
use crate::ReadOnlyAccount;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(target_os = "linux")]
|
||||||
async fn expiration() {
|
async fn expiration() {
|
||||||
let settings = EncryptionSettings {
|
let settings = EncryptionSettings {
|
||||||
rotation_period_msgs: 1,
|
rotation_period_msgs: 1,
|
||||||
|
|
|
@ -16,7 +16,6 @@ use aes_gcm::{
|
||||||
aead::{generic_array::GenericArray, Aead, NewAead},
|
aead::{generic_array::GenericArray, Aead, NewAead},
|
||||||
Aes256Gcm,
|
Aes256Gcm,
|
||||||
};
|
};
|
||||||
use base64::{decode_config, encode_config, DecodeError, URL_SAFE_NO_PAD};
|
|
||||||
use getrandom::getrandom;
|
use getrandom::getrandom;
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
encryption::DeviceKeys,
|
encryption::DeviceKeys,
|
||||||
|
@ -42,19 +41,12 @@ use matrix_sdk_common::{
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SignatureError,
|
error::SignatureError,
|
||||||
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
|
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
|
||||||
|
utilities::{decode_url_safe as decode, encode_url_safe as encode, DecodeError},
|
||||||
UserIdentity,
|
UserIdentity,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NONCE_SIZE: usize = 12;
|
const NONCE_SIZE: usize = 12;
|
||||||
|
|
||||||
fn encode<T: AsRef<[u8]>>(input: T) -> String {
|
|
||||||
encode_config(input, URL_SAFE_NO_PAD)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
|
|
||||||
decode_config(input, URL_SAFE_NO_PAD)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error type reporting failures in the Signign operations.
|
/// Error type reporting failures in the Signign operations.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum SigningError {
|
pub enum SigningError {
|
||||||
|
|
|
@ -316,8 +316,7 @@ mod test {
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::keys::claim_keys::Response as KeyClaimResponse,
|
api::r0::keys::claim_keys::Response as KeyClaimResponse,
|
||||||
identifiers::{user_id, DeviceIdBox, DeviceKeyAlgorithm, UserId},
|
identifiers::{user_id, DeviceIdBox, UserId},
|
||||||
instant::{Duration, Instant},
|
|
||||||
};
|
};
|
||||||
use matrix_sdk_test::async_test;
|
use matrix_sdk_test::async_test;
|
||||||
|
|
||||||
|
@ -422,8 +421,13 @@ mod test {
|
||||||
// This test doesn't run on macos because we're modifying the session
|
// This test doesn't run on macos because we're modifying the session
|
||||||
// creation time so we can get around the UNWEDGING_INTERVAL.
|
// creation time so we can get around the UNWEDGING_INTERVAL.
|
||||||
#[async_test]
|
#[async_test]
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(target_os = "linux")]
|
||||||
async fn session_unwedging() {
|
async fn session_unwedging() {
|
||||||
|
use matrix_sdk_common::{
|
||||||
|
identifiers::DeviceKeyAlgorithm,
|
||||||
|
instant::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
let manager = session_manager().await;
|
let manager = session_manager().await;
|
||||||
let bob = bob_account();
|
let bob = bob_account();
|
||||||
let (_, mut session) = bob.create_session_for(&manager.account).await;
|
let (_, mut session) = bob.create_session_for(&manager.account).await;
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
pub use base64::DecodeError;
|
||||||
|
use base64::{decode_config, encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
|
||||||
|
|
||||||
|
/// Decode the input as base64 with no padding.
|
||||||
|
pub fn decode(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
|
||||||
|
decode_config(input, STANDARD_NO_PAD)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode the input as URL safe base64 with no padding.
|
||||||
|
pub fn decode_url_safe(input: impl AsRef<[u8]>) -> Result<Vec<u8>, DecodeError> {
|
||||||
|
decode_config(input, URL_SAFE_NO_PAD)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode the input as base64 with no padding.
|
||||||
|
pub fn encode(input: impl AsRef<[u8]>) -> String {
|
||||||
|
encode_config(input, STANDARD_NO_PAD)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode the input as URL safe base64 with no padding.
|
||||||
|
pub fn encode_url_safe(input: impl AsRef<[u8]>) -> String {
|
||||||
|
encode_config(input, URL_SAFE_NO_PAD)
|
||||||
|
}
|
|
@ -380,7 +380,7 @@ mod test {
|
||||||
assert!(bob.is_done());
|
assert!(bob.is_done());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(target_os = "linux")]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn timing_out() {
|
async fn timing_out() {
|
||||||
let (alice_machine, bob) = setup_verification_machine().await;
|
let (alice_machine, bob) = setup_verification_machine().await;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
use std::{collections::BTreeMap, convert::TryInto};
|
use std::{collections::BTreeMap, convert::TryInto};
|
||||||
|
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
use tracing::{trace, warn};
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
use olm_rs::sas::OlmSas;
|
use olm_rs::sas::OlmSas;
|
||||||
|
@ -21,15 +22,19 @@ use olm_rs::sas::OlmSas;
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
api::r0::to_device::DeviceIdOrAllDevices,
|
api::r0::to_device::DeviceIdOrAllDevices,
|
||||||
events::{
|
events::{
|
||||||
key::verification::{cancel::CancelCode, mac::MacToDeviceEventContent},
|
key::verification::{
|
||||||
|
cancel::CancelCode, mac::MacToDeviceEventContent, start::StartToDeviceEventContent,
|
||||||
|
},
|
||||||
AnyToDeviceEventContent, EventType, ToDeviceEvent,
|
AnyToDeviceEventContent, EventType, ToDeviceEvent,
|
||||||
},
|
},
|
||||||
identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId},
|
identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId},
|
||||||
uuid::Uuid,
|
uuid::Uuid,
|
||||||
|
CanonicalJsonValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, UserIdentities},
|
||||||
|
utilities::encode,
|
||||||
ReadOnlyAccount, ToDeviceRequest,
|
ReadOnlyAccount, ToDeviceRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,6 +45,29 @@ pub struct SasIds {
|
||||||
pub other_identity: Option<UserIdentities>,
|
pub other_identity: Option<UserIdentities>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate the commitment for a accept event from the public key and the
|
||||||
|
/// start event.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `public_key` - Our own ephemeral public key that is used for the
|
||||||
|
/// interactive verification.
|
||||||
|
///
|
||||||
|
/// * `content` - The `m.key.verification.start` event content that started the
|
||||||
|
/// interactive verification process.
|
||||||
|
pub fn calculate_commitment(public_key: &str, content: &StartToDeviceEventContent) -> String {
|
||||||
|
let json_content: CanonicalJsonValue = serde_json::to_value(content)
|
||||||
|
.expect("Can't serialize content")
|
||||||
|
.try_into()
|
||||||
|
.expect("Can't canonicalize content");
|
||||||
|
|
||||||
|
encode(
|
||||||
|
Sha256::new()
|
||||||
|
.chain(&format!("{}{}", public_key, json_content))
|
||||||
|
.finalize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a tuple of an emoji and a description of the emoji using a number.
|
/// Get a tuple of an emoji and a description of the emoji using a number.
|
||||||
///
|
///
|
||||||
/// This is taken directly from the [spec]
|
/// This is taken directly from the [spec]
|
||||||
|
@ -493,12 +521,38 @@ pub fn content_to_request(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use matrix_sdk_common::events::key::verification::start::StartToDeviceEventContent;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
use super::{bytes_to_decimal, bytes_to_emoji, bytes_to_emoji_index, emoji_from_index};
|
use super::{
|
||||||
|
bytes_to_decimal, bytes_to_emoji, bytes_to_emoji_index, calculate_commitment,
|
||||||
|
emoji_from_index,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_emoji_generation() {
|
fn commitment_calculation() {
|
||||||
|
let commitment = "CCQmB4JCdB0FW21FdAnHj/Hu8+W9+Nb0vgwPEnZZQ4g";
|
||||||
|
|
||||||
|
let public_key = "Q/NmNFEUS1fS+YeEmiZkjjblKTitrKOAk7cPEumcMlg";
|
||||||
|
let content = json!({
|
||||||
|
"from_device":"XOWLHHFSWM",
|
||||||
|
"transaction_id":"bYxBsirjUJO9osar6ST4i2M2NjrYLA7l",
|
||||||
|
"method":"m.sas.v1",
|
||||||
|
"key_agreement_protocols":["curve25519-hkdf-sha256","curve25519"],
|
||||||
|
"hashes":["sha256"],
|
||||||
|
"message_authentication_codes":["hkdf-hmac-sha256","hmac-sha256"],
|
||||||
|
"short_authentication_string":["decimal","emoji"]
|
||||||
|
});
|
||||||
|
|
||||||
|
let content: StartToDeviceEventContent = serde_json::from_value(content).unwrap();
|
||||||
|
let calculated_commitment = calculate_commitment(public_key, &content);
|
||||||
|
|
||||||
|
assert_eq!(commitment, &calculated_commitment);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn emoji_generation() {
|
||||||
let bytes = vec![0, 0, 0, 0, 0, 0];
|
let bytes = vec![0, 0, 0, 0, 0, 0];
|
||||||
let index: Vec<(&'static str, &'static str)> = vec![0, 0, 0, 0, 0, 0, 0]
|
let index: Vec<(&'static str, &'static str)> = vec![0, 0, 0, 0, 0, 0, 0]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -516,7 +570,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decimal_generation() {
|
fn decimal_generation() {
|
||||||
let bytes = vec![0, 0, 0, 0, 0];
|
let bytes = vec![0, 0, 0, 0, 0];
|
||||||
let result = bytes_to_decimal(bytes);
|
let result = bytes_to_decimal(bytes);
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use olm_rs::{sas::OlmSas, utility::OlmUtility};
|
use olm_rs::sas::OlmSas;
|
||||||
|
|
||||||
use matrix_sdk_common::{
|
use matrix_sdk_common::{
|
||||||
events::{
|
events::{
|
||||||
|
@ -40,8 +40,11 @@ use matrix_sdk_common::{
|
||||||
identifiers::{DeviceId, UserId},
|
identifiers::{DeviceId, UserId},
|
||||||
uuid::Uuid,
|
uuid::Uuid,
|
||||||
};
|
};
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
use super::helpers::{get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds};
|
use super::helpers::{
|
||||||
|
calculate_commitment, get_decimal, get_emoji, get_mac_content, receive_mac_event, SasIds,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
identities::{ReadOnlyDevice, UserIdentities},
|
identities::{ReadOnlyDevice, UserIdentities},
|
||||||
|
@ -176,7 +179,7 @@ pub struct Started {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Accepted {
|
pub struct Accepted {
|
||||||
accepted_protocols: Arc<AcceptedProtocols>,
|
accepted_protocols: Arc<AcceptedProtocols>,
|
||||||
json_start_content: String,
|
start_content: Arc<StartToDeviceEventContent>,
|
||||||
commitment: String,
|
commitment: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,8 +351,7 @@ impl SasState<Created> {
|
||||||
let accepted_protocols =
|
let accepted_protocols =
|
||||||
AcceptedProtocols::try_from(content.clone()).map_err(|c| self.clone().cancel(c))?;
|
AcceptedProtocols::try_from(content.clone()).map_err(|c| self.clone().cancel(c))?;
|
||||||
|
|
||||||
let json_start_content = serde_json::to_string(&self.as_content())
|
let start_content = self.as_content().into();
|
||||||
.expect("Can't deserialize start event content");
|
|
||||||
|
|
||||||
Ok(SasState {
|
Ok(SasState {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
|
@ -358,9 +360,9 @@ impl SasState<Created> {
|
||||||
creation_time: self.creation_time,
|
creation_time: self.creation_time,
|
||||||
last_event_time: self.last_event_time,
|
last_event_time: self.last_event_time,
|
||||||
state: Arc::new(Accepted {
|
state: Arc::new(Accepted {
|
||||||
json_start_content,
|
start_content,
|
||||||
commitment: content.commitment.clone(),
|
commitment: content.commitment.clone(),
|
||||||
accepted_protocols: Arc::new(accepted_protocols),
|
accepted_protocols: accepted_protocols.into(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -391,12 +393,14 @@ impl SasState<Started> {
|
||||||
) -> Result<SasState<Started>, SasState<Canceled>> {
|
) -> Result<SasState<Started>, SasState<Canceled>> {
|
||||||
if let StartMethod::MSasV1(content) = &event.content.method {
|
if let StartMethod::MSasV1(content) = &event.content.method {
|
||||||
let sas = OlmSas::new();
|
let sas = OlmSas::new();
|
||||||
let utility = OlmUtility::new();
|
|
||||||
|
|
||||||
let json_content =
|
|
||||||
serde_json::to_string(&event.content).expect("Can't serialize content");
|
|
||||||
let pubkey = sas.public_key();
|
let pubkey = sas.public_key();
|
||||||
let commitment = utility.sha256_utf8_msg(&format!("{}{}", pubkey, json_content));
|
let commitment = calculate_commitment(&pubkey, &event.content);
|
||||||
|
|
||||||
|
error!(
|
||||||
|
"Calculated commitment for pubkey {} and content {:?} {}",
|
||||||
|
pubkey, event.content, commitment
|
||||||
|
);
|
||||||
|
|
||||||
let sas = SasState {
|
let sas = SasState {
|
||||||
inner: Arc::new(Mutex::new(sas)),
|
inner: Arc::new(Mutex::new(sas)),
|
||||||
|
@ -540,11 +544,7 @@ impl SasState<Accepted> {
|
||||||
self.check_event(&event.sender, &event.content.transaction_id)
|
self.check_event(&event.sender, &event.content.transaction_id)
|
||||||
.map_err(|c| self.clone().cancel(c))?;
|
.map_err(|c| self.clone().cancel(c))?;
|
||||||
|
|
||||||
let utility = OlmUtility::new();
|
let commitment = calculate_commitment(&event.content.key, &self.state.start_content);
|
||||||
let commitment = utility.sha256_utf8_msg(&format!(
|
|
||||||
"{}{}",
|
|
||||||
event.content.key, self.state.json_start_content
|
|
||||||
));
|
|
||||||
|
|
||||||
if self.state.commitment != commitment {
|
if self.state.commitment != commitment {
|
||||||
Err(self.cancel(CancelCode::InvalidMessage))
|
Err(self.cancel(CancelCode::InvalidMessage))
|
||||||
|
|
Loading…
Reference in New Issue