Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: Authenticated backup #2659

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ futures-executor = "0.3.21"
futures-util = { version = "0.3.26", default-features = false, features = ["alloc"] }
http = "0.2.6"
itertools = "0.11.0"
ruma = { version = "0.9.0", features = ["client-api-c", "compat-upload-signatures", "compat-user-id", "compat-arbitrary-length-ids"] }
ruma-common = "0.12.0"
ruma = { git = "https://github.com/uhoreg/ruma", rev = "a1034ef3774d8d1d7486c5ef57df87cac297ceda", features = ["client-api-c", "compat-upload-signatures", "compat-user-id"] }
ruma-common = { git = "https://github.com/uhoreg/ruma", rev = "a1034ef3774d8d1d7486c5ef57df87cac297ceda" }
once_cell = "1.16.0"
serde = "1.0.151"
serde_html_form = "0.2.0"
Expand Down
15 changes: 12 additions & 3 deletions bindings/matrix-sdk-crypto-ffi/src/backup_recovery_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub struct BackupRecoveryKey {
#[derive(Debug, Error, uniffi::Error)]
#[uniffi(flat_error)]
pub enum PkDecryptionError {
/// Invalid format
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
#[error("Invalid format")]
Format(),
/// An internal libolm error happened during decryption.
#[error("Error decryption a PkMessage {0}")]
Olm(#[from] DecryptionError),
Expand Down Expand Up @@ -161,10 +164,16 @@ impl BackupRecoveryKey {
pub fn decrypt_v1(
&self,
ephemeral_key: String,
mac: String,
macv1: Option<String>,
macv2: Option<String>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really a fan of this function signature, wasn't a fan before this PR either.

I added a different one here:

/// Try to decrypt the given session data using this
/// [`BackupDecryptionKey`].
pub fn decrypt_session_data(
&self,
session_data: EncryptedSessionData,
) -> Result<Vec<u8>, DecryptionError> {
let message = Message {
ciphertext: session_data.ciphertext.into_inner(),
mac: session_data.mac.into_inner(),
// TODO: Remove the unwrap.
ephemeral_key: Curve25519PublicKey::from_slice(session_data.ephemeral.as_bytes())
.unwrap(),
};
let pk = self.get_pk_decryption();
pk.decrypt(&message)
}
.

ciphertext: String,
) -> Result<String, PkDecryptionError> {
self.inner.decrypt_v1(&ephemeral_key, &mac, &ciphertext).map_err(|e| e.into())
let mac = match (macv1, macv2) {
(_, Some(macv2)) => Ok(Mac::V2(&macv2)),
(Some(macv1), _) => Ok(Mac::V1(&macv1)),
(None, None) => Err(PkDecryptionError::Format),
}?;
self.inner.decrypt_v1(&ephemeral_key, mac, &ciphertext).map_err(|e| e.into())
}
}

Expand Down Expand Up @@ -208,7 +217,7 @@ mod tests {
let mac = key_backup_data.session_data.mac.encode();

let _ = recovery_key
.decrypt_v1(ephemeral, mac, ciphertext)
.decrypt_v1(ephemeral, Some(mac), None, ciphertext)
.expect("The backed up key should be decrypted successfully");
}
}
65 changes: 62 additions & 3 deletions crates/matrix-sdk-crypto/src/backups/keys/backup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use std::{
sync::{Arc, Mutex},
};

use hmac::{Hmac, Mac as MacT};
use sha2::Sha256;

use ruma::{
api::client::backup::{EncryptedSessionDataInit, KeyBackupData, KeyBackupDataInit},
serde::Base64,
Expand All @@ -28,9 +31,14 @@ use zeroize::Zeroizing;
use super::{compat::PkEncryption, decryption::DecodeError};
use crate::olm::InboundGroupSession;

pub type HmacSha256 = Hmac<Sha256>;

pub type HmacSha256Key = Zeroizing<[u8; 32]>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use Box<[u8; 32]> for this. And add derive Zeroize and ZeroizeOnDrop on the container type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I do that, and pass the Box[<u8; 32]> as a function argument, as in MegolmV1BackupKey::new, then it will automagically do the right thing when it becomes part of the struct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If by right thing you mean that it will zeroize the Box<[u8; 32]> when the whole struct gets dropped, then yes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried deriving Zeroize and ZeroizeOnDrop on the container type, which is InnerBackupKey

pub type HmacSha256Key = Box<[u8; 32]>;

#[derive(Debug, Zeroize, ZeroizeOnDrop)]
struct InnerBackupKey {
    key: Curve25519PublicKey,
    mac_key: Option<HmacSha256Key>,
    signatures: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, String>>,
    version: Mutex<Option<String>>,
}

but that gives me errors:

error[E0599]: the method `zeroize` exists for mutable reference `&mut Curve25519PublicKey`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:17
    |
38  | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                 ^^^^^^^ method cannot be called on `&mut Curve25519PublicKey` due to unsatisfied trait bounds
    |
   ::: /home/hubert/.cargo/registry/src/index.crates.io-6f17d22bba15001f/vodozemac-0.5.0/src/types/curve25519.rs:110:1
    |
110 | pub struct Curve25519PublicKey {
    | ------------------------------
    | |
    | doesn't satisfy `Curve25519PublicKey: DefaultIsZeroes`
    | doesn't satisfy `Curve25519PublicKey: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `Curve25519PublicKey: DefaultIsZeroes`
            which is required by `Curve25519PublicKey: Zeroize`
            `&mut Curve25519PublicKey: DefaultIsZeroes`
            which is required by `&mut Curve25519PublicKey: Zeroize`
    = note: this error originates in the derive macro `Zeroize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize` exists for mutable reference `&mut Option<Box<[u8; 32]>>`, but its trait bounds were not satisfied
  --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:17
   |
38 | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
   |                 ^^^^^^^ method cannot be called on `&mut Option<Box<[u8; 32]>>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `&mut std::option::Option<Box<[u8; 32]>>: DefaultIsZeroes`
           which is required by `&mut std::option::Option<Box<[u8; 32]>>: Zeroize`
   = note: this error originates in the derive macro `Zeroize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize` exists for mutable reference `&mut BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, String>>`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:17
    |
38  |   #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                   ^^^^^^^ method cannot be called due to unsatisfied trait bounds
    |
   ::: /home/hubert/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/collections/btree/map.rs:172:1
    |
172 | / pub struct BTreeMap<
173 | |     K,
174 | |     V,
175 | |     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global,
176 | | > {
    | | -
    | | |
    | |_doesn't satisfy `_: DefaultIsZeroes`
    |   doesn't satisfy `_: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: DefaultIsZeroes`
            which is required by `BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: Zeroize`
            `&mut BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: DefaultIsZeroes`
            which is required by `&mut BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: Zeroize`
    = note: this error originates in the derive macro `Zeroize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize` exists for mutable reference `&mut Mutex<Option<String>>`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:17
    |
38  | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                 ^^^^^^^ method cannot be called on `&mut Mutex<Option<String>>` due to unsatisfied trait bounds
    |
   ::: /home/hubert/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sync/mutex.rs:175:1
    |
175 | pub struct Mutex<T: ?Sized> {
    | ---------------------------
    | |
    | doesn't satisfy `_: DefaultIsZeroes`
    | doesn't satisfy `_: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `std::sync::Mutex<std::option::Option<std::string::String>>: DefaultIsZeroes`
            which is required by `std::sync::Mutex<std::option::Option<std::string::String>>: Zeroize`
            `&mut std::sync::Mutex<std::option::Option<std::string::String>>: DefaultIsZeroes`
            which is required by `&mut std::sync::Mutex<std::option::Option<std::string::String>>: Zeroize`
    = note: this error originates in the derive macro `Zeroize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize_or_on_drop` exists for mutable reference `&mut Curve25519PublicKey`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:26
    |
38  | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                          ^^^^^^^^^^^^^ method cannot be called on `&mut Curve25519PublicKey` due to unsatisfied trait bounds
    |
   ::: /home/hubert/.cargo/registry/src/index.crates.io-6f17d22bba15001f/vodozemac-0.5.0/src/types/curve25519.rs:110:1
    |
110 | pub struct Curve25519PublicKey {
    | ------------------------------
    | |
    | doesn't satisfy `Curve25519PublicKey: AssertZeroize`
    | doesn't satisfy `Curve25519PublicKey: ZeroizeOnDrop`
    | doesn't satisfy `Curve25519PublicKey: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `Curve25519PublicKey: Zeroize`
            which is required by `Curve25519PublicKey: AssertZeroize`
            `Curve25519PublicKey: ZeroizeOnDrop`
            which is required by `&&mut Curve25519PublicKey: AssertZeroizeOnDrop`
            `&mut Curve25519PublicKey: Zeroize`
            which is required by `&mut Curve25519PublicKey: AssertZeroize`
    = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize_or_on_drop` exists for mutable reference `&mut Option<Box<[u8; 32]>>`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:26
    |
38  | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                          ^^^^^^^^^^^^^ method cannot be called on `&mut Option<Box<[u8; 32]>>` due to unsatisfied trait bounds
    |
   ::: /home/hubert/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:563:1
    |
563 | pub enum Option<T> {
    | ------------------
    | |
    | doesn't satisfy `std::option::Option<Box<[u8; 32]>>: AssertZeroize`
    | doesn't satisfy `std::option::Option<Box<[u8; 32]>>: ZeroizeOnDrop`
    | doesn't satisfy `std::option::Option<Box<[u8; 32]>>: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `std::option::Option<Box<[u8; 32]>>: Zeroize`
            which is required by `std::option::Option<Box<[u8; 32]>>: AssertZeroize`
            `std::option::Option<Box<[u8; 32]>>: ZeroizeOnDrop`
            which is required by `&&mut std::option::Option<Box<[u8; 32]>>: AssertZeroizeOnDrop`
            `&mut std::option::Option<Box<[u8; 32]>>: Zeroize`
            which is required by `&mut std::option::Option<Box<[u8; 32]>>: AssertZeroize`
    = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize_or_on_drop` exists for mutable reference `&mut BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, String>>`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:26
    |
38  |   #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                            ^^^^^^^^^^^^^ method cannot be called due to unsatisfied trait bounds
    |
   ::: /home/hubert/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/collections/btree/map.rs:172:1
    |
172 | / pub struct BTreeMap<
173 | |     K,
174 | |     V,
175 | |     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global,
176 | | > {
    | | -
    | | |
    | | doesn't satisfy `_: AssertZeroize`
    | |_doesn't satisfy `_: ZeroizeOnDrop`
    |   doesn't satisfy `_: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: Zeroize`
            which is required by `BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: AssertZeroize`
            `BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: ZeroizeOnDrop`
            which is required by `&&mut BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: AssertZeroizeOnDrop`
            `&mut BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: Zeroize`
            which is required by `&mut BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, std::string::String>>: AssertZeroize`
    = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize_or_on_drop` exists for mutable reference `&mut Mutex<Option<String>>`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:38:26
    |
38  | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                          ^^^^^^^^^^^^^ method cannot be called on `&mut Mutex<Option<String>>` due to unsatisfied trait bounds
    |
   ::: /home/hubert/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sync/mutex.rs:175:1
    |
175 | pub struct Mutex<T: ?Sized> {
    | ---------------------------
    | |
    | doesn't satisfy `_: AssertZeroize`
    | doesn't satisfy `_: ZeroizeOnDrop`
    | doesn't satisfy `_: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `std::sync::Mutex<std::option::Option<std::string::String>>: Zeroize`
            which is required by `std::sync::Mutex<std::option::Option<std::string::String>>: AssertZeroize`
            `std::sync::Mutex<std::option::Option<std::string::String>>: ZeroizeOnDrop`
            which is required by `&&mut std::sync::Mutex<std::option::Option<std::string::String>>: AssertZeroizeOnDrop`
            `&mut std::sync::Mutex<std::option::Option<std::string::String>>: Zeroize`
            which is required by `&mut std::sync::Mutex<std::option::Option<std::string::String>>: AssertZeroize`
    = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0599`.

Should I do something else, or is there a way to fix this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error complains that not all the types in the fields of the struct implement Zeroize. But this is completely fine since only one field contains private key material which should be zeroized.

You can make the compiler happy by implementing Zeroize manually, or use the #[zeroize(skip) attribute on the fields which don't need to be zeroized, more info about this macro can be found in the zeroize crate docs: https://docs.rs/zeroize/latest/zeroize/index.html#custom-derive-support

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did

pub type HmacSha256Key = Box<[u8; 32]>;

#[derive(Debug, Zeroize, ZeroizeOnDrop)]
struct InnerBackupKey {
    #[zeroize(skip)]
    key: Curve25519PublicKey,
    mac_key: Option<HmacSha256Key>,
    #[zeroize(skip)]
    signatures: Signatures,
    #[zeroize(skip)]
    version: Mutex<Option<String>>,
}

but I get

error[E0599]: the method `zeroize` exists for mutable reference `&mut Option<Box<[u8; 32]>>`, but its trait bounds were not satisfied
  --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:34:17
   |
34 | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
   |                 ^^^^^^^ method cannot be called on `&mut Option<Box<[u8; 32]>>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `&mut std::option::Option<Box<[u8; 32]>>: DefaultIsZeroes`
           which is required by `&mut std::option::Option<Box<[u8; 32]>>: Zeroize`
   = note: this error originates in the derive macro `Zeroize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: the method `zeroize_or_on_drop` exists for mutable reference `&mut Option<Box<[u8; 32]>>`, but its trait bounds were not satisfied
   --> crates/matrix-sdk-crypto/src/backups/keys/backup.rs:34:26
    |
34  | #[derive(Debug, Zeroize, ZeroizeOnDrop)]
    |                          ^^^^^^^^^^^^^ method cannot be called on `&mut Option<Box<[u8; 32]>>` due to unsatisfied trait bounds
    |
   ::: /home/hubert/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:563:1
    |
563 | pub enum Option<T> {
    | ------------------
    | |
    | doesn't satisfy `std::option::Option<Box<[u8; 32]>>: AssertZeroize`
    | doesn't satisfy `std::option::Option<Box<[u8; 32]>>: ZeroizeOnDrop`
    | doesn't satisfy `std::option::Option<Box<[u8; 32]>>: Zeroize`
    |
    = note: the following trait bounds were not satisfied:
            `std::option::Option<Box<[u8; 32]>>: Zeroize`
            which is required by `std::option::Option<Box<[u8; 32]>>: AssertZeroize`
            `std::option::Option<Box<[u8; 32]>>: ZeroizeOnDrop`
            which is required by `&&mut std::option::Option<Box<[u8; 32]>>: AssertZeroizeOnDrop`
            `&mut std::option::Option<Box<[u8; 32]>>: Zeroize`
            which is required by `&mut std::option::Option<Box<[u8; 32]>>: AssertZeroize`
    = note: this error originates in the derive macro `ZeroizeOnDrop` (in Nightly builds, run with -Z macro-backtrace for more info)

So it seems like zeroize doesn't like Option. One solution that I can think of is to make mac_key: HmacSha256Key, so it always has some sort of key stored, but add a boolean flag that indicates whether the key is actually used. But that doesn't seem very Rust-y to me. Are there any better solutions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the Option<T> per se, it's the Option<Box<T>>, not quite sure why that would be since zeroizing implements Zeroize for both Option<T> and Box<T>. Alright, let's stick to the Zeroizing thing since this seems to be too annoying.


#[derive(Debug)]
struct InnerBackupKey {
key: Curve25519PublicKey,
mac_key: Option<HmacSha256Key>,
signatures: BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceKeyId, String>>,
version: Mutex<Option<String>>,
}
Expand All @@ -52,10 +60,11 @@ impl std::fmt::Debug for MegolmV1BackupKey {
}

impl MegolmV1BackupKey {
pub(super) fn new(key: Curve25519PublicKey, version: Option<String>) -> Self {
pub(super) fn new(key: Curve25519PublicKey, mac_key: Option<HmacSha256Key>, version: Option<String>) -> Self {
Self {
inner: InnerBackupKey {
key,
mac_key,
signatures: Default::default(),
version: Mutex::new(version),
}
Expand All @@ -78,7 +87,7 @@ impl MegolmV1BackupKey {
let key = Curve25519PublicKey::from_base64(public_key)?;

let inner =
InnerBackupKey { key, signatures: Default::default(), version: Mutex::new(None) };
InnerBackupKey { key, mac_key: None, signatures: Default::default(), version: Mutex::new(None) };

Ok(MegolmV1BackupKey { inner: inner.into() })
}
Expand All @@ -93,6 +102,11 @@ impl MegolmV1BackupKey {
self.inner.version.lock().unwrap().clone()
}

/// Get the MAC key that is used with the backup key
pub fn mac_key(&self) -> &Option<HmacSha256Key> {
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
&self.inner.mac_key
}

/// Set the backup version that this `MegolmV1BackupKey` will be used with.
///
/// The key won't be able to encrypt room keys unless a version has been
Expand All @@ -118,11 +132,21 @@ impl MegolmV1BackupKey {
Zeroizing::new(serde_json::to_vec(&key).expect("Can't serialize exported room key"));

let message = pk.encrypt(&key);
let mac2 = if let Some(mac_key) = self.mac_key() {
let mut hmac = HmacSha256::new_from_slice(mac_key.as_ref())
.expect("We should be able to create a Hmac object from a 32 byte key");
hmac.update(&message.ciphertext);
let mac: Base64 = Base64::new(hmac.finalize().into_bytes().to_vec());
Some(mac)
} else {
None
};

let session_data = EncryptedSessionDataInit {
ephemeral: Base64::new(message.ephemeral_key.to_vec()),
ciphertext: Base64::new(message.ciphertext),
mac: Base64::new(message.mac),
mac: Some(Base64::new(message.mac.unwrap())),
mac2,
}
.into();

Expand All @@ -139,3 +163,38 @@ impl MegolmV1BackupKey {
.into()
}
}

#[cfg(test)]
mod tests {
use matrix_sdk_test::async_test;
use ruma::{device_id, room_id, user_id};
use crate::{
backups::keys::decryption::Mac,
store::BackupDecryptionKey,
EncryptionSettings,
OlmMachine,
};

#[async_test]
async fn create_mac2() -> Result<(), ()> {
let decryption_key = BackupDecryptionKey::new().expect("Can't create new recovery key");

let backup_key = decryption_key.megolm_v1_public_key();

let olm_machine = OlmMachine::new(user_id!("@alice:localhost"), device_id!("ABCDEFG")).await;
let settings = EncryptionSettings::default();
let (_, inbound) = olm_machine.account().create_group_session_pair(room_id!("!room_id:localhost"), settings)
.await
.expect("Could not create group session");
let key_backup_data = backup_key.encrypt(inbound).await;
let ephemeral = key_backup_data.session_data.ephemeral.encode();
let ciphertext = key_backup_data.session_data.ciphertext.encode();
let mac2 = key_backup_data.session_data.mac2.unwrap().encode();

let _ = decryption_key
.decrypt_v1(&ephemeral, Mac::V2(&mac2), &ciphertext)
.expect("The backed up key should be decrypted successfully");

Ok(())
}
}
18 changes: 10 additions & 8 deletions crates/matrix-sdk-crypto/src/backups/keys/compat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,10 @@ impl PkDecryption {
let cipher = Aes256CbcDec::new(keys.aes_key(), keys.iv());
let decrypted = cipher.decrypt_padded_vec_mut::<Pkcs7>(&message.ciphertext)?;

let hmac = keys.hmac();
hmac.verify_truncated_left(&message.mac)?;
if let Some(mac) = message.mac.as_ref() {
let hmac = keys.hmac();
hmac.verify_truncated_left(mac)?;
}

Ok(decrypted)
}
Expand Down Expand Up @@ -162,7 +164,7 @@ impl PkEncryption {
let mut mac = hmac.finalize().into_bytes().to_vec();
mac.truncate(MAC_LENGTH);

Message { ciphertext, mac, ephemeral_key: Curve25519PublicKey::from(&ephemeral_key) }
Message { ciphertext, mac: Some(mac), ephemeral_key: Curve25519PublicKey::from(&ephemeral_key) }
}
}

Expand All @@ -177,19 +179,19 @@ pub enum MessageDecodeError {
#[derive(Debug)]
pub struct Message {
pub ciphertext: Vec<u8>,
pub mac: Vec<u8>,
pub mac: Option<Vec<u8>>,
pub ephemeral_key: Curve25519PublicKey,
}

impl Message {
pub fn from_base64(
ciphertext: &str,
mac: &str,
mac: Option<&str>,
ephemeral_key: &str,
) -> Result<Self, MessageDecodeError> {
Ok(Self {
ciphertext: base64_decode(ciphertext)?,
mac: base64_decode(mac)?,
mac: mac.map(|m| base64_decode(m)).transpose()?,
ephemeral_key: Curve25519PublicKey::from_base64(ephemeral_key)?,
})
}
Expand Down Expand Up @@ -220,15 +222,15 @@ mod tests {
type Error = MessageDecodeError;

fn try_from(value: PkMessage) -> Result<Self, Self::Error> {
Self::from_base64(&value.ciphertext, &value.mac, &value.ephemeral_key)
Self::from_base64(&value.ciphertext, Some(&value.mac), &value.ephemeral_key)
}
}

impl From<Message> for PkMessage {
fn from(val: Message) -> Self {
PkMessage {
ciphertext: base64_encode(val.ciphertext),
mac: base64_encode(val.mac),
mac: base64_encode(val.mac.unwrap()),
ephemeral_key: val.ephemeral_key.to_base64(),
}
}
Expand Down
Loading