This repository has no description
2.9 kB
86 lines
1--- a/src/main.rs
2+++ b/src/main.rs
3@@ -238,7 +238,7 @@
4 })?;
5
6 let v = Handle::current()
7- .block_on(fetch_key(&mut self.agent, keygrip))
8+ .block_on(fetch_recipient_key(&mut self.agent, keygrip))
9 .map_err(|e| RecipientError::Recipient {
10 index,
11 message: format!("failed to fetch recipient key by keygrip: {e}"),
12@@ -337,6 +337,65 @@
13 let key = parse_public_key(key)?;
14 Ok(key)
15 }
16+/// Encryption only needs the public key, which gpg-agent doesn't know unless
17+/// the secret key (or a smartcard stub) is available. Fall back to the gpg
18+/// keyring in that case.
19+async fn fetch_recipient_key(
20+ agent: &mut Agent,
21+ keygrip: Keygrip,
22+) -> Result<Key<PublicParts, UnspecifiedRole>> {
23+ match fetch_key(agent, keygrip.clone()).await {
24+ Ok(key) => Ok(key),
25+ Err(agent_err) => fetch_key_from_keyring(&keygrip)
26+ .map_err(|e| anyhow!("{agent_err}; gpg keyring: {e}")),
27+ }
28+}
29+/// Exports the key by keygrip with gpg and builds the same S-expression
30+/// READKEY would return, so the resulting key is identical to the agent path.
31+fn fetch_key_from_keyring(keygrip: &Keygrip) -> Result<Key<PublicParts, UnspecifiedRole>> {
32+ use sequoia_openpgp::cert::CertParser;
33+ use sequoia_openpgp::parse::Parse as _;
34+
35+ let out = std::process::Command::new("gpg")
36+ .args(["--batch", "--export"])
37+ .arg(format!("&{keygrip}"))
38+ .stderr(std::process::Stdio::null())
39+ .output()
40+ .context("failed to run gpg")?;
41+ ensure!(out.status.success(), "gpg --export failed: {}", out.status);
42+ ensure!(!out.stdout.is_empty(), "key not found");
43+
44+ let s = |v: &[u8]| Sexp::String(String_::new(v));
45+ let l = |v: Vec<Sexp>| Sexp::List(v);
46+ for cert in CertParser::from_bytes(&out.stdout)? {
47+ for ka in cert?.keys() {
48+ let mpis = ka.key().mpis();
49+ if Keygrip::of(mpis).ok().as_ref() != Some(keygrip) {
50+ continue;
51+ }
52+ let alg = match mpis {
53+ PublicKey::RSA { e, n } => l(vec![
54+ s(b"rsa"),
55+ l(vec![s(b"n"), s(n.value())]),
56+ l(vec![s(b"e"), s(e.value())]),
57+ ]),
58+ PublicKey::ECDH {
59+ curve: Curve::Cv25519,
60+ q,
61+ ..
62+ } => l(vec![
63+ s(b"ecc"),
64+ l(vec![s(b"curve"), s(b"Curve25519")]),
65+ l(vec![s(b"flags"), s(b"djb-tweak")]),
66+ l(vec![s(b"q"), s(q.value())]),
67+ ]),
68+ _ => bail!("unsupported key algorithm: {}", ka.key().pk_algo()),
69+ };
70+ return parse_public_key(l(vec![s(b"public-key"), alg]));
71+ }
72+ }
73+ bail!("key not found")
74+}
75 async fn fetch_keypair(agent: &mut Agent, keygrip: Keygrip) -> Result<KeyPair> {
76 let key = fetch_key(agent, keygrip).await?;
77
78@@ -368,7 +427,7 @@
79 let keygripid: Keygrip = keygrip.parse()?;
80 let keygripbytes = hex::decode(keygripid.to_string()).expect("keygrip is hex");
81
82- Handle::current().block_on(fetch_key(&mut agent, keygripid))?;
83+ Handle::current().block_on(fetch_recipient_key(&mut agent, keygripid))?;
84
85 println!(
86 "{}",