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