1use std::collections::{BTreeMap, HashSet};
4use std::io::Cursor;
5
6use anyhow::{bail, Context as _, Result};
7use deltachat_contact_tools::EmailAddress;
8use pgp::armor::BlockType;
9use pgp::composed::{
10 Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
11 SignedPublicSubKey, SignedSecretKey, StandaloneSignature, SubkeyParamsBuilder,
12};
13use pgp::crypto::ecc_curve::ECCCurve;
14use pgp::crypto::hash::HashAlgorithm;
15use pgp::crypto::sym::SymmetricKeyAlgorithm;
16use pgp::types::{CompressionAlgorithm, PublicKeyTrait, StringToKey};
17use rand::thread_rng;
18use tokio::runtime::Handle;
19
20use crate::key::{DcKey, Fingerprint};
21
22#[cfg(test)]
23pub(crate) const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt";
24
25pub const HEADER_SETUPCODE: &str = "passphrase-begin";
26
27const SYMMETRIC_KEY_ALGORITHM: SymmetricKeyAlgorithm = SymmetricKeyAlgorithm::AES128;
29
30const HASH_ALGORITHM: HashAlgorithm = HashAlgorithm::SHA2_256;
32
33pub fn split_armored_data(buf: &[u8]) -> Result<(BlockType, BTreeMap<String, String>, Vec<u8>)> {
37 use std::io::Read;
38
39 let cursor = Cursor::new(buf);
40 let mut dearmor = pgp::armor::Dearmor::new(cursor);
41
42 let mut bytes = Vec::with_capacity(buf.len());
43
44 dearmor.read_to_end(&mut bytes)?;
45 let typ = dearmor.typ.context("failed to parse type")?;
46
47 let headers = dearmor
49 .headers
50 .into_iter()
51 .map(|(key, values)| {
52 (
53 key.trim().to_lowercase(),
54 values
55 .last()
56 .map_or_else(String::new, |s| s.trim().to_string()),
57 )
58 })
59 .collect();
60
61 Ok((typ, headers, bytes))
62}
63
64#[derive(Debug, Clone, Eq, PartialEq)]
69pub struct KeyPair {
70 pub public: SignedPublicKey,
72
73 pub secret: SignedSecretKey,
75}
76
77impl KeyPair {
78 pub fn new(secret: SignedSecretKey) -> Result<Self> {
82 use crate::key::DcSecretKey;
83
84 let public = secret.split_public_key()?;
85 Ok(Self { public, secret })
86 }
87}
88
89pub(crate) fn create_keypair(addr: EmailAddress) -> Result<KeyPair> {
94 let signing_key_type = PgpKeyType::EdDSALegacy;
95 let encryption_key_type = PgpKeyType::ECDH(ECCCurve::Curve25519);
96
97 let user_id = format!("<{addr}>");
98 let key_params = SecretKeyParamsBuilder::default()
99 .key_type(signing_key_type)
100 .can_certify(true)
101 .can_sign(true)
102 .primary_user_id(user_id)
103 .passphrase(None)
104 .preferred_symmetric_algorithms(smallvec![
105 SymmetricKeyAlgorithm::AES256,
106 SymmetricKeyAlgorithm::AES192,
107 SymmetricKeyAlgorithm::AES128,
108 ])
109 .preferred_hash_algorithms(smallvec![
110 HashAlgorithm::SHA2_256,
111 HashAlgorithm::SHA2_384,
112 HashAlgorithm::SHA2_512,
113 HashAlgorithm::SHA2_224,
114 HashAlgorithm::SHA1,
115 ])
116 .preferred_compression_algorithms(smallvec![
117 CompressionAlgorithm::ZLIB,
118 CompressionAlgorithm::ZIP,
119 ])
120 .subkey(
121 SubkeyParamsBuilder::default()
122 .key_type(encryption_key_type)
123 .can_encrypt(true)
124 .passphrase(None)
125 .build()
126 .context("failed to build subkey parameters")?,
127 )
128 .build()
129 .context("failed to build key parameters")?;
130
131 let mut rng = thread_rng();
132 let secret_key = key_params
133 .generate(&mut rng)
134 .context("failed to generate the key")?
135 .sign(&mut rng, || "".into())
136 .context("failed to sign secret key")?;
137 secret_key
138 .verify()
139 .context("invalid secret key generated")?;
140
141 let key_pair = KeyPair::new(secret_key)?;
142 key_pair
143 .public
144 .verify()
145 .context("invalid public key generated")?;
146 Ok(key_pair)
147}
148
149fn select_pk_for_encryption(key: &SignedPublicKey) -> Option<&SignedPublicSubKey> {
155 key.public_subkeys
156 .iter()
157 .find(|subkey| subkey.is_encryption_key())
158}
159
160pub async fn pk_encrypt(
163 plain: &[u8],
164 public_keys_for_encryption: Vec<SignedPublicKey>,
165 private_key_for_signing: Option<SignedSecretKey>,
166 compress: bool,
167) -> Result<String> {
168 let lit_msg = Message::new_literal_bytes("", plain);
169
170 Handle::current()
171 .spawn_blocking(move || {
172 let pkeys: Vec<&SignedPublicSubKey> = public_keys_for_encryption
173 .iter()
174 .filter_map(select_pk_for_encryption)
175 .collect();
176
177 let mut rng = thread_rng();
178
179 let encrypted_msg = if let Some(ref skey) = private_key_for_signing {
180 let signed_msg = lit_msg.sign(&mut rng, skey, || "".into(), HASH_ALGORITHM)?;
181 let compressed_msg = if compress {
182 signed_msg.compress(CompressionAlgorithm::ZLIB)?
183 } else {
184 signed_msg
185 };
186 compressed_msg.encrypt_to_keys_seipdv1(&mut rng, SYMMETRIC_KEY_ALGORITHM, &pkeys)?
187 } else {
188 lit_msg.encrypt_to_keys_seipdv1(&mut rng, SYMMETRIC_KEY_ALGORITHM, &pkeys)?
189 };
190
191 let encoded_msg = encrypted_msg.to_armored_string(Default::default())?;
192
193 Ok(encoded_msg)
194 })
195 .await?
196}
197
198pub fn pk_calc_signature(
200 plain: &[u8],
201 private_key_for_signing: &SignedSecretKey,
202) -> Result<String> {
203 let mut rng = thread_rng();
204 let msg = Message::new_literal_bytes("", plain).sign(
205 &mut rng,
206 private_key_for_signing,
207 || "".into(),
208 HASH_ALGORITHM,
209 )?;
210 let signature = msg.into_signature().to_armored_string(Default::default())?;
211 Ok(signature)
212}
213
214pub fn pk_decrypt(
219 ctext: Vec<u8>,
220 private_keys_for_decryption: &[SignedSecretKey],
221) -> Result<pgp::composed::Message> {
222 let cursor = Cursor::new(ctext);
223 let (msg, _headers) = Message::from_armor_single(cursor)?;
224
225 let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption.iter().collect();
226
227 let (msg, _key_ids) = msg.decrypt(|| "".into(), &skeys[..])?;
228
229 let msg = msg.decompress()?;
232
233 Ok(msg)
234}
235
236pub fn valid_signature_fingerprints(
242 msg: &pgp::composed::Message,
243 public_keys_for_validation: &[SignedPublicKey],
244) -> Result<HashSet<Fingerprint>> {
245 let mut ret_signature_fingerprints: HashSet<Fingerprint> = Default::default();
246 if let signed_msg @ pgp::composed::Message::Signed { .. } = msg {
247 for pkey in public_keys_for_validation {
248 if signed_msg.verify(&pkey.primary_key).is_ok() {
249 let fp = pkey.dc_fingerprint();
250 ret_signature_fingerprints.insert(fp);
251 }
252 }
253 }
254 Ok(ret_signature_fingerprints)
255}
256
257pub fn pk_validate(
259 content: &[u8],
260 signature: &[u8],
261 public_keys_for_validation: &[SignedPublicKey],
262) -> Result<HashSet<Fingerprint>> {
263 let mut ret: HashSet<Fingerprint> = Default::default();
264
265 let standalone_signature = StandaloneSignature::from_armor_single(Cursor::new(signature))?.0;
266
267 for pkey in public_keys_for_validation {
268 if standalone_signature.verify(pkey, content).is_ok() {
269 let fp = pkey.dc_fingerprint();
270 ret.insert(fp);
271 }
272 }
273 Ok(ret)
274}
275
276pub async fn symm_encrypt(passphrase: &str, plain: &[u8]) -> Result<String> {
278 let lit_msg = Message::new_literal_bytes("", plain);
279 let passphrase = passphrase.to_string();
280
281 tokio::task::spawn_blocking(move || {
282 let mut rng = thread_rng();
283 let s2k = StringToKey::new_default(&mut rng);
284 let msg = lit_msg.encrypt_with_password_seipdv1(
285 &mut rng,
286 s2k,
287 SYMMETRIC_KEY_ALGORITHM,
288 || passphrase,
289 )?;
290
291 let encoded_msg = msg.to_armored_string(Default::default())?;
292
293 Ok(encoded_msg)
294 })
295 .await?
296}
297
298pub async fn symm_decrypt<T: std::io::Read + std::io::Seek>(
300 passphrase: &str,
301 ctext: T,
302) -> Result<Vec<u8>> {
303 let (enc_msg, _) = Message::from_armor_single(ctext)?;
304
305 let passphrase = passphrase.to_string();
306 tokio::task::spawn_blocking(move || {
307 let msg = enc_msg.decrypt_with_password(|| passphrase)?;
308
309 match msg.get_content()? {
310 Some(content) => Ok(content),
311 None => bail!("Decrypted message is empty"),
312 }
313 })
314 .await?
315}
316
317#[cfg(test)]
318mod tests {
319 use std::sync::LazyLock;
320 use tokio::sync::OnceCell;
321
322 use super::*;
323 use crate::test_utils::{alice_keypair, bob_keypair};
324
325 fn pk_decrypt_and_validate(
326 ctext: Vec<u8>,
327 private_keys_for_decryption: &[SignedSecretKey],
328 public_keys_for_validation: &[SignedPublicKey],
329 ) -> Result<(pgp::composed::Message, HashSet<Fingerprint>)> {
330 let msg = pk_decrypt(ctext, private_keys_for_decryption)?;
331 let ret_signature_fingerprints =
332 valid_signature_fingerprints(&msg, public_keys_for_validation)?;
333
334 Ok((msg, ret_signature_fingerprints))
335 }
336
337 #[test]
338 fn test_split_armored_data_1() {
339 let (typ, _headers, base64) = split_armored_data(
340 b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\naGVsbG8gd29ybGQ=\n-----END PGP MESSAGE-----",
341 )
342 .unwrap();
343
344 assert_eq!(typ, BlockType::Message);
345 assert!(!base64.is_empty());
346 assert_eq!(
347 std::string::String::from_utf8(base64).unwrap(),
348 "hello world"
349 );
350 }
351
352 #[test]
353 fn test_split_armored_data_2() {
354 let (typ, headers, base64) = split_armored_data(
355 b"-----BEGIN PGP PRIVATE KEY BLOCK-----\nAutocrypt-Prefer-Encrypt: mutual \n\naGVsbG8gd29ybGQ=\n-----END PGP PRIVATE KEY BLOCK-----"
356 )
357 .unwrap();
358
359 assert_eq!(typ, BlockType::PrivateKey);
360 assert!(!base64.is_empty());
361 assert_eq!(headers.get(HEADER_AUTOCRYPT), Some(&"mutual".to_string()));
362 }
363
364 #[test]
365 fn test_create_keypair() {
366 let keypair0 = create_keypair(EmailAddress::new("foo@bar.de").unwrap()).unwrap();
367 let keypair1 = create_keypair(EmailAddress::new("two@zwo.de").unwrap()).unwrap();
368 assert_ne!(keypair0.public, keypair1.public);
369 }
370
371 struct TestKeys {
374 alice_secret: SignedSecretKey,
375 alice_public: SignedPublicKey,
376 bob_secret: SignedSecretKey,
377 bob_public: SignedPublicKey,
378 }
379
380 impl TestKeys {
381 fn new() -> TestKeys {
382 let alice = alice_keypair();
383 let bob = bob_keypair();
384 TestKeys {
385 alice_secret: alice.secret.clone(),
386 alice_public: alice.public,
387 bob_secret: bob.secret.clone(),
388 bob_public: bob.public,
389 }
390 }
391 }
392
393 static CLEARTEXT: &[u8] = b"This is a test";
395
396 static KEYS: LazyLock<TestKeys> = LazyLock::new(TestKeys::new);
398
399 static CTEXT_SIGNED: OnceCell<String> = OnceCell::const_new();
400 static CTEXT_UNSIGNED: OnceCell<String> = OnceCell::const_new();
401
402 async fn ctext_signed() -> &'static String {
404 CTEXT_SIGNED
405 .get_or_init(|| async {
406 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
407 let compress = true;
408
409 pk_encrypt(
410 CLEARTEXT,
411 keyring,
412 Some(KEYS.alice_secret.clone()),
413 compress,
414 )
415 .await
416 .unwrap()
417 })
418 .await
419 }
420
421 async fn ctext_unsigned() -> &'static String {
423 CTEXT_UNSIGNED
424 .get_or_init(|| async {
425 let keyring = vec![KEYS.alice_public.clone(), KEYS.bob_public.clone()];
426 let compress = true;
427
428 pk_encrypt(CLEARTEXT, keyring, None, compress)
429 .await
430 .unwrap()
431 })
432 .await
433 }
434
435 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
436 async fn test_encrypt_signed() {
437 assert!(!ctext_signed().await.is_empty());
438 assert!(ctext_signed()
439 .await
440 .starts_with("-----BEGIN PGP MESSAGE-----"));
441 }
442
443 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
444 async fn test_encrypt_unsigned() {
445 assert!(!ctext_unsigned().await.is_empty());
446 assert!(ctext_unsigned()
447 .await
448 .starts_with("-----BEGIN PGP MESSAGE-----"));
449 }
450
451 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
452 async fn test_decrypt_singed() {
453 let decrypt_keyring = vec![KEYS.alice_secret.clone()];
455 let sig_check_keyring = vec![KEYS.alice_public.clone()];
456 let (msg, valid_signatures) = pk_decrypt_and_validate(
457 ctext_signed().await.as_bytes().to_vec(),
458 &decrypt_keyring,
459 &sig_check_keyring,
460 )
461 .unwrap();
462 assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
463 assert_eq!(valid_signatures.len(), 1);
464
465 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
467 let sig_check_keyring = vec![KEYS.alice_public.clone()];
468 let (msg, valid_signatures) = pk_decrypt_and_validate(
469 ctext_signed().await.as_bytes().to_vec(),
470 &decrypt_keyring,
471 &sig_check_keyring,
472 )
473 .unwrap();
474 assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
475 assert_eq!(valid_signatures.len(), 1);
476 }
477
478 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
479 async fn test_decrypt_no_sig_check() {
480 let keyring = vec![KEYS.alice_secret.clone()];
481 let (msg, valid_signatures) =
482 pk_decrypt_and_validate(ctext_signed().await.as_bytes().to_vec(), &keyring, &[])
483 .unwrap();
484 assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
485 assert_eq!(valid_signatures.len(), 0);
486 }
487
488 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
489 async fn test_decrypt_signed_no_key() {
490 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
492 let sig_check_keyring = vec![KEYS.bob_public.clone()];
493 let (msg, valid_signatures) = pk_decrypt_and_validate(
494 ctext_signed().await.as_bytes().to_vec(),
495 &decrypt_keyring,
496 &sig_check_keyring,
497 )
498 .unwrap();
499 assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
500 assert_eq!(valid_signatures.len(), 0);
501 }
502
503 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
504 async fn test_decrypt_unsigned() {
505 let decrypt_keyring = vec![KEYS.bob_secret.clone()];
506 let (msg, valid_signatures) = pk_decrypt_and_validate(
507 ctext_unsigned().await.as_bytes().to_vec(),
508 &decrypt_keyring,
509 &[],
510 )
511 .unwrap();
512 assert_eq!(msg.get_content().unwrap().unwrap(), CLEARTEXT);
513 assert_eq!(valid_signatures.len(), 0);
514 }
515}