From b5563c2a1a1adaf81412a63d89e1f3075444b68d Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 16 Dec 2024 18:15:03 -0500 Subject: [PATCH 1/2] use the dehydrated device format implemented by vodozemac --- Cargo.lock | 3 +- Cargo.toml | 2 ++ .../src/dehydrated_devices.rs | 34 ------------------- crates/matrix-sdk-crypto/src/olm/account.rs | 4 +-- 4 files changed, 5 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52a3ed545fa..29abf694f97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6366,8 +6366,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vodozemac" version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4b56780b7827dd72c3c6398c3048752bebf8d1d84ec19b606b15dbc3c850b8" +source = "git+https://github.com/uhoreg/vodozemac.git?branch=dehydration_format#048b0f3b74998d3cc90181807e5e079f85bc343c" dependencies = [ "aes", "arrayvec", diff --git a/Cargo.toml b/Cargo.toml index 56c356e22d5..f063b0d3d36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,6 +144,8 @@ tracing-core = { git = "https://github.com/element-hq/tracing.git", rev = "ca943 tracing-subscriber = { git = "https://github.com/element-hq/tracing.git", rev = "ca9431f74d37c9d3b5e6a9f35b2c706711dab7dd" } tracing-appender = { git = "https://github.com/element-hq/tracing.git", rev = "ca9431f74d37c9d3b5e6a9f35b2c706711dab7dd" } paranoid-android = { git = "https://github.com/element-hq/paranoid-android.git", rev = "69388ac5b4afeed7be4401c70ce17f6d9a2cf19b" } +vodozemac = { git = "https://github.com/uhoreg/vodozemac.git", branch = "dehydration_format" } + [workspace.lints.rust] rust_2018_idioms = "warn" diff --git a/crates/matrix-sdk-crypto/src/dehydrated_devices.rs b/crates/matrix-sdk-crypto/src/dehydrated_devices.rs index f10a180fc63..ae05374c1bf 100644 --- a/crates/matrix-sdk-crypto/src/dehydrated_devices.rs +++ b/crates/matrix-sdk-crypto/src/dehydrated_devices.rs @@ -43,7 +43,6 @@ use std::sync::Arc; -use hkdf::Hkdf; use ruma::{ api::client::dehydrated_device::{put_dehydrated_device, DehydratedDeviceData}, assign, @@ -51,7 +50,6 @@ use ruma::{ serde::Raw, DeviceId, }; -use sha2::Sha256; use thiserror::Error; use tracing::{instrument, trace}; use vodozemac::LibolmPickleError; @@ -136,7 +134,6 @@ impl DehydratedDevices { device_id: &DeviceId, device_data: Raw, ) -> Result { - let pickle_key = expand_pickle_key(pickle_key, device_id); let rehydrated = self.inner.rehydrate(&pickle_key, device_id, device_data).await?; Ok(RehydratedDevice { rehydrated, original: self.inner.to_owned() }) @@ -330,7 +327,6 @@ impl DehydratedDevice { trace!("Creating an upload request for a dehydrated device"); - let pickle_key = expand_pickle_key(pickle_key, &self.store.static_account().device_id); let device_id = self.store.static_account().device_id.clone(); let device_data = account.dehydrate(&pickle_key); let initial_device_display_name = Some(initial_device_display_name); @@ -345,36 +341,6 @@ impl DehydratedDevice { } } -/// We're using the libolm-compatible pickle format and its encryption scheme. -/// -/// The libolm pickle encryption scheme uses HKDF to deterministically expand an -/// input key material, usually 32 bytes, into a AES key, MAC key, and the -/// initialization vector (IV). -/// -/// This means that the same input key material will always end up producing the -/// same AES key, and IV. -/// -/// This encryption scheme is used in the Olm double ratchet and was designed to -/// minimize the size of the ciphertext. As a tradeof, it requires a unique -/// input key material for each plaintext that gets encrypted, otherwise IV -/// reuse happens. -/// -/// To combat the IV reuse, we're going to create a per-dehydrated-device unique -/// pickle key by expanding the key itself with the device ID used as the salt. -fn expand_pickle_key(key: &[u8; 32], device_id: &DeviceId) -> Box<[u8; 32]> { - // TODO: Perhaps we should put this into vodozemac with a new pickle - // minimalistic pickle format using the [`matrix_pickle`] crate. - // - // [`matrix_pickle`]: https://docs.rs/matrix-pickle/latest/matrix_pickle/ - let kdf: Hkdf = Hkdf::new(Some(device_id.as_bytes()), key); - let mut key = Box::new([0u8; 32]); - - kdf.expand(b"dehydrated-device-pickle-key", key.as_mut_slice()) - .expect("We should be able to expand the 32 byte pickle key"); - - key -} - #[cfg(test)] mod tests { use std::{collections::BTreeMap, iter}; diff --git a/crates/matrix-sdk-crypto/src/olm/account.rs b/crates/matrix-sdk-crypto/src/olm/account.rs index efdd1402ab8..289d39f8904 100644 --- a/crates/matrix-sdk-crypto/src/olm/account.rs +++ b/crates/matrix-sdk-crypto/src/olm/account.rs @@ -694,7 +694,7 @@ impl Account { pub(crate) fn dehydrate(&self, pickle_key: &[u8; 32]) -> Raw { let device_pickle = self .inner - .to_libolm_pickle(pickle_key) + .to_dehydrated_device(pickle_key) .expect("We should be able to convert a freshly created Account into a libolm pickle"); let data = DehydratedDeviceData::V1(DehydratedDeviceV1::new(device_pickle)); @@ -711,7 +711,7 @@ impl Account { match data { DehydratedDeviceData::V1(d) => { - let account = InnerAccount::from_libolm_pickle(&d.device_pickle, pickle_key)?; + let account = InnerAccount::from_dehydrated_device(&d.device_pickle, device_id.as_str(), pickle_key)?; Ok(Self::new_helper(account, user_id, device_id)) } _ => Err(DehydrationError::Json(serde_json::Error::custom(format!( From b0dd695b102db51777fc04142b670780136c9546 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Mon, 16 Dec 2024 21:41:35 -0500 Subject: [PATCH 2/2] make it work with latest main, and fmt --- crates/matrix-sdk-crypto/src/dehydrated_devices.rs | 5 +++-- crates/matrix-sdk-crypto/src/olm/account.rs | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/matrix-sdk-crypto/src/dehydrated_devices.rs b/crates/matrix-sdk-crypto/src/dehydrated_devices.rs index d959499f337..04ca0ca0095 100644 --- a/crates/matrix-sdk-crypto/src/dehydrated_devices.rs +++ b/crates/matrix-sdk-crypto/src/dehydrated_devices.rs @@ -134,7 +134,8 @@ impl DehydratedDevices { device_id: &DeviceId, device_data: Raw, ) -> Result { - let rehydrated = self.inner.rehydrate(&pickle_key, device_id, device_data).await?; + let rehydrated = + self.inner.rehydrate(pickle_key.inner.as_ref(), device_id, device_data).await?; Ok(RehydratedDevice { rehydrated, original: self.inner.to_owned() }) } @@ -367,7 +368,7 @@ impl DehydratedDevice { trace!("Creating an upload request for a dehydrated device"); let device_id = self.store.static_account().device_id.clone(); - let device_data = account.dehydrate(&pickle_key); + let device_data = account.dehydrate(pickle_key.inner.as_ref()); let initial_device_display_name = Some(initial_device_display_name); transaction.commit().await?; diff --git a/crates/matrix-sdk-crypto/src/olm/account.rs b/crates/matrix-sdk-crypto/src/olm/account.rs index 289d39f8904..ccdb7be0467 100644 --- a/crates/matrix-sdk-crypto/src/olm/account.rs +++ b/crates/matrix-sdk-crypto/src/olm/account.rs @@ -711,7 +711,11 @@ impl Account { match data { DehydratedDeviceData::V1(d) => { - let account = InnerAccount::from_dehydrated_device(&d.device_pickle, device_id.as_str(), pickle_key)?; + let account = InnerAccount::from_dehydrated_device( + &d.device_pickle, + device_id.as_str(), + pickle_key, + )?; Ok(Self::new_helper(account, user_id, device_id)) } _ => Err(DehydrationError::Json(serde_json::Error::custom(format!(