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

Reshare Megolm session after the other party unwedges #3604

Merged
merged 13 commits into from
Jun 28, 2024

Conversation

uhoreg
Copy link
Member

@uhoreg uhoreg commented Jun 25, 2024

Fixes element-hq/element-meta#2389

For each recipient device, we keep an "Olm wedging index" that indicates (approximately) how many times they tried to unwedge the Olm session. When we share a Megolm session, we store the Olm wedging index. This allows us to tell whether the Olm session was unwedged since we shared the Megolm session, so that we know if we should re-share it.

We detect an attempt to unwedge the Olm session by checking if the Olm message that we decrypted is from a brand new Olm session. This is not entirely accurate, since this could happen, for example, if Alice and Bob create Olm sessions at the same time. This may result in some Megolm sessions being re-shared unnecessarily, but should not make a huge difference.

Copy link

codecov bot commented Jun 25, 2024

Codecov Report

Attention: Patch coverage is 87.87879% with 4 lines in your changes missing coverage. Please review.

Project coverage is 84.18%. Comparing base (1c92633) to head (7273f28).

Files Patch % Lines
...trix-sdk-crypto/src/olm/group_sessions/outbound.rs 75.00% 3 Missing ⚠️
...k-crypto/src/session_manager/group_sessions/mod.rs 80.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3604   +/-   ##
=======================================
  Coverage   84.17%   84.18%           
=======================================
  Files         255      255           
  Lines       26515    26530   +15     
=======================================
+ Hits        22319    22334   +15     
  Misses       4196     4196           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@poljar poljar requested review from poljar and removed request for bnjbvr June 25, 2024 09:18
Copy link
Contributor

@poljar poljar left a comment

Choose a reason for hiding this comment

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

Quite the clever implementation, nice. I left some smaller nits we should address before we merge.

@@ -69,7 +69,7 @@ const ROTATION_MESSAGES: u64 = 100;
pub(crate) enum ShareState {
NotShared,
SharedButChangedSenderKey,
Shared(u32),
Shared(u32, u32),
Copy link
Contributor

Choose a reason for hiding this comment

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

This could take some docs, a single u32 could be figured out relatively easily, two of them double the confusion. It probably makes sense to convert the enum variant from a tuple variant to one with named fields, i.e.:

Shared {
    megolm_index: u32,
    wedging_foo: u32,
}

crates/matrix-sdk-crypto/src/olm/account.rs Show resolved Hide resolved
// we use the session. If we don't have their device, this
// means that we haven't tried to send them any Megolm sessions
// yet, so we don't need to worry about it.
if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
Copy link
Contributor

Choose a reason for hiding this comment

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

This section is clustered together as well, please add some whitespace to make this easier to read.

d.into_iter().filter(|d| match outbound.is_shared_with(d) {
ShareState::NotShared => true,
ShareState::Shared(_msg_index, olm_wedging_index) => {
// If the recipient device's Olm wedging index is
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be wrapped at a really small width, we wrap at 80 chars, cargo fmt will do this automatically for you if the lines are too long.

.await?;
let mut changes =
Changes { sessions: vec![result.session.clone()], ..Default::default() };
// Any new Olm session will bump the Olm wedging index for the
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be also wrapped manually, though it doesn't look as comical as the other doc comment, wouldn't hurt to let cargo fmt format this as well.

// yet, so we don't need to worry about it.
if let Some(device) = store.get_device_from_curve_key(sender, sender_key).await? {
let mut read_only_device = device.inner;
read_only_device.olm_wedging_index += 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Technically this can panic, what do you think about using our SequenceNumber type for this. Not that it's terribly important but since we have such a type, seems like we could just use it for correctness sake:

/// A numeric type that can represent an infinite ordered sequence.
///
/// It uses wrapping arithmetic to make sure we never run out of numbers. (2**64
/// should be enough for anyone, but it's easy enough just to make it wrap.)
//
/// Internally it uses a *signed* counter so that we can compare values via a
/// subtraction. For example, suppose we've just overflowed from i64::MAX to
/// i64::MIN. (i64::MAX.wrapping_sub(i64::MIN)) is -1, which tells us that
/// i64::MAX comes before i64::MIN in the sequence.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct SequenceNumber(i64);
impl Display for SequenceNumber {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl PartialOrd for SequenceNumber {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.0.wrapping_sub(other.0).cmp(&0))
}
}
impl Ord for SequenceNumber {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.wrapping_sub(other.0).cmp(&0)
}
}
impl SequenceNumber {
fn increment(&mut self) {
self.0 = self.0.wrapping_add(1)
}
fn previous(&self) -> Self {
Self(self.0.wrapping_sub(1))
}
}

You would need to derive Serialize and Deserialize for it with the transparent attribute, but that's about it.

@uhoreg uhoreg requested a review from poljar June 26, 2024 20:01
@uhoreg
Copy link
Member Author

uhoreg commented Jun 26, 2024

I think I've addressed all the review comments

@@ -70,7 +71,7 @@ const ROTATION_MESSAGES: u64 = 100;
pub(crate) enum ShareState {
NotShared,
SharedButChangedSenderKey,
Shared(u32),
Shared { message_index: u32, olm_wedging_index: SequenceNumber },
Copy link
Contributor

Choose a reason for hiding this comment

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

This still isn't documented.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, I misunderstood your original comment and thought that switching to named fields would avoid the need for documentation. I've added docs, and while I was there, added docs for the rest of the enum too.

Copy link
Contributor

@poljar poljar left a comment

Choose a reason for hiding this comment

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

Nice, thanks for adding the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ensure that existing megolm sessions are re-shared after a wedged olm session
2 participants