VYPR
Medium severity5.5NVD Advisory· Published May 14, 2024· Updated Apr 15, 2026

CVE-2024-34353

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.

PackageAffected versionsPatched versions
matrix-sdk-cryptocrates.io
>= 0.7.0, < 0.7.10.7.1

Patches

3
fa10bbb5dd0f

fix(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");
    +    }
     }
    
71136e44c03c

Fix 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")]
    

Vulnerability 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

News mentions

0

No linked articles in our index yet.