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 69238e9c0..e697f2dea 100644 --- a/src/dc_e2ee.rs +++ b/src/dc_e2ee.rs @@ -76,6 +76,11 @@ pub unsafe fn dc_e2ee_encrypt( let mut peerstates: Vec = Vec::new(); *helper = Default::default(); + info!( + context, + 0, "dc_e2ee_encrypt guaruanteed={}", e2ee_guaranteed + ); + if !(recipients_addr.is_null() || in_out_message.is_null() || !(*in_out_message).mm_parent.is_null() @@ -106,6 +111,10 @@ pub unsafe fn dc_e2ee_encrypt( iter1 = (*recipients_addr).first; while !iter1.is_null() { let recipient_addr = to_string((*iter1).data as *const libc::c_char); + info!( + context, + 0, "dc_e2ee_encrypt recipient_addr {}", recipient_addr + ); if recipient_addr != addr { let peerstate = Peerstate::from_addr(context, &context.sql, &recipient_addr); @@ -115,11 +124,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 +594,8 @@ 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); + info!(context, 0, "setting peerstate from header for {:?}", p.addr); + assert!(p.save_to_db(&context.sql, true)); peerstate = Some(p); } } @@ -812,7 +833,7 @@ unsafe fn decrypt_recursive( } unsafe fn decrypt_part( - context: &Context, + _context: &Context, mime: *mut mailmime, private_keyring: &Keyring, public_keyring_for_validate: &Keyring, diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index dfbb84f86..1675eff49 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -1041,6 +1041,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: &mut e2ee_helper, ); } + info!( + (*factory).context, + 0, "encryption successful {}", e2ee_helper.encryption_successfull + ); + if 0 != e2ee_helper.encryption_successfull { (*factory).out_encrypted = 1; if 0 != do_gossip { diff --git a/src/peerstate.rs b/src/peerstate.rs index d7f8009ab..ddd72594b 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -166,7 +166,7 @@ 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;"; - + info!(context, 0, "peerstate.from_addr {}", addr); Self::from_stmt(context, query, &[addr]) } @@ -188,21 +188,38 @@ impl<'a> Peerstate<'a> { P: IntoIterator, P::Item: rusqlite::ToSql, { + info!(context, 0, "from_stmt query {:?}", query); 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)?); + info!(context, 0, "from_stmt res.addr {:?} starting", res.addr); res.last_seen = row.get(1)?; 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)?; + + let t: Result = row.get(8); + let gkf: String = match t { + Err(_) => String::from(""), + Ok(res) => res, + }; res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) }; - let vkf: String = row.get(10)?; + + let t: Result = row.get(10); + let vkf: String = match t { + Err(_) => String::from(""), + Ok(res) => res, + }; res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) }; res.public_key = row @@ -217,7 +234,7 @@ 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 @@ -400,6 +417,10 @@ impl<'a> Peerstate<'a> { } if self.to_save == Some(ToSave::All) || create { + info!( + self.context, + 0, "update acpeerstates with peerstate {:?}", self + ); success = sql::execute( self.context, sql, @@ -422,6 +443,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 +520,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 {