matrix-sdk: Expose the import/export keys methods.
parent
618a58ba34
commit
ffd2843b0a
|
@ -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"]
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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::{
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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)))]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue