CVE-2024-34353
Description
The matrix-sdk-crypto crate, part of the Matrix Rust SDK project, is an implementation of a Matrix end-to-end encryption state machine in Rust. In Matrix, the server-side key backup stores encrypted copies of Matrix message keys. This facilitates key sharing between a user's devices and provides a redundant copy in case all devices are lost. The key backup uses asymmetric cryptography, with each server-side key backup assigned a unique public-private key pair. Due to a logic bug introduced in commit 71136e44c03c79f80d6d1a2446673bc4d53a2067, matrix-sdk-crypto version 0.7.0 will sometimes log the private part of the backup key pair to Rust debug logs (using the tracing crate). This issue has been resolved in matrix-sdk-crypto version 0.7.1. No known workarounds are available.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
matrix-sdk-cryptocrates.io | >= 0.7.0, < 0.7.1 | 0.7.1 |
Patches
3fa10bbb5dd0ffix(crypto): Avoid incorrect usage of private backup key
2 files changed · +105 −8
crates/matrix-sdk-crypto/src/backups/mod.rs+33 −2 modified@@ -626,8 +626,10 @@ mod tests { use serde_json::json; use crate::{ - olm::BackedUpRoomKey, store::BackupDecryptionKey, types::RoomKeyBackupInfo, OlmError, - OlmMachine, + olm::BackedUpRoomKey, + store::{BackupDecryptionKey, Changes, CryptoStore, MemoryStore}, + types::RoomKeyBackupInfo, + OlmError, OlmMachine, }; fn room_key() -> BackedUpRoomKey { @@ -850,4 +852,33 @@ mod tests { assert!(result.trusted()); } + + #[async_test] + async fn test_fix_backup_key_mismatch() { + let store = MemoryStore::new(); + + let backup_decryption_key = BackupDecryptionKey::new().unwrap(); + + store + .save_changes(Changes { + backup_decryption_key: Some(backup_decryption_key.clone()), + backup_version: Some("1".to_owned()), + ..Default::default() + }) + .await + .unwrap(); + + // Create the machine using `with_store` and without a call to enable_backup_v1, + // like regenerate_olm would do + let alice = OlmMachine::with_store(alice_id(), alice_device_id(), store).await.unwrap(); + + let binding = alice.backup_machine().backup_key.read().await; + let machine_backup_key = binding.as_ref().unwrap(); + + assert_eq!( + machine_backup_key.to_base64(), + backup_decryption_key.megolm_v1_public_key().to_base64(), + "The OlmMachine loaded the wrong backup key." + ); + } }
crates/matrix-sdk-crypto/src/machine.rs+72 −6 modified@@ -313,13 +313,16 @@ impl OlmMachine { } }; + // FIXME: This is a workaround for `regenerate_olm` clearing the backup + // state. Ideally, backups should not get automatically enabled since + // the `OlmMachine` doesn't get enough info from the homeserver for this + // to work reliably. let saved_keys = store.load_backup_keys().await?; let maybe_backup_key = saved_keys.decryption_key.and_then(|k| { if let Some(version) = saved_keys.backup_version { - MegolmV1BackupKey::from_base64(&k.to_base64()).ok().map(|k| { - k.set_version(version); - k - }) + let megolm_v1_backup_key = k.megolm_v1_public_key(); + megolm_v1_backup_key.set_version(version); + Some(megolm_v1_backup_key) } else { None } @@ -2169,8 +2172,10 @@ pub(crate) mod tests { use crate::{ error::EventError, machine::{EncryptionSyncChanges, OlmMachine}, - olm::{InboundGroupSession, OutboundGroupSession, VerifyJson}, - store::Changes, + olm::{ + BackedUpRoomKey, ExportedRoomKey, InboundGroupSession, OutboundGroupSession, VerifyJson, + }, + store::{BackupDecryptionKey, Changes, CryptoStore, MemoryStore, RoomSettings}, types::{ events::{ room::encrypted::{EncryptedToDeviceEvent, ToDeviceEncryptedEventContent}, @@ -4071,4 +4076,65 @@ pub(crate) mod tests { // The waiting should successfully complete. wait.await.unwrap(); } + + #[async_test] + async fn test_fix_incorrect_usage_of_backup_key_causing_decryption_errors() { + let store = MemoryStore::new(); + + let backup_decryption_key = BackupDecryptionKey::new().unwrap(); + + store + .save_changes(Changes { + backup_decryption_key: Some(backup_decryption_key.clone()), + backup_version: Some("1".to_owned()), + ..Default::default() + }) + .await + .unwrap(); + + // Some valid key data + let data = json!({ + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!room:id", + "sender_key": "FOvlmz18LLI3k/llCpqRoKT90+gFF8YhuL+v1YBXHlw", + "session_id": "/2K+V777vipCxPZ0gpY9qcpz1DYaXwuMRIu0UEP0Wa0", + "session_key": "AQAAAAAclzWVMeWBKH+B/WMowa3rb4ma3jEl6n5W4GCs9ue65CruzD3ihX+85pZ9hsV9Bf6fvhjp76WNRajoJYX0UIt7aosjmu0i+H+07hEQ0zqTKpVoSH0ykJ6stAMhdr6Q4uW5crBmdTTBIsqmoWsNJZKKoE2+ldYrZ1lrFeaJbjBIY/9ivle++74qQsT2dIKWPanKc9Q2Gl8LjESLtFBD9Fmt", + "sender_claimed_keys": { + "ed25519": "F4P7f1Z0RjbiZMgHk1xBCG3KC4/Ng9PmxLJ4hQ13sHA" + }, + "forwarding_curve25519_key_chain": ["DBPC2zr6c9qimo9YRFK3RVr0Two/I6ODb9mbsToZN3Q", "bBc/qzZFOOKshMMT+i4gjS/gWPDoKfGmETs9yfw9430"] + }); + + let backed_up_room_key: BackedUpRoomKey = serde_json::from_value(data).unwrap(); + + // Create the machine using `with_store` and without a call to enable_backup_v1, + // like regenerate_olm would do + let alice = OlmMachine::with_store(user_id(), alice_device_id(), store).await.unwrap(); + + let exported_key = ExportedRoomKey::from_backed_up_room_key( + room_id!("!room:id").to_owned(), + "/2K+V777vipCxPZ0gpY9qcpz1DYaXwuMRIu0UEP0Wa0".into(), + backed_up_room_key, + ); + + alice.store().import_exported_room_keys(vec![exported_key], |_, _| {}).await.unwrap(); + + let (_, request) = alice.backup_machine().backup().await.unwrap().unwrap(); + + let key_backup_data = request.rooms[&room_id!("!room:id").to_owned()] + .sessions + .get("/2K+V777vipCxPZ0gpY9qcpz1DYaXwuMRIu0UEP0Wa0") + .unwrap() + .deserialize() + .unwrap(); + + let ephemeral = key_backup_data.session_data.ephemeral.encode(); + let ciphertext = key_backup_data.session_data.ciphertext.encode(); + let mac = key_backup_data.session_data.mac.encode(); + + // Prior to the fix for GHSA-9ggc-845v-gcgv, this would produce a `Mac(MacError)` + backup_decryption_key + .decrypt_v1(&ephemeral, &mac, &ciphertext) + .expect("The backed up key should be decrypted successfully"); + } }
71136e44c03cFix regenerate_olm_machine loosing backup state
1 file changed · +27 −0
crates/matrix-sdk/src/encryption/mod.rs+27 −0 modified@@ -1180,6 +1180,10 @@ impl Encryption { drop(olm_machine_guard); // Recreate the OlmMachine. self.client.base_client().regenerate_olm().await?; + // we need to trigger that so it gets back the cached key if known + // otherwise the new olm machine `BackupMachine#backup_key` will be out of sync + // and say backup is disabled. + self.client.encryption().backups().setup_and_resume().await?; } } Ok(()) @@ -1452,6 +1456,21 @@ mod tests { let initial_olm_machine = client1.olm_machine().await.clone().expect("must have an olm machine"); + // Also enable backup to check that new machine has the same backup keys. + let decryption_key = matrix_sdk_base::crypto::store::BackupDecryptionKey::new() + .expect("Can't create new recovery key"); + let backup_key = decryption_key.megolm_v1_public_key(); + backup_key.set_version("1".to_owned()); + initial_olm_machine + .backup_machine() + .save_decryption_key(Some(decryption_key.to_owned()), Some("1".to_owned())) + .await + .expect("Should save"); + + initial_olm_machine.backup_machine().enable_backup_v1(backup_key.clone()).await.unwrap(); + + assert!(client1.encryption().backups().are_enabled().await); + // The other client can't take the lock too. let acquired2 = client2.encryption().try_lock_store_once().await.unwrap(); assert!(acquired2.is_none()); @@ -1486,7 +1505,15 @@ mod tests { // But now its olm machine has been invalidated and thus regenerated! let olm_machine = client1.olm_machine().await.clone().expect("must have an olm machine"); + + let backup_key_new = olm_machine.backup_machine().get_backup_keys().await.unwrap(); assert!(!initial_olm_machine.same_as(&olm_machine)); + assert!(backup_key_new.decryption_key.is_some()); + assert_eq!( + backup_key_new.decryption_key.unwrap().megolm_v1_public_key().to_base64(), + backup_key.to_base64() + ); + assert!(client1.encryption().backups().are_enabled().await); } #[cfg(feature = "sqlite")]
c99f66576689Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-9ggc-845v-gcgvghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-34353ghsaADVISORY
- github.com/matrix-org/matrix-rust-sdk/commit/71136e44c03c79f80d6d1a2446673bc4d53a2067nvdWEB
- github.com/matrix-org/matrix-rust-sdk/commit/fa10bbb5dd0f9120a51aa1854cec752e25790bb0nvdWEB
- github.com/matrix-org/matrix-rust-sdk/releases/tag/matrix-sdk-crypto-0.7.1nvdWEB
- github.com/matrix-org/matrix-rust-sdk/security/advisories/GHSA-9ggc-845v-gcgvnvdWEB
- crates.io/crates/matrix-sdk-crypto/0.7.1nvd
News mentions
0No linked articles in our index yet.