This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / pkgs / age-plugin-gpg / keyring-fallback.patch
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 "{}",