1use std::collections::BTreeMap;
4use std::fmt;
5use std::io::Cursor;
6
7use anyhow::{Context as _, Result, bail, ensure};
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::{LogExt, info};
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> {
75 let bytes = data.as_bytes();
76 let res = Self::from_armor_single(Cursor::new(bytes));
77 let (key, _headers) = match res {
78 Err(pgp::errors::Error::NoMatchingPacket { .. }) => match Self::is_private() {
79 true => bail!("No private key packet found"),
80 false => bail!("No public key packet found"),
81 },
82 _ => res.context("rPGP error")?,
83 };
84 Ok(key)
85 }
86
87 fn to_bytes(&self) -> Vec<u8> {
89 let mut buf = Vec::new();
94 self.to_writer(&mut buf).unwrap();
95 buf
96 }
97
98 fn to_base64(&self) -> String {
100 base64::engine::general_purpose::STANDARD.encode(DcKey::to_bytes(self))
101 }
102
103 fn to_asc(&self, header: Option<(&str, &str)>) -> String;
110
111 fn dc_fingerprint(&self) -> Fingerprint;
113
114 fn is_private() -> bool;
115 fn key_id(&self) -> KeyId;
116}
117
118pub(crate) async fn load_self_public_key_opt(context: &Context) -> Result<Option<SignedPublicKey>> {
122 let Some(public_key_bytes) = context
123 .sql
124 .query_row_optional(
125 "SELECT public_key
126 FROM keypairs
127 WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
128 (),
129 |row| {
130 let bytes: Vec<u8> = row.get(0)?;
131 Ok(bytes)
132 },
133 )
134 .await?
135 else {
136 return Ok(None);
137 };
138 let public_key = SignedPublicKey::from_slice(&public_key_bytes)?;
139 Ok(Some(public_key))
140}
141
142pub(crate) async fn load_self_public_key(context: &Context) -> Result<SignedPublicKey> {
146 match load_self_public_key_opt(context).await? {
147 Some(public_key) => Ok(public_key),
148 None => {
149 let keypair = generate_keypair(context).await?;
150 Ok(keypair.public)
151 }
152 }
153}
154
155pub(crate) async fn load_self_public_keyring(context: &Context) -> Result<Vec<SignedPublicKey>> {
157 let keys = context
158 .sql
159 .query_map(
160 r#"SELECT public_key
161 FROM keypairs
162 ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
163 (),
164 |row| row.get::<_, Vec<u8>>(0),
165 |keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
166 )
167 .await?
168 .into_iter()
169 .filter_map(|bytes| SignedPublicKey::from_slice(&bytes).log_err(context).ok())
170 .collect();
171 Ok(keys)
172}
173
174pub(crate) async fn self_fingerprint(context: &Context) -> Result<&str> {
181 if let Some(fp) = context.self_fingerprint.get() {
182 Ok(fp)
183 } else {
184 let fp = load_self_public_key(context).await?.dc_fingerprint().hex();
185 Ok(context.self_fingerprint.get_or_init(|| fp))
186 }
187}
188
189pub(crate) async fn self_fingerprint_opt(context: &Context) -> Result<Option<&str>> {
196 if let Some(fp) = context.self_fingerprint.get() {
197 Ok(Some(fp))
198 } else if let Some(key) = load_self_public_key_opt(context).await? {
199 let fp = key.dc_fingerprint().hex();
200 Ok(Some(context.self_fingerprint.get_or_init(|| fp)))
201 } else {
202 Ok(None)
203 }
204}
205
206pub(crate) async fn load_self_secret_key(context: &Context) -> Result<SignedSecretKey> {
207 let private_key = context
208 .sql
209 .query_row_optional(
210 "SELECT private_key
211 FROM keypairs
212 WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
213 (),
214 |row| {
215 let bytes: Vec<u8> = row.get(0)?;
216 Ok(bytes)
217 },
218 )
219 .await?;
220 match private_key {
221 Some(bytes) => SignedSecretKey::from_slice(&bytes),
222 None => {
223 let keypair = generate_keypair(context).await?;
224 Ok(keypair.secret)
225 }
226 }
227}
228
229pub(crate) async fn load_self_secret_keyring(context: &Context) -> Result<Vec<SignedSecretKey>> {
230 let keys = context
231 .sql
232 .query_map(
233 r#"SELECT private_key
234 FROM keypairs
235 ORDER BY id=(SELECT value FROM config WHERE keyname='key_id') DESC"#,
236 (),
237 |row| row.get::<_, Vec<u8>>(0),
238 |keys| keys.collect::<Result<Vec<_>, _>>().map_err(Into::into),
239 )
240 .await?
241 .into_iter()
242 .filter_map(|bytes| SignedSecretKey::from_slice(&bytes).log_err(context).ok())
243 .collect();
244 Ok(keys)
245}
246
247impl DcKey for SignedPublicKey {
248 fn to_asc(&self, header: Option<(&str, &str)>) -> String {
249 let headers =
254 header.map(|(key, value)| BTreeMap::from([(key.to_string(), vec![value.to_string()])]));
255 let mut buf = Vec::new();
256 self.to_armored_writer(&mut buf, headers.as_ref().into())
257 .unwrap_or_default();
258 std::string::String::from_utf8(buf).unwrap_or_default()
259 }
260
261 fn is_private() -> bool {
262 false
263 }
264
265 fn dc_fingerprint(&self) -> Fingerprint {
266 self.fingerprint().into()
267 }
268
269 fn key_id(&self) -> KeyId {
270 KeyDetails::key_id(self)
271 }
272}
273
274impl DcKey for SignedSecretKey {
275 fn to_asc(&self, header: Option<(&str, &str)>) -> String {
276 let headers =
281 header.map(|(key, value)| BTreeMap::from([(key.to_string(), vec![value.to_string()])]));
282 let mut buf = Vec::new();
283 self.to_armored_writer(&mut buf, headers.as_ref().into())
284 .unwrap_or_default();
285 std::string::String::from_utf8(buf).unwrap_or_default()
286 }
287
288 fn is_private() -> bool {
289 true
290 }
291
292 fn dc_fingerprint(&self) -> Fingerprint {
293 self.fingerprint().into()
294 }
295
296 fn key_id(&self) -> KeyId {
297 KeyDetails::key_id(&**self)
298 }
299}
300
301pub(crate) trait DcSecretKey {
305 fn split_public_key(&self) -> Result<SignedPublicKey>;
307}
308
309impl DcSecretKey for SignedSecretKey {
310 fn split_public_key(&self) -> Result<SignedPublicKey> {
311 self.verify()?;
312 let unsigned_pubkey = self.public_key();
313 let mut rng = thread_rng();
314 let signed_pubkey = unsigned_pubkey.sign(
315 &mut rng,
316 &self.primary_key,
317 self.primary_key.public_key(),
318 &Password::empty(),
319 )?;
320 Ok(signed_pubkey)
321 }
322}
323
324async fn generate_keypair(context: &Context) -> Result<KeyPair> {
325 let addr = context.get_primary_self_addr().await?;
326 let addr = EmailAddress::new(&addr)?;
327 let _guard = context.generating_key_mutex.lock().await;
328
329 match load_keypair(context).await? {
331 Some(key_pair) => Ok(key_pair),
332 None => {
333 let start = tools::Time::now();
334 info!(context, "Generating keypair.");
335 let keypair = Handle::current()
336 .spawn_blocking(move || crate::pgp::create_keypair(addr))
337 .await??;
338
339 store_self_keypair(context, &keypair).await?;
340 info!(
341 context,
342 "Keypair generated in {:.3}s.",
343 time_elapsed(&start).as_secs(),
344 );
345 Ok(keypair)
346 }
347 }
348}
349
350pub(crate) async fn load_keypair(context: &Context) -> Result<Option<KeyPair>> {
351 let res = context
352 .sql
353 .query_row_optional(
354 "SELECT public_key, private_key
355 FROM keypairs
356 WHERE id=(SELECT value FROM config WHERE keyname='key_id')",
357 (),
358 |row| {
359 let pub_bytes: Vec<u8> = row.get(0)?;
360 let sec_bytes: Vec<u8> = row.get(1)?;
361 Ok((pub_bytes, sec_bytes))
362 },
363 )
364 .await?;
365
366 Ok(if let Some((pub_bytes, sec_bytes)) = res {
367 Some(KeyPair {
368 public: SignedPublicKey::from_slice(&pub_bytes)?,
369 secret: SignedSecretKey::from_slice(&sec_bytes)?,
370 })
371 } else {
372 None
373 })
374}
375
376pub(crate) async fn store_self_keypair(context: &Context, keypair: &KeyPair) -> Result<()> {
389 let mut config_cache_lock = context.sql.config_cache.write().await;
390 let new_key_id = context
391 .sql
392 .transaction(|transaction| {
393 let public_key = DcKey::to_bytes(&keypair.public);
394 let secret_key = DcKey::to_bytes(&keypair.secret);
395
396 transaction
400 .execute(
401 "INSERT INTO keypairs (public_key, private_key)
402 VALUES (?,?)",
403 (&public_key, &secret_key),
404 )
405 .context("Failed to insert keypair")?;
406
407 let new_key_id = transaction.last_insert_rowid();
408
409 transaction.execute(
414 "INSERT INTO config (keyname, value) VALUES ('key_id', ?)",
415 (new_key_id,),
416 )?;
417 Ok(Some(new_key_id))
418 })
419 .await?;
420
421 if let Some(new_key_id) = new_key_id {
422 config_cache_lock.insert("key_id".to_string(), Some(new_key_id.to_string()));
424 }
425
426 Ok(())
427}
428
429pub async fn preconfigure_keypair(context: &Context, secret_data: &str) -> Result<()> {
435 let secret = SignedSecretKey::from_asc(secret_data)?;
436 let public = secret.split_public_key()?;
437 let keypair = KeyPair { public, secret };
438 store_self_keypair(context, &keypair).await?;
439 Ok(())
440}
441
442#[derive(Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
444pub struct Fingerprint(Vec<u8>);
445
446impl Fingerprint {
447 pub fn new(v: Vec<u8>) -> Fingerprint {
449 debug_assert_eq!(v.len(), 20);
450 Fingerprint(v)
451 }
452
453 pub fn hex(&self) -> String {
458 hex::encode_upper(&self.0)
459 }
460}
461
462impl From<pgp::types::Fingerprint> for Fingerprint {
463 fn from(fingerprint: pgp::types::Fingerprint) -> Fingerprint {
464 Self::new(fingerprint.as_bytes().into())
465 }
466}
467
468impl fmt::Debug for Fingerprint {
469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470 f.debug_struct("Fingerprint")
471 .field("hex", &self.hex())
472 .finish()
473 }
474}
475
476impl fmt::Display for Fingerprint {
478 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
479 for (i, c) in self.hex().chars().enumerate() {
481 if i > 0 && i % 20 == 0 {
482 writeln!(f)?;
483 } else if i > 0 && i % 4 == 0 {
484 write!(f, " ")?;
485 }
486 write!(f, "{c}")?;
487 }
488 Ok(())
489 }
490}
491
492impl std::str::FromStr for Fingerprint {
494 type Err = anyhow::Error;
495
496 fn from_str(input: &str) -> Result<Self> {
497 let hex_repr: String = input
498 .to_uppercase()
499 .chars()
500 .filter(|&c| c.is_ascii_hexdigit())
501 .collect();
502 let v: Vec<u8> = hex::decode(&hex_repr)?;
503 ensure!(v.len() == 20, "wrong fingerprint length: {}", hex_repr);
504 let fp = Fingerprint::new(v);
505 Ok(fp)
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use std::sync::{Arc, LazyLock};
512
513 use super::*;
514 use crate::config::Config;
515 use crate::test_utils::{TestContext, alice_keypair};
516
517 static KEYPAIR: LazyLock<KeyPair> = LazyLock::new(alice_keypair);
518
519 #[test]
520 fn test_from_armored_string() {
521 let private_key = SignedSecretKey::from_asc(
522 "-----BEGIN PGP PRIVATE KEY BLOCK-----
523
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
5767yPJeQ==
577=KZk/
578-----END PGP PRIVATE KEY BLOCK-----",
579 )
580 .expect("failed to decode");
581 let binary = DcKey::to_bytes(&private_key);
582 SignedSecretKey::from_slice(&binary).expect("invalid private key");
583 }
584
585 #[test]
586 fn test_asc_roundtrip() {
587 let key = KEYPAIR.public.clone();
588 let asc = key.to_asc(Some(("spam", "ham")));
589 let key2 = SignedPublicKey::from_asc(&asc).unwrap();
590 assert_eq!(key, key2);
591
592 let key = KEYPAIR.secret.clone();
593 let asc = key.to_asc(Some(("spam", "ham")));
594 let key2 = SignedSecretKey::from_asc(&asc).unwrap();
595 assert_eq!(key, key2);
596 }
597
598 #[test]
599 fn test_from_slice_roundtrip() {
600 let public_key = KEYPAIR.public.clone();
601 let private_key = KEYPAIR.secret.clone();
602
603 let binary = DcKey::to_bytes(&public_key);
604 let public_key2 = SignedPublicKey::from_slice(&binary).expect("invalid public key");
605 assert_eq!(public_key, public_key2);
606
607 let binary = DcKey::to_bytes(&private_key);
608 let private_key2 = SignedSecretKey::from_slice(&binary).expect("invalid private key");
609 assert_eq!(private_key, private_key2);
610 }
611
612 #[test]
613 fn test_from_slice_bad_data() {
614 let mut bad_data: [u8; 4096] = [0; 4096];
615 for (i, v) in bad_data.iter_mut().enumerate() {
616 *v = (i & 0xff) as u8;
617 }
618 for j in 0..(4096 / 40) {
619 let slice = &bad_data.get(j..j + 4096 / 2 + j).unwrap();
620 assert!(SignedPublicKey::from_slice(slice).is_err());
621 assert!(SignedSecretKey::from_slice(slice).is_err());
622 }
623 }
624
625 #[test]
636 fn test_ignore_trailing_garbage() {
637 for garbage in [
639 b"\x02\xfc\xaa\x38\x4b\x5c".as_slice(),
640 b"\x02\xfc\xaa".as_slice(),
641 b"\x01\x02\x03\x04\x05".as_slice(),
642 ] {
643 let private_key = KEYPAIR.secret.clone();
644
645 let mut binary = DcKey::to_bytes(&private_key);
646 binary.extend(garbage);
647
648 let private_key2 =
649 SignedSecretKey::from_slice(&binary).expect("Failed to ignore garbage");
650
651 assert_eq!(private_key.dc_fingerprint(), private_key2.dc_fingerprint());
652 }
653 }
654
655 #[test]
656 fn test_base64_roundtrip() {
657 let key = KEYPAIR.public.clone();
658 let base64 = key.to_base64();
659 let key2 = SignedPublicKey::from_base64(&base64).unwrap();
660 assert_eq!(key, key2);
661 }
662
663 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
664 async fn test_load_self_generate_public() {
665 let t = TestContext::new().await;
666 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
667 .await
668 .unwrap();
669 let key = load_self_public_key(&t).await;
670 assert!(key.is_ok());
671 }
672
673 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
674 async fn test_load_self_generate_secret() {
675 let t = TestContext::new().await;
676 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
677 .await
678 .unwrap();
679 let key = load_self_secret_key(&t).await;
680 assert!(key.is_ok());
681 }
682
683 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
684 async fn test_load_self_generate_concurrent() {
685 use std::thread;
686
687 let t = TestContext::new().await;
688 t.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
689 .await
690 .unwrap();
691 let thr0 = {
692 let ctx = t.clone();
693 thread::spawn(move || {
694 tokio::runtime::Runtime::new()
695 .unwrap()
696 .block_on(load_self_public_key(&ctx))
697 })
698 };
699 let thr1 = {
700 let ctx = t.clone();
701 thread::spawn(move || {
702 tokio::runtime::Runtime::new()
703 .unwrap()
704 .block_on(load_self_public_key(&ctx))
705 })
706 };
707 let res0 = thr0.join().unwrap();
708 let res1 = thr1.join().unwrap();
709 assert_eq!(res0.unwrap(), res1.unwrap());
710 }
711
712 #[test]
713 fn test_split_key() {
714 let pubkey = KEYPAIR.secret.split_public_key().unwrap();
715 assert_eq!(pubkey.primary_key, KEYPAIR.public.primary_key);
716 }
717
718 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
720 async fn test_save_self_key_twice() {
721 let t = TestContext::new().await;
724 let ctx = Arc::new(t);
725
726 let nrows = || async {
727 ctx.sql
728 .count("SELECT COUNT(*) FROM keypairs;", ())
729 .await
730 .unwrap()
731 };
732 assert_eq!(nrows().await, 0);
733 store_self_keypair(&ctx, &KEYPAIR).await.unwrap();
734 assert_eq!(nrows().await, 1);
735
736 let res = store_self_keypair(&ctx, &KEYPAIR).await;
738 assert!(res.is_err());
739
740 assert_eq!(nrows().await, 1);
741 }
742
743 #[test]
744 fn test_fingerprint_from_str() {
745 let res = Fingerprint::new(vec![
746 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
747 ]);
748
749 let fp: Fingerprint = "0102030405060708090A0B0c0d0e0F1011121314".parse().unwrap();
750 assert_eq!(fp, res);
751
752 let fp: Fingerprint = "zzzz 0102 0304 0506\n0708090a0b0c0D0E0F1011121314 yyy"
753 .parse()
754 .unwrap();
755 assert_eq!(fp, res);
756
757 assert!("1".parse::<Fingerprint>().is_err());
758 }
759
760 #[test]
761 fn test_fingerprint_hex() {
762 let fp = Fingerprint::new(vec![
763 1, 2, 4, 8, 16, 32, 64, 128, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
764 ]);
765 assert_eq!(fp.hex(), "0102040810204080FF0A0B0C0D0E0F1011121314");
766 }
767
768 #[test]
769 fn test_fingerprint_to_string() {
770 let fp = Fingerprint::new(vec![
771 1, 2, 4, 8, 16, 32, 64, 128, 255, 1, 2, 4, 8, 16, 32, 64, 128, 255, 19, 20,
772 ]);
773 assert_eq!(
774 fp.to_string(),
775 "0102 0408 1020 4080 FF01\n0204 0810 2040 80FF 1314"
776 );
777 }
778}