1use std::{
108 convert::TryFrom,
109 path::Path,
110 path::PathBuf,
111 ops::Deref,
112 ops::DerefMut,
113 pin::Pin,
114};
115
116use sequoia_openpgp as openpgp;
117use sequoia_ipc as ipc;
118use openpgp::{
119 Cert,
120 cert::ValidCert,
121 crypto::{
122 self,
123 Password,
124 S2K,
125 mem::Protected,
126 mpi::SecretKeyChecksum,
127 },
128 fmt::hex,
129 packet::{
130 Key,
131 key::{
132 KeyRole,
133 PublicParts,
134 SecretParts,
135 UnspecifiedRole,
136 SecretKeyMaterial,
137 },
138 SKESK,
139 },
140 policy::Policy,
141 types::{
142 HashAlgorithm,
143 Timestamp,
144 },
145};
146use ipc::{
147 Keygrip,
148 sexp::Sexp,
149};
150
151use futures::stream::StreamExt;
152
153#[macro_use]
154mod macros;
155
156mod babel;
157mod utils;
158
159pub mod assuan;
160use assuan::Response;
161use assuan::escape;
162
163pub mod gnupg;
164pub use gnupg::Context;
165
166pub mod keyinfo;
167pub mod cardinfo;
168
169#[cfg(test)]
170mod tests;
171
172trace_module!(TRACE);
173
174pub use sequoia_ipc;
178
179#[derive(thiserror::Error, Debug)]
180#[non_exhaustive]
182pub enum Error {
183 #[error("GnuPG's home directory ({0}) doesn't exist")]
185 GnuPGHomeMissing(PathBuf),
186 #[error("Unknown key: {0}")]
188 UnknownKey(Keygrip),
189 #[error("No smartcards are connected")]
191 NoSmartcards,
192 #[error("{0} already exists: {1}")]
194 KeyExists(Keygrip, String),
195
196 #[error(transparent)]
197 Io(#[from] std::io::Error),
198
199 #[error(transparent)]
200 Utf8(#[from] std::str::Utf8Error),
201
202 #[error(transparent)]
203 Assuan(#[from] assuan::Error),
204
205 #[error(transparent)]
206 GnuPG(#[from] gnupg::Error),
207
208 #[error(transparent)]
209 KeyInfo(#[from] keyinfo::Error),
210
211 #[error(transparent)]
212 OpenPGP(#[from] openpgp::Error),
213
214 #[error(transparent)]
215 Other(#[from] anyhow::Error),
216}
217
218type Result<R, E=Error> = std::result::Result<R, E>;
219
220#[derive(Debug, Clone, PartialEq, Eq)]
222pub enum PinentryMode {
223 Ask,
225 Cancel,
227 Error,
229 Loopback,
231}
232
233impl Default for PinentryMode {
234 fn default() -> Self {
235 PinentryMode::Ask
236 }
237}
238
239impl PinentryMode {
240 pub fn as_str(&self) -> &'static str {
242 match self {
243 PinentryMode::Ask => "ask",
244 PinentryMode::Cancel => "cancel",
245 PinentryMode::Error => "error",
246 PinentryMode::Loopback => "loopback",
247 }
248 }
249}
250
251impl std::fmt::Display for PinentryMode {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 write!(f, "{}", self.as_str())
254 }
255}
256
257impl std::str::FromStr for PinentryMode {
258 type Err = anyhow::Error;
259
260 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
261 match s.to_lowercase().as_str() {
262 "ask" => Ok(PinentryMode::Ask),
263 "default" => Ok(PinentryMode::Ask),
264 "cancel" => Ok(PinentryMode::Cancel),
265 "error" => Ok(PinentryMode::Error),
266 "loopback" => Ok(PinentryMode::Loopback),
267 _ => Err(anyhow::anyhow!("Unknown pinentry mode {:?}", s)),
268 }
269 }
270}
271
272fn trace_data_sent(data: &[u8]) {
273 tracer!(TRACE, "trace_data_sent");
274 let mut data = stfu8::encode_u8(data);
275 if data.len() > 80 && data.starts_with("D ") {
276 data = format!("{}... ({} bytes)",
277 data.chars().take(65).collect::<String>(),
278 data.len());
279 }
280 t!("SENT: {}", data);
281}
282
283fn trace_data_received(data: &[u8]) {
284 tracer!(TRACE, "trace_data_received");
285 let mut data = stfu8::encode_u8(data);
286 if data.len() > 80 && data.starts_with("D ") {
287 data = format!("{}... ({} bytes)",
288 data.chars().take(65).collect::<String>(),
289 data.len());
290 }
291 t!("RECV: {}", data);
292}
293
294#[derive(Debug)]
296pub struct Agent {
297 socket_path: PathBuf,
298 c: assuan::Client,
299 pinentry_mode: Option<PinentryMode>,
300
301 version: String,
303
304 restricted: bool,
306}
307
308impl Deref for Agent {
309 type Target = assuan::Client;
310
311 fn deref(&self) -> &Self::Target {
312 &self.c
313 }
314}
315
316impl DerefMut for Agent {
317 fn deref_mut(&mut self) -> &mut Self::Target {
318 &mut self.c
319 }
320}
321
322impl futures::Stream for Agent {
323 type Item = Result<assuan::Response>;
324
325 fn poll_next(mut self: Pin<&mut Self>, cx: &mut futures::task::Context<'_>)
331 -> futures::task::Poll<Option<Self::Item>>
332 {
333 Pin::new(&mut self.c).poll_next(cx)
334 }
335}
336
337impl Agent {
338 pub async fn connect(ctx: &gnupg::Context) -> Result<Self> {
346 tracer!(TRACE, "connect");
347
348 async fn transaction(ctx: &gnupg::Context) -> Result<Agent> {
349 t!("Starting daemon if not running");
350
351 let homedir = ctx.homedir();
355
356 #[cfg(not(windows))]
362 if let Some(homedir) = homedir {
363 if ! homedir.exists() {
364 return Err(Error::GnuPGHomeMissing(
365 homedir.to_path_buf()).into());
366 }
367 }
368
369 ctx.start("gpg-agent")?;
371
372 t!("Connecting to daemon");
373 let path = ctx.socket("agent")?;
374 Agent::connect_to_agent(path).await
375 }
376
377 transaction(ctx).await.map_err(|e| {
378 t!("failed: {}", e);
379 e
380 })
381 }
382
383 pub async fn connect_to<P>(homedir: P) -> Result<Self>
391 where P: AsRef<Path>
392 {
393 let ctx = gnupg::Context::with_homedir(homedir)?;
394 Self::connect(&ctx).await
395 }
396
397 pub async fn connect_to_default() -> Result<Self>
405 {
406 let ctx = gnupg::Context::new()?;
407 Self::connect(&ctx).await
408 }
409
410 pub async fn connect_to_ephemeral() -> Result<Self>
415 {
416 let ctx = gnupg::Context::ephemeral()?;
417 Self::connect(&ctx).await
418 }
419
420 pub async fn connect_to_agent<P>(socket_path: P) -> Result<Self>
425 where P: AsRef<Path>
426 {
427 let socket_path = socket_path.as_ref();
428
429 let mut connection = assuan::Client::connect(socket_path).await?;
430 if TRACE.load(std::sync::atomic::Ordering::Relaxed) {
431 connection.trace_data_sent(Box::new(trace_data_sent));
432 connection.trace_data_received(Box::new(trace_data_received));
433 }
434
435 let mut agent = Agent {
436 socket_path: socket_path.to_path_buf(),
437 c: connection,
438 pinentry_mode: None,
439 restricted: false,
440 version: "unknown".into(),
441 };
442
443 let restricted =
445 agent.send_simple("GETINFO restricted").await.is_ok();
446 agent.restricted = restricted;
447
448 if let Ok(v) = agent.get_agent_version().await {
449 agent.version = v;
450 }
451
452 Ok(agent)
453 }
454
455 async fn get_agent_version(&mut self) -> Result<String> {
457 self.send("GETINFO version")?;
458
459 let mut version = Vec::new();
460 while let Some(response) = self.next().await {
461 let response = response?;
462 match response {
463 Response::Ok { .. } => break,
464 Response::Comment { .. } => (),
465 Response::Data { partial } =>
466 version.extend_from_slice(&partial[..]),
467 Response::Error { message, .. } =>
468 return self.operation_failed(&message).await,
469 response =>
470 return protocol_error(&response),
471 }
472 }
473
474 self.next().await; Ok(String::from_utf8(version).map_err(|e| e.utf8_error())?)
476 }
477
478 pub fn is_restricted(&self) -> bool {
480 self.restricted
481 }
482
483 pub async fn reload(&mut self) -> Result<()>
485 {
486 self.send_simple("RELOADAGENT").await?;
487 Ok(())
488 }
489
490 pub fn set_pinentry_mode(&mut self, mode: PinentryMode) {
492 self.pinentry_mode = Some(mode);
493 }
494
495 pub fn suppress_pinentry(mut self) -> Self {
500 self.pinentry_mode = Some(PinentryMode::Error);
501 self
502 }
503
504 pub async fn has_key(&mut self,
506 key: &Key<PublicParts, UnspecifiedRole>)
507 -> Result<bool>
508 {
509 let grip = Keygrip::of(key.mpis())?;
510 Ok(self.send_simple(format!("HAVEKEY {}", grip)).await.is_ok())
511 }
512
513 pub async fn list_keys(&mut self) -> Result<keyinfo::KeyInfoList> {
515 self.send("KEYINFO --list")?;
516
517 let mut keys = Vec::new();
518 while let Some(response) = self.next().await {
519 let response = response?;
520 match response {
521 Response::Ok { .. } => break,
522 Response::Comment { .. } => (),
523 Response::Status { ref keyword, ref message } => {
524 if keyword == "KEYINFO" {
525 keys.push(keyinfo::KeyInfo::parse(message)?)
526 } else {
527 return protocol_error(&response);
528 }
529 }
530 Response::Inquire { .. } => return protocol_error(&response),
532 Response::Error { ref message, .. } =>
533 return self.operation_failed(message).await,
534 response =>
535 return protocol_error(&response),
536 }
537 }
538
539 self.next().await; Ok(keyinfo::KeyInfoList::from_iter(keys.into_iter()))
541 }
542
543 pub async fn key_info(&mut self, keygrip: &Keygrip) -> Result<keyinfo::KeyInfo> {
545 self.send(format!("KEYINFO {}", keygrip.to_string()))?;
546
547 let mut keyinfo = None;
548 while let Some(response) = self.next().await {
549 let response = response?;
550 match response {
551 Response::Ok { .. } => break,
552 Response::Comment { .. } => (),
553 Response::Status { ref keyword, ref message } => {
554 if keyword == "KEYINFO" {
555 if keyinfo.is_none() {
556 keyinfo = Some(keyinfo::KeyInfo::parse(message)?);
557 } else {
558 return protocol_error(&response);
560 }
561 } else {
562 return protocol_error(&response);
563 }
564 }
565 Response::Inquire { .. } => return protocol_error(&response),
567 Response::Error { ref message, .. } =>
568 return self.operation_failed(message).await,
569 response =>
570 return protocol_error(&response),
571 }
572 }
573
574 self.next().await; if let Some(keyinfo) = keyinfo {
576 Ok(keyinfo)
577 } else {
578 Err(Error::UnknownKey(keygrip.clone()).into())
579 }
580 }
581
582 pub async fn card_info(&mut self) -> Result<cardinfo::CardInfo> {
586 self.send("learn --sendinfo")?;
587
588 let mut raw: Vec<(String, String)> = Vec::new();
589 while let Some(response) = self.next().await {
590 let response = response?;
591 match response {
592 Response::Ok { .. } => break,
593 Response::Comment { .. } => (),
594 Response::Status { ref keyword, ref message } => {
595 if keyword != "PROGRESS" {
597 raw.push((keyword.clone(), message.clone()));
598 }
599 }
600 Response::Inquire { .. } => return protocol_error(&response),
602 Response::Error { ref message, .. } => {
603 if message.as_ref().map(|m| m.starts_with("100663406 "))
604 .unwrap_or(false)
605 {
606 return self.operation_failed_as(
608 Error::NoSmartcards.into()).await;
609 } else {
610 return self.operation_failed(message).await;
611 }
612 }
613 response =>
614 return protocol_error(&response),
615 }
616 }
617
618 self.next().await; if raw.is_empty() {
621 Err(Error::NoSmartcards.into())
622 } else {
623 cardinfo::CardInfo::parse(raw)
624 }
625 }
626
627 pub async fn import(&mut self,
642 policy: &dyn Policy,
643 cert: &Cert,
644 key: &Key<SecretParts, UnspecifiedRole>,
645 unattended: bool,
646 overwrite: bool)
647 -> Result<bool>
648 {
649 use ipc::sexp::*;
650
651 let keygrip = Keygrip::of(key.mpis())?;
652
653 fn c(name: &str, data: &[u8]) -> Sexp {
655 Sexp::List(vec![Sexp::String(name.as_bytes().into()),
656 Sexp::String(data.into())])
657 }
658
659 fn s(name: &str, data: impl ToString) -> Sexp {
661 c(name, data.to_string().as_bytes())
662 }
663
664 fn add_signed_mpi(list: &mut Vec<Sexp>, v: &[u8]) {
665 let mut v = v.to_vec();
666
667 if v[0] & 0x80 > 0 {
671 v.insert(0, 0);
672 }
673
674 add_raw(list, "_", &v);
675 }
676
677 fn add(list: &mut Vec<Sexp>, mpi: &mpi::MPI) {
678 add_signed_mpi(list, mpi.value());
679 }
680 fn addp(list: &mut Vec<Sexp>, checksum: &mut u16, mpi: &mpi::ProtectedMPI) {
681 add_signed_mpi(list, mpi.value());
682
683 use openpgp::serialize::MarshalInto;
684 *checksum = checksum.wrapping_add(
685 mpi.to_vec().expect("infallible").iter()
686 .fold(0u16, |acc, v| acc.wrapping_add(*v as u16)));
687 }
688
689 fn add_ecc_scalar(list: &mut Vec<Sexp>,
690 mode: EccScalarChecksumMode,
691 checksum: &mut u16,
692 s: &mpi::ProtectedMPI)
693 {
694 match mode {
695 EccScalarChecksumMode::MPI =>
696 addp(list, checksum, s),
697
698 EccScalarChecksumMode::SOS => {
699 let mut v = s.value().to_vec();
700
701 if *v.get(0).unwrap_or(&0) & 0x80 > 0 {
705 v.insert(0, 0);
706 }
707
708 add_raw(list, "_", &v);
709
710 let nbits = v.len() as u32 * 8
712 - s.value().get(0).map(|o| o.leading_zeros())
713 .unwrap_or(0);
714
715 *checksum = checksum
717 .wrapping_add(nbits as u16 >> 8)
718 .wrapping_add(nbits as u16 & 0xff)
719 .wrapping_add(v.iter().fold(
720 0u16, |acc, v| acc.wrapping_add(*v as u16)));
721 },
722 }
723 }
724
725 fn add_raw(list: &mut Vec<Sexp>, name: &str, data: &[u8]) {
726 list.push(Sexp::String(name.into()));
727 list.push(Sexp::String(data.into()));
728 }
729
730 use openpgp::crypto::mpi::{self, PublicKey};
731 let mut skey = vec![Sexp::String("skey".into())];
732 let curve = match key.mpis() {
733 PublicKey::RSA { e, n, } => {
734 add(&mut skey, n);
735 add(&mut skey, e);
736 None
737 },
738 PublicKey::DSA { p, q, g, y, } => {
739 add(&mut skey, p);
740 add(&mut skey, q);
741 add(&mut skey, g);
742 add(&mut skey, y);
743 None
744 },
745 PublicKey::ElGamal { p, g, y, } => {
746 add(&mut skey, p);
747 add(&mut skey, g);
748 add(&mut skey, y);
749 None
750 },
751 PublicKey::EdDSA { curve, q, }
752 | PublicKey::ECDSA { curve, q, }
753 | PublicKey::ECDH { curve, q, .. } => {
754 add(&mut skey, q);
755 Some(curve.clone())
756 },
757 PublicKey::Unknown { mpis, rest, } => {
758 for m in mpis.iter() {
759 add(&mut skey, m);
760 }
761 add_raw(&mut skey, "_", rest);
762 None
763 },
764 _ => return
765 Err(openpgp::Error::UnsupportedPublicKeyAlgorithm(key.pk_algo())
766 .into()),
767 };
768
769 let mut checksum = 0u16;
772 let protection = match key.secret() {
773 SecretKeyMaterial::Encrypted(e) => {
774 let mut p =
775 vec![Sexp::String("protection".into())];
776 p.push(Sexp::String(match e.checksum() {
777 Some(SecretKeyChecksum::SHA1) => "sha1",
778 Some(SecretKeyChecksum::Sum16) => "sum",
779 None => "none", }.into()));
781 p.push(Sexp::String(babel::Fish(e.algo()).to_string().as_str().into()));
782
783 let iv_len = e.algo().block_size().unwrap_or(0);
784 let iv = e.ciphertext().map(|c| &c[..iv_len.min(c.len())])
785 .unwrap_or(&[]);
786 p.push(Sexp::String(iv.into()));
787
788 #[allow(deprecated)]
789 match e.s2k() {
790 S2K::Iterated { hash, salt, hash_bytes, } => {
791 p.push(Sexp::String("3".into()));
792 p.push(Sexp::String(babel::Fish(*hash).to_string().as_str().into()));
793 p.push(Sexp::String(salt[..].into()));
794 p.push(Sexp::String(
795 utils::s2k_encode_iteration_count(*hash_bytes)
796 .unwrap_or_default().to_string().as_str().into()));
797 },
798 S2K::Salted { hash, salt } => {
799 p.push(Sexp::String("1".into()));
800 p.push(Sexp::String(babel::Fish(*hash).to_string().as_str().into()));
801 p.push(Sexp::String(salt[..].into()));
802 p.push(Sexp::String("0".into()));
803 },
804 S2K::Simple { hash } => {
805 p.push(Sexp::String("0".into()));
806 p.push(Sexp::String(babel::Fish(*hash).to_string().as_str().into()));
807 p.push(Sexp::String([][..].into()));
808 p.push(Sexp::String("0".into()));
809 },
810 S2K::Private { .. } | S2K::Unknown { .. } | _ => {
811 return Err(anyhow::anyhow!("Unsupported protection mode").into());
812 },
813 }
814
815 if let Ok(c) = e.ciphertext() {
816 skey.push(Sexp::String("e".into()));
817 skey.push(Sexp::String(c[iv_len.min(c.len())..].into()));
819 } else {
820 return Err(anyhow::anyhow!("Failed to parse ciphertext").into());
821 }
822
823 Sexp::List(p)
824 },
825 SecretKeyMaterial::Unencrypted(u) => {
826 u.map(|s| match s {
827 mpi::SecretKeyMaterial::RSA { d, p, q, u, } => {
828 addp(&mut skey, &mut checksum, d);
829 addp(&mut skey, &mut checksum, p);
830 addp(&mut skey, &mut checksum, q);
831 addp(&mut skey, &mut checksum, u);
832 },
833 mpi::SecretKeyMaterial::DSA { x, }
834 | mpi::SecretKeyMaterial::ElGamal { x, } =>
835 addp(&mut skey, &mut checksum, x),
836
837 mpi::SecretKeyMaterial::EdDSA { scalar, }
838 | mpi::SecretKeyMaterial::ECDSA { scalar, }
839 | mpi::SecretKeyMaterial::ECDH { scalar, } => {
840 let mode = EccScalarChecksumMode::for_version(
841 &self.version);
842 add_ecc_scalar(&mut skey, mode, &mut checksum, scalar);
843 },
844
845 mpi::SecretKeyMaterial::Unknown { mpis, rest, } => {
846 for m in mpis.iter() {
847 addp(&mut skey, &mut checksum, m);
848 }
849 add_raw(&mut skey, "_", rest);
850 checksum = checksum.wrapping_add(
851 rest.iter()
852 .fold(0u16, |acc, v| acc.wrapping_add(*v as u16)));
853 },
854 _ => (), });
856 s("protection", "none")
857 },
858 };
859
860 let mut transfer_key = vec![
861 Sexp::String("openpgp-private-key".into()),
862 s("version", key.version()),
863 s("algo", babel::Fish(key.pk_algo())), ];
865 if let Some(curve) = curve {
866 transfer_key.push(s("curve", curve.to_string()));
867 }
868 transfer_key.push(Sexp::List(skey));
869 transfer_key.push(s("csum", checksum));
870 transfer_key.push(protection);
871
872 let transfer_key = Sexp::List(transfer_key);
873
874 let mut buf = Vec::new();
876 transfer_key.serialize(&mut buf)?;
877 while buf.len() % 8 > 0 {
878 buf.push(0);
879 }
880 let padded_transfer_key = Protected::from(buf);
881
882 self.send_simple(
883 format!("SETKEYDESC {}",
884 escape(Self::make_import_prompt(policy, cert, key)))).await?;
885
886 let kek = self.send_simple("KEYWRAP_KEY --import").await?;
888
889 let encrypted_transfer_key = openpgp::crypto::ecdh::aes_key_wrap(
891 openpgp::types::SymmetricAlgorithm::AES128,
892 &kek,
893 &padded_transfer_key)?;
894 assert_eq!(padded_transfer_key.len() + 8, encrypted_transfer_key.len());
895
896 let mut imported = false;
898
899 if let Some(mode) = self.pinentry_mode.as_ref().map(|m| m.to_string()) {
901 self.send_simple(
902 format!("OPTION pinentry-mode={}", mode)).await?;
903 }
904 self.send(format!("IMPORT_KEY --timestamp={}{}{}",
905 chrono::DateTime::<chrono::Utc>::from(key.creation_time())
906 .format("%Y%m%dT%H%M%S"),
907 if unattended { " --unattended" } else { "" },
908 if overwrite { " --force" } else { "" },
909 ))?;
910 while let Some(response) = self.next().await {
911 match response? {
912 Response::Ok { .. }
913 | Response::Comment { .. }
914 | Response::Status { .. } =>
915 (), Response::Inquire { keyword, .. } => {
917 match keyword.as_str() {
918 "KEYDATA" => {
919 self.data(&encrypted_transfer_key)?;
920 self.next().await;
922
923 while let Some(r) = self.next().await {
925 match r? {
926 Response::Status { .. } =>
928 (), Response::Ok { .. } => {
930 imported = true;
931 break;
932 },
933 Response::Inquire { .. } =>
936 self.acknowledge_inquiry().await?,
937 Response::Error { code, message } => {
938 match code {
939 0x4008023 => {
940 return self.operation_failed_as(
942 Error::KeyExists(
943 keygrip.clone(),
944 message.unwrap_or_else(|| {
945 "key exists".into()
946 })).into()
947 ).await;
948 }
949 _ => {
950 return self.operation_failed(
951 &message).await;
952 },
953 }
954 },
955 response =>
956 return protocol_error(&response),
957 }
958 }
959
960 },
962 _ => self.acknowledge_inquiry().await?,
963 }
964 },
965 Response::Error { ref message, .. } =>
966 return self.operation_failed(message).await,
967 response =>
968 return protocol_error(&response),
969 }
970 }
971
972 Ok(imported)
973 }
974
975 fn make_import_prompt(policy: &dyn Policy, cert: &Cert,
976 key: &Key<SecretParts, UnspecifiedRole>)
977 -> String
978 {
979 let primary_id = cert.keyid();
980 let keyid = key.keyid();
981 let uid = utils::best_effort_primary_uid(policy, cert);
982
983 match (primary_id == keyid, Some(uid)) {
984 (true, Some(uid)) => format!(
985 "Please enter the passphrase to \
986 unlock the OpenPGP secret key:\n\
987 {}\n\
988 ID {:X}, created {}.",
989 uid,
990 keyid,
991 Timestamp::try_from(key.creation_time())
992 .expect("creation time is representable"),
993 ),
994 (false, Some(uid)) => format!(
995 "Please enter the passphrase to \
996 unlock the OpenPGP secret key:\n\
997 {}\n\
998 ID {:X}, created {} (main key ID {}).",
999 uid,
1000 keyid,
1001 Timestamp::try_from(key.creation_time())
1002 .expect("creation time is representable"),
1003 primary_id,
1004 ),
1005 (true, None) => format!(
1006 "Please enter the passphrase to \
1007 unlock the OpenPGP secret key:\n\
1008 ID {:X}, created {}.",
1009 keyid,
1010 Timestamp::try_from(key.creation_time())
1011 .expect("creation time is representable"),
1012 ),
1013 (false, None) => format!(
1014 "Please enter the passphrase to \
1015 unlock the OpenPGP secret key:\n\
1016 ID {:X}, created {} (main key ID {}).",
1017 keyid,
1018 Timestamp::try_from(key.creation_time())
1019 .expect("creation time is representable"),
1020 primary_id,
1021 ),
1022 }
1023 }
1024
1025 pub async fn preset_passphrase(&mut self,
1036 keygrip: &Keygrip,
1037 password: Password)
1038 -> Result<()>
1039 {
1040 let escaped = password
1041 .map(|p| {
1042 p.iter().map(|b| format!("{:02X}", b))
1043 .collect::<String>()
1044 });
1045 self.send_simple(
1046 format!("PRESET_PASSPHRASE {} -1 {}",
1047 keygrip, escaped)).await
1048 .map_err(|err| {
1049 err
1050 })?;
1051
1052 Ok(())
1053 }
1054
1055 pub async fn get_passphrase<P>(&mut self,
1067 cache_id: &Option<String>,
1068 err_msg: &Option<String>,
1069 prompt: Option<String>,
1070 desc_msg: Option<String>,
1071 newsymkey: bool,
1072 repeat: usize,
1073 check: bool,
1074 qualitybar: bool,
1075 mut pinentry_cb: P)
1076 -> Result<Password>
1077 where
1078 P: FnMut(&mut Agent, Response) -> Result<Option<Protected>>,
1079 {
1080 self.send(format!(
1081 "GET_PASSPHRASE --data --repeat={}{}{}{} -- {} {} {} {}",
1082 repeat,
1083 if (repeat > 0 && check) || newsymkey { " --check" } else { "" },
1084 if qualitybar { " --qualitybar" } else { "" },
1085 if newsymkey { " --newsymkey" } else { "" },
1086 cache_id.as_ref().map(escape).unwrap_or_else(|| "X".into()),
1087 err_msg.as_ref().map(escape).unwrap_or_else(|| "X".into()),
1088 prompt.as_ref().map(escape).unwrap_or_else(|| "X".into()),
1089 desc_msg.as_ref().map(escape).unwrap_or_else(|| "X".into()),
1090 ))?;
1091
1092 let mut password = Vec::new();
1093 while let Some(response) = self.next().await {
1094 match response? {
1095 r @ Response::Ok { .. }
1096 | r @ Response::Comment { .. }
1097 | r @ Response::Status { .. } => {
1098 pinentry_cb(self, r)?;
1099 },
1100 r @ Response::Inquire { .. } => {
1101 match pinentry_cb(self, r) {
1102 Ok(Some(data)) => {
1103 self.data(&data[..])?;
1104 while let Some(r) = self.next().await {
1106 if matches!(r?, Response::Ok { .. }) {
1107 break;
1108 }
1109 }
1110
1111 }
1113 Ok(None) => {
1114 self.acknowledge_inquiry().await?;
1115 }
1116 Err(err) => {
1117 self.acknowledge_inquiry().await?;
1118 return Err(err);
1119 }
1120 }
1121 },
1122 Response::Data { partial } => {
1123 let partial = Protected::from(partial);
1125 password.extend_from_slice(&partial);
1126 },
1127 Response::Error { ref message, .. } =>
1128 return self.operation_failed(message).await,
1129 }
1130 }
1131 let password = Password::from(password);
1132
1133 Ok(password)
1134 }
1135
1136 pub async fn forget_passphrase<C, P>(&mut self,
1148 cache_id: C,
1149 mut pinentry_cb: P)
1150 -> Result<()>
1151 where
1152 C: AsRef<str>,
1153 P: FnMut(Vec<u8>),
1154 {
1155 self.send(format!("CLEAR_PASSPHRASE {}", escape(cache_id.as_ref())))?;
1156 while let Some(response) = self.next().await {
1157 match response? {
1158 Response::Ok { .. }
1159 | Response::Comment { .. }
1160 | Response::Status { .. } =>
1161 (), Response::Inquire { keyword, parameters } => {
1163 match keyword.as_str() {
1164 "PINENTRY_LAUNCHED" => {
1165 pinentry_cb(parameters.unwrap_or_default());
1166 },
1167 _ => (),
1168 }
1169 self.acknowledge_inquiry().await?
1170 },
1171 Response::Error { ref message, .. } =>
1172 return self.operation_failed(message).await,
1173 response =>
1174 return protocol_error(&response),
1175 }
1176 }
1177 Ok(())
1178 }
1179
1180 async fn acknowledge_inquiry(&mut self) -> Result<()> {
1185 self.send("END")?;
1186 self.next().await; Ok(())
1188 }
1189
1190 async fn operation_failed<T>(&mut self, message: &Option<String>)
1198 -> Result<T>
1199 {
1200 self.operation_failed_as(
1201 assuan::Error::OperationFailed(
1202 message.as_ref().map(|e| e.to_string())
1203 .unwrap_or_else(|| "Unknown reason".into()))
1204 .into()).await
1205 }
1206
1207 async fn operation_failed_as<T>(&mut self, err: Error)
1208 -> Result<T>
1209 {
1210 tracer!(TRACE, "operation_failed_as");
1211
1212 if let Some(response) = self.next().await {
1213 t!("Got unexpected response {:?}", response);
1214 Err(assuan::Error::ProtocolError(
1215 format!("Got unexpected response {:?}", response))
1216 .into())
1217 } else {
1218 t!("Operation failed: {}", err);
1219 Err(err)
1220 }
1221 }
1222
1223 fn options(&self) -> Vec<String> {
1225 use std::env::var;
1226
1227 if self.is_restricted() {
1228 return vec![];
1231 }
1232
1233 let mut r = Vec::new();
1234
1235 if let Ok(tty) = var("GPG_TTY") {
1236 r.push(format!("OPTION ttyname={}", tty));
1237 } else {
1238 #[cfg(unix)]
1239 unsafe {
1240 use std::ffi::CStr;
1241 let tty = libc::ttyname(0);
1242 if ! tty.is_null() {
1243 if let Ok(tty) = CStr::from_ptr(tty).to_str() {
1244 r.push(format!("OPTION ttyname={}", tty));
1245 }
1246 }
1247 }
1248 }
1249
1250 if let Ok(term) = var("TERM") {
1251 r.push(format!("OPTION ttytype={}", term));
1252 }
1253
1254 if let Ok(display) = var("DISPLAY") {
1255 r.push(format!("OPTION display={}", display));
1256 }
1257
1258 if let Ok(xauthority) = var("XAUTHORITY") {
1259 r.push(format!("OPTION xauthority={}", xauthority));
1260 }
1261
1262 if let Ok(dbus) = var("DBUS_SESSION_BUS_ADDRESS") {
1263 r.push(format!("OPTION putenv=DBUS_SESSION_BUS_ADDRESS={}", dbus));
1264 }
1265
1266 r.reverse();
1270 r
1271 }
1272
1273 pub fn keypair<R>(&self, key: &Key<PublicParts, R>)
1286 -> Result<KeyPair>
1287 where R: KeyRole
1288 {
1289 let mut pair = KeyPair::new_for_socket(&self.socket_path, key)?;
1290 pair.pinentry_mode = self.pinentry_mode.clone();
1291 Ok(pair)
1292 }
1293
1294 pub async fn export(&mut self, key: Key<PublicParts, UnspecifiedRole>)
1298 -> Result<Key<SecretParts, UnspecifiedRole>>
1299 {
1300 let keygrip = Keygrip::of(key.mpis())?;
1301
1302 let kek = self.send_simple("KEYWRAP_KEY --export").await?;
1303
1304 let encrypted_key = self.send_simple(
1305 format!("EXPORT_KEY {}", keygrip.to_string())).await?;
1306
1307 let secret_key = openpgp::crypto::ecdh::aes_key_unwrap(
1308 openpgp::types::SymmetricAlgorithm::AES128,
1309 &kek,
1310 &encrypted_key.as_ref())?;
1311
1312 let mut secret_key = secret_key.as_ref();
1315 while ! secret_key.is_empty() && secret_key[secret_key.len() - 1] == 0 {
1316 secret_key = &secret_key[..secret_key.len() - 1];
1317 }
1318
1319 let sexp = Sexp::from_bytes(secret_key)?;
1320
1321 let secret_key = sexp.to_secret_key(Some(key.mpis()))?;
1322
1323 let (key, _) = key.add_secret(secret_key.into());
1324
1325 Ok(key)
1326 }
1327}
1328
1329enum EccScalarChecksumMode {
1338 MPI,
1340
1341 SOS,
1349}
1350
1351impl EccScalarChecksumMode {
1352 fn for_version(v: &str) -> EccScalarChecksumMode {
1353 if v.starts_with("2.0.")
1354 || v.starts_with("2.1.")
1355 || v.starts_with("2.2.")
1356 {
1357 EccScalarChecksumMode::MPI
1358 } else {
1359 EccScalarChecksumMode::SOS
1360 }
1361 }
1362}
1363
1364fn protocol_error<T>(response: &Response) -> Result<T> {
1367 tracer!(TRACE, "operation_failed");
1368
1369 t!("Got unexpected response {:?}", response);
1370 Err(assuan::Error::ProtocolError(
1371 format!("Got unexpected response {:?}", response))
1372 .into())
1373}
1374
1375pub fn cacheid_of(s2k: &S2K) -> Option<String> {
1380 #[allow(deprecated)]
1381 let salt = match s2k {
1382 S2K::Iterated { salt, .. } => &salt[..8],
1383 S2K::Salted { salt, .. } => &salt[..8],
1384 _ => return None,
1385 };
1386
1387 Some(format!("S{}", openpgp::fmt::hex::encode(&salt)))
1388}
1389
1390pub fn cacheid_over_all(skesks: &[SKESK]) -> Option<String> {
1406 if skesks.is_empty() {
1407 return None;
1408 }
1409
1410 let mut cacheid = [0; 8];
1411
1412 for skesk in skesks {
1413 let s2k = match skesk {
1414 SKESK::V4(skesk) => skesk.s2k(),
1415 _ => continue,
1416 };
1417
1418 #[allow(deprecated)]
1419 let salt = match s2k {
1420 S2K::Iterated { salt, .. } => &salt[..8],
1421 S2K::Salted { salt, .. } => &salt[..8],
1422 _ => continue,
1423 };
1424
1425 cacheid.iter_mut().zip(salt.iter()).for_each(|(p, s)| *p ^= *s);
1426 }
1427
1428 Some(format!("S{}", openpgp::fmt::hex::encode(&cacheid)))
1429}
1430
1431pub struct KeyPair {
1440 public: Key<PublicParts, UnspecifiedRole>,
1441 agent_socket: PathBuf,
1442 password: Option<crypto::Password>,
1443 pinentry_mode: Option<PinentryMode>,
1444 password_prompt: String,
1445 delete_prompt: String,
1446}
1447
1448impl KeyPair {
1449 fn default_password_prompt(key: &Key<PublicParts, UnspecifiedRole>)
1450 -> String
1451 {
1452 format!(
1453 "Please enter the passphrase to \
1454 unlock the OpenPGP secret key:\n\
1455 ID {:X}, created {}.",
1456 key.keyid(), Timestamp::try_from(key.creation_time()).unwrap())
1457 }
1458
1459 fn default_delete_prompt(key: &Key<PublicParts, UnspecifiedRole>)
1460 -> String
1461 {
1462 format!(
1463 "Do you really want to permanently delete the OpenPGP secret key:\n\
1464 ID {:X}, created {}.",
1465 key.keyid(), Timestamp::try_from(key.creation_time()).unwrap())
1466 }
1467
1468 pub fn new_for_gnupg_context<R>(ctx: &gnupg::Context,
1474 key: &Key<PublicParts, R>)
1475 -> Result<KeyPair>
1476 where R: KeyRole
1477 {
1478 let key = key.role_as_unspecified();
1479 Ok(KeyPair {
1480 password: None,
1481 pinentry_mode: None,
1482 password_prompt: Self::default_password_prompt(key),
1483 delete_prompt: Self::default_delete_prompt(key),
1484 public: key.clone(),
1485 agent_socket: ctx.socket("agent")?.into(),
1486 })
1487 }
1488
1489 pub fn new_for_socket<P, R>(agent_socket: P, key: &Key<PublicParts, R>)
1495 -> Result<KeyPair>
1496 where P: AsRef<Path>,
1497 R: KeyRole
1498 {
1499 let key = key.role_as_unspecified();
1500 Ok(KeyPair {
1501 password: None,
1502 pinentry_mode: None,
1503 password_prompt: Self::default_password_prompt(key),
1504 delete_prompt: Self::default_delete_prompt(key),
1505 public: key.clone(),
1506 agent_socket: agent_socket.as_ref().to_path_buf(),
1507 })
1508 }
1509
1510 pub fn with_cert(self, cert: &ValidCert) -> Self {
1520 let primary_id = cert.keyid();
1521 let keyid = self.public.keyid();
1522 let password_prompt = match (primary_id == keyid,
1523 cert.primary_userid()
1524 .map(|uid| uid.clone())
1525 .ok())
1526 {
1527 (true, Some(uid)) => format!(
1528 "Please enter the passphrase to \
1529 unlock the OpenPGP secret key:\n\
1530 {}\n\
1531 ID {:X}, created {}.",
1532 uid.userid(),
1533 keyid,
1534 Timestamp::try_from(self.public.creation_time())
1535 .expect("creation time is representable"),
1536 ),
1537 (false, Some(uid)) => format!(
1538 "Please enter the passphrase to \
1539 unlock the OpenPGP secret key:\n\
1540 {}\n\
1541 ID {:X}, created {} (main key ID {}).",
1542 uid.userid(),
1543 keyid,
1544 Timestamp::try_from(self.public.creation_time())
1545 .expect("creation time is representable"),
1546 primary_id,
1547 ),
1548 (true, None) => format!(
1549 "Please enter the passphrase to \
1550 unlock the OpenPGP secret key:\n\
1551 ID {:X}, created {}.",
1552 keyid,
1553 Timestamp::try_from(self.public.creation_time())
1554 .expect("creation time is representable"),
1555 ),
1556 (false, None) => format!(
1557 "Please enter the passphrase to \
1558 unlock the OpenPGP secret key:\n\
1559 ID {:X}, created {} (main key ID {}).",
1560 keyid,
1561 Timestamp::try_from(self.public.creation_time())
1562 .expect("creation time is representable"),
1563 primary_id,
1564 ),
1565 };
1566
1567 let delete_prompt = match cert.primary_userid()
1568 .map(|uid| uid.clone())
1569 .ok()
1570 {
1571 Some(uid) => format!(
1572 "Do you really want to permanently delete the OpenPGP secret key:\n\
1573 {}\n\
1574 ID {:X}, created {}.",
1575 uid.userid(),
1576 keyid,
1577 Timestamp::try_from(self.public.creation_time())
1578 .expect("creation time is representable"),
1579 ),
1580 None => format!(
1581 "Do you really want to permanently delete the OpenPGP secret key:\n\
1582 ID {:X}, created {}.",
1583 keyid,
1584 Timestamp::try_from(self.public.creation_time())
1585 .expect("creation time is representable"),
1586 ),
1587 };
1588
1589 self.with_password_prompt(password_prompt)
1590 .with_delete_prompt(delete_prompt)
1591 }
1592
1593 pub fn with_password(mut self, password: crypto::Password) -> Self {
1604 self.password = Some(password);
1605 self
1606 }
1607
1608 pub fn set_pinentry_mode(mut self, mode: PinentryMode) -> Self {
1610 self.pinentry_mode = Some(mode);
1611 self
1612 }
1613
1614 pub fn suppress_pinentry(mut self) -> Self {
1619 self.pinentry_mode = Some(PinentryMode::Error);
1620 self
1621 }
1622
1623 pub fn with_password_prompt(mut self, prompt: String) -> Self {
1631 self.password_prompt = prompt;
1632 self
1633 }
1634
1635 pub fn with_delete_prompt(mut self, prompt: String) -> Self {
1643 self.delete_prompt = prompt;
1644 self
1645 }
1646
1647 pub async fn password(&mut self, preset_password: bool)
1652 -> Result<()>
1653 {
1654 let keygrip = Keygrip::of(self.public.mpis())?;
1655
1656 let mut agent = Agent::connect_to_agent(&self.agent_socket).await?;
1658
1659 for option in agent.options() {
1660 agent.send_simple(option).await?;
1661 }
1662
1663 if let Some(mode) = agent.pinentry_mode.clone() {
1664 agent.send_simple(
1665 format!("OPTION pinentry-mode={}", mode.as_str())).await?;
1666 }
1667 agent.send_simple(
1668 format!("SETKEYDESC {}",
1669 assuan::escape(&self.password_prompt))).await?;
1670
1671 agent.send_simple(
1672 format!("PASSWD {}{}",
1673 if preset_password {
1674 "--preset "
1675 } else {
1676 ""
1677 },
1678 keygrip.to_string()))
1679 .await?;
1680
1681 Ok(())
1682 }
1683
1684 pub async fn delete_key(&mut self, stub_only: bool)
1689 -> Result<()>
1690 {
1691 let keygrip = Keygrip::of(self.public.mpis())?;
1692
1693 let mut agent = Agent::connect_to_agent(&self.agent_socket).await?;
1695
1696 for option in agent.options() {
1697 agent.send_simple(option).await?;
1698 }
1699
1700 if let Some(mode) = agent.pinentry_mode.clone() {
1701 agent.send_simple(
1702 format!("OPTION pinentry-mode={}", mode.as_str())).await?;
1703 }
1704 agent.send_simple(
1705 format!("SETKEYDESC {}",
1706 assuan::escape(&self.delete_prompt))).await?;
1707
1708 agent.send_simple(
1709 format!("DELETE_KEY {}{}",
1710 if stub_only {
1711 "--stub-only "
1712 } else {
1713 ""
1714 },
1715 keygrip.to_string()))
1716 .await?;
1717
1718 Ok(())
1719 }
1720}
1721
1722impl KeyPair {
1723 pub async fn sign_async(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
1728 -> openpgp::Result<openpgp::crypto::mpi::Signature>
1729 {
1730 let mut agent = Agent::connect_to_agent(&self.agent_socket).await?;
1733
1734 for option in agent.options() {
1735 agent.send_simple(option).await?;
1736 }
1737
1738 if self.password.is_some() {
1739 agent.send_simple("OPTION pinentry-mode=loopback").await?;
1740 } else if let Some(ref mode) = self.pinentry_mode {
1741 agent.send_simple(
1742 format!("OPTION pinentry-mode={}", mode.as_str())).await?;
1743 }
1744
1745 let grip = Keygrip::of(self.public.mpis())?;
1746 agent.send_simple(format!("SIGKEY {}", grip)).await?;
1747 agent.send_simple(
1748 format!("SETKEYDESC {}",
1749 assuan::escape(&self.password_prompt))).await?;
1750
1751 let algo = u8::from(hash_algo);
1752 let digest = hex::encode(&digest);
1753 agent.send_simple(format!("SETHASH {} {}", algo, digest)).await?;
1754 agent.send("PKSIGN")?;
1755
1756 let mut data = Vec::new();
1757 while let Some(r) = agent.next().await {
1758 match r? {
1759 assuan::Response::Ok { .. }
1760 | assuan::Response::Comment { .. }
1761 | assuan::Response::Status { .. } =>
1762 (), assuan::Response::Inquire { keyword, .. } =>
1764 match (keyword.as_str(), &self.password) {
1765 ("PASSPHRASE", Some(p)) => {
1766 p.map(|p| agent.data(p))?;
1767 agent.next().await;
1769 },
1770 _ => agent.acknowledge_inquiry().await?,
1771 },
1772 assuan::Response::Error { ref message, .. } =>
1773 return Ok(agent.operation_failed(message).await?),
1774 assuan::Response::Data { ref partial } =>
1775 data.extend_from_slice(partial),
1776 }
1777 }
1778
1779 Sexp::from_bytes(&data)?.to_signature()
1780 }
1781}
1782
1783impl crypto::Signer for KeyPair {
1784 fn public(&self) -> &Key<PublicParts, UnspecifiedRole> {
1785 &self.public
1786 }
1787
1788 fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8])
1789 -> openpgp::Result<openpgp::crypto::mpi::Signature>
1790 {
1791 use tokio::runtime::{Handle, Builder};
1792
1793 if Handle::try_current().is_err() {
1796 let rt = Builder::new_current_thread()
1799 .enable_io()
1800 .build()?;
1801 rt.block_on(self.sign_async(hash_algo, digest))
1802 } else {
1803 std::thread::scope(|s| {
1807 s.spawn(move || {
1808 let rt = Builder::new_current_thread()
1809 .enable_io()
1810 .build()?;
1811 rt.block_on(self.sign_async(hash_algo, digest))
1812 }).join()
1813 }).map_err(map_panic)?
1814 }
1815 }
1816}
1817
1818impl KeyPair {
1819 pub async fn decrypt_async(&mut self, ciphertext: &crypto::mpi::Ciphertext,
1824 plaintext_len: Option<usize>)
1825 -> openpgp::Result<crypto::SessionKey>
1826 {
1827 let mut agent = Agent::connect_to_agent(&self.agent_socket).await?;
1829
1830 for option in agent.options() {
1831 agent.send_simple(option).await?;
1832 }
1833
1834 if self.password.is_some() {
1835 agent.send_simple("OPTION pinentry-mode=loopback").await?;
1836 } else if let Some(ref mode) = self.pinentry_mode {
1837 agent.send_simple(
1838 format!("OPTION pinentry-mode={}", mode.as_str())).await?;
1839 }
1840
1841 let grip = Keygrip::of(self.public.mpis())?;
1842 agent.send_simple(format!("SETKEY {}", grip)).await?;
1843 agent.send_simple(format!("SETKEYDESC {}",
1844 assuan::escape(&self.password_prompt))).await?;
1845 agent.send("PKDECRYPT")?;
1846 let mut padding = true;
1847 let mut data = Vec::new();
1848 while let Some(r) = agent.next().await {
1849 match r? {
1850 assuan::Response::Ok { .. } |
1851 assuan::Response::Comment { .. } =>
1852 (), assuan::Response::Inquire { ref keyword, .. } =>
1854 match (keyword.as_str(), &self.password) {
1855 ("PASSPHRASE", Some(p)) => {
1856 p.map(|p| agent.data(p))?;
1857 agent.next().await;
1859 },
1860 ("CIPHERTEXT", _) => {
1861 let mut buf = Vec::new();
1862 Sexp::try_from(ciphertext)?.serialize(&mut buf)?;
1863 agent.data(&buf)?;
1864 agent.next().await;
1866 },
1867 _ => agent.acknowledge_inquiry().await?,
1868 },
1869 assuan::Response::Status { ref keyword, ref message } =>
1870 if keyword == "PADDING" {
1871 padding = message != "0";
1872 },
1873 assuan::Response::Error { ref message, .. } =>
1874 return Ok(agent.operation_failed(message).await?),
1875 assuan::Response::Data { ref partial } =>
1876 data.extend_from_slice(partial),
1877 }
1878 }
1879
1880 if data.iter().last() == Some(&0) {
1885 let l = data.len();
1886 data.truncate(l - 1);
1887 }
1888
1889 Sexp::from_bytes(&data)?.finish_decryption(
1890 &self.public, ciphertext, plaintext_len, padding)
1891 }
1892}
1893
1894impl crypto::Decryptor for KeyPair {
1895 fn public(&self) -> &Key<PublicParts, UnspecifiedRole> {
1896 &self.public
1897 }
1898
1899 fn decrypt(&mut self, ciphertext: &crypto::mpi::Ciphertext,
1900 plaintext_len: Option<usize>)
1901 -> openpgp::Result<crypto::SessionKey>
1902 {
1903 use tokio::runtime::{Handle, Builder};
1904
1905 if Handle::try_current().is_err() {
1908 let rt = Builder::new_current_thread()
1911 .enable_io()
1912 .build()?;
1913 rt.block_on(self.decrypt_async(ciphertext, plaintext_len))
1914 } else {
1915 std::thread::scope(|s| {
1919 s.spawn(move || {
1920 let rt = Builder::new_current_thread()
1921 .enable_io()
1922 .build()?;
1923 rt.block_on(self.decrypt_async(ciphertext, plaintext_len))
1924 }).join()
1925 }).map_err(map_panic)?
1926 }
1927 }
1928}
1929
1930fn map_panic(_: Box<dyn std::any::Any + std::marker::Send>) -> anyhow::Error
1935{
1936 anyhow::anyhow!("worker thread panicked")
1937}
1938
1939#[cfg(test)]
1940mod test {
1941 use super::*;
1942
1943 use std::fs::File;
1944 use std::io::Write;
1945
1946 use anyhow::Context;
1947
1948 use openpgp::{
1949 Cert,
1950 crypto::{
1951 mpi::Ciphertext,
1952 Decryptor,
1953 Signer,
1954 },
1955 parse::Parse,
1956 policy::StandardPolicy,
1957 };
1958
1959 use ipc::Keygrip;
1960
1961 const P: &StandardPolicy = &StandardPolicy::new();
1962
1963 async fn import_keys(agent: &mut Agent, cert: &Cert) -> Result<()> {
1964 assert!(cert.is_tsk(),
1965 "{} does not contain secret key material", cert.fingerprint());
1966
1967 for k in cert.keys().secret() {
1968 agent.import(P,
1969 &cert, k.key().parts_as_secret().expect("have secret"),
1970 true, true).await?;
1971 }
1972
1973 Ok(())
1974 }
1975
1976 async fn import_testy_new(agent: &mut Agent) -> Result<Cert> {
1977 let cert = Cert::from_bytes(crate::tests::key("testy-new-private.pgp"))?;
1978 import_keys(agent, &cert).await?;
1979 Ok(cert)
1980 }
1981
1982 #[test]
1993 fn signer_in_async_context() -> Result<()> {
1994 async fn async_context() -> Result<()> {
1995 let ctx = match gnupg::Context::ephemeral() {
1996 Ok(c) => c,
1997 Err(e) => {
1998 eprintln!("Failed to create ephemeral context: {}", e);
1999 eprintln!("Most likely, GnuPG isn't installed.");
2000 eprintln!("Skipping test.");
2001 return Ok(());
2002 },
2003 };
2004
2005 let key = Cert::from_bytes(crate::tests::key("testy-new.pgp"))?
2006 .primary_key().key().clone();
2007 let mut pair = KeyPair::new_for_gnupg_context(&ctx, &key)?;
2008 let algo = HashAlgorithm::default();
2009 let digest = algo.context()?.for_digest().into_digest()?;
2010 let _ = pair.sign(algo, &digest);
2011 Ok(())
2012 }
2013
2014 let rt = tokio::runtime::Runtime::new()?;
2015 rt.block_on(async_context())
2016 }
2017
2018 #[test]
2029 fn decryptor_in_async_context() -> Result<()> {
2030 async fn async_context() -> Result<()> {
2031 let ctx = match gnupg::Context::ephemeral() {
2032 Ok(c) => c,
2033 Err(e) => {
2034 eprintln!("Failed to create ephemeral context: {}", e);
2035 eprintln!("Most likely, GnuPG isn't installed.");
2036 eprintln!("Skipping test.");
2037 return Ok(());
2038 },
2039 };
2040
2041 let key = Cert::from_bytes(crate::tests::key("testy-new.pgp"))?
2042 .keys().nth(1).unwrap().key().clone();
2043 let mut pair = KeyPair::new_for_gnupg_context(&ctx, &key)?;
2044 let ciphertext = Ciphertext::ECDH {
2045 e: vec![].into(),
2046 key: vec![].into_boxed_slice(),
2047 };
2048 let _ = pair.decrypt(&ciphertext, None);
2049 Ok(())
2050 }
2051
2052 let rt = tokio::runtime::Runtime::new()?;
2053 rt.block_on(async_context())
2054 }
2055
2056 #[cfg(not(windows))]
2062 #[tokio::test]
2063 async fn non_existent_home_directory() -> Result<()> {
2064 let tempdir = tempfile::tempdir()?;
2065 let homedir = tempdir.path().join("foo");
2066
2067 let result = Agent::connect_to(&homedir).await;
2068 match result {
2069 Ok(_agent) => panic!("Created an agent for a non-existent home!"),
2070 Err(err) => {
2071 if let Error::GnuPGHomeMissing(_) = err {
2072 } else {
2074 panic!("Expected Error::GnuPGHomeMissing, got: {}", err);
2075 }
2076 }
2077 }
2078
2079 Ok(())
2080 }
2081
2082 #[tokio::test]
2084 async fn import_key_and_sign() -> Result<()> {
2085 let ctx = match gnupg::Context::ephemeral() {
2086 Ok(c) => c,
2087 Err(e) => {
2088 eprintln!("Failed to create ephemeral context: {}", e);
2089 eprintln!("Most likely, GnuPG isn't installed.");
2090 eprintln!("Skipping test.");
2091 return Ok(());
2092 },
2093 };
2094
2095 let mut agent = Agent::connect(&ctx).await?;
2096
2097 let testy = import_testy_new(&mut agent).await?;
2098
2099 let key = testy.primary_key().key();
2100 let mut pair = KeyPair::new_for_gnupg_context(&ctx, &key)?;
2101 let algo = HashAlgorithm::default();
2102 let digest = algo.context()?.for_digest().into_digest()?;
2103 if let Err(err) = pair.sign(algo, &digest) {
2104 panic!("Signing: {}", err);
2105 }
2106 Ok(())
2107 }
2108
2109 #[tokio::test]
2111 async fn list_keys() -> Result<()> {
2112 use std::collections::HashSet;
2113
2114 let ctx = match gnupg::Context::ephemeral() {
2115 Ok(c) => c,
2116 Err(e) => {
2117 eprintln!("Failed to create ephemeral context: {}", e);
2118 eprintln!("Most likely, GnuPG isn't installed.");
2119 eprintln!("Skipping test.");
2120 return Ok(());
2121 },
2122 };
2123
2124 let mut agent = Agent::connect(&ctx).await?;
2125
2126 let keys = agent.list_keys().await.expect("list keys works");
2127 assert_eq!(keys.len(), 0);
2128
2129 let testy = import_testy_new(&mut agent).await?;
2130 let testy_keygrips = testy.keys().into_iter()
2131 .map(|ka| Keygrip::of(ka.key().mpis()))
2132 .collect::<Result<HashSet<Keygrip>, _>>()
2133 .expect("can compute keygrip");
2134 assert!(testy_keygrips.len() > 0);
2135
2136 let keys = agent.list_keys().await.expect("list keys works");
2137 let keys = keys.into_iter()
2138 .map(|k| k.keygrip().clone())
2139 .collect::<HashSet<Keygrip>>();
2140
2141 assert_eq!(testy_keygrips, keys);
2142
2143 Ok(())
2144 }
2145
2146 #[tokio::test]
2148 async fn key_info() -> Result<()> {
2149 use std::collections::HashSet;
2150
2151 let ctx = match gnupg::Context::ephemeral() {
2152 Ok(c) => c,
2153 Err(e) => {
2154 eprintln!("Failed to create ephemeral context: {}", e);
2155 eprintln!("Most likely, GnuPG isn't installed.");
2156 eprintln!("Skipping test.");
2157 return Ok(());
2158 },
2159 };
2160
2161 let mut agent = Agent::connect(&ctx).await?;
2162
2163 let testy = import_testy_new(&mut agent).await?;
2164 let testy_keygrips = testy.keys().into_iter()
2165 .map(|ka| Keygrip::of(ka.key().mpis()))
2166 .collect::<Result<HashSet<Keygrip>, _>>()
2167 .expect("can compute keygrip");
2168 assert!(testy_keygrips.len() > 0);
2169
2170 for ka in testy.keys() {
2171 let keygrip = Keygrip::of(ka.key().mpis()).expect("has a keygrip");
2172 match agent.key_info(&keygrip).await {
2173 Ok(info) => {
2174 assert_eq!(info.keygrip(), &keygrip);
2175 }
2176 Err(err) => {
2177 panic!("Getting keyinfo for {}: {}", keygrip, err);
2178 }
2179 }
2180 }
2181
2182 Ok(())
2183 }
2184
2185 #[cfg(not(windows))]
2193 #[tokio::test]
2194 async fn preset_passphrase() -> Result<()> {
2195 trace(true);
2196 tracer!(TRACE, "preset_passphrase");
2197
2198 async fn test(file: &str, password: &str)
2199 -> Result<()>
2200 {
2201 let ctx = match gnupg::Context::ephemeral() {
2202 Ok(c) => c,
2203 Err(e) => {
2204 eprintln!("Failed to create ephemeral context: {}", e);
2205 eprintln!("Most likely, GnuPG isn't installed.");
2206 eprintln!("Skipping test.");
2207 return Ok(());
2208 },
2209 };
2210
2211 let mut agent = Agent::connect(&ctx).await?;
2212
2213 let cert = Cert::from_bytes(crate::tests::key(file))?;
2214 import_keys(&mut agent, &cert).await?;
2215 let vc = cert.with_policy(P, None).expect("valid cert");
2216 let vka = vc.keys().for_signing().next().expect("have signing key");
2217 let key_keygrip = Keygrip::of(vka.key().mpis()).expect("has a keygrip");
2218
2219 t!("Testing with {} ({})", vka.key().fingerprint(), key_keygrip);
2220 t!("gpg knows about:");
2221 let mut saw = false;
2222 for (i, info) in agent.list_keys().await.expect("works").iter().enumerate() {
2223 t!("{}. {}: {:?}, password cached: {}",
2224 i + 1, info.keygrip(), info.protection(),
2225 info.passphrase_cached());
2226 if info.keygrip() == &key_keygrip {
2227 saw = true;
2228 }
2229 }
2230 assert!(saw, "Failed to load test key");
2231
2232 let r = agent.preset_passphrase(&key_keygrip, "wrong".into()).await;
2235 match r {
2236 Ok(()) => panic!("preset_passphrase should fail if \
2237 'allow-preset-passphrase' is not set."),
2238 Err(err) => {
2239 if let Error::Assuan(assuan::Error::OperationFailed(_)) = err {
2240 } else {
2242 panic!("Expected assuan::Error::OperationFailed, \
2243 but got {}", err);
2244 }
2245 }
2246 }
2247
2248 let dot_gnupg = ctx.homedir().expect("have a homedir");
2250 let conf = dot_gnupg.join("gpg-agent.conf");
2251 let mut f = File::options().create(true).append(true).open(&conf)
2252 .with_context(|| {
2253 format!("Opening {}", conf.display())
2254 }).expect("can open gpg-agent.conf");
2255 writeln!(&mut f, "allow-preset-passphrase").expect("can write");
2256 drop(f);
2257
2258 agent.reload().await.expect("valid");
2259
2260 let r = agent.preset_passphrase(
2263 &key_keygrip, "this is the wrong passphrase".into()).await;
2264 match r {
2265 Ok(()) => (),
2266 Err(err) => {
2267 panic!("Failed to set password: {}", err);
2268 }
2269 }
2270
2271 let algo = HashAlgorithm::default();
2272 let digest = algo.context()?.for_digest().into_digest()?;
2273 let mut pair = KeyPair::new_for_gnupg_context(&ctx, vka.key())?
2274 .suppress_pinentry();
2276 match pair.sign(algo, &digest) {
2277 Ok(_) => {
2278 panic!("Signing should have failed");
2279 }
2280 Err(err) => {
2281 t!("Signing failed (expected): {}", err);
2282 }
2283 }
2284
2285 let r = agent.preset_passphrase(&key_keygrip, password.into()).await;
2288 match r {
2289 Ok(()) => (),
2290 Err(err) => {
2291 panic!("Failed to set password: {}", err);
2292 }
2293 }
2294
2295 t!("gpg-agent's key info:");
2296 for (i, info) in agent.list_keys().await.unwrap().iter().enumerate()
2297 {
2298 t!(" {}. {}: {:?}, password cached: {}",
2299 i + 1, info.keygrip(), info.protection(),
2300 info.passphrase_cached());
2301 }
2302
2303 let algo = HashAlgorithm::default();
2304 let digest = algo.context()?.for_digest().into_digest()?;
2305 let mut pair = KeyPair::new_for_gnupg_context(&ctx, vka.key())?
2306 .suppress_pinentry();
2308 match pair.sign(algo, &digest) {
2309 Ok(_) => {
2310 }
2311 Err(err) => {
2312 panic!("Signing failed (unexpected): {}", err);
2313 }
2314 }
2315
2316 agent.forget_passphrase(&key_keygrip.to_string(), |_| ()).await
2319 .expect("can forget");
2320
2321 let algo = HashAlgorithm::default();
2322 let digest = algo.context()?.for_digest().into_digest()?;
2323 let mut pair = KeyPair::new_for_gnupg_context(&ctx, vka.key())?
2324 .suppress_pinentry();
2326 match pair.sign(algo, &digest) {
2327 Ok(_) => {
2328 panic!("Signing should have failed");
2329 }
2330 Err(err) => {
2331 t!("Signing failed (expected): {}", err);
2332 }
2333 }
2334
2335 Ok(())
2336 }
2337
2338 test("password-xyzzy-private.pgp", "xyzzy").await.expect("all okay");
2339 test("password-foospacespacebar-private.pgp", "foo bar").await.expect("all okay");
2340
2341 Ok(())
2342 }
2343
2344 #[tokio::test]
2346 async fn export() -> Result<()> {
2347 use std::collections::HashSet;
2348
2349 let ctx = match gnupg::Context::ephemeral() {
2350 Ok(c) => c,
2351 Err(e) => {
2352 eprintln!("Failed to create ephemeral context: {}", e);
2353 eprintln!("Most likely, GnuPG isn't installed.");
2354 eprintln!("Skipping test.");
2355 return Ok(());
2356 },
2357 };
2358
2359 let mut agent = Agent::connect(&ctx).await?;
2360
2361 let testy = import_testy_new(&mut agent).await?;
2362 let testy_keygrips = testy.keys().into_iter()
2363 .map(|ka| Keygrip::of(ka.key().mpis()))
2364 .collect::<Result<HashSet<Keygrip>, _>>()
2365 .expect("can compute keygrip");
2366 assert!(testy_keygrips.len() > 0);
2367
2368 for ka in testy.keys().secret() {
2369 let keygrip = Keygrip::of(ka.key().mpis()).expect("has a keygrip");
2370 match agent.export(ka.key().parts_as_public().clone()).await {
2371 Ok(key) => {
2372 assert_eq!(ka.key(), &key);
2373 }
2374 Err(err) => {
2375 panic!("Exporting key {}: {}", keygrip, err);
2376 }
2377 }
2378 }
2379
2380 Ok(())
2381 }
2382}