1use std::collections::{BTreeMap, HashSet};
4use std::io::{BufRead, Cursor};
5
6use anyhow::{Context as _, Result};
7use chrono::SubsecRound;
8use deltachat_contact_tools::EmailAddress;
9use pgp::armor::BlockType;
10use pgp::composed::{
11 ArmorOptions, DecryptionOptions, Deserializable, DetachedSignature, KeyType as PgpKeyType,
12 Message, MessageBuilder, SecretKeyParamsBuilder, SignedPublicKey, SignedPublicSubKey,
13 SignedSecretKey, SubkeyParamsBuilder, TheRing,
14};
15use pgp::crypto::ecc_curve::ECCCurve;
16use pgp::crypto::hash::HashAlgorithm;
17use pgp::crypto::sym::SymmetricKeyAlgorithm;
18use pgp::packet::{SignatureConfig, SignatureType, Subpacket, SubpacketData};
19use pgp::types::{CompressionAlgorithm, KeyDetails, Password, PublicKeyTrait, StringToKey};
20use rand::thread_rng;
21use tokio::runtime::Handle;
22
23use crate::key::{DcKey, Fingerprint};
24
25#[cfg(test)]
26pub(crate) const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt";
27
28pub const HEADER_SETUPCODE: &str = "passphrase-begin";
29
30const SYMMETRIC_KEY_ALGORITHM: SymmetricKeyAlgorithm = SymmetricKeyAlgorithm::AES128;
32
33const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::Sha256;
35
36pub fn split_armored_data(buf: &[u8]) -> Result<(BlockType, BTreeMap<String, String>, Vec<u8>)> {
40 use std::io::Read;
41
42 let cursor = Cursor::new(buf);
43 let mut dearmor = pgp::armor::Dearmor::new(cursor);
44
45 let mut bytes = Vec::with_capacity(buf.len());
46
47 dearmor.read_to_end(&mut bytes)?;
48 let typ = dearmor.typ.context("failed to parse type")?;
49
50 let headers = dearmor
52 .headers
53 .into_iter()
54 .map(|(key, values)| {
55 (
56 key.trim().to_lowercase(),
57 values
58 .last()
59 .map_or_else(String::new, |s| s.trim().to_string()),
60 )
61 })
62 .collect();
63
64 Ok((typ, headers, bytes))
65}
66
67#[derive(Debug, Clone, Eq, PartialEq)]
72pub struct KeyPair {
73 pub public: SignedPublicKey,
75
76 pub secret: SignedSecretKey,
78}
79
80impl KeyPair {
81 pub fn new(secret: SignedSecretKey) -> Result<Self> {
85 use crate::key::DcSecretKey;
86
87 let public = secret.split_public_key()?;
88 Ok(Self { public, secret })
89 }
90}
91
92pub(crate) fn create_keypair(addr: EmailAddress) -> Result<KeyPair> {
97 let signing_key_type = PgpKeyType::Ed25519Legacy;
98 let encryption_key_type = PgpKeyType::ECDH(ECCCurve::Curve25519);
99
100 let user_id = format!("<{addr}>");
101 let key_params = SecretKeyParamsBuilder::default()
102 .key_type(signing_key_type)
103 .can_certify(true)
104 .can_sign(true)
105 .primary_user_id(user_id)
106 .passphrase(None)
107 .preferred_symmetric_algorithms(smallvec![
108 SymmetricKeyAlgorithm::AES256,
109 SymmetricKeyAlgorithm::AES192,
110 SymmetricKeyAlgorithm::AES128,
111 ])
112 .preferred_hash_algorithms(smallvec![
113 HashAlgorithm::Sha256,
114 HashAlgorithm::Sha384,
115 HashAlgorithm::Sha512,
116 HashAlgorithm::Sha224,
117 ])
118 .preferred_compression_algorithms(smallvec![
119 CompressionAlgorithm::ZLIB,
120 CompressionAlgorithm::ZIP,
121 ])
122 .subkey(
123 SubkeyParamsBuilder::default()
124 .key_type(encryption_key_type)
125 .can_encrypt(true)
126 .passphrase(None)
127 .build()
128 .context("failed to build subkey parameters")?,
129 )
130 .build()
131 .context("failed to build key parameters")?;
132
133 let mut rng = thread_rng();
134 let secret_key = key_params
135 .generate(&mut rng)
136 .context("failed to generate the key")?
137 .sign(&mut rng, &Password::empty())
138 .context("failed to sign secret key")?;
139 secret_key
140 .verify()
141 .context("invalid secret key generated")?;
142
143 let key_pair = KeyPair::new(secret_key)?;
144 key_pair
145 .public
146 .verify()
147 .context("invalid public key generated")?;
148 Ok(key_pair)
149}
150
151fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
157 key.public_subkeys
158 .iter()
159 .find(|subkey| subkey.is_encryption_key())
160}
161
162pub async fn pk_encrypt(
165 plain: Vec<u8>,
166 public_keys_for_encryption: Vec<SignedPublicKey>,
167 private_key_for_signing: Option<SignedSecretKey>,
168 compress: bool,
169) -> Result<String> {
170 Handle::current()
171 .spawn_blocking(move || {
172 let mut rng = thread_rng();
173
174 let pkeys = public_keys_for_encryption
175 .iter()
176 .filter_map(select_pk_for_encryption);
177
178 let msg = MessageBuilder::from_bytes("", plain);
179 let mut msg = msg.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
180 for pkey in pkeys {
181 msg.encrypt_to_key(&mut rng, &pkey)?;
182 }
183
184 if let Some(ref skey) = private_key_for_signing {
185 msg.sign(&**skey, Password::empty(), HASH_ALGORITHM);
186 if compress {
187 msg.compression(CompressionAlgorithm::ZLIB);
188 }
189 }
190
191 let encoded_msg = msg.to_armored_string(&mut rng, Default::default())?;
192
193 Ok(encoded_msg)
194 })
195 .await?
196}
197
198pub fn pk_calc_signature(
200 plain: Vec<u8>,
201 private_key_for_signing: &SignedSecretKey,
202) -> Result<String> {
203 let rng = thread_rng();
204
205 let mut config = SignatureConfig::from_key(
206 rng,
207 &private_key_for_signing.primary_key,
208 SignatureType::Binary,
209 )?;
210
211 config.hashed_subpackets = vec![
212 Subpacket::regular(SubpacketData::IssuerFingerprint(
213 private_key_for_signing.fingerprint(),
214 ))?,
215 Subpacket::critical(SubpacketData::SignatureCreationTime(
216 chrono::Utc::now().trunc_subsecs(0),
217 ))?,
218 ];
219 config.unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(
220 private_key_for_signing.key_id(),
221 ))?];
222
223 let signature = config.sign(
224 &private_key_for_signing.primary_key,
225 &Password::empty(),
226 plain.as_slice(),
227 )?;
228
229 let sig = DetachedSignature::new(signature);
230
231 Ok(sig.to_armored_string(ArmorOptions::default())?)
232}
233
234pub fn pk_decrypt(
239 ctext: Vec<u8>,
240 private_keys_for_decryption: &[SignedSecretKey],
241) -> Result<pgp::composed::Message<'static>> {
242 let cursor = Cursor::new(ctext);
243 let (msg, _headers) = Message::from_armor(cursor)?;
244
245 let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect();
246 let empty_pw = Password::empty();
247
248 let decrypt_options = DecryptionOptions::new();
249 let ring = TheRing {
250 secret_keys: skeys,
251 key_passwords: vec![&empty_pw],
252 message_password: vec![],
253 session_keys: vec![],
254 decrypt_options,
255 };
256 let (msg, ring_result) = msg.decrypt_the_ring(ring, true)?;
257 anyhow::ensure!(
258 !ring_result.secret_keys.is_empty(),
259 "decryption failed, no matching secret keys"
260 );
261
262 let msg = msg.decompress()?;
264
265 Ok(msg)
266}
267
268pub fn valid_signature_fingerprints(
274 msg: &pgp::composed::Message,
275 public_keys_for_validation: &[SignedPublicKey],
276) -> HashSet<Fingerprint> {
277 let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default();
278 if msg.is_signed() {
279 for pkey in public_keys_for_validation {
280 if msg.verify(&pkey.primary_key).is_ok() {
281 let fp = pkey.dc_fingerprint();
282 ret_signature_fingerprints.insert(fp);
283 }
284 }
285 }
286 ret_signature_fingerprints
287}
288
289pub fn pk_validate(
291 content: &[u8],
292 signature: &[u8],
293 public_keys_for_validation: &[SignedPublicKey],
294) -> Result<HashSet<Fingerprint>> {
295 let mut ret: HashSet<Fingerprint> = Default::default();
296
297 let detached_signature = DetachedSignature::from_armor_single(Cursor::new(signature))?.0;
298
299 for pkey in public_keys_for_validation {
300 if detached_signature.verify(pkey, content).is_ok() {
301 let fp = pkey.dc_fingerprint();
302 ret.insert(fp);
303 }
304 }
305 Ok(ret)
306}
307
308pub async fn symm_encrypt(passphrase: &str, plain: Vec<u8>) -> Result<String> {
310 let passphrase = Password::from(passphrase.to_string());
311
312 tokio::task::spawn_blocking(move || {
313 let mut rng = thread_rng();
314 let s2k = StringToKey::new_default(&mut rng);
315 let builder = MessageBuilder::from_bytes("", plain);
316 let mut builder = builder.seipd_v1(&mut rng, SYMMETRIC_KEY_ALGORITHM);
317 builder.encrypt_with_password(s2k, &passphrase)?;
318
319 let encoded_msg = builder.to_armored_string(&mut rng, Default::default())?;
320
321 Ok(encoded_msg)
322 })
323 .await?
324}
325
326pub async fn symm_decrypt<T: BufRead + std::fmt::Debug + 'static + Send>(
328 passphrase: &str,
329 ctext: T,
330) -> Result<Vec<u8>> {
331 let passphrase = passphrase.to_string();
332 tokio::task::spawn_blocking(move || {
333 let (enc_msg, _) = Message::from_armor(ctext)?;
334 let password = Password::from(passphrase);
335
336 let msg = enc_msg.decrypt_with_password(&password)?;
337 let res = msg.decompress()?.as_data_vec()?;
338 Ok(res)
339 })
340 .await?
341}
342
343#[cfg(test)]
344mod tests {
345 use std::sync::LazyLock;
346 use tokio::sync::OnceCell;
347
348 use super::*;
349 use crate::test_utils::{alice_keypair, bob_keypair};
350
351 fn pk_decrypt_and_validate<'a>(
352 ctext: &'a [u8],
353 private_keys_for_decryption: &'a [SignedSecretKey],
354 public_keys_for_validation: &[SignedPublicKey],
355 ) -> Result<(
356 pgp::composed::Message<'static>,
357 HashSet<Fingerprint>,
358 Vec<u8>,
359 )> {
360 let mut msg = pk_decrypt(ctext.to_vec(), private_keys_for_decryption)?;
361 let content = msg.as_data_vec()?;
362 let ret_signature_fingerprints =
363 valid_signature_fingerprints(&msg, public_keys_for_validation);
364
365 Ok((msg, ret_signature_fingerprints, content))
366 }
367
368 #[test]
369 fn test_split_armored_data_1() {
370 let (typ, _headers, base64) = split_armored_data(
371 b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\naGVsbG8gd29ybGQ=\n-----END PGP MESSAGE-----",
372 )
373 .unwrap();
374
375 assert_eq!(typ, BlockType::Message);
376 assert!(!base64.is_empty());
377 assert_eq!(
378 std::string::String::from_utf8(base64).unwrap(),
379 "hello world"
380 );
381 }
382
383 #[test]
384 fn test_split_armored_data_2() {
385 let (typ, headers, base64) = split_armored_data(
386 b"-----BEGIN PGP PRIVATE KEY BLOCK-----\nAutocrypt-Prefer-Encrypt: mutual \n\naGVsbG8gd29ybGQ=\n-----END PGP PRIVATE KEY BLOCK-----"
387 )
388 .unwrap();
389
390 assert_eq!(typ, BlockType::PrivateKey);
391 assert!(!base64.is_empty());
392 assert_eq!(headers.get(HEADER_AUTOCRYPT), Some(&"mutual".to_string()));
393 }
394
395 #[test]
396 fn test_create_keypair() {
397 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
398 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
399 assert_ne!(keypair0.public, keypair1.public);
400 }
401
402 struct TestKeys {
405 alice_secret: SignedSecretKey,
406 alice_public: SignedPublicKey,
407 bob_secret: SignedSecretKey,
408 bob_public: SignedPublicKey,
409 }
410
411 impl TestKeys {
412 fn new() -> TestKeys {
413 let alice = alice_keypair();
414 let bob = bob_keypair();
415 TestKeys {
416 alice_secret: alice.secret.clone(),
417 alice_public: alice.public,
418 bob_secret: bob.secret.clone(),
419 bob_public: bob.public,
420 }
421 }
422 }
423
424 static CLEARTEXT: &[u8] = b"This is a test";
426
427 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
429
430 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
431 static CTEXT_UNSIGNED: OnceCell<String> = OnceCell::const_new();
432
433 async fn ctext_signed() -> &'static String {
435 CTEXT_SIGNED
436 .get_or_init(|| async {
437 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
438 let compress = true;
439
440 pk_encrypt(
441 CLEARTEXT.to_vec(),
442 keyring,
443 Some(KEYS.alice_secret.clone()),
444 compress,
445 )
446 .await
447 .unwrap()
448 })
449 .await
450 }
451
452 async fn ctext_unsigned() -> &'static String {
454 CTEXT_UNSIGNED
455 .get_or_init(|| async {
456 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
457 let compress = true;
458
459 pk_encrypt(CLEARTEXT.to_vec(), keyring, None, compress)
460 .await
461 .unwrap()
462 })
463 .await
464 }
465
466 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
467 async fn test_encrypt_signed() {
468 assert!(!ctext_signed().await.is_empty());
469 assert!(
470 ctext_signed()
471 .await
472 .starts_with("-----BEGIN PGP MESSAGE-----")
473 );
474 }
475
476 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
477 async fn test_encrypt_unsigned() {
478 assert!(!ctext_unsigned().await.is_empty());
479 assert!(
480 ctext_unsigned()
481 .await
482 .starts_with("-----BEGIN PGP MESSAGE-----")
483 );
484 }
485
486 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
487 async fn test_decrypt_singed() {
488 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
490 let sig_check_keyring = vec![KEYS.alice_public.clone()];
491 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
492 ctext_signed().await.as_bytes(),
493 &decrypt_keyring,
494 &sig_check_keyring,
495 )
496 .unwrap();
497 assert_eq!(content, CLEARTEXT);
498 assert_eq!(valid_signatures.len(), 1);
499
500 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
502 let sig_check_keyring = vec![KEYS.alice_public.clone()];
503 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
504 ctext_signed().await.as_bytes(),
505 &decrypt_keyring,
506 &sig_check_keyring,
507 )
508 .unwrap();
509 assert_eq!(content, CLEARTEXT);
510 assert_eq!(valid_signatures.len(), 1);
511 }
512
513 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
514 async fn test_decrypt_no_sig_check() {
515 let keyring = vec![KEYS.alice_secret.clone()];
516 let (_msg, valid_signatures, content) =
517 pk_decrypt_and_validate(ctext_signed().await.as_bytes(), &keyring, &[]).unwrap();
518 assert_eq!(content, CLEARTEXT);
519 assert_eq!(valid_signatures.len(), 0);
520 }
521
522 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
523 async fn test_decrypt_signed_no_key() {
524 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
526 let sig_check_keyring = vec![KEYS.bob_public.clone()];
527 let (_msg, valid_signatures, content) = pk_decrypt_and_validate(
528 ctext_signed().await.as_bytes(),
529 &decrypt_keyring,
530 &sig_check_keyring,
531 )
532 .unwrap();
533 assert_eq!(content, CLEARTEXT);
534 assert_eq!(valid_signatures.len(), 0);
535 }
536
537 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
538 async fn test_decrypt_unsigned() {
539 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
540 let (_msg, valid_signatures, content) =
541 pk_decrypt_and_validate(ctext_unsigned().await.as_bytes(), &decrypt_keyring, &[])
542 .unwrap();
543 assert_eq!(content, CLEARTEXT);
544 assert_eq!(valid_signatures.len(), 0);
545 }
546}