matrix-sdk: Expose the import/export keys methods.

master
Damir Jelić 2020-09-11 16:34:39 +02:00
parent 618a58ba34
commit ffd2843b0a
11 changed files with 154 additions and 15 deletions

View File

@ -34,6 +34,7 @@ serde_json = "1.0.57"
thiserror = "1.0.20" thiserror = "1.0.20"
tracing = "0.1.19" tracing = "0.1.19"
url = "2.1.1" url = "2.1.1"
zeroize = "1.1.0"
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" }
@ -55,6 +56,11 @@ features = ["std", "std-future"]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
futures-timer = "3.0.2" futures-timer = "3.0.2"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
version = "0.2.22"
default-features = false
features = ["fs", "blocking"]
[target.'cfg(target_arch = "wasm32")'.dependencies.futures-timer] [target.'cfg(target_arch = "wasm32")'.dependencies.futures-timer]
version = "3.0.2" version = "3.0.2"
features = ["wasm-bindgen"] features = ["wasm-bindgen"]

View File

@ -22,12 +22,16 @@ use std::{
result::Result as StdResult, result::Result as StdResult,
sync::Arc, sync::Arc,
}; };
#[cfg(feature = "encryption")]
use std::{io::Write, path::PathBuf};
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use dashmap::DashMap; use dashmap::DashMap;
use futures_timer::Delay as sleep; use futures_timer::Delay as sleep;
use reqwest::header::{HeaderValue, InvalidHeaderValue}; use reqwest::header::{HeaderValue, InvalidHeaderValue};
use url::Url; use url::Url;
#[cfg(feature = "encryption")]
use zeroize::Zeroizing;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use tracing::{debug, warn}; use tracing::{debug, warn};
@ -36,7 +40,10 @@ 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, ToDeviceRequest}; use matrix_sdk_base::crypto::{
decrypt_key_export, encrypt_key_export, olm::InboundGroupSession, store::CryptoStoreError,
OutgoingRequests, ToDeviceRequest,
};
use matrix_sdk_common::{ use matrix_sdk_common::{
api::r0::{ api::r0::{
@ -1479,7 +1486,6 @@ impl Client {
/// ///
/// println!("{:?}", device.is_trusted()); /// println!("{:?}", device.is_trusted());
/// ///
///
/// let verification = device.start_verification().await.unwrap(); /// let verification = device.start_verification().await.unwrap();
/// # }); /// # });
/// ``` /// ```
@ -1534,6 +1540,126 @@ impl Client {
http_client: self.http_client.clone(), http_client: self.http_client.clone(),
}) })
} }
/// Export E2EE keys that match the given predicate encrypting them with the
/// given passphrase.
///
/// # Arguments
///
/// * `path` - The file path where the exported key file will be saved.
///
/// * `passphrase` - The passphrase that will be used to encrypt the exported
/// room keys.
///
/// * `predicate` - A closure that will be called for every known
/// `InboundGroupSession`, which represents a room key. If the closure
/// returns `true` the `InboundGroupSessoin` will be included in the export,
/// if the closure returns `false` it will not be included.
///
/// # Examples
///
/// ```no_run
/// # use std::{path::PathBuf, time::Duration};
/// # use matrix_sdk::{
/// # Client, SyncSettings,
/// # api::r0::typing::create_typing_event::Typing,
/// # identifiers::room_id,
/// # };
/// # use futures::executor::block_on;
/// # use url::Url;
/// # block_on(async {
/// # let homeserver = Url::parse("http://localhost:8080").unwrap();
/// # let mut client = Client::new(homeserver).unwrap();
/// let path = PathBuf::from("/home/example/e2e-keys.txt");
/// // Export all room keys.
/// client
/// .export_keys(path, "secret-passphrase", |_| true)
/// .await
/// .expect("Can't export keys.");
///
/// // Export only the room keys for a certain room.
/// let path = PathBuf::from("/home/example/e2e-room-keys.txt");
/// let room_id = room_id!("!test:localhost");
///
/// client
/// .export_keys(path, "secret-passphrase", |s| s.room_id() == &room_id)
/// .await
/// .expect("Can't export keys.");
/// # });
/// ```
#[cfg(feature = "encryption")]
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub async fn export_keys(
&self,
path: PathBuf,
passphrase: &str,
predicate: impl FnMut(&InboundGroupSession) -> bool,
) -> Result<()> {
let olm = self
.base_client
.olm_machine()
.await
.ok_or(Error::AuthenticationRequired)?;
let keys = olm.export_keys(predicate).await?;
let passphrase = Zeroizing::new(passphrase.to_owned());
let encrypt = move || -> Result<()> {
let export: String = encrypt_key_export(&keys, &passphrase, 500_000)?;
let mut file = std::fs::File::create(path).unwrap();
file.write_all(&export.into_bytes()).unwrap();
Ok(())
};
let task = tokio::task::spawn_blocking(encrypt);
task.await.expect("Task join error")
}
/// Import E2EE keys from the given file path.
///
/// ```no_run
/// # use std::{path::PathBuf, time::Duration};
/// # use matrix_sdk::{
/// # Client, SyncSettings,
/// # api::r0::typing::create_typing_event::Typing,
/// # identifiers::room_id,
/// # };
/// # use futures::executor::block_on;
/// # use url::Url;
/// # block_on(async {
/// # let homeserver = Url::parse("http://localhost:8080").unwrap();
/// # let mut client = Client::new(homeserver).unwrap();
/// let path = PathBuf::from("/home/example/e2e-keys.txt");
/// client
/// .import_keys(path, "secret-passphrase")
/// .await
/// .expect("Can't import keys");
/// # });
/// ```
#[cfg(feature = "encryption")]
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub async fn import_keys(&self, path: PathBuf, passphrase: &str) -> Result<()> {
let olm = self
.base_client
.olm_machine()
.await
.ok_or(Error::AuthenticationRequired)?;
let passphrase = Zeroizing::new(passphrase.to_owned());
let decrypt = move || {
let file = std::fs::File::open(path)?;
decrypt_key_export(file, &passphrase)
};
let task = tokio::task::spawn_blocking(decrypt);
let import = task.await.expect("Task join error").unwrap();
olm.import_keys(import).await.unwrap();
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -14,8 +14,8 @@
use std::{ops::Deref, result::Result as StdResult}; use std::{ops::Deref, result::Result as StdResult};
use matrix_sdk_base::{ use matrix_sdk_base::crypto::{
CryptoStoreError, Device as BaseDevice, LocalTrust, ReadOnlyDevice, store::CryptoStoreError, Device as BaseDevice, LocalTrust, ReadOnlyDevice,
UserDevices as BaseUserDevices, UserDevices as BaseUserDevices,
}; };
use matrix_sdk_common::{ use matrix_sdk_common::{

View File

@ -24,7 +24,7 @@ use serde_json::Error as JsonError;
use thiserror::Error; use thiserror::Error;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use matrix_sdk_base::CryptoStoreError; use matrix_sdk_base::crypto::store::CryptoStoreError;
/// Result type of the rust-sdk. /// Result type of the rust-sdk.
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;

View File

@ -44,11 +44,11 @@ compile_error!("one of 'native-tls' or 'rustls-tls' features must be enabled");
#[cfg(all(feature = "native-tls", feature = "rustls-tls",))] #[cfg(all(feature = "native-tls", feature = "rustls-tls",))]
compile_error!("only one of 'native-tls' or 'rustls-tls' features can be enabled"); compile_error!("only one of 'native-tls' or 'rustls-tls' features can be enabled");
#[cfg(not(target_arch = "wasm32"))]
pub use matrix_sdk_base::JsonStore;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
#[cfg_attr(feature = "docs", doc(cfg(encryption)))] #[cfg_attr(feature = "docs", doc(cfg(encryption)))]
pub use matrix_sdk_base::LocalTrust; pub use matrix_sdk_base::crypto::LocalTrust;
#[cfg(not(target_arch = "wasm32"))]
pub use matrix_sdk_base::JsonStore;
pub use matrix_sdk_base::{ pub use matrix_sdk_base::{
CustomEvent, Error as BaseError, EventEmitter, Room, RoomState, Session, StateStore, SyncRoom, CustomEvent, Error as BaseError, EventEmitter, Room, RoomState, Session, StateStore, SyncRoom,
}; };

View File

@ -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 matrix_sdk_base::{ReadOnlyDevice, Sas as BaseSas}; use matrix_sdk_base::crypto::{ReadOnlyDevice, Sas as BaseSas};
use matrix_sdk_common::api::r0::to_device::send_event_to_device::Request as ToDeviceRequest; use matrix_sdk_common::api::r0::to_device::send_event_to_device::Request as ToDeviceRequest;
use crate::{error::Result, http_client::HttpClient}; use crate::{error::Result, http_client::HttpClient};

View File

@ -1860,6 +1860,12 @@ impl BaseClient {
panic!("The client hasn't been logged in") panic!("The client hasn't been logged in")
} }
} }
/// Get the olm machine.
pub async fn olm_machine(&self) -> Option<OlmMachine> {
let olm = self.olm.lock().await;
olm.as_ref().cloned()
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -56,10 +56,7 @@ 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 as crypto;
store::CryptoStoreError, Device, IncomingResponse, LocalTrust, OutgoingRequest,
OutgoingRequests, ReadOnlyDevice, Sas, ToDeviceRequest, UserDevices,
};
#[cfg(feature = "messages")] #[cfg(feature = "messages")]
#[cfg_attr(feature = "docs", doc(cfg(messages)))] #[cfg_attr(feature = "docs", doc(cfg(messages)))]

View File

@ -14,5 +14,5 @@ version = "0.1.0"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
syn = "1.0.39" syn = "1.0.40"
quote = "1.0.7" quote = "1.0.7"

View File

@ -26,7 +26,7 @@ matrix-sdk-common-macros = { version = "0.1.0", path = "../matrix_sdk_common_mac
matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" } matrix-sdk-common = { version = "0.1.0", path = "../matrix_sdk_common" }
olm-rs = { version = "0.6.0", features = ["serde"] } olm-rs = { version = "0.6.0", features = ["serde"] }
getrandom = "0.1.14" getrandom = "0.2.0"
serde = { version = "1.0.115", features = ["derive", "rc"] } 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"

View File

@ -40,6 +40,10 @@ pub use olm_rs::{
use super::{ExportedGroupSessionKey, ExportedRoomKey, GroupSessionKey}; use super::{ExportedGroupSessionKey, ExportedRoomKey, GroupSessionKey};
use crate::error::{EventError, MegolmResult}; use crate::error::{EventError, MegolmResult};
// TODO add creation times to the inbound grop sessions so we can export
// sessions that were created between some time period, this should only be set
// for non-imported sessoins.
/// Inbound group session. /// Inbound group session.
/// ///
/// Inbound group sessions are used to exchange room messages between a group of /// Inbound group sessions are used to exchange room messages between a group of