1use std::collections::BTreeMap;
4use std::fmt;
5use std::io::Cursor;
6
7use anyhow::{bail, ensure, Context as _, Result};
8use base64::Engine as _;
9use deltachat_contact_tools::EmailAddress;
10use pgp::composed::Deserializable;
11pub use pgp::composed::{SignedPublicKey, SignedSecretKey};
12use pgp::ser::Serialize;
13use pgp::types::{KeyDetails, KeyId, Password};
14use rand::thread_rng;
15use tokio::runtime::Handle;
16
17use crate::context::Context;
18use crate::log::{info, LogExt};
19use crate::pgp::KeyPair;
20use crate::tools::{self, time_elapsed};
21
22pub(crate) trait DcKey: Serialize + Deserializable + Clone {
28 fn from_slice(bytes: &[u8]) -> Result<Self> {
30 let res = <Self as Deserializable>::from_bytes(Cursor::new(bytes));
31 if let Ok(res) = res {
32 return Ok(res);
33 }
34
35 for garbage_bytes in 3..std::cmp::min(bytes.len(), 10) {
51 let res = <Self as Deserializable>::from_bytes(Cursor::new(
52 bytes
53 .get(..bytes.len().saturating_sub(garbage_bytes))
54 .unwrap_or_default(),
55 ));
56 if let Ok(res) = res {
57 return Ok(res);
58 }
59 }
60
61 Ok(res?)
63 }
64
65 fn from_base64(data: &str) -> Result<Self> {
67 let cleaned: String = data.split_whitespace().collect();
69 let bytes = base64::engine::general_purpose::STANDARD.decode(cleaned.as_bytes())?;
70 Self::from_slice(&bytes)
71 }
72
73 fn from_asc(data: &str) -> Result<(Self, BTreeMap<String, String>)> {
78 let bytes = data.as_bytes();
79 let res = Self::from_armor_single(Cursor::new(bytes));
80 let (key, headers) = match res {
81 Err(pgp::errors::Error::NoMatchingPacket { .. }) => match Self::is_private() {
82 true => bail!("No private key packet found"),
83 false => bail!("No public key packet found"),
84 },
85 _ => res.context("rPGP error")?,
86 };
87 let headers = headers
88 .into_iter()
89 .map(|(key, values)| {
90 (
91 key.trim().to_lowercase(),
92 values
93 .last()
94 .map_or_else(String::new, |s| s.trim().to_string()),
95 )
96 })
97 .collect();
98 Ok((key, headers))
99 }
100
101 fn to_bytes(&self) -> Vec<u8> {
103 let mut buf = Vec::new();
108 self.to_writer(&mut buf).unwrap();
109 buf
110 }
111
112 fn to_base64(&self) -> String {
114 base64::engine::general_purpose::STANDARD.encode(DcKey::to_bytes(self))
115 }
116
117 fn to_asc(&self, header: Option<(&str, &str)>) -> String;
124
125 fn dc_fingerprint(&self) -> Fingerprint;
127
128 fn is_private() -> bool;
129 fn key_id(&self) -> KeyId;
130}
131
132pub(crate) async fn load_self_public_key_opt(context: &Context) -> Result<Option<SignedPublicKey>> {
136 let Some(public_key_bytes) = context
137 .sql
138 .query_row_optional(
139 "SELECT public_key
140 FROM keypairs
141 WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
142 (),
143 |row| {
144 let bytes: Vec<u8> = row.get(0)?;
145 Ok(bytes)
146 },
147 )
148 .await?
149 else {
150 return Ok(None);
151 };
152 let public_key = SignedPublicKey::from_slice(&public_key_bytes)?;
153 Ok(Some(public_key))
154}
155
156pub(crate) async fn load_self_public_key(context: &Context) -> Result<SignedPublicKey> {
160 match load_self_public_key_opt(context).await? {
161 Some(public_key) => Ok(public_key),
162 None => {
163 let keypair = generate_keypair(context).await?;
164 Ok(keypair.public)
165 }
166 }
167}
168
169pub(crate) async fn load_self_public_keyring(context: &Context) -> Result<Vec<SignedPublicKey>> {
171 let keys = context
172 .sql
173 .query_map(
174 r#"SELECT public_key
175 FROM keypairs
176 ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
177 (),
178 |row| row.get::<_, Vec<u8>>(0),
179 |keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
180 )
181 .await?
182 .into_iter()
183 .filter_map(|bytes| SignedPublicKey::from_slice(&bytes).log_err(context).ok())
184 .collect();
185 Ok(keys)
186}
187
188pub(crate) async fn self_fingerprint(context: &Context) -> Result<&str> {
195 if let Some(fp) = context.self_fingerprint.get() {
196 Ok(fp)
197 } else {
198 let fp = load_self_public_key(context).await?.dc_fingerprint().hex();
199 Ok(context.self_fingerprint.get_or_init(|| fp))
200 }
201}
202
203pub(crate) async fn self_fingerprint_opt(context: &Context) -> Result<Option<&str>> {
210 if let Some(fp) = context.self_fingerprint.get() {
211 Ok(Some(fp))
212 } else if let Some(key) = load_self_public_key_opt(context).await? {
213 let fp = key.dc_fingerprint().hex();
214 Ok(Some(context.self_fingerprint.get_or_init(|| fp)))
215 } else {
216 Ok(None)
217 }
218}
219
220pub(crate) async fn load_self_secret_key(context: &Context) -> Result<SignedSecretKey> {
221 let private_key = context
222 .sql
223 .query_row_optional(
224 "SELECT private_key
225 FROM keypairs
226 WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
227 (),
228 |row| {
229 let bytes: Vec<u8> = row.get(0)?;
230 Ok(bytes)
231 },
232 )
233 .await?;
234 match private_key {
235 Some(bytes) => SignedSecretKey::from_slice(&bytes),
236 None => {
237 let keypair = generate_keypair(context).await?;
238 Ok(keypair.secret)
239 }
240 }
241}
242
243pub(crate) async fn load_self_secret_keyring(context: &Context) -> Result<Vec<SignedSecretKey>> {
244 let keys = context
245 .sql
246 .query_map(
247 r#"SELECT private_key
248 FROM keypairs
249 ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
250 (),
251 |row| row.get::<_, Vec<u8>>(0),
252 |keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
253 )
254 .await?
255 .into_iter()
256 .filter_map(|bytes| SignedSecretKey::from_slice(&bytes).log_err(context).ok())
257 .collect();
258 Ok(keys)
259}
260
261impl DcKey for SignedPublicKey {
262 fn to_asc(&self, header: Option<(&str, &str)>) -> String {
263 let headers =
268 header.map(|(key, value)| BTreeMap::from([(key.to_string(), vec![value.to_string()])]));
269 let mut buf = Vec::new();
270 self.to_armored_writer(&mut buf, headers.as_ref().into())
271 .unwrap_or_default();
272 std::string::String::from_utf8(buf).unwrap_or_default()
273 }
274
275 fn is_private() -> bool {
276 false
277 }
278
279 fn dc_fingerprint(&self) -> Fingerprint {
280 self.fingerprint().into()
281 }
282
283 fn key_id(&self) -> KeyId {
284 KeyDetails::key_id(self)
285 }
286}
287
288impl DcKey for SignedSecretKey {
289 fn to_asc(&self, header: Option<(&str, &str)>) -> String {
290 let headers =
295 header.map(|(key, value)| BTreeMap::from([(key.to_string(), vec![value.to_string()])]));
296 let mut buf = Vec::new();
297 self.to_armored_writer(&mut buf, headers.as_ref().into())
298 .unwrap_or_default();
299 std::string::String::from_utf8(buf).unwrap_or_default()
300 }
301
302 fn is_private() -> bool {
303 true
304 }
305
306 fn dc_fingerprint(&self) -> Fingerprint {
307 self.fingerprint().into()
308 }
309
310 fn key_id(&self) -> KeyId {
311 KeyDetails::key_id(&**self)
312 }
313}
314
315pub(crate) trait DcSecretKey {
319 fn split_public_key(&self) -> Result<SignedPublicKey>;
321}
322
323impl DcSecretKey for SignedSecretKey {
324 fn split_public_key(&self) -> Result<SignedPublicKey> {
325 self.verify()?;
326 let unsigned_pubkey = self.public_key();
327 let mut rng = thread_rng();
328 let signed_pubkey = unsigned_pubkey.sign(
329 &mut rng,
330 &self.primary_key,
331 self.primary_key.public_key(),
332 &Password::empty(),
333 )?;
334 Ok(signed_pubkey)
335 }
336}
337
338async fn generate_keypair(context: &Context) -> Result<KeyPair> {
339 let addr = context.get_primary_self_addr().await?;
340 let addr = EmailAddress::new(&addr)?;
341 let _guard = context.generating_key_mutex.lock().await;
342
343 match load_keypair(context).await? {
345 Some(key_pair) => Ok(key_pair),
346 None => {
347 let start = tools::Time::now();
348 info!(context, "Generating keypair.");
349 let keypair = Handle::current()
350 .spawn_blocking(move || crate::pgp::create_keypair(addr))
351 .await??;
352
353 store_self_keypair(context, &keypair).await?;
354 info!(
355 context,
356 "Keypair generated in {:.3}s.",
357 time_elapsed(&start).as_secs(),
358 );
359 Ok(keypair)
360 }
361 }
362}
363
364pub(crate) async fn load_keypair(context: &Context) -> Result<Option<KeyPair>> {
365 let res = context
366 .sql
367 .query_row_optional(
368 "SELECT public_key, private_key
369 FROM keypairs
370 WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
371 (),
372 |row| {
373 let pub_bytes: Vec<u8> = row.get(0)?;
374 let sec_bytes: Vec<u8> = row.get(1)?;
375 Ok((pub_bytes, sec_bytes))
376 },
377 )
378 .await?;
379
380 Ok(if let Some((pub_bytes, sec_bytes)) = res {
381 Some(KeyPair {
382 public: SignedPublicKey::from_slice(&pub_bytes)?,
383 secret: SignedSecretKey::from_slice(&sec_bytes)?,
384 })
385 } else {
386 None
387 })
388}
389
390pub(crate) async fn store_self_keypair(context: &Context, keypair: &KeyPair) -> Result<()> {
403 let mut config_cache_lock = context.sql.config_cache.write().await;
404 let new_key_id = context
405 .sql
406 .transaction(|transaction| {
407 let public_key = DcKey::to_bytes(&keypair.public);
408 let secret_key = DcKey::to_bytes(&keypair.secret);
409
410 transaction
414 .execute(
415 "INSERT INTO keypairs (public_key, private_key)
416 VALUES (?,?)",
417 (&public_key, &secret_key),
418 )
419 .context("Failed to insert keypair")?;
420
421 let new_key_id = transaction.last_insert_rowid();
422
423 transaction.execute(
428 "INSERT INTO config (keyname, value) VALUES ('key_id', ?)",
429 (new_key_id,),
430 )?;
431 Ok(Some(new_key_id))
432 })
433 .await?;
434
435 if let Some(new_key_id) = new_key_id {
436 config_cache_lock.insert("key_id".to_string(), Some(new_key_id.to_string()));
438 }
439
440 Ok(())
441}
442
443pub async fn preconfigure_keypair(context: &Context, secret_data: &str) -> Result<()> {
449 let secret = SignedSecretKey::from_asc(secret_data)?.0;
450 let public = secret.split_public_key()?;
451 let keypair = KeyPair { public, secret };
452 store_self_keypair(context, &keypair).await?;
453 Ok(())
454}
455
456#[derive(Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
458pub struct Fingerprint(Vec<u8>);
459
460impl Fingerprint {
461 pub fn new(v: Vec<u8>) -> Fingerprint {
463 debug_assert_eq!(v.len(), 20);
464 Fingerprint(v)
465 }
466
467 pub fn hex(&self) -> String {
472 hex::encode_upper(&self.0)
473 }
474}
475
476impl From<pgp::types::Fingerprint> for Fingerprint {
477 fn from(fingerprint: pgp::types::Fingerprint) -> Fingerprint {
478 Self::new(fingerprint.as_bytes().into())
479 }
480}
481
482impl fmt::Debug for Fingerprint {
483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484 f.debug_struct("Fingerprint")
485 .field("hex", &self.hex())
486 .finish()
487 }
488}
489
490impl fmt::Display for Fingerprint {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 for (i, c) in self.hex().chars().enumerate() {
495 if i > 0 && i % 20 == 0 {
496 writeln!(f)?;
497 } else if i > 0 && i % 4 == 0 {
498 write!(f, " ")?;
499 }
500 write!(f, "{c}")?;
501 }
502 Ok(())
503 }
504}
505
506impl std::str::FromStr for Fingerprint {
508 type Err = anyhow::Error;
509
510 fn from_str(input: &str) -> Result<Self> {
511 let hex_repr: String = input
512 .to_uppercase()
513 .chars()
514 .filter(|&c| c.is_ascii_hexdigit())
515 .collect();
516 let v: Vec<u8> = hex::decode(&hex_repr)?;
517 ensure!(v.len() == 20, "wrong fingerprint length: {}", hex_repr);
518 let fp = Fingerprint::new(v);
519 Ok(fp)
520 }
521}
522
523#[cfg(test)]
524mod tests {
525 use std::sync::{Arc, LazyLock};
526
527 use super::*;
528 use crate::config::Config;
529 use crate::test_utils::{alice_keypair, TestContext};
530
531 static KEYPAIR: LazyLock<KeyPair> = LazyLock::new(alice_keypair);
532
533 #[test]
534 fn test_from_armored_string() {
535 let (private_key, _) = SignedSecretKey::from_asc(
536 "-----BEGIN PGP PRIVATE KEY BLOCK-----
537
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
5907yPJeQ==
591=KZk/
592-----END PGP PRIVATE KEY BLOCK-----",
593 )
594 .expect("failed to decode");
595 let binary = DcKey::to_bytes(&private_key);
596 SignedSecretKey::from_slice(&binary).expect("invalid private key");
597 }
598
599 #[test]
600 fn test_asc_roundtrip() {
601 let key = KEYPAIR.public.clone();
602 let asc = key.to_asc(Some(("spam", "ham")));
603 let (key2, hdrs) = SignedPublicKey::from_asc(&asc).unwrap();
604 assert_eq!(key, key2);
605 assert_eq!(hdrs.len(), 1);
606 assert_eq!(hdrs.get("spam"), Some(&String::from("ham")));
607
608 let key = KEYPAIR.secret.clone();
609 let asc = key.to_asc(Some(("spam", "ham")));
610 let (key2, hdrs) = SignedSecretKey::from_asc(&asc).unwrap();
611 assert_eq!(key, key2);
612 assert_eq!(hdrs.len(), 1);
613 assert_eq!(hdrs.get("spam"), Some(&String::from("ham")));
614 }
615
616 #[test]
617 fn test_from_slice_roundtrip() {
618 let public_key = KEYPAIR.public.clone();
619 let private_key = KEYPAIR.secret.clone();
620
621 let binary = DcKey::to_bytes(&public_key);
622 let public_key2 = SignedPublicKey::from_slice(&binary).expect("invalid public key");
623 assert_eq!(public_key, public_key2);
624
625 let binary = DcKey::to_bytes(&private_key);
626 let private_key2 = SignedSecretKey::from_slice(&binary).expect("invalid private key");
627 assert_eq!(private_key, private_key2);
628 }
629
630 #[test]
631 fn test_from_slice_bad_data() {
632 let mut bad_data: [u8; 4096] = [0; 4096];
633 for (i, v) in bad_data.iter_mut().enumerate() {
634 *v = (i & 0xff) as u8;
635 }
636 for j in 0..(4096 / 40) {
637 let slice = &bad_data.get(j..j + 4096 / 2 + j).unwrap();
638 assert!(SignedPublicKey::from_slice(slice).is_err());
639 assert!(SignedSecretKey::from_slice(slice).is_err());
640 }
641 }
642
643 #[test]
654 fn test_ignore_trailing_garbage() {
655 for garbage in [
657 b"\x02\xfc\xaa\x38\x4b\x5c".as_slice(),
658 b"\x02\xfc\xaa".as_slice(),
659 b"\x01\x02\x03\x04\x05".as_slice(),
660 ] {
661 let private_key = KEYPAIR.secret.clone();
662
663 let mut binary = DcKey::to_bytes(&private_key);
664 binary.extend(garbage);
665
666 let private_key2 =
667 SignedSecretKey::from_slice(&binary).expect("Failed to ignore garbage");
668
669 assert_eq!(private_key.dc_fingerprint(), private_key2.dc_fingerprint());
670 }
671 }
672
673 #[test]
674 fn test_base64_roundtrip() {
675 let key = KEYPAIR.public.clone();
676 let base64 = key.to_base64();
677 let key2 = SignedPublicKey::from_base64(&base64).unwrap();
678 assert_eq!(key, key2);
679 }
680
681 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
682 async fn test_load_self_generate_public() {
683 let t = TestContext::new().await;
684 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
685 .await
686 .unwrap();
687 let key = load_self_public_key(&t).await;
688 assert!(key.is_ok());
689 }
690
691 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
692 async fn test_load_self_generate_secret() {
693 let t = TestContext::new().await;
694 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
695 .await
696 .unwrap();
697 let key = load_self_secret_key(&t).await;
698 assert!(key.is_ok());
699 }
700
701 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
702 async fn test_load_self_generate_concurrent() {
703 use std::thread;
704
705 let t = TestContext::new().await;
706 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
707 .await
708 .unwrap();
709 let thr0 = {
710 let ctx = t.clone();
711 thread::spawn(move || {
712 tokio::runtime::Runtime::new()
713 .unwrap()
714 .block_on(load_self_public_key(&ctx))
715 })
716 };
717 let thr1 = {
718 let ctx = t.clone();
719 thread::spawn(move || {
720 tokio::runtime::Runtime::new()
721 .unwrap()
722 .block_on(load_self_public_key(&ctx))
723 })
724 };
725 let res0 = thr0.join().unwrap();
726 let res1 = thr1.join().unwrap();
727 assert_eq!(res0.unwrap(), res1.unwrap());
728 }
729
730 #[test]
731 fn test_split_key() {
732 let pubkey = KEYPAIR.secret.split_public_key().unwrap();
733 assert_eq!(pubkey.primary_key, KEYPAIR.public.primary_key);
734 }
735
736 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
738 async fn test_save_self_key_twice() {
739 let t = TestContext::new().await;
742 let ctx = Arc::new(t);
743
744 let nrows = || async {
745 ctx.sql
746 .count("SELECT COUNT(*) FROM keypairs;", ())
747 .await
748 .unwrap()
749 };
750 assert_eq!(nrows().await, 0);
751 store_self_keypair(&ctx, &KEYPAIR).await.unwrap();
752 assert_eq!(nrows().await, 1);
753
754 let res = store_self_keypair(&ctx, &KEYPAIR).await;
756 assert!(res.is_err());
757
758 assert_eq!(nrows().await, 1);
759 }
760
761 #[test]
762 fn test_fingerprint_from_str() {
763 let res = Fingerprint::new(vec![
764 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
765 ]);
766
767 let fp: Fingerprint = "0102030405060708090A0B0c0d0e0F1011121314".parse().unwrap();
768 assert_eq!(fp, res);
769
770 let fp: Fingerprint = "zzzz 0102 0304 0506\n0708090a0b0c0D0E0F1011121314 yyy"
771 .parse()
772 .unwrap();
773 assert_eq!(fp, res);
774
775 assert!("1".parse::<Fingerprint>().is_err());
776 }
777
778 #[test]
779 fn test_fingerprint_hex() {
780 let fp = Fingerprint::new(vec![
781 1, 2, 4, 8, 16, 32, 64, 128, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
782 ]);
783 assert_eq!(fp.hex(), "0102040810204080FF0A0B0C0D0E0F1011121314");
784 }
785
786 #[test]
787 fn test_fingerprint_to_string() {
788 let fp = Fingerprint::new(vec![
789 1, 2, 4, 8, 16, 32, 64, 128, 255, 1, 2, 4, 8, 16, 32, 64, 128, 255, 19, 20,
790 ]);
791 assert_eq!(
792 fp.to_string(),
793 "0102 0408 1020 4080 FF01\n0204 0810 2040 80FF 1314"
794 );
795 }
796}