From 60a8b47ad0807392771231a0482da3acd2dfaa3a Mon Sep 17 00:00:00 2001 From: Alexander Krotov Date: Sat, 26 Sep 2020 02:15:42 +0300 Subject: [PATCH] e2ee: require quorum to enable encryption Previously, standard Autocrypt rule was used to determine whether encryption is enabled: if at least one recipient does not prefer encryption, encryption is disabled. This rule has been problematic in large groups, because the larger is the group, the higher is the chance that one of the users does not prefer encryption. New rule requires a majority of users to prefer encryption. Note that it does not affect 1:1 chats, because it is required that *strictly* more than a half users in a chat prefer encryption. --- python/tests/test_account.py | 1 - src/e2ee.rs | 40 ++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/python/tests/test_account.py b/python/tests/test_account.py index bc96b4c19..16df51a86 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -1017,7 +1017,6 @@ class TestOnlineAccount: assert msg_in.text == text2 assert ac1.get_config("addr") in [x.addr for x in msg_in.chat.get_contacts()] - @pytest.mark.xfail(reason="Quorum rule is not used yet") def test_prefer_encrypt(self, acfactory, lp): """Test quorum rule for encryption preference in 1:1 and group chat.""" ac1, ac2, ac3 = acfactory.get_many_online_accounts(3) diff --git a/src/e2ee.rs b/src/e2ee.rs index f6bfeec4a..0693c57d9 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -51,23 +51,41 @@ impl EncryptHelper { } /// Determines if we can and should encrypt. + /// + /// For encryption to be enabled, `e2ee_guaranteed` should be true, or strictly more than a half + /// of peerstates should prefer encryption. Own preference is counted equally to peer + /// preferences, even if message copy is not sent to self. + /// + /// `e2ee_guaranteed` should be set to true for replies to encrypted messages (as required by + /// Autocrypt Level 1, version 1.1) and for messages sent in verified groups. + /// + /// Returns an error if `e2ee_guaranteed` is true, but one or more keys are missing. + /// + /// Always returns `false` if one of the peerstates does not support Autocrypt (is in "reset" + /// state) or does not have a known key. pub fn should_encrypt( &self, context: &Context, e2ee_guaranteed: bool, peerstates: &[(Option, &str)], ) -> Result { - if !(self.prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed) { - return Ok(false); - } - + let mut prefer_encrypt_count = if self.prefer_encrypt == EncryptPreference::Mutual { + 1 + } else { + 0 + }; for (peerstate, addr) in peerstates { match peerstate { Some(peerstate) => { - if peerstate.prefer_encrypt != EncryptPreference::Mutual && !e2ee_guaranteed { - info!(context, "peerstate for {:?} is no-encrypt", addr); - return Ok(false); - } + info!( + context, + "peerstate for {:?} is {}", addr, peerstate.prefer_encrypt + ); + match peerstate.prefer_encrypt { + EncryptPreference::NoPreference => {} + EncryptPreference::Mutual => prefer_encrypt_count += 1, + EncryptPreference::Reset => return Ok(false), + }; } None => { let msg = format!("peerstate for {:?} missing, cannot encrypt", addr); @@ -81,7 +99,11 @@ impl EncryptHelper { } } - Ok(true) + // Count number of recipients, including self. + // This does not depend on whether we send a copy to self or not. + let recipients_count = peerstates.len() + 1; + + Ok(e2ee_guaranteed || 2 * prefer_encrypt_count > recipients_count) } /// Tries to encrypt the passed in `mail`.