1#![doc = include_str!("../README.md")]
2
3use std::{
4 collections::{
5 HashMap,
6 },
7 sync::atomic,
8};
9use std::sync::{Arc, RwLock, RwLockReadGuard};
10use libc::{
11 c_char,
12 c_int,
13 c_void,
14 size_t,
15};
16
17use sequoia_openpgp as openpgp;
18use openpgp::{
19 Fingerprint,
20 KeyHandle,
21 KeyID,
22 cert::{
23 Cert,
24 },
25 crypto::{
26 Password,
27 mem::Protected,
28 },
29 packet::{
30 Key,
31 key::{SecretParts, UnspecifiedParts, UnspecifiedRole},
32 UserID,
33 },
34 policy::{
35 HashAlgoSecurity,
36 NullPolicy,
37 StandardPolicy,
38 },
39 serialize::Serialize,
40 types::HashAlgorithm,
41};
42
43const TRACE: bool = cfg!(debug_assertions)
45 && option_env!("SEQUOIA_OCTOPUS_DISABLE_TRACING").is_none();
46
47const THUNDERBIRD_WORKAROUND: bool = true;
53
54#[allow(unused_macros)]
55macro_rules! stub {
56 ($s: ident) => {
57 #[no_mangle] pub extern "C"
58 fn $s() -> crate::RnpResult {
59 log!("\nSTUB: {}\n", stringify!($s));
60 crate::RNP_ERROR_NOT_IMPLEMENTED
61 }
62 };
63}
64
65#[allow(dead_code)]
66#[macro_use]
67pub mod error;
68use error::*;
69
70#[allow(dead_code)]
71pub mod stubs;
72
73use sequoia_ipc::Keygrip;
74
75pub mod keystore;
76use keystore::Keystore;
77
78pub mod buffer;
79use buffer::*;
80
81#[allow(dead_code)]
82pub mod flags;
83use flags::*;
84#[allow(dead_code)]
85pub mod io;
86use io::*;
87#[allow(dead_code)]
88pub mod utils;
89use utils::*;
90#[allow(dead_code)]
91pub mod conversions;
92use conversions::*;
93
94pub mod version;
95#[allow(dead_code)]
96pub mod op_verify;
97#[allow(dead_code)]
98pub mod op_encrypt;
99#[allow(dead_code)]
100pub mod op_sign;
101pub mod recombine;
102#[allow(dead_code)]
103pub mod op_generate;
104#[allow(dead_code)]
105pub mod signature;
106use signature::RnpSignature;
107#[allow(dead_code)]
108pub mod key;
109use key::RnpKey;
110#[allow(dead_code)]
111pub mod iter;
112#[allow(dead_code)]
113pub mod userid;
114use userid::RnpUserID;
115#[allow(dead_code)]
116pub mod import;
117pub mod security_rules;
118pub mod dump_packets;
119pub mod armor;
120
121#[allow(dead_code)]
124pub mod gpg;
125
126pub mod tbprofile;
127pub mod wot;
128pub mod parcimonie;
129
130pub const NP: &NullPolicy = unsafe { &NullPolicy::new() };
131
132#[allow(dead_code)]
133fn cert_dump(cert: &Cert) {
134 use openpgp::packet::key::SecretKeyMaterial;
135
136 eprintln!("Cert: {}, {}", cert.fingerprint(),
137 cert.with_policy(&StandardPolicy::new(), None)
138 .map(|cert| {
139 cert.primary_userid()
140 .map(|ua| {
141 String::from_utf8_lossy(ua.userid().value())
142 .into_owned()
143 })
144 .unwrap_or("<No UserID>"[..].into())
145 })
146 .unwrap_or("<Invalid>".into()));
147
148 for (i, k) in cert.keys().enumerate() {
149 eprint!(" {}. {}", i, k.key().fingerprint());
150 match k.key().optional_secret() {
151 Some(SecretKeyMaterial::Unencrypted(_)) => {
152 eprint!(" has unencrypted secret key material");
153 }
154 Some(SecretKeyMaterial::Encrypted(_)) => {
155 eprint!(" has encrypted secret key material");
156 }
157 None => {
158 eprint!(" has NO secret key material");
159 }
160 }
161 eprintln!("");
162 }
163}
164
165#[derive(Default)]
166pub struct RnpContext {
167 policy: Arc<RwLock<StandardPolicy<'static>>>,
168 certs: Keystore,
169 unlocked_keys: HashMap<Fingerprint, Key<SecretParts, UnspecifiedRole>>,
170 password_cb: Option<(RnpPasswordCb, *mut c_void)>,
171 plaintext_cache: recombine::PlaintextCache,
172}
173
174type RnpPasswordCb = unsafe extern "C" fn(*mut RnpContext,
175 *mut c_void,
176 *const RnpKey,
177 *const c_char,
178 *mut c_char,
179 size_t) -> bool;
180
181#[no_mangle] pub unsafe extern "C"
182fn rnp_ffi_create(ctx: *mut *mut RnpContext,
183 pub_fmt: *const c_char,
184 sec_fmt: *const c_char)
185 -> RnpResult
186{
187 rnp_function!(rnp_ffi_create, crate::TRACE);
188 assert_ptr!(ctx);
189 let pub_fmt = assert_str!(pub_fmt);
190 let sec_fmt = assert_str!(sec_fmt);
191 if pub_fmt != "GPG" || sec_fmt != "GPG" {
192 rnp_return_status!(RNP_ERROR_BAD_FORMAT);
193 }
194
195 if let Some(profile) = tbprofile::TBProfile::path() {
202 let maybe_create_keyring = |path: std::path::PathBuf| {
203 if let Ok(mut sink) =
205 std::fs::OpenOptions::new().write(true).create_new(true)
206 .open(&path)
207 {
208 match openpgp::Packet::Marker(Default::default())
217 .serialize(&mut sink)
218 {
219 Ok(_) =>
220 t!("Created new empty keyring in {}", path.display()),
221 Err(e) =>
222 t!("Creating new empty keyring in {} failed: {}",
223 path.display(), e),
224 }
225 } else if let Ok(mut sink) =
226 std::fs::OpenOptions::new().write(true).create(false)
227 .open(&path)
228 {
229 if let Ok(0) = sink.metadata().map(|m| m.len()) {
231 match openpgp::Packet::Marker(Default::default())
236 .serialize(&mut sink)
237 {
238 Ok(_) =>
239 t!("Wrote marker to empty keyring in {}",
240 path.display()),
241 Err(e) =>
242 t!("Writing marker to empty keyring in {} \
243 failed: {}", path.display(), e),
244 }
245 }
246 }
247 };
248
249 maybe_create_keyring(profile.join("pubring.gpg"));
250 maybe_create_keyring(profile.join("secring.gpg"));
251 }
252
253 let mut policy = sequoia_policy_config::ConfiguredStandardPolicy::new();
254 if let Err(e) = policy.parse_default_config() {
255 global_warn!("Reading crypto policy: {}", e);
256 }
257 let mut policy = policy.build();
258
259 let now = std::time::SystemTime::now();
264 for (algo, prop) in [
265 (HashAlgorithm::MD5, HashAlgoSecurity::CollisionResistance),
266 (HashAlgorithm::MD5, HashAlgoSecurity::SecondPreImageResistance),
267 (HashAlgorithm::SHA1, HashAlgoSecurity::CollisionResistance),
268 ]
269 {
270 let cutoff = policy.hash_cutoff(algo, prop);
271 t!("{} for {:?}: {:?}", algo, prop, cutoff);
272 if cutoff.unwrap_or(now) >= now {
273 warn!("Your crypto policy enables {} in contexts where {:?} is \
274 needed ({:?}). Unconditionally rejecting it.",
275 algo, prop, cutoff);
276 policy.reject_hash_property_at(
277 algo, prop, std::time::UNIX_EPOCH);
278 }
279 }
280
281 *ctx = Box::into_raw(Box::new(RnpContext {
282 policy: Arc::new(RwLock::new(policy)),
283 ..Default::default()
284 }));
285 rnp_success!()
286}
287
288#[no_mangle] pub unsafe extern "C"
289fn rnp_ffi_destroy(ctx: *mut RnpContext) -> RnpResult {
290 rnp_function!(rnp_ffi_destroy, crate::TRACE);
291 arg!(ctx);
292 if ! ctx.is_null() {
293 drop(Box::from_raw(ctx));
294 }
295 rnp_success!()
296}
297
298#[no_mangle] pub unsafe extern "C"
299fn rnp_ffi_set_log_fd(ctx: *mut RnpContext, _fd: c_int) -> RnpResult {
300 rnp_function!(rnp_ffi_set_log_fd, crate::TRACE);
301 let _ = assert_ptr_mut!(ctx);
302 rnp_success!()
303}
304
305#[no_mangle] pub unsafe extern "C"
306fn rnp_ffi_set_pass_provider(ctx: *mut RnpContext,
307 cb: RnpPasswordCb,
308 cookie: *mut c_void)
309 -> RnpResult {
310 rnp_function!(rnp_ffi_set_pass_provider, crate::TRACE);
311 let ctx = assert_ptr_mut!(ctx);
312 arg!(cb);
313 arg!(cookie);
314 ctx.password_cb = Some((cb, cookie));
315 rnp_success!()
316}
317
318impl RnpContext {
319 pub fn policy(&self) -> RwLockReadGuard<StandardPolicy<'static>> {
320 self.policy.read().unwrap()
321 }
322
323 pub fn insert_cert(&mut self, cert: Cert) {
333 self.certs.write().insert(cert.strip_secret_key_material());
334 }
335
336 pub fn insert_cert_external(&mut self, cert: Cert) {
348 self.certs.write().insert_external(cert.strip_secret_key_material());
349 }
350
351 pub fn insert_key(&mut self, cert: Cert) {
361 self.certs.write().insert(cert);
362 }
363
364 pub fn cert(&self, by: &RnpIdentifier) -> Option<Cert> {
378 rnp_function!(RnpContext::cert, TRACE);
379
380 use RnpIdentifier::*;
381 let cert = match by {
382 UserID(id) => self.cert_by_userid(id),
383 KeyID(id) => self.cert_by_subkey_id(id),
384 Fingerprint(fp) => self.cert_by_subkey_fp(fp),
385 Keygrip(grip) => self.cert_by_subkey_grip(grip),
386 };
387
388 t!("Lookup by {:?} returned cert {:?}",
389 by,
390 cert.as_ref().map(|c| c.fingerprint()));
391
392 cert
393 }
394
395 pub fn cert_by_userid(&self, uid: &UserID) -> Option<Cert> {
411 let mut r_cert = None;
412
413 for cert in self.certs.read().iter() {
415 if cert.userids().any(|u| u.userid() == uid) {
416 r_cert = Some(cert.clone());
417 break;
418 }
419 }
420
421 r_cert
422 }
423
424 pub fn cert_by_subkey_handle(&self, handle: &KeyHandle) -> Option<Cert> {
432 match handle {
433 KeyHandle::Fingerprint(fp) => self.cert_by_subkey_fp(fp),
434 KeyHandle::KeyID(id) => self.cert_by_subkey_id(id),
435 }
436 }
437
438 pub fn cert_by_subkey_fp(&self, fp: &Fingerprint) -> Option<Cert> {
446 self.certs.read().by_fp(fp).nth(0).map(|c| c.clone())
447 }
448
449 pub fn cert_by_subkey_id(&self, id: &KeyID) -> Option<Cert> {
457 let ks = self.certs.read();
458
459 let r = ks.by_primary_id(id).nth(0)
460 .or_else(|| ks.by_subkey_id(id).nth(0))
461 .map(|c| c.clone());
462 r
463 }
464
465 pub fn cert_by_subkey_grip(&self, grip: &Keygrip) -> Option<Cert> {
473 let ks = self.certs.read();
474
475 let r = ks.by_primary_grip(grip).nth(0)
476 .or_else(|| ks.by_subkey_grip(grip).nth(0))
477 .map(|c| c.clone());
478 r
479 }
480}
481
482#[derive(Debug)]
483pub enum RnpPasswordFor {
484 AddSubkey,
485 AddUserID,
486 Sign,
487 Decrypt,
488 Unlock,
489 Protect,
490 Unprotect,
491 DecryptSymmetric,
492 EncryptSymmetric,
493}
494
495impl RnpPasswordFor {
496 fn pgp_context(&self) -> *const c_char {
497 use RnpPasswordFor::*;
498 (match self {
499 AddSubkey => b"add subkey\x00".as_ptr(),
500 AddUserID => b"add userid\x00".as_ptr(),
501 Sign => b"sign\x00".as_ptr(),
502 Decrypt => b"decrypt\x00".as_ptr(),
503 Unlock => b"unlock\x00".as_ptr(),
504 Protect => b"protect\x00".as_ptr(),
505 Unprotect => b"unprotect\x00".as_ptr(),
506 DecryptSymmetric => b"decrypt (symmetric)\x00".as_ptr(),
507 EncryptSymmetric => b"encrypt (symmetric)\x00".as_ptr(),
508 }) as *const c_char
509 }
510}
511
512impl RnpContext {
513 pub fn request_password(&mut self,
514 key: Option<&RnpKey>,
515 reason: RnpPasswordFor) -> Option<Password> {
516 rnp_function!(RnpContext::request_password, TRACE);
517 t!("key = {:?}, reason = {:?}", key.map(|k| k.fingerprint()), reason);
518
519 if let Some((f, cookie)) = self.password_cb {
520 let mut buf: Protected = vec![0; 128].into();
521 let len = buf.len();
522 let ok = unsafe {
523 f(self,
524 cookie,
525 key.map(|k| k as *const _).unwrap_or(std::ptr::null()),
526 reason.pgp_context(),
527 buf.as_mut().as_mut_ptr() as *mut c_char,
528 len)
529 };
530
531 if ! ok {
532 t!("password_cb returned failure");
533 return None;
534 }
535
536 if let Some(got) = buf.iter().position(|b| *b == 0) {
537 t!("password_cb returned a password");
538 Some(Password::from(&buf[..got]))
539 } else {
540 eprintln!("sequoia-octopus: given password exceeded buffer");
541 None
542 }
543 } else {
544 t!("No password_cb set");
545 None
546 }
547 }
548
549 pub fn decrypt_key_for(&mut self,
551 cert: Option<&Cert>,
552 mut key: Key<SecretParts, UnspecifiedRole>,
553 reason: RnpPasswordFor)
554 -> openpgp::Result<Key<SecretParts, UnspecifiedRole>>
555 {
556 rnp_function!(RnpContext::decrypt_key_for, TRACE);
557 t!("cert = {:?}, key = {}, reason = {:?}",
558 cert.map(|c| c.fingerprint()),
559 key.fingerprint(),
560 reason);
561
562 if ! key.has_unencrypted_secret() {
563 if let Some(k) = self.unlocked_keys.get(&key.fingerprint()) {
564 t!("Found unlocked key in cache");
567 return Ok(k.clone());
568 }
569
570 let rnp_key = if let Some(cert) = cert {
571 RnpKey::new(self, key.into(), cert)
572 } else {
573 RnpKey::without_cert(self, key.into())
574 };
575
576 if let Some(pw) = self.request_password(Some(&rnp_key), reason) {
577 key = Key::<UnspecifiedParts, UnspecifiedRole>::from(rnp_key)
578 .parts_into_secret()?;
579 let k = key.clone();
580 key.secret_mut().decrypt_in_place(&k, &pw)
581 .map_err(|_| Error::BadPassword)?;
582 t!("Key decrypted successfully")
583 } else {
584 return Err(anyhow::anyhow!("no password given"));
585 }
586 } else {
587 t!("Key is not encrypted, nothing to do");
588 }
589 Ok(key)
590 }
591
592 pub fn key_is_locked(&mut self, key: &Key<SecretParts, UnspecifiedRole>)
594 -> bool {
595 ! self.unlocked_keys.contains_key(&key.fingerprint())
596 }
597
598 pub fn key_lock(&mut self, key: &Key<SecretParts, UnspecifiedRole>) {
600 self.unlocked_keys.remove(&key.fingerprint());
601 }
602
603 pub fn key_unlock(&mut self,
608 mut key: Key<SecretParts, UnspecifiedRole>,
609 password: Option<Password>)
610 -> openpgp::Result<()>
611 {
612 rnp_function!(RnpContext::key_unlock, crate::TRACE);
613 t!("key: {}; password: {}",
614 key.fingerprint(),
615 if password.is_some() { "Some(_)" } else { "None" });
616
617 if ! key.has_unencrypted_secret() {
618 if let Some(pw) = password
619 .or_else(|| self.request_password(
620 None, RnpPasswordFor::Unlock))
621 {
622 let k = key.clone();
623 key.secret_mut().decrypt_in_place(&k, &pw)
624 .map_err(|_| Error::BadPassword)?;
625 } else {
626 return Err(anyhow::anyhow!("no password given"));
627 }
628 }
629
630 assert!(key.has_unencrypted_secret());
631 self.unlocked_keys.insert(key.fingerprint(), key);
632 Ok(())
633 }
634
635 pub fn key_unlocked_ref(&self,
638 key: &Key<UnspecifiedParts, UnspecifiedRole>)
639 -> Option<&Key<SecretParts, UnspecifiedRole>>
640 {
641 self.unlocked_keys.get(&key.fingerprint())
642 }
643}
644
645#[no_mangle] pub unsafe extern "C"
646fn rnp_load_keys(ctx: *mut RnpContext,
647 format: *const c_char,
648 input: *mut RnpInput,
649 flags: RnpLoadSaveFlags)
650 -> RnpResult {
651 rnp_function!(rnp_load_keys, TRACE);
652
653 let ctx = assert_ptr_mut!(ctx);
654 let format = assert_str!(format);
655 let input = assert_ptr_mut!(input);
656 arg!(flags);
657
658 static BANNER_SHOWN: atomic::AtomicBool = atomic::AtomicBool::new(false);
659 if ! BANNER_SHOWN.load(atomic::Ordering::Relaxed) {
660 warn!("Your Thunderbird is using Sequoia's Octopus, version {}\n\
661 (sequoia-openpgp: {}). For details, and to report issues please\n\
662 see https://gitlab.com/sequoia-pgp/sequoia-octopus-librnp .",
663 env!("CARGO_PKG_VERSION"), sequoia_openpgp::VERSION);
664 if let Some(path) = crate::tbprofile::TBProfile::path() {
665 warn!("Your Thunderbird profile appears to be: {:?}", path);
666 } else {
667 warn!("Failed to detect your Thunderbird profile. Please report\n\
668 open an issue at https://gitlab.com/sequoia-pgp/sequoia-octopus-librnp .");
669 }
670 BANNER_SHOWN.store(true, atomic::Ordering::Relaxed);
671 }
672
673 if format != "GPG" {
674 rnp_return_status!(RNP_ERROR_BAD_FORMAT);
675 }
676
677 let input_size = input.size();
678
679 match flags {
680 RNP_LOAD_SAVE_PUBLIC_KEYS => {
681 if let Ok(input_size) = input_size {
682 if let Some(profile) = tbprofile::TBProfile::path() {
683 let pubring = profile.join("pubring.gpg");
684 if let Ok(pubring) = std::fs::metadata(pubring) {
685 let file_size = pubring.len();
686 t!("input is {} bytes, pubring.gpg is {} bytes.",
687 input_size, file_size);
688 if input_size == file_size {
689 t!("Looks like a match. Periodically flushing \
690 the keystore to disk.");
691 rnp_try_or!((*ctx).certs.set_directory(profile),
692 RNP_ERROR_GENERIC);
693 } else {
694 t!("pubring.gpg does not match input. \
695 Conservatively disabling flushing the \
696 keystore to disk.");
697 }
698 }
699 }
700 }
701
702 if let Err(err) = (*ctx).certs.load_gpg_keyring_in_background(
704 (*ctx).policy.clone())
705 {
706 warn!("Import gpg's keyring: {}", err);
707 }
708
709 let policy = ctx.policy().clone();
710 rnp_try_or!(ctx.certs.start_parcimonie(policy), RNP_ERROR_GENERIC);
711 }
712 RNP_LOAD_SAVE_SECRET_KEYS => (),
713 f => {
714 warn!("sequoia-octopus: unexpected flags to rnp_load_keys: {:x}",
715 f);
716 rnp_return_status!(RNP_ERROR_BAD_PARAMETERS);
717 },
718 }
719
720 use std::io::Read;
721 let mut data = Vec::new();
722 if let Err(err) = input.read_to_end(&mut data)
723 {
724 warn!("sequoia-octopus: Error reading input: {}", err);
725 rnp_return_status!(RNP_ERROR_GENERIC);
726 }
727
728 let policy = (*ctx).policy.clone();
729 if let Err(err) = (*ctx).certs.load_keyring_in_background(
730 data, flags == RNP_LOAD_SAVE_SECRET_KEYS, policy)
731 {
732 warn!("sequoia-octopus: Error reading certs: {}", err);
733 rnp_return_status!(RNP_ERROR_GENERIC);
734 }
735
736 rnp_success!()
737}
738
739#[no_mangle] pub unsafe extern "C"
740fn rnp_save_keys(ctx: *mut RnpContext,
741 format: *const c_char,
742 output: *mut RnpOutput,
743 flags: RnpLoadSaveFlags)
744 -> RnpResult {
745 rnp_function!(rnp_save_keys, TRACE);
746 let ctx = assert_ptr_mut!(ctx);
747 let format = assert_str!(format);
748 let output = assert_ptr_mut!(output);
749 if format != "GPG" {
750 rnp_return_status!(RNP_ERROR_BAD_FORMAT);
751 }
752
753 let mut r = Ok(());
754 let mut count = 0;
755 match flags {
756 RNP_LOAD_SAVE_PUBLIC_KEYS => {
757 let _ = (*ctx).certs.block_on_load();
758 for cert in (*ctx).certs.read().to_save().filter(|cert| ! cert.is_tsk()) {
759 if let Err(err) = cert.serialize(output) {
760 r = Err(err);
761 break;
762 } else {
763 count += 1;
764 }
765 }
766 },
767 RNP_LOAD_SAVE_SECRET_KEYS => {
768 let _ = (*ctx).certs.block_on_load();
769 for cert in (*ctx).certs.read().to_save().filter(|cert| cert.is_tsk()) {
770 if let Err(err) = cert.as_tsk().serialize(output) {
771 r = Err(err);
772 break;
773 } else {
774 count += 1;
775 }
776 }
777 }
778 f => {
779 warn!("unexpected flags to rnp_save_keys: {:x}", f);
780 rnp_return_status!(RNP_ERROR_BAD_PARAMETERS);
781 },
782 };
783
784 if count == 0 {
785 if let Err(err) = openpgp::Packet::Marker(Default::default())
791 .serialize(output)
792 {
793 if r.is_ok() {
794 r = Err(err);
795 }
796 }
797 }
798
799 rnp_return_status!(if let Err(err) = r {
800 warn!("failed saving keys: {}", err);
801 RNP_ERROR_GENERIC
802 } else {
803 RNP_SUCCESS
804 })
805}
806
807#[no_mangle] pub unsafe extern "C"
808fn rnp_get_public_key_count(ctx: *mut RnpContext,
809 count: *mut size_t)
810 -> RnpResult {
811 rnp_function!(rnp_get_public_key_count, crate::TRACE);
812 let ctx = assert_ptr_mut!(ctx);
813 let count = assert_ptr_mut!(count);
814
815 let _ = ctx.certs.block_on_load();
818
819 let mut ks = ctx.certs.write();
821 ks.key_on_agent_hard(&Fingerprint::V4(Default::default()));
822 drop(ks);
823
824 let ks = ctx.certs.read();
825 *count = ks.iter().filter(|cert| {
826 if cert.is_tsk() {
827 false
828 } else if ks.key_on_agent(&cert.fingerprint()).0 {
829 false
830 } else {
831 true
832 }
833 }).count();
834 t!("-> {}", *count);
835 rnp_success!()
836}
837
838#[no_mangle] pub unsafe extern "C"
839fn rnp_get_secret_key_count(ctx: *mut RnpContext,
840 count: *mut size_t)
841 -> RnpResult {
842 rnp_function!(rnp_get_secret_key_count, TRACE);
843 let ctx = assert_ptr_mut!(ctx);
844 let count = assert_ptr_mut!(count);
845
846 let _ = ctx.certs.block_on_load();
849
850 let mut ks = ctx.certs.write();
852 ks.key_on_agent_hard(&Fingerprint::V4(Default::default()));
853 drop(ks);
854
855 let ks = ctx.certs.read();
856 *count = ks.iter().filter(|cert| {
857 if cert.is_tsk() {
858 true
859 } else {
860 let fpr = &cert.fingerprint();
861 ks.key_on_agent(fpr).0
862 }
863 }).count();
864 t!("-> {}", *count);
865 rnp_success!()
866}