use std::borrow::Cow;
use std::time::SystemTime;
use std::sync::OnceLock;
use sequoia_openpgp as openpgp;
use openpgp::Cert;
use openpgp::Fingerprint;
use openpgp::KeyHandle;
use openpgp::KeyID;
use openpgp::Result;
use openpgp::cert::raw::RawCert;
use openpgp::cert::ValidCert;
use openpgp::packet::Key;
use openpgp::packet::UserID;
use openpgp::packet::key;
use openpgp::policy::Policy;
use openpgp::serialize::SerializeInto;
use super::TRACE;
#[derive(Clone)]
pub struct LazyCert<'a> {
raw: OnceLock<RawCert<'a>>,
cert: OnceLock<Cow<'a, Cert>>,
}
impl<'a> std::fmt::Debug for LazyCert<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LazyCert")
.field("fingerprint", &self.fingerprint())
.field("subkeys",
&self.subkeys().map(|k| k.fingerprint())
.collect::<Vec<Fingerprint>>())
.field("userids",
&self.userids().collect::<Vec<UserID>>())
.finish()
}
}
impl<'a> LazyCert<'a> {
pub(crate) fn is_parsed(&self) -> bool {
self.cert.get().is_some()
}
pub fn from_cert(cert: Cert) -> Self {
tracer!(TRACE, "LazyCert::from_cert");
t!("Adding a parsed cert: {}", cert.fingerprint());
Self {
raw: OnceLock::new(),
cert: OnceLock::from(Cow::Owned(cert)),
}
}
pub fn from_cert_ref(cert: &'a Cert) -> Self {
tracer!(TRACE, "LazyCert::from_cert_ref");
t!("Adding a parsed cert: {}", cert.fingerprint());
Self {
raw: OnceLock::new(),
cert: OnceLock::from(Cow::Borrowed(cert)),
}
}
pub fn from_raw_cert(raw: RawCert<'a>) -> Self {
Self {
raw: OnceLock::from(raw),
cert: OnceLock::new(),
}
}
pub fn raw_cert(&self) -> Option<&RawCert<'a>> {
self.raw.get()
}
pub fn into_raw_cert(mut self) -> std::result::Result<RawCert<'a>, Self> {
match self.raw.take() {
Some(raw) => Ok(raw),
None => Err(self),
}
}
pub fn fingerprint(&self) -> Fingerprint {
if let Some(cert) = self.cert.get() {
cert.fingerprint()
} else if let Some(raw) = self.raw.get() {
raw.fingerprint()
} else {
unreachable!("cert or raw must be set")
}
}
pub fn keyid(&self) -> KeyID {
KeyID::from(self.fingerprint())
}
pub fn key_handle(&self) -> KeyHandle {
KeyHandle::from(self.fingerprint())
}
pub fn userids(&self)
-> impl Iterator<Item=UserID> + '_
{
if let Some(cert) = self.cert.get() {
Box::new(cert.userids().map(|ua| ua.userid().clone()))
as Box<dyn Iterator<Item=UserID> + '_>
} else if let Some(raw) = self.raw.get() {
Box::new(raw.userids())
as Box<dyn Iterator<Item=UserID> + '_>
} else {
unreachable!("cert or raw must be set")
}
}
pub fn keys(&self)
-> impl Iterator<Item=Key<key::PublicParts, key::UnspecifiedRole>> + '_
{
if let Some(cert) = self.cert.get() {
Box::new(cert.keys().map(|ka| ka.key().clone()))
as Box<dyn Iterator<Item=Key<_, _>> + '_>
} else if let Some(raw) = self.raw.get() {
Box::new(
raw
.keys()
.collect::<Vec<Key<_, _>>>()
.into_iter())
as Box<dyn Iterator<Item=Key<_, _>> + '_>
} else {
unreachable!("cert or raw must be set")
}
}
pub fn primary_key(&self) -> Key<key::PublicParts, key::PrimaryRole> {
self.keys().next().expect("have a primary key").role_into_primary()
}
pub fn subkeys<'b>(&'b self)
-> impl Iterator<Item=Key<key::PublicParts,
key::UnspecifiedRole>> + 'b
{
self.keys().skip(1)
}
pub fn to_cert(&self) -> Result<&Cert> {
tracer!(TRACE, "LazyCert::to_cert");
if let Some(cert) = self.cert.get() {
return Ok(cert);
}
if let Some(raw) = self.raw.get() {
t!("Resolving {}", raw.fingerprint());
match Cert::try_from(raw) {
Ok(cert) => {
self.cert.set(Cow::Owned(cert))
.expect("just checked that it was empty");
}
Err(err) => {
return Err(err);
}
}
}
if let Some(cert) = self.cert.get() {
return Ok(cert);
} else {
unreachable!("cert or raw must be set")
}
}
pub fn into_cert(self) -> Result<Cert> {
let _ = self.to_cert()?;
Ok(self.cert.into_inner().expect("valid").into_owned())
}
pub fn with_policy<'b, T>(&'b self, policy: &'b dyn Policy, time: T)
-> Result<ValidCert<'b>>
where
T: Into<Option<SystemTime>>,
{
let cert = self.to_cert()?;
cert.with_policy(policy, time)
}
pub fn is_tsk(&self) -> bool {
if let Some(cert) = self.cert.get() {
cert.is_tsk()
} else if let Some(raw) = self.raw.get() {
raw.keys().any(|key| key.has_secret())
} else {
unreachable!("cert or raw must be set")
}
}
}
impl<'a> From<Cert> for LazyCert<'a> {
fn from(cert: Cert) -> Self {
LazyCert::from_cert(cert)
}
}
impl<'a> From<&'a Cert> for LazyCert<'a> {
fn from(cert: &'a Cert) -> Self {
LazyCert::from_cert_ref(cert)
}
}
impl<'a> From<RawCert<'a>> for LazyCert<'a> {
fn from(cert: RawCert<'a>) -> Self {
LazyCert::from_raw_cert(cert)
}
}
impl<'a> LazyCert<'a> {
pub fn to_vec(&self) -> Result<Vec<u8>> {
if let Some(raw) = self.raw.get() {
Ok(raw.as_bytes().to_vec())
} else if let Some(cert) = self.cert.get() {
Ok(cert.to_vec()?)
} else {
unreachable!("raw or cert must be set");
}
}
pub fn export(&self, o: &mut dyn std::io::Write) -> Result<()> {
use openpgp::serialize::Marshal;
let cert = self.to_cert()?;
Ok(cert.export(o)?)
}
}