1use std::collections::BTreeSet;
4use std::io::Cursor;
5
6use anyhow::{bail, Result};
7use mail_builder::mime::MimePart;
8use num_traits::FromPrimitive;
9
10use crate::aheader::{Aheader, EncryptPreference};
11use crate::config::Config;
12use crate::context::Context;
13use crate::key::{load_self_public_key, load_self_secret_key, SignedPublicKey};
14use crate::peerstate::Peerstate;
15use crate::pgp;
16
17#[derive(Debug)]
18pub struct EncryptHelper {
19 pub prefer_encrypt: EncryptPreference,
20 pub addr: String,
21 pub public_key: SignedPublicKey,
22}
23
24impl EncryptHelper {
25 pub async fn new(context: &Context) -> Result<EncryptHelper> {
26 let prefer_encrypt =
27 EncryptPreference::from_i32(context.get_config_int(Config::E2eeEnabled).await?)
28 .unwrap_or_default();
29 let addr = context.get_primary_self_addr().await?;
30 let public_key = load_self_public_key(context).await?;
31
32 Ok(EncryptHelper {
33 prefer_encrypt,
34 addr,
35 public_key,
36 })
37 }
38
39 pub fn get_aheader(&self) -> Aheader {
40 let pk = self.public_key.clone();
41 let addr = self.addr.to_string();
42 Aheader::new(addr, pk, self.prefer_encrypt)
43 }
44
45 pub(crate) async fn should_encrypt(
47 &self,
48 context: &Context,
49 peerstates: &[(Option<Peerstate>, String)],
50 ) -> Result<bool> {
51 let is_chatmail = context.is_chatmail().await?;
52 for (peerstate, _addr) in peerstates {
53 if let Some(peerstate) = peerstate {
54 if is_chatmail || peerstate.prefer_encrypt != EncryptPreference::Reset {
57 continue;
58 }
59 }
60 return Ok(false);
61 }
62 Ok(true)
63 }
64
65 pub(crate) fn encryption_keyring(
73 &self,
74 context: &Context,
75 verified: bool,
76 peerstates: &[(Option<Peerstate>, String)],
77 ) -> Result<(Vec<SignedPublicKey>, BTreeSet<String>)> {
78 let mut keyring = vec![self.public_key.clone()];
81 let mut missing_key_addresses = BTreeSet::new();
82
83 if peerstates.is_empty() {
84 return Ok((keyring, missing_key_addresses));
85 }
86
87 let mut verifier_addresses: Vec<&str> = Vec::new();
88
89 for (peerstate, addr) in peerstates {
90 if let Some(peerstate) = peerstate {
91 if let Some(key) = peerstate.clone().take_key(verified) {
92 keyring.push(key);
93 verifier_addresses.push(addr);
94 } else {
95 warn!(context, "Encryption key for {addr} is missing.");
96 missing_key_addresses.insert(addr.clone());
97 }
98 } else {
99 warn!(context, "Peerstate for {addr} is missing.");
100 missing_key_addresses.insert(addr.clone());
101 }
102 }
103
104 debug_assert!(
105 !keyring.is_empty(),
106 "At least our own key is in the keyring"
107 );
108 if keyring.len() <= 1 {
109 bail!("No recipient keys are available, cannot encrypt");
110 }
111
112 if verified {
115 for (peerstate, _addr) in peerstates {
116 if let Some(peerstate) = peerstate {
117 if let (Some(key), Some(verifier)) = (
118 peerstate.secondary_verified_key.as_ref(),
119 peerstate.secondary_verifier.as_deref(),
120 ) {
121 if verifier_addresses.contains(&verifier) {
122 keyring.push(key.clone());
123 }
124 }
125 }
126 }
127 }
128
129 Ok((keyring, missing_key_addresses))
130 }
131
132 pub async fn encrypt(
134 self,
135 context: &Context,
136 keyring: Vec<SignedPublicKey>,
137 mail_to_encrypt: MimePart<'static>,
138 compress: bool,
139 ) -> Result<String> {
140 let sign_key = load_self_secret_key(context).await?;
141
142 let mut raw_message = Vec::new();
143 let cursor = Cursor::new(&mut raw_message);
144 mail_to_encrypt.clone().write_part(cursor).ok();
145
146 let ctext = pgp::pk_encrypt(raw_message, keyring, Some(sign_key), compress).await?;
147
148 Ok(ctext)
149 }
150
151 pub async fn sign(self, context: &Context, mail: &MimePart<'static>) -> Result<String> {
154 let sign_key = load_self_secret_key(context).await?;
155 let mut buffer = Vec::new();
156 mail.clone().write_part(&mut buffer)?;
157 let signature = pgp::pk_calc_signature(buffer, &sign_key)?;
158 Ok(signature)
159 }
160}
161
162pub async fn ensure_secret_key_exists(context: &Context) -> Result<()> {
170 load_self_public_key(context).await?;
171 Ok(())
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use crate::chat::send_text_msg;
178 use crate::config::Config;
179 use crate::key::DcKey;
180 use crate::message::{Message, Viewtype};
181 use crate::param::Param;
182 use crate::receive_imf::receive_imf;
183 use crate::test_utils::{bob_keypair, TestContext, TestContextManager};
184
185 mod ensure_secret_key_exists {
186 use super::*;
187
188 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
189 async fn test_prexisting() {
190 let t = TestContext::new_alice().await;
191 assert!(ensure_secret_key_exists(&t).await.is_ok());
192 }
193
194 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
195 async fn test_not_configured() {
196 let t = TestContext::new().await;
197 assert!(ensure_secret_key_exists(&t).await.is_err());
198 }
199 }
200
201 #[test]
202 fn test_mailmime_parse() {
203 let plain = b"Chat-Disposition-Notification-To: hello@world.de
204Chat-Group-ID: CovhGgau8M-
205Chat-Group-Name: Delta Chat Dev
206Subject: =?utf-8?Q?Chat=3A?= Delta Chat =?utf-8?Q?Dev=3A?= sidenote for
207 =?utf-8?Q?all=3A?= rust core master ...
208Content-Type: text/plain; charset=\"utf-8\"; protected-headers=\"v1\"
209Content-Transfer-Encoding: quoted-printable
210
211sidenote for all: things are trick atm recomm=
212end not to try to run with desktop or ios unless you are ready to hunt bugs
213
214-- =20
215Sent with my Delta Chat Messenger: https://delta.chat";
216 let mail = mailparse::parse_mail(plain).expect("failed to parse valid message");
217
218 assert_eq!(mail.headers.len(), 6);
219 assert!(
220 mail.get_body().unwrap().starts_with(
221 "sidenote for all: things are trick atm recommend not to try to run with desktop or ios unless you are ready to hunt bugs")
222 );
223 }
224
225 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
226 async fn test_encrypted_no_autocrypt() -> anyhow::Result<()> {
227 let mut tcm = TestContextManager::new();
228 let alice = tcm.alice().await;
229 let bob = tcm.bob().await;
230
231 let chat_alice = alice.create_email_chat(&bob).await.id;
232 let chat_bob = bob.create_email_chat(&alice).await.id;
233
234 let mut msg = Message::new(Viewtype::Text);
236 let sent = alice.send_msg(chat_alice, &mut msg).await;
237
238 let msg = bob.recv_msg(&sent).await;
240 assert!(!msg.get_showpadlock());
241
242 let peerstate_alice = Peerstate::from_addr(&bob.ctx, "alice@example.org")
243 .await?
244 .expect("no peerstate found in the database");
245 assert_eq!(peerstate_alice.prefer_encrypt, EncryptPreference::Mutual);
246
247 let mut msg = Message::new(Viewtype::Text);
249 let sent = bob.send_msg(chat_bob, &mut msg).await;
250
251 let msg = alice.recv_msg(&sent).await;
255 assert!(msg.get_showpadlock());
256
257 let peerstate_bob = Peerstate::from_addr(&alice.ctx, "bob@example.net")
258 .await?
259 .expect("no peerstate found in the database");
260 assert_eq!(peerstate_bob.prefer_encrypt, EncryptPreference::Mutual);
261
262 let mut msg = Message::new(Viewtype::Text);
266 msg.param.set_int(Param::SkipAutocrypt, 1);
267 let sent = alice.send_msg(chat_alice, &mut msg).await;
268
269 let msg = bob.recv_msg(&sent).await;
270 assert!(msg.get_showpadlock());
271 let peerstate_alice = Peerstate::from_addr(&bob.ctx, "alice@example.org")
272 .await?
273 .expect("no peerstate found in the database");
274 assert_eq!(peerstate_alice.prefer_encrypt, EncryptPreference::Mutual);
275
276 let mut msg = Message::new(Viewtype::Text);
278 msg.force_plaintext();
279 let sent = alice.send_msg(chat_alice, &mut msg).await;
280
281 let msg = bob.recv_msg(&sent).await;
282 assert!(!msg.get_showpadlock());
283 let peerstate_alice = Peerstate::from_addr(&bob.ctx, "alice@example.org")
284 .await?
285 .expect("no peerstate found in the database");
286 assert_eq!(peerstate_alice.prefer_encrypt, EncryptPreference::Mutual);
287
288 let mut msg = Message::new(Viewtype::Text);
290 msg.force_plaintext();
291 msg.param.set_int(Param::SkipAutocrypt, 1);
292 let sent = alice.send_msg(chat_alice, &mut msg).await;
293
294 let msg = bob.recv_msg(&sent).await;
295 assert!(!msg.get_showpadlock());
296 let peerstate_alice = Peerstate::from_addr(&bob.ctx, "alice@example.org")
297 .await?
298 .expect("no peerstate found in the database");
299 assert_eq!(peerstate_alice.prefer_encrypt, EncryptPreference::Reset);
300
301 Ok(())
302 }
303
304 fn new_peerstates(prefer_encrypt: EncryptPreference) -> Vec<(Option<Peerstate>, String)> {
305 let addr = "bob@foo.bar";
306 let pub_key = bob_keypair().public;
307 let peerstate = Peerstate {
308 addr: addr.into(),
309 last_seen: 13,
310 last_seen_autocrypt: 14,
311 prefer_encrypt,
312 public_key: Some(pub_key.clone()),
313 public_key_fingerprint: Some(pub_key.dc_fingerprint()),
314 gossip_key: Some(pub_key.clone()),
315 gossip_timestamp: 15,
316 gossip_key_fingerprint: Some(pub_key.dc_fingerprint()),
317 verified_key: Some(pub_key.clone()),
318 verified_key_fingerprint: Some(pub_key.dc_fingerprint()),
319 verifier: None,
320 secondary_verified_key: None,
321 secondary_verified_key_fingerprint: None,
322 secondary_verifier: None,
323 backward_verified_key_id: None,
324 fingerprint_changed: false,
325 };
326 vec![(Some(peerstate), addr.to_string())]
327 }
328
329 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
330 async fn test_should_encrypt() -> Result<()> {
331 let t = TestContext::new_alice().await;
332 let encrypt_helper = EncryptHelper::new(&t).await.unwrap();
333
334 let ps = new_peerstates(EncryptPreference::NoPreference);
335 assert!(encrypt_helper.should_encrypt(&t, &ps).await?);
336
337 let ps = new_peerstates(EncryptPreference::Reset);
338 assert!(!encrypt_helper.should_encrypt(&t, &ps).await?);
339
340 let ps = new_peerstates(EncryptPreference::Mutual);
341 assert!(encrypt_helper.should_encrypt(&t, &ps).await?);
342
343 let ps = vec![(None, "bob@foo.bar".to_string())];
345 assert!(!encrypt_helper.should_encrypt(&t, &ps).await?);
346 Ok(())
347 }
348
349 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
350 async fn test_chatmail_can_send_unencrypted() -> Result<()> {
351 let mut tcm = TestContextManager::new();
352 let bob = &tcm.bob().await;
353 bob.set_config_bool(Config::IsChatmail, true).await?;
354 let bob_chat_id = receive_imf(
355 bob,
356 b"From: alice@example.org\n\
357 To: bob@example.net\n\
358 Message-ID: <2222@example.org>\n\
359 Date: Sun, 22 Mar 3000 22:37:58 +0000\n\
360 \n\
361 Hello\n",
362 false,
363 )
364 .await?
365 .unwrap()
366 .chat_id;
367 bob_chat_id.accept(bob).await?;
368 send_text_msg(bob, bob_chat_id, "hi".to_string()).await?;
369 let sent_msg = bob.pop_sent_msg().await;
370 let msg = Message::load_from_db(bob, sent_msg.sender_msg_id).await?;
371 assert!(!msg.get_showpadlock());
372 Ok(())
373 }
374}