diff --git a/python/tests/conftest.py b/python/tests/conftest.py index fca88e94a..2ee4ad7fd 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -120,6 +120,8 @@ def acfactory(pytestconfig, tmpdir, request): pytest.skip("specify a --liveconfig file to run tests with real accounts") self.live_count += 1 configdict = self.configlist.pop(0) + if "e2ee_enabled" not in configdict: + configdict["e2ee_enabled"] = "1" tmpdb = tmpdir.join("livedb%d" % self.live_count) ac = Account(tmpdb.strpath, logid="ac{}".format(self.live_count)) ac._evlogger.init_time = self.init_time diff --git a/python/tests/test_account.py b/python/tests/test_account.py index c9fa5db94..5acf972c4 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -427,6 +427,37 @@ class TestOnlineAccount: lp.step("2") assert msg_out.is_out_mdn_received() + def test_send_and_receive_will_encrypt_decrypt(self, acfactory, lp): + lp.sec("starting accounts, waiting for configuration") + ac1 = acfactory.get_online_configuring_account() + ac2 = acfactory.get_online_configuring_account() + c2 = ac1.create_contact(email=ac2.get_config("addr")) + chat = ac1.create_chat_by_contact(c2) + assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL + + wait_configuration_progress(ac1, 1000) + wait_configuration_progress(ac2, 1000) + + lp.sec("sending text message from ac1 to ac2") + msg_out = chat.send_text("message1") + + lp.sec("wait for ac2 to receive message") + ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev[2] == msg_out.id + msg_in = ac2.get_message_by_id(msg_out.id) + assert msg_in.text == "message1" + + lp.sec("create new chat with contact and send back (encrypted) message") + chat2b = ac2.create_chat_by_message(msg_in) + chat2b.send_text("message-back") + + lp.sec("wait for ac1 to receive message") + ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG") + assert ev[1] == chat.id + assert ev[2] > msg_out.id + msg_back = ac1.get_message_by_id(ev[2]) + assert msg_back.text == "message-back" + def test_saved_mime_on_received_message(self, acfactory, lp): lp.sec("starting accounts, waiting for configuration") ac1 = acfactory.get_online_configuring_account() diff --git a/src/dc_e2ee.rs b/src/dc_e2ee.rs index 41946412b..db0c15a89 100644 --- a/src/dc_e2ee.rs +++ b/src/dc_e2ee.rs @@ -115,11 +115,22 @@ pub unsafe fn dc_e2ee_encrypt( || 0 != e2ee_guaranteed) { let peerstate = peerstate.unwrap(); + info!( + context, + 0, "dc_e2ee_encrypt {} has peerstate", recipient_addr + ); if let Some(key) = peerstate.peek_key(min_verified as usize) { keyring.add_owned(key.clone()); peerstates.push(peerstate); } } else { + info!( + context, + 0, + "dc_e2ee_encrypt {} HAS NO peerstate {}", + recipient_addr, + peerstate.is_some() + ); do_encrypt = 0i32; /* if we cannot encrypt to a single recipient, we cannot encrypt the message at all */ break; @@ -574,7 +585,7 @@ pub unsafe fn dc_e2ee_decrypt( } } else if let Some(ref header) = autocryptheader { let p = Peerstate::from_header(context, header, message_time); - p.save_to_db(&context.sql, true); + assert!(p.save_to_db(&context.sql, true)); peerstate = Some(p); } } diff --git a/src/peerstate.rs b/src/peerstate.rs index d7f8009ab..de0e01abe 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -166,7 +166,6 @@ impl<'a> Peerstate<'a> { pub fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option { let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;"; - Self::from_stmt(context, query, &[addr]) } @@ -191,6 +190,11 @@ impl<'a> Peerstate<'a> { context .sql .query_row(query, params, |row| { + /* all the above queries start with this: SELECT + addr, last_seen, last_seen_autocrypt, prefer_encrypted, + public_key, gossip_timestamp, gossip_key, public_key_fingerprint, + gossip_key_fingerprint, verified_key, verified_key_fingerprint + */ let mut res = Self::new(context); res.addr = Some(row.get(0)?); @@ -198,13 +202,34 @@ impl<'a> Peerstate<'a> { res.last_seen_autocrypt = row.get(2)?; res.prefer_encrypt = EncryptPreference::from_i32(row.get(3)?).unwrap_or_default(); res.gossip_timestamp = row.get(5)?; - let pkf: String = row.get(7)?; - res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) }; - let gkf: String = row.get(8)?; - res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) }; - let vkf: String = row.get(10)?; - res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) }; + res.public_key_fingerprint = row.get(7)?; + if res + .public_key_fingerprint + .as_ref() + .map(|s| s.is_empty()) + .unwrap_or_default() + { + res.public_key_fingerprint = None; + } + res.gossip_key_fingerprint = row.get(8)?; + if res + .gossip_key_fingerprint + .as_ref() + .map(|s| s.is_empty()) + .unwrap_or_default() + { + res.gossip_key_fingerprint = None; + } + res.verified_key_fingerprint = row.get(10)?; + if res + .verified_key_fingerprint + .as_ref() + .map(|s| s.is_empty()) + .unwrap_or_default() + { + res.verified_key_fingerprint = None; + } res.public_key = row .get(4) .ok() @@ -217,7 +242,8 @@ impl<'a> Peerstate<'a> { .get(9) .ok() .and_then(|blob: Vec| Key::from_slice(&blob, KeyType::Public)); - res.verified_key = if vk == res.gossip_key { + + res.verified_key = if vk == res.gossip_key && res.gossip_key.is_some() { VerifiedKey::Gossip } else if vk == res.public_key { VerifiedKey::Public @@ -422,6 +448,7 @@ impl<'a> Peerstate<'a> { &self.addr, ], ).is_ok(); + assert_eq!(success, true); } else if self.to_save == Some(ToSave::Timestamps) { success = sql::execute( self.context, @@ -498,6 +525,40 @@ mod tests { assert_eq!(peerstate, peerstate_new); } + #[test] + fn test_peerstate_with_empty_gossip_key_save_to_db() { + let ctx = crate::test_utils::dummy_context(); + let addr = "hello@mail.com"; + + let pub_key = crate::key::Key::from_base64("xsBNBFztUVkBCADYaQl/UOUpRPd32nLRzx8eU0eI+jQEnG+g5anjYA+3oct1rROGl5SygjMULDKdaUy27O3o9Srsti0YjA7uxZnavIqhSopJhFidqY1M1wA9JZa/duucZdNwUGbjGIRsS/4Cjr5+3svscK24hVYub1dvDWXpwUTnj3K6xOEnJdoM+MhCqtSD5+zcJhFc9vyZm9ZTGWUxAhKh0iJTcCD8V6CQ3XZ2z9GruwzZT/FTFovWrz7m3TUI2OdSSHh0eZLRGEoxMCT/vzflAFGAr8ijCaRsEIfqP6FW8uQWnFTqkjxEUCZG6XkeFHB84aj5jqYG/1KCLjL5vEKwfl1tz/WnPhY7ABEBAAHNEDxoZWxsb0BtYWlsLmNvbT7CwIkEEAEIADMCGQEFAlztUVoCGwMECwkIBwYVCAkKCwIDFgIBFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7ijAwf+PTsuawUax9cNPn1bN90H+g9qyHZJMEwKXtUnNaXJxPW3iB7ThhpCiCzsZwP7+l7ArS8tmLeNDw2bENtcf1XCv4wovP2fdXOP3QOUUFX/GdakcTwv7DzC7CO0grB1HtaPhGw/6UX2o2cx2i9xiUf4Givq2MfCbgAW5zloH6WXGPb6yLQYJXxqDIphr4+uZDb+bMAyWHN/DUkAjHrV8nnVki7PMHqzzZpwglalxMX8RGeiGZE39ALJKL/Og87DMFah87/yoxQWGoS7Wqv0XDcCPKoTCPrpk8pOe2KEsq/lz215nefHd4aRpfUX5YCYa8HPvvfPQbGF73uvyQw5w7qjis7ATQRc7VFZAQgAt8ONdnX6KEEQ5Jw6ilJ+LBtY44SP5t0I3eK+goKepgIiKhjGDa+Mntyi4jdhH+HO6kvK5SHMh2sPp4rRO/WKHJwWFySyM1OdyiywhyH0J9R5rBY4vPHsJjf6vSKJdWLWT+ho1fNet2IIC+jVCYli91MAMbRvk6EKVj1nCc+67giOahXEkHt6xxkeCGlOvbw8hxGj1A8+AC1BLms/OR3oc4JMi9O3kq6uG0z9tlUEerac9HVwcjoO1XLe+hJhoT5H+TbnGjPuhuURP3pFiIKHpbRYgUfdSAY0dTObO7t4I5y/drPOrCTnWrBUg2wXAECUhpRKow9/ai2YemLv9KqhhwARAQABwsB2BBgBCAAgBQJc7VFaAhsMFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7jmyggAhs4QzCzIbT2OsAReBxkxtm0AI+g1HZ1KFKof5NDHfgv9C/Qu1I8mKEjlZzA4qFyPmLqntgwJ0RuFy6gLbljZBNCFO7vB478AhYtnWjuKZmA40HUPwcB1hEJ31c42akzfUbioY1TLLepngdsJg7Cm8O+rhI9+1WRA66haJDgFs793SVUDyJh8f9NX50l5zR87/bsV30CFSw0q4OSSy9VI/z+2g5khn1LnuuOrCfFnYIPYtJED1BfkXkosxGlgbzy79VvGmI9d23x4atDK7oBPCzIj+lP8sytJ0u3HOguXi9OgDitKy+Pt1r8gH8frdktMJr5Ts6DW+tIn2vR23KR8aA==", KeyType::Public).unwrap(); + + let mut peerstate = Peerstate { + context: &ctx.ctx, + addr: Some(addr.into()), + last_seen: 10, + last_seen_autocrypt: 11, + prefer_encrypt: EncryptPreference::Mutual, + public_key: Some(pub_key.clone()), + public_key_fingerprint: Some(pub_key.fingerprint()), + gossip_key: None, + gossip_timestamp: 12, + gossip_key_fingerprint: None, + verified_key: VerifiedKey::None, + verified_key_fingerprint: None, + to_save: Some(ToSave::All), + degrade_event: None, + }; + + assert!(peerstate.save_to_db(&ctx.ctx.sql, true), "failed to save"); + + let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into()) + .expect("failed to load peerstate from db"); + + // clear to_save, as that is not persissted + peerstate.to_save = None; + assert_eq!(peerstate, peerstate_new); + } + // TODO: don't copy this from stress.rs #[allow(dead_code)] struct TestContext {