-
Notifications
You must be signed in to change notification settings - Fork 268
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
Conversation
I think this is basically ready (modulo merge conflicts). Some notes:
|
Can't we enforce this in the decryption method?
Well we know if a key was received directly via the enum KeySource {
Direct
Backup { authenticated: bool },
OldStyleImport,
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks sane. I think the biggest improvement would be to have a very descriptive enum where we can also properly document what each of the variants means.
@@ -161,10 +164,16 @@ impl BackupRecoveryKey { | |||
pub fn decrypt_v1( | |||
&self, | |||
ephemeral_key: String, | |||
mac: String, | |||
macv1: Option<String>, | |||
macv2: Option<String>, |
There was a problem hiding this comment.
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:
matrix-rust-sdk/crates/matrix-sdk-crypto/src/backups/keys/decryption.rs
Lines 225 to 242 in 2f41fe1
/// 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) | |
} |
@@ -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]>; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
I was planning on doing that, but the decryption function |
I've made all the requested changes except for:
The account pickle format has changed, so I think we need to handle upgrading, but I'm not sure how that's supposed to be done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The functionality is mostly there, but I have some questions.
/// or if it was imported. | ||
pub imported: bool, | ||
/// Where we obtained the group session from. | ||
pub key_source: KeySource, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do we migrate data?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've implemented migration for here. How do we migrate in the FFI part? Do we just replace imported
with key_source
in PickledInboundGroupSession
and leave it up to the app to migrate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be mostly ready now. Just a few minor questions.
@@ -47,6 +47,7 @@ pbkdf2 = { version = "0.12.2", default-features = false } | |||
rand = { workspace = true } | |||
rmp-serde = "1.1.1" | |||
ruma = { workspace = true, features = ["rand", "canonical-json", "unstable-msc3814"] } | |||
ruma-client-api = { workspace = true, features = ["unstable-msc4048"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this should be behind a feature flag, but how do I make a feature flag here enable the unstable-msc4048
feature flag in Ruma?
// FIXME: should we be passing the entire `unsigned` field? Also, how do | ||
// we handle other properties? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we be passing the entire unsigned
field? Also, how do we handle other properties?
/// or if it was imported. | ||
pub imported: bool, | ||
/// Where we obtained the group session from. | ||
pub key_source: KeySource, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've implemented migration for here. How do we migrate in the FFI part? Do we just replace imported
with key_source
in PickledInboundGroupSession
and leave it up to the app to migrate?
pub fn decrypt_session_data( | ||
&self, | ||
session_data: EncryptedSessionData, | ||
) -> Result<BackedUpRoomKey, PkDecryptionError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler complains here about:
error[E0277]: the trait bound `BackedUpRoomKey: LowerReturn<UniFfiTag>` is not satisfied
--> bindings/matrix-sdk-crypto-ffi/src/backup_recovery_key.rs:88:1
|
88 | #[uniffi::export]
| ^^^^^^^^^^^^^^^^^ the trait `LowerReturn<UniFfiTag>` is not implemented for `BackedUpRoomKey`
|
= help: the following other types implement trait `LowerReturn<UT>`:
<bool as LowerReturn<UT>>
<i8 as LowerReturn<UT>>
<i16 as LowerReturn<UT>>
<i32 as LowerReturn<UT>>
<i64 as LowerReturn<UT>>
<u8 as LowerReturn<UT>>
<u16 as LowerReturn<UT>>
<u32 as LowerReturn<UT>>
and 59 others
= note: required for `Result<BackedUpRoomKey, PkDecryptionError>` to implement `LowerReturn<UniFfiTag>`
= note: this error originates in the attribute macro `uniffi::export` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `BackedUpRoomKey: LowerReturn<UniFfiTag>` is not satisfied
--> bindings/matrix-sdk-crypto-ffi/src/backup_recovery_key.rs:210:10
|
210 | ) -> Result<BackedUpRoomKey, PkDecryptionError> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `LowerReturn<UniFfiTag>` is not implemented for `BackedUpRoomKey`
|
= help: the following other types implement trait `LowerReturn<UT>`:
<bool as LowerReturn<UT>>
<i8 as LowerReturn<UT>>
<i16 as LowerReturn<UT>>
<i32 as LowerReturn<UT>>
<i64 as LowerReturn<UT>>
<u8 as LowerReturn<UT>>
<u16 as LowerReturn<UT>>
<u32 as LowerReturn<UT>>
and 59 others
= note: required for `Result<BackedUpRoomKey, PkDecryptionError>` to implement `LowerReturn<UniFfiTag>`
I'm not sure what's the best way to fix it. Does BackedUpRoomKey
need to implement some trait? Or get wrapped in something?
Closing since this has been put on the back-burner for now. |
Initial implementation for authenticated backups (MSC4048)
Requires changes to Ruma: ruma/ruma#1677 (but needs to be updated to use unstable prefix)
Only touches matrix-sdk-crypto so far.