keyfork/crates/keyfork-shard/src/openpgp/keyring.rs

178 lines
6.0 KiB
Rust

#![allow(clippy::expect_fun_call)]
use std::sync::{Arc, Mutex};
use keyfork_bug::{bug, POISONED_MUTEX};
use keyfork_prompt::{Error as PromptError, PromptHandler};
use super::openpgp::{
self,
cert::Cert,
packet::{PKESK, SKESK},
parse::stream::{DecryptionHelper, MessageLayer, MessageStructure, VerificationHelper},
policy::NullPolicy,
KeyHandle, KeyID,
};
use anyhow::Context;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Secret key was not found")]
SecretKeyNotFound,
#[error("Prompt failed: {0}")]
Prompt(#[from] PromptError),
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub struct Keyring<P: PromptHandler> {
full_certs: Vec<Cert>,
root: Option<Cert>,
pm: Arc<Mutex<P>>,
}
impl<P: PromptHandler> Keyring<P> {
pub fn new(certs: impl AsRef<[Cert]>, p: Arc<Mutex<P>>) -> Result<Self> {
Ok(Self {
full_certs: certs.as_ref().to_vec(),
root: Default::default(),
pm: p,
})
}
pub fn is_empty(&self) -> bool {
self.full_certs.is_empty()
}
// Sets the root cert, returning the old cert
pub fn set_root_cert(&mut self, cert: impl Into<Option<Cert>>) -> Option<Cert> {
let mut cert = cert.into();
std::mem::swap(&mut self.root, &mut cert);
cert
}
pub fn root_cert(&self) -> Option<&Cert> {
self.root.as_ref()
}
pub fn get_cert_for_primary_keyid<'a>(&'a self, keyid: &KeyID) -> Option<&'a Cert> {
self.full_certs.iter().find(|cert| &cert.keyid() == keyid)
}
}
impl<P: PromptHandler> VerificationHelper for &mut Keyring<P> {
fn get_certs(&mut self, ids: &[KeyHandle]) -> openpgp::Result<Vec<Cert>> {
Ok(ids
.iter()
.flat_map(|kh| {
self.root
.iter()
.filter(move |cert| &cert.key_handle() == kh)
})
.cloned()
.collect())
}
fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
for layer in structure {
#[allow(unused_variables)]
match layer {
MessageLayer::Compression { algo } => {}
MessageLayer::Encryption {
sym_algo,
aead_algo,
} => {}
MessageLayer::SignatureGroup { results } => {
match &results[..] {
[Ok(_)] => {
return Ok(());
}
_ => {
// FIXME: anyhow leak: VerificationError impl std::error::Error
// return Err(e.context("Invalid signature"));
return Err(anyhow::anyhow!("Error validating signature; either multiple signatures were passed or the single signature was not valid"));
}
}
/*
for result in results {
if let Err(e) = result {
return Err(anyhow::anyhow!("Invalid signature: {e}"));
}
}
*/
}
}
}
Ok(())
}
}
impl<P: PromptHandler> DecryptionHelper for &mut Keyring<P> {
fn decrypt<D>(
&mut self,
pkesks: &[PKESK],
_skesks: &[SKESK],
sym_algo: Option<openpgp::types::SymmetricAlgorithm>,
mut decrypt: D,
) -> openpgp::Result<Option<openpgp::Fingerprint>>
where
D: FnMut(openpgp::types::SymmetricAlgorithm, &openpgp::crypto::SessionKey) -> bool,
{
let null = NullPolicy::new();
// unoptimized route: use all locally stored certs
for pkesk in pkesks {
for cert in self.full_certs.iter().filter(|cert| {
pkesk.recipient().is_wildcard()
|| cert.keys().any(|k| &k.keyid() == pkesk.recipient())
}) {
let name = cert
.userids()
.next()
.and_then(|userid| userid.userid().name2().transpose())
.transpose()
.ok()
.flatten();
for key in cert
.keys()
.with_policy(&null, None)
.for_storage_encryption()
.secret()
{
let secret_key = key.key().clone();
let mut keypair = if secret_key.has_unencrypted_secret() {
secret_key
.into_keypair()
.context("Has unencrypted secret")?
} else {
let message = if let Some(name) = name.as_ref() {
format!("Decryption key for {} ({name}): ", secret_key.keyid())
} else {
format!("Decryption key for {}: ", secret_key.keyid())
};
let passphrase = self
.pm
.lock()
.expect(bug!(POISONED_MUTEX))
.prompt_passphrase(&message)
.context("Decryption passphrase")?;
secret_key
.decrypt_secret(&passphrase.as_str().into())
.context("has_unencrypted_secret is false, could not decrypt secret")?
.into_keypair()
.context("just-decrypted key")?
};
if pkesk
.decrypt(&mut keypair, sym_algo)
.is_some_and(|(algo, sk)| decrypt(algo, &sk))
{
return Ok(Some(key.fingerprint()));
}
}
}
}
Err(Error::SecretKeyNotFound.into())
}
}