use pgp::{
composed::SignedPublicKey,
packet::{SignatureConfig, SignatureType, Subpacket, SubpacketData},
types::{KeyDetails, Tag, Timestamp},
};
use rand::thread_rng;
use rpgpie::certificate::{Certificate, Checked};
use sop::Password;
use crate::{Certs, Keys, RPGSOPOCT, card, card::get_card};
pub(crate) struct CertifyUserID {
userids: Vec<String>,
with_key_password: Vec<Password>,
no_require_self_sig: bool,
certifiers: Vec<Certificate>,
}
impl CertifyUserID {
pub(crate) fn new() -> CertifyUserID {
Self {
userids: Default::default(),
with_key_password: Default::default(),
no_require_self_sig: false,
certifiers: Default::default(),
}
}
}
impl<'a> sop::ops::CertifyUserID<'a, RPGSOPOCT, Certs, Keys> for CertifyUserID {
fn userid(
mut self: Box<Self>,
userid: String,
) -> Box<dyn sop::ops::CertifyUserID<'a, RPGSOPOCT, Certs, Keys> + 'a> {
self.userids.push(userid);
self
}
fn with_key_password(
mut self: Box<Self>,
password: Password,
) -> sop::Result<Box<dyn sop::ops::CertifyUserID<'a, RPGSOPOCT, Certs, Keys> + 'a>> {
self.with_key_password.push(password);
Ok(self)
}
fn no_require_self_sig(
mut self: Box<Self>,
) -> Box<dyn sop::ops::CertifyUserID<'a, RPGSOPOCT, Certs, Keys> + 'a> {
self.no_require_self_sig = true;
self
}
fn keys(
mut self: Box<Self>,
keys: &Keys,
) -> sop::Result<Box<dyn sop::ops::CertifyUserID<'a, RPGSOPOCT, Certs, Keys> + 'a>> {
for key in &keys.keys {
self.certifiers.push(key.clone());
}
Ok(self)
}
fn certify(self: Box<Self>, certs: &Certs) -> sop::Result<Certs> {
if self.no_require_self_sig {
unimplemented!("no-require-self-sig is currently unsupported"); }
let now = Timestamp::now();
let mut cards = Vec::new();
for certifier in self.certifiers {
let ccert: Checked = certifier.clone().into();
if ccert
.primary_valid_at(now)
.map_err(|_e| sop::errors::Error::KeyCannotSign)?
{
let spk: SignedPublicKey = certifier.into();
let signer = spk.primary_key;
log::info!(
"Trying to certify with signer: {:02x?}",
signer.fingerprint()
);
let card = get_card(&signer, openpgp_card::ocard::KeyType::Signing)
.map_err(|_| sop::errors::Error::UnspecifiedFailure)?;
cards.push(card);
} else {
return Err(sop::errors::Error::KeyCannotSign);
}
}
let mut res = vec![];
for target in &certs.certs {
let mut updated: SignedPublicKey = target.clone().into();
for uid in &self.userids {
let checked: Checked = target.clone().into();
let checked_users = checked.user_ids();
let Some(su) = checked_users
.iter()
.find(|user| user.id.id() == uid.as_bytes())
else {
return Err(sop::errors::Error::CertUseridNoMatch);
};
for card in &mut cards {
let sig = card::do_on_card(
card,
openpgp_card::ocard::KeyType::Signing,
&self.with_key_password,
&|| {
eprintln!("Touch confirmation required for certification with rsop-oct")
},
|cs| {
let mut sc = SignatureConfig::from_key(
thread_rng(),
cs,
SignatureType::CertGeneric,
)?;
sc.hashed_subpackets = vec![
Subpacket::regular(SubpacketData::IssuerFingerprint(
cs.fingerprint(),
))?,
Subpacket::regular(SubpacketData::SignatureCreationTime(now))?,
];
sc.unhashed_subpackets = vec![Subpacket::regular(
SubpacketData::IssuerKeyId(cs.legacy_key_id()),
)?];
sc.sign_certification_third_party(
cs,
&pgp::types::Password::empty(),
&updated.primary_key,
Tag::UserId,
&su.id,
)
.map_err(|e| crate::Error::OcardRpgp(e.into()))
},
)
.map_err(|e| {
eprintln!("Error while using card: {e:?}");
sop::errors::Error::UnspecifiedFailure
})?;
if let Some(su) = updated
.details
.users
.iter_mut()
.find(|user| user.id.id() == uid.as_bytes())
{
su.signatures.push(sig);
} else {
return Err(sop::errors::Error::CertUseridNoMatch);
};
}
}
res.push(updated.into());
}
if res.is_empty() {
eprintln!("No certificates to output");
return Err(sop::errors::Error::UnspecifiedFailure);
}
Ok(Certs {
certs: res,
source_name: None,
})
}
}