use std::io;
use std::cmp;
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::path::Path;
use std::mem;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::time;
use crate::{
crypto::{
Signer,
hash::Digest,
},
Error,
Result,
SignatureType,
packet,
packet::Signature,
packet::Key,
packet::key,
packet::Tag,
packet::UserID,
packet::UserAttribute,
packet::Unknown,
Packet,
PacketPile,
seal,
KeyID,
Fingerprint,
KeyHandle,
policy::Policy,
};
use crate::parse::{Parse, PacketParserResult, PacketParser};
use crate::types::{
AEADAlgorithm,
CompressionAlgorithm,
Features,
HashAlgorithm,
KeyServerPreferences,
ReasonForRevocation,
RevocationKey,
RevocationStatus,
SymmetricAlgorithm,
};
pub mod amalgamation;
mod builder;
mod bindings;
pub mod bundle;
mod parser;
mod revoke;
pub use self::builder::{CertBuilder, CipherSuite};
pub use parser::{
CertParser,
};
pub(crate) use parser::{
CertValidator,
CertValidity,
KeyringValidator,
KeyringValidity,
};
pub use revoke::{
SubkeyRevocationBuilder,
CertRevocationBuilder,
UserAttributeRevocationBuilder,
UserIDRevocationBuilder,
};
pub mod prelude;
use prelude::*;
const TRACE : bool = false;
fn canonical_signature_order(a: Option<time::SystemTime>, b: Option<time::SystemTime>)
-> Ordering {
a.cmp(&b).reverse()
}
fn sig_cmp(a: &Signature, b: &Signature) -> Ordering {
match canonical_signature_order(a.signature_creation_time(),
b.signature_creation_time()) {
Ordering::Equal => a.mpis().cmp(b.mpis()),
r => r
}
}
impl fmt::Display for Cert {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.fingerprint())
}
}
#[derive(Debug, Clone, PartialEq)]
struct ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
bundles: Vec<ComponentBundle<C>>,
}
impl<C> Deref for ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
type Target = Vec<ComponentBundle<C>>;
fn deref(&self) -> &Self::Target {
&self.bundles
}
}
impl<C> DerefMut for ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
fn deref_mut(&mut self) -> &mut Vec<ComponentBundle<C>> {
&mut self.bundles
}
}
impl<C> Into<Vec<ComponentBundle<C>>> for ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
fn into(self) -> Vec<ComponentBundle<C>> {
self.bundles
}
}
impl<C> IntoIterator for ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
type Item = ComponentBundle<C>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.bundles.into_iter()
}
}
impl<C> ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
fn new() -> Self {
Self { bundles: vec![] }
}
}
impl<C> ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
fn sort_and_dedup<F, F2>(&mut self, cmp: F, merge: F2)
where F: Fn(&C, &C) -> Ordering,
F2: Fn(&mut C, &mut C)
{
self.bundles.sort_unstable_by(
|a, b| cmp(&a.component, &b.component));
self.bundles.dedup_by(|a, b| {
if cmp(&a.component, &b.component) == Ordering::Equal {
merge(&mut a.component, &mut b.component);
b.self_signatures.append(&mut a.self_signatures);
b.certifications.append(&mut a.certifications);
b.self_revocations.append(&mut a.self_revocations);
b.other_revocations.append(&mut a.other_revocations);
true
} else {
false
}
});
for b in self.bundles.iter_mut() {
b.sort_and_dedup();
}
}
}
type KeyBundles<KeyPart, KeyRole> = ComponentBundles<Key<KeyPart, KeyRole>>;
type SubkeyBundles<KeyPart> = KeyBundles<KeyPart, key::SubordinateRole>;
#[allow(dead_code)]
type GenericKeyBundles
= ComponentBundles<Key<key::UnspecifiedParts, key::UnspecifiedRole>>;
type UserIDBundles = ComponentBundles<UserID>;
type UserAttributeBundles = ComponentBundles<UserAttribute>;
type UnknownBundles = ComponentBundles<Unknown>;
pub trait Preferences<'a>: seal::Sealed {
fn preferred_symmetric_algorithms(&self)
-> Option<&'a [SymmetricAlgorithm]>;
fn preferred_hash_algorithms(&self) -> Option<&'a [HashAlgorithm]>;
fn preferred_compression_algorithms(&self)
-> Option<&'a [CompressionAlgorithm]>;
fn preferred_aead_algorithms(&self) -> Option<&'a [AEADAlgorithm]>;
fn key_server_preferences(&self) -> Option<KeyServerPreferences>;
fn preferred_key_server(&self) -> Option<&'a [u8]>;
fn features(&self) -> Option<Features>;
}
#[derive(Debug, Clone, PartialEq)]
pub struct Cert {
primary: PrimaryKeyBundle<key::PublicParts>,
userids: UserIDBundles,
user_attributes: UserAttributeBundles,
subkeys: SubkeyBundles<key::PublicParts>,
unknowns: UnknownBundles,
bad: Vec<packet::Signature>,
}
assert_send_and_sync!(Cert);
impl std::str::FromStr for Cert {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Self::from_bytes(s.as_bytes())
}
}
impl<'a> Parse<'a, Cert> for Cert {
fn from_reader<R: io::Read + Send + Sync>(reader: R) -> Result<Self> {
Cert::try_from(PacketParser::from_reader(reader)?)
}
fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
Cert::try_from(PacketParser::from_file(path)?)
}
fn from_bytes<D: AsRef<[u8]> + ?Sized + Send + Sync>(data: &'a D) -> Result<Self> {
Cert::try_from(PacketParser::from_bytes(data)?)
}
}
impl Cert {
pub fn primary_key(&self) -> PrimaryKeyAmalgamation<key::PublicParts>
{
PrimaryKeyAmalgamation::new(&self)
}
pub fn revocation_status<T>(&self, policy: &dyn Policy, t: T) -> RevocationStatus
where T: Into<Option<time::SystemTime>>
{
let t = t.into();
let vkao = self.primary_key().with_policy(policy, t).ok();
let mut sig = vkao.as_ref().map(|vka| vka.binding_signature());
if let Some(direct) = vkao.as_ref()
.and_then(|vka| vka.direct_key_signature().ok())
{
match (direct.signature_creation_time(),
sig.and_then(|s| s.signature_creation_time())) {
(Some(ds), Some(bs)) if ds > bs =>
sig = Some(direct),
_ => ()
}
}
self.primary_key().bundle()._revocation_status(policy, t, true, sig)
}
pub fn revoke(&self, primary_signer: &mut dyn Signer,
code: ReasonForRevocation, reason: &[u8])
-> Result<Signature>
{
CertRevocationBuilder::new()
.set_reason_for_revocation(code, reason)?
.build(primary_signer, &self, None)
}
#[cfg(test)]
fn set_validity_period_as_of(self, policy: &dyn Policy,
primary_signer: &mut dyn Signer,
expiration: Option<time::Duration>,
now: time::SystemTime)
-> Result<Cert>
{
let primary = self.primary_key().with_policy(policy, now)?;
let sigs = primary.set_validity_period_as_of(primary_signer,
expiration,
now)?;
self.insert_packets(sigs)
}
pub fn set_expiration_time<T>(&self, policy: &dyn Policy, t: T,
primary_signer: &mut dyn Signer,
expiration: Option<time::SystemTime>)
-> Result<Vec<Signature>>
where T: Into<Option<time::SystemTime>>,
{
let primary = self.primary_key().with_policy(policy, t.into())?;
primary.set_expiration_time(primary_signer, expiration)
}
fn primary_userid_relaxed<'a, T>(&'a self, policy: &'a dyn Policy, t: T,
valid_cert: bool)
-> Result<ValidUserIDAmalgamation<'a>>
where T: Into<Option<std::time::SystemTime>>
{
let t = t.into().unwrap_or_else(std::time::SystemTime::now);
ValidComponentAmalgamation::primary(self, self.userids.iter(),
policy, t, valid_cert)
}
pub fn userids(&self) -> UserIDAmalgamationIter {
ComponentAmalgamationIter::new(self, self.userids.iter())
}
pub fn user_attributes(&self) -> UserAttributeAmalgamationIter {
ComponentAmalgamationIter::new(self, self.user_attributes.iter())
}
pub fn keys(&self) -> KeyAmalgamationIter<key::PublicParts, key::UnspecifiedRole>
{
KeyAmalgamationIter::new(self)
}
pub(crate) fn subkeys(&self) -> ComponentAmalgamationIter<Key<key::PublicParts,
key::SubordinateRole>>
{
ComponentAmalgamationIter::new(self, self.subkeys.iter())
}
pub fn unknowns(&self) -> UnknownComponentAmalgamationIter {
ComponentAmalgamationIter::new(self, self.unknowns.iter())
}
pub fn bad_signatures(&self)
-> impl Iterator<Item = &Signature> + Send + Sync {
self.bad.iter()
}
pub fn revocation_keys<'a>(&'a self, policy: &dyn Policy)
-> Box<dyn Iterator<Item = &'a RevocationKey> + 'a>
{
let mut keys = std::collections::HashSet::new();
let pk_sec = self.primary_key().hash_algo_security();
self.userids()
.flat_map(|ua| {
let sec = ua.hash_algo_security;
ua.self_signatures()
.filter(move |sig| {
policy.signature(sig, sec).is_ok()
})
})
.chain(self.primary_key()
.self_signatures()
.filter(|sig| {
policy.signature(sig, pk_sec).is_ok()
}))
.flat_map(|sig| sig.revocation_keys())
.for_each(|rk| { keys.insert(rk); });
Box::new(keys.into_iter())
}
pub fn into_packets(self) -> impl Iterator<Item=Packet> + Send + Sync {
fn rewrite(mut p: impl Iterator<Item=Packet> + Send + Sync)
-> impl Iterator<Item=Packet> + Send + Sync
{
let k: Packet = match p.next().unwrap() {
Packet::PublicKey(k) => {
if k.has_secret() {
Packet::SecretKey(k.parts_into_secret().unwrap())
} else {
Packet::PublicKey(k)
}
}
Packet::PublicSubkey(k) => {
if k.has_secret() {
Packet::SecretSubkey(k.parts_into_secret().unwrap())
} else {
Packet::PublicSubkey(k)
}
}
_ => unreachable!(),
};
std::iter::once(k).chain(p)
}
rewrite(self.primary.into_packets())
.chain(self.userids.into_iter().flat_map(|b| b.into_packets()))
.chain(self.user_attributes.into_iter().flat_map(|b| b.into_packets()))
.chain(self.subkeys.into_iter().flat_map(|b| rewrite(b.into_packets())))
.chain(self.unknowns.into_iter().flat_map(|b| b.into_packets()))
.chain(self.bad.into_iter().map(|s| s.into()))
}
pub fn from_packets(p: impl Iterator<Item=Packet> + Send + Sync) -> Result<Self> {
let mut i = parser::CertParser::from_iter(p);
if let Some(cert_result) = i.next() {
if i.next().is_some() {
Err(Error::MalformedCert(
"Additional packets found, is this a keyring?".into()
).into())
} else {
cert_result
}
} else {
Err(Error::MalformedCert("No data".into()).into())
}
}
pub fn into_packet_pile(self) -> PacketPile {
self.into()
}
fn canonicalize(mut self) -> Self {
tracer!(TRACE, "canonicalize", 0);
macro_rules! check {
($desc:expr, $binding:expr, $sigs:ident,
$verify_method:ident, $($verify_args:expr),*) => ({
t!("check!({}, {}, {:?}, {}, ...)",
$desc, stringify!($binding), $binding.$sigs,
stringify!($verify_method));
for mut sig in mem::replace(&mut $binding.$sigs, Vec::new())
.into_iter()
{
match sig.$verify_method(self.primary.key(),
self.primary.key(),
$($verify_args),*) {
Ok(()) => $binding.$sigs.push(sig),
Err(err) => {
t!("Sig {:02X}{:02X}, type = {} \
doesn't belong to {}: {:?}",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ(), $desc, err);
self.bad.push(sig);
}
}
}
});
($desc:expr, $binding:expr, $sigs:ident,
$verify_method:ident) => ({
check!($desc, $binding, $sigs, $verify_method,)
});
}
macro_rules! check_3rd_party {
($desc:expr, // a description of the component
$binding:expr, // the binding to check
$sigs:ident, // a vector of sigs in $binding to check
$lookup_fn:expr, // a function to lookup keys
$verify_method:ident, // the method to call to verify it
$hash_method:ident, // the method to call to compute the hash
$($verify_args:expr),* // additional arguments to pass to the above
) => ({
t!("check_3rd_party!({}, {}, {:?}, {}, {}, ...)",
$desc, stringify!($binding), $binding.$sigs,
stringify!($verify_method), stringify!($hash_method));
for mut sig in mem::replace(&mut $binding.$sigs, Vec::new())
.into_iter()
{
let key = self.primary.key();
match sig.hash_algo().context().and_then(|mut ctx| {
sig.$hash_method(&mut ctx, key, $($verify_args),*);
ctx.into_digest()
}) {
Ok(hash) => {
if &sig.digest_prefix()[..] == &hash[..2] {
if let Some(key) = $lookup_fn(&sig) {
if let Ok(()) = sig.$verify_method(
&key, self.primary.key(), $($verify_args),*)
{
$binding.$sigs.push(sig);
} else {
t!("Sig {:02X}{:02X}, type = {} \
doesn't belong to {}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
sig.typ(), $desc);
self.bad.push(sig);
}
} else {
$binding.$sigs.push(sig);
}
} else {
t!("Sig {:02X}{:02X}, type = {} \
doesn't belong to {} (computed hash's prefix: {:02X}{:02X})",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ(), $desc,
hash[0], hash[1]);
self.bad.push(sig);
}
},
Err(e) => {
t!("Sig {:02X}{:02X}, type = {}: \
Hashing failed: {}",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ(), e);
self.bad.push(sig);
},
}
}
});
($desc:expr, $binding:expr, $sigs:ident, $lookup_fn:expr,
$verify_method:ident, $hash_method:ident) => ({
check_3rd_party!($desc, $binding, $sigs, $lookup_fn,
$verify_method, $hash_method, )
});
}
fn lookup_fn(_: &Signature)
-> Option<Key<key::PublicParts, key::UnspecifiedRole>> {
None
}
check!("primary key",
self.primary, self_signatures, verify_direct_key);
check!("primary key",
self.primary, self_revocations, verify_primary_key_revocation);
check_3rd_party!("primary key",
self.primary, certifications, lookup_fn,
verify_direct_key, hash_direct_key);
check_3rd_party!("primary key",
self.primary, other_revocations, lookup_fn,
verify_primary_key_revocation, hash_direct_key);
for binding in self.userids.iter_mut() {
check!(format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, self_signatures, verify_userid_binding,
binding.userid());
check!(format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, self_revocations, verify_userid_revocation,
binding.userid());
check_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, certifications, lookup_fn,
verify_userid_binding, hash_userid_binding,
binding.userid());
check_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, other_revocations, lookup_fn,
verify_userid_revocation, hash_userid_binding,
binding.userid());
}
for binding in self.user_attributes.iter_mut() {
check!("user attribute",
binding, self_signatures, verify_user_attribute_binding,
binding.user_attribute());
check!("user attribute",
binding, self_revocations, verify_user_attribute_revocation,
binding.user_attribute());
check_3rd_party!(
"user attribute",
binding, certifications, lookup_fn,
verify_user_attribute_binding, hash_user_attribute_binding,
binding.user_attribute());
check_3rd_party!(
"user attribute",
binding, other_revocations, lookup_fn,
verify_user_attribute_revocation, hash_user_attribute_binding,
binding.user_attribute());
}
for binding in self.subkeys.iter_mut() {
check!(format!("subkey {}", binding.key().keyid()),
binding, self_signatures, verify_subkey_binding,
binding.key());
check!(format!("subkey {}", binding.key().keyid()),
binding, self_revocations, verify_subkey_revocation,
binding.key());
check_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding, certifications, lookup_fn,
verify_subkey_binding, hash_subkey_binding,
binding.key());
check_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding, other_revocations, lookup_fn,
verify_subkey_revocation, hash_subkey_binding,
binding.key());
}
let mut bad_sigs: Vec<(Option<usize>, Signature)> =
mem::replace(&mut self.bad, Vec::new()).into_iter()
.map(|sig| (None, sig)).collect();
for (i, c) in self.unknowns.iter_mut().enumerate() {
for sig in mem::replace(&mut c.certifications, Vec::new()) {
bad_sigs.push((Some(i), sig));
}
}
let primary_fp: KeyHandle = self.key_handle();
let primary_keyid = KeyHandle::KeyID(primary_fp.clone().into());
'outer: for (unknown_idx, mut sig) in bad_sigs {
let mut found_component = false;
let issuers =
sig.get_issuers();
let is_selfsig =
issuers.is_empty()
|| issuers.contains(&primary_fp)
|| issuers.contains(&primary_keyid);
macro_rules! check_one {
($desc:expr, $sigs:expr, $sig:expr,
$verify_method:ident, $($verify_args:expr),*) => ({
if is_selfsig {
t!("check_one!({}, {:?}, {:?}, {}, ...)",
$desc, $sigs, $sig,
stringify!($verify_method));
if let Ok(())
= $sig.$verify_method(self.primary.key(),
self.primary.key(),
$($verify_args),*)
{
t!("Sig {:02X}{:02X}, {:?} \
was out of place. Belongs to {}.",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
$sigs.push($sig);
continue 'outer;
}
}
});
($desc:expr, $sigs:expr, $sig:expr,
$verify_method:ident) => ({
check_one!($desc, $sigs, $sig, $verify_method,)
});
}
macro_rules! check_one_3rd_party {
($desc:expr, // a description of the component
$sigs:expr, // where to put $sig if successful
$sig:ident, // the signature to check
$lookup_fn:expr, // a function to lookup keys
$verify_method:ident, // the method to verify it
$hash_method:ident, // the method to compute the hash
$($verify_args:expr),* // additional arguments for the above
) => ({
if ! is_selfsig {
t!("check_one_3rd_party!({}, {}, {:?}, {}, {}, ...)",
$desc, stringify!($sigs), $sig,
stringify!($verify_method), stringify!($hash_method));
if let Some(key) = $lookup_fn(&sig) {
if let Ok(()) = sig.$verify_method(&key,
self.primary.key(),
$($verify_args),*)
{
t!("Sig {:02X}{:02X}, {:?} \
was out of place. Belongs to {}.",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
$sigs.push($sig);
continue 'outer;
}
} else {
let key = self.primary.key();
if let Ok(hash) = sig.hash_algo().context()
.and_then(|mut ctx| {
sig.$hash_method(&mut ctx, key,
$($verify_args),*);
ctx.into_digest()
})
{
if &sig.digest_prefix()[..] == &hash[..2] {
t!("Sig {:02X}{:02X}, {:?} \
was out of place. Likely belongs to {}.",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
$sigs.push($sig.clone());
t!("Will keep trying to match this sig to \
other components (found before? {:?})...",
found_component);
found_component = true;
}
}
}
}
});
($desc:expr, $sigs:expr, $sig:ident, $lookup_fn:expr,
$verify_method:ident, $hash_method:ident) => ({
check_one_3rd_party!($desc, $sigs, $sig, $lookup_fn,
$verify_method, $hash_method, )
});
}
use SignatureType::*;
match sig.typ() {
DirectKey => {
check_one!("primary key", self.primary.self_signatures,
sig, verify_direct_key);
check_one_3rd_party!(
"primary key", self.primary.certifications, sig,
lookup_fn,
verify_direct_key, hash_direct_key);
},
KeyRevocation => {
check_one!("primary key", self.primary.self_revocations,
sig, verify_primary_key_revocation);
check_one_3rd_party!(
"primary key", self.primary.other_revocations, sig,
lookup_fn, verify_primary_key_revocation,
hash_direct_key);
},
GenericCertification | PersonaCertification
| CasualCertification | PositiveCertification =>
{
for binding in self.userids.iter_mut() {
check_one!(format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.self_signatures, sig,
verify_userid_binding, binding.userid());
check_one_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.certifications, sig, lookup_fn,
verify_userid_binding, hash_userid_binding,
binding.userid());
}
for binding in self.user_attributes.iter_mut() {
check_one!("user attribute",
binding.self_signatures, sig,
verify_user_attribute_binding,
binding.user_attribute());
check_one_3rd_party!(
"user attribute",
binding.certifications, sig, lookup_fn,
verify_user_attribute_binding,
hash_user_attribute_binding,
binding.user_attribute());
}
},
CertificationRevocation => {
for binding in self.userids.iter_mut() {
check_one!(format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.self_revocations, sig,
verify_userid_revocation,
binding.userid());
check_one_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.other_revocations, sig, lookup_fn,
verify_userid_revocation, hash_userid_binding,
binding.userid());
}
for binding in self.user_attributes.iter_mut() {
check_one!("user attribute",
binding.self_revocations, sig,
verify_user_attribute_revocation,
binding.user_attribute());
check_one_3rd_party!(
"user attribute",
binding.other_revocations, sig, lookup_fn,
verify_user_attribute_revocation,
hash_user_attribute_binding,
binding.user_attribute());
}
},
SubkeyBinding => {
for binding in self.subkeys.iter_mut() {
check_one!(format!("subkey {}", binding.key().keyid()),
binding.self_signatures, sig,
verify_subkey_binding, binding.key());
check_one_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding.certifications, sig, lookup_fn,
verify_subkey_binding, hash_subkey_binding,
binding.key());
}
},
SubkeyRevocation => {
for binding in self.subkeys.iter_mut() {
check_one!(format!("subkey {}", binding.key().keyid()),
binding.self_revocations, sig,
verify_subkey_revocation, binding.key());
check_one_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding.other_revocations, sig, lookup_fn,
verify_subkey_revocation, hash_subkey_binding,
binding.key());
}
},
typ => {
t!("Odd signature type: {:?}", typ);
},
}
if found_component {
continue;
}
t!("{} {:02X}{:02X}, {:?} doesn't belong \
to any known component or is bad.",
if is_selfsig { "Self-sig" } else { "3rd-party-sig" },
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ());
if let Some(i) = unknown_idx {
self.unknowns[i].certifications.push(sig);
} else {
self.bad.push(sig);
}
}
if self.bad.len() > 0 {
t!("{}: ignoring {} bad self signatures",
self.keyid(), self.bad.len());
}
self.primary.sort_and_dedup();
self.bad.sort_by(Signature::normalized_cmp);
self.bad.dedup_by(|a, b| a.normalized_eq(b));
self.bad.sort_by(sig_cmp);
self.userids.sort_and_dedup(UserID::cmp, |_, _| {});
self.user_attributes.sort_and_dedup(UserAttribute::cmp, |_, _| {});
self.subkeys.sort_and_dedup(Key::public_cmp,
|a, b| {
if ! b.has_secret() && a.has_secret() {
std::mem::swap(a, b);
}
});
let primary_fp: KeyHandle = self.key_handle();
let primary_keyid = KeyHandle::KeyID(primary_fp.clone().into());
for c in self.unknowns.iter_mut() {
parser::split_sigs(&primary_fp, &primary_keyid, c);
}
self.unknowns.sort_and_dedup(Unknown::best_effort_cmp, |_, _| {});
self
}
pub fn key_handle(&self) -> KeyHandle {
self.primary.key().key_handle()
}
pub fn fingerprint(&self) -> Fingerprint {
self.primary.key().fingerprint()
}
pub fn keyid(&self) -> KeyID {
self.primary.key().keyid()
}
pub fn merge_public(self, other: Cert) -> Result<Self> {
let other_public = Cert::from_packets(
other.into_packets().map(|p| match p {
Packet::PublicKey(k) => k.take_secret().0.into(),
Packet::SecretKey(k) => k.take_secret().0.into(),
Packet::PublicSubkey(k) => k.take_secret().0.into(),
Packet::SecretSubkey(k) => k.take_secret().0.into(),
p => p,
}))?;
self.merge_public_and_secret(other_public)
}
pub fn merge_public_and_secret(mut self, mut other: Cert) -> Result<Self> {
if self.fingerprint() != other.fingerprint() {
return Err(Error::InvalidArgument(
"Primary key mismatch".into()).into());
}
if ! self.primary.key().has_secret()
&& other.primary.key().has_secret()
{
std::mem::swap(self.primary.key_mut(), other.primary.key_mut());
}
self.primary.self_signatures.append(
&mut other.primary.self_signatures);
self.primary.certifications.append(
&mut other.primary.certifications);
self.primary.self_revocations.append(
&mut other.primary.self_revocations);
self.primary.other_revocations.append(
&mut other.primary.other_revocations);
self.userids.append(&mut other.userids);
self.user_attributes.append(&mut other.user_attributes);
self.subkeys.append(&mut other.subkeys);
self.bad.append(&mut other.bad);
Ok(self.canonicalize())
}
fn valid_start<T>(tag: T) -> Result<()>
where T: Into<Tag>
{
let tag = tag.into();
match tag {
Tag::SecretKey | Tag::PublicKey => Ok(()),
_ => Err(Error::MalformedCert(
format!("A certificate does not start with a {}",
tag).into()).into()),
}
}
fn valid_packet<T>(tag: T) -> Result<()>
where T: Into<Tag>
{
let tag = tag.into();
match tag {
Tag::Reserved
| Tag::PKESK
| Tag::SKESK
| Tag::OnePassSig
| Tag::CompressedData
| Tag::SED
| Tag::Literal
| Tag::SEIP
| Tag::MDC
| Tag::AED =>
{
Err(Error::MalformedCert(
format!("A certificate cannot not include a {}",
tag).into()).into())
}
_ => Ok(()),
}
}
pub fn insert_packets<I>(self, packets: I)
-> Result<Self>
where I: IntoIterator,
I::Item: Into<Packet>,
{
let mut combined = self.into_packets().collect::<Vec<_>>();
fn replace_or_push<P, R>(acc: &mut Vec<Packet>, k: Key<P, R>)
where P: key::KeyParts,
R: key::KeyRole,
Packet: From<packet::Key<P, R>>,
{
for q in acc.iter_mut() {
let replace = match q {
Packet::PublicKey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
Packet::SecretKey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
Packet::PublicSubkey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
Packet::SecretSubkey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
_ => false,
};
if replace {
*q = k.into();
return;
}
}
acc.push(k.into());
};
fn rop_sig(acc: &mut Vec<Packet>, sig: Signature)
{
for q in acc.iter_mut() {
let replace = match q {
Packet::Signature(s) => s.normalized_eq(&sig),
_ => false,
};
if replace {
*q = sig.into();
return;
}
}
acc.push(sig.into());
};
for p in packets {
let p = p.into();
Cert::valid_packet(&p)?;
match p {
Packet::PublicKey(k) => replace_or_push(&mut combined, k),
Packet::SecretKey(k) => replace_or_push(&mut combined, k),
Packet::PublicSubkey(k) => replace_or_push(&mut combined, k),
Packet::SecretSubkey(k) => replace_or_push(&mut combined, k),
Packet::Signature(sig) => rop_sig(&mut combined, sig),
p => combined.push(p),
}
}
Cert::try_from(combined)
}
pub fn is_tsk(&self) -> bool {
if self.primary_key().has_secret() {
return true;
}
self.subkeys().any(|sk| {
sk.key().has_secret()
})
}
pub fn strip_secret_key_material(mut self) -> Cert {
let (pk, _sk) = self.primary.component.take_secret();
self.primary.component = pk;
let subkeys = self.subkeys.into_iter()
.map(|mut kb| {
let (pk, _sk) = kb.component.take_secret();
kb.component = pk;
kb
})
.collect::<Vec<_>>();
self.subkeys = ComponentBundles { bundles: subkeys, };
self
}
pub fn retain_userids<P>(mut self, mut predicate: P) -> Cert
where P: FnMut(UserIDAmalgamation) -> bool,
{
let mut keep = vec![false; self.userids.len()];
for (i, a) in self.userids().enumerate() {
keep[i] = predicate(a);
}
let mut i = 0;
self.userids.retain(|_| (keep[i], i += 1).0);
self
}
pub fn retain_user_attributes<P>(mut self, mut predicate: P) -> Cert
where P: FnMut(UserAttributeAmalgamation) -> bool,
{
let mut keep = vec![false; self.user_attributes.len()];
for (i, a) in self.user_attributes().enumerate() {
keep[i] = predicate(a);
}
let mut i = 0;
self.user_attributes.retain(|_| (keep[i], i += 1).0);
self
}
pub fn retain_subkeys<P>(mut self, mut predicate: P) -> Cert
where P: FnMut(SubordinateKeyAmalgamation<crate::packet::key::PublicParts>) -> bool,
{
let mut keep = vec![false; self.subkeys.len()];
for (i, a) in self.keys().subkeys().enumerate() {
keep[i] = predicate(a);
}
let mut i = 0;
self.subkeys.retain(|_| (keep[i], i += 1).0);
self
}
pub fn with_policy<'a, T>(&'a self, policy: &'a dyn Policy, time: T)
-> Result<ValidCert<'a>>
where T: Into<Option<time::SystemTime>>,
{
let time = time.into().unwrap_or_else(time::SystemTime::now);
self.primary_key().with_policy(policy, time)?;
Ok(ValidCert {
cert: self,
policy,
time,
})
}
}
impl TryFrom<PacketParserResult<'_>> for Cert {
type Error = anyhow::Error;
fn try_from(ppr: PacketParserResult) -> Result<Self> {
let mut parser = parser::CertParser::from(ppr);
if let Some(cert_result) = parser.next() {
if parser.next().is_some() {
Err(Error::MalformedCert(
"Additional packets found, is this a keyring?".into()
).into())
} else {
cert_result
}
} else {
Err(Error::MalformedCert("No data".into()).into())
}
}
}
impl TryFrom<Vec<Packet>> for Cert {
type Error = anyhow::Error;
fn try_from(p: Vec<Packet>) -> Result<Self> {
Cert::from_packets(p.into_iter())
}
}
impl TryFrom<Packet> for Cert {
type Error = anyhow::Error;
fn try_from(p: Packet) -> Result<Self> {
vec![ p ].try_into()
}
}
impl TryFrom<PacketPile> for Cert {
type Error = anyhow::Error;
fn try_from(p: PacketPile) -> Result<Self> {
Self::from_packets(p.into_children())
}
}
impl From<Cert> for Vec<Packet> {
fn from(cert: Cert) -> Self {
cert.into_packets().collect::<Vec<_>>()
}
}
pub struct IntoIter(Box<dyn Iterator<Item=Packet> + Send + Sync>);
assert_send_and_sync!(IntoIter);
impl Iterator for IntoIter {
type Item = Packet;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl IntoIterator for Cert
{
type Item = Packet;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter(Box::new(self.into_packets()))
}
}
#[derive(Debug, Clone)]
pub struct ValidCert<'a> {
cert: &'a Cert,
policy: &'a dyn Policy,
time: time::SystemTime,
}
assert_send_and_sync!(ValidCert<'_>);
impl<'a> std::ops::Deref for ValidCert<'a> {
type Target = Cert;
fn deref(&self) -> &Self::Target {
self.cert
}
}
impl<'a> fmt::Display for ValidCert<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.fingerprint())
}
}
impl<'a> ValidCert<'a> {
pub fn cert(&self) -> &'a Cert {
self.cert
}
pub fn time(&self) -> time::SystemTime {
self.time
}
pub fn policy(&self) -> &'a dyn Policy {
self.policy
}
pub fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
-> Result<ValidCert<'a>>
where T: Into<Option<time::SystemTime>>,
{
self.cert.with_policy(policy, time)
}
pub fn direct_key_signature(&self) -> Result<&'a Signature>
{
self.cert.primary.binding_signature(self.policy(), self.time())
}
pub fn revocation_status(&self) -> RevocationStatus<'a> {
self.cert.revocation_status(self.policy, self.time)
}
pub fn alive(&self) -> Result<()> {
self.primary_key().alive()
}
pub fn primary_key(&self)
-> ValidPrimaryKeyAmalgamation<'a, key::PublicParts>
{
self.cert.primary_key().with_policy(self.policy, self.time)
.expect("A ValidKeyAmalgamation must have a ValidPrimaryKeyAmalgamation")
}
pub fn keys(&self) -> ValidKeyAmalgamationIter<'a, key::PublicParts, key::UnspecifiedRole> {
self.cert.keys().with_policy(self.policy, self.time)
}
pub fn primary_userid(&self) -> Result<ValidUserIDAmalgamation<'a>>
{
self.cert.primary_userid_relaxed(self.policy(), self.time(), true)
}
pub fn userids(&self) -> ValidUserIDAmalgamationIter<'a> {
self.cert.userids().with_policy(self.policy, self.time)
}
pub fn primary_user_attribute(&self)
-> Result<ValidComponentAmalgamation<'a, UserAttribute>>
{
ValidComponentAmalgamation::primary(self.cert,
self.cert.user_attributes.iter(),
self.policy(), self.time(), true)
}
pub fn user_attributes(&self) -> ValidUserAttributeAmalgamationIter<'a> {
self.cert.user_attributes().with_policy(self.policy, self.time)
}
}
macro_rules! impl_pref {
($subpacket:ident, $rt:ty) => {
fn $subpacket(&self) -> Option<$rt>
{
if let Ok(u) = self.primary_userid() {
u.$subpacket()
} else if let Ok(sig) = self.direct_key_signature() {
sig.$subpacket()
} else {
None
}
}
}
}
impl<'a> seal::Sealed for ValidCert<'a> {}
impl<'a> Preferences<'a> for ValidCert<'a>
{
impl_pref!(preferred_symmetric_algorithms, &'a [SymmetricAlgorithm]);
impl_pref!(preferred_hash_algorithms, &'a [HashAlgorithm]);
impl_pref!(preferred_compression_algorithms, &'a [CompressionAlgorithm]);
impl_pref!(preferred_aead_algorithms, &'a [AEADAlgorithm]);
impl_pref!(key_server_preferences, KeyServerPreferences);
impl_pref!(preferred_key_server, &'a [u8]);
impl_pref!(features, Features);
}
#[cfg(test)]
mod test {
use crate::serialize::Serialize;
use crate::policy::StandardPolicy as P;
use crate::types::Curve;
use crate::packet::signature;
use crate::policy::HashAlgoSecurity;
use super::*;
use crate::{
KeyID,
types::KeyFlags,
};
fn parse_cert(data: &[u8], as_message: bool) -> Result<Cert> {
if as_message {
let pile = PacketPile::from_bytes(data).unwrap();
Cert::try_from(pile)
} else {
Cert::from_bytes(data)
}
}
#[test]
fn broken() {
use crate::types::Timestamp;
for i in 0..2 {
let cert = parse_cert(crate::tests::key("testy-broken-no-pk.pgp"),
i == 0);
assert_match!(Error::MalformedCert(_)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = parse_cert(crate::tests::key("testy-broken-no-uid.pgp"),
i == 0);
assert!(cert.is_ok());
let cert = parse_cert(crate::tests::key("testy-broken-no-sig-on-subkey.pgp"),
i == 0).unwrap();
assert_eq!(cert.primary.key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(cert.userids.len(), 1);
assert_eq!(cert.userids[0].userid().value(),
&b"Testy McTestface <testy@example.org>"[..]);
assert_eq!(cert.userids[0].self_signatures.len(), 1);
assert_eq!(cert.userids[0].self_signatures[0].digest_prefix(),
&[ 0xc6, 0x8f ]);
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.subkeys.len(), 1);
}
}
#[test]
fn basics() {
use crate::types::Timestamp;
for i in 0..2 {
let cert = parse_cert(crate::tests::key("testy.pgp"),
i == 0).unwrap();
assert_eq!(cert.primary.key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(format!("{:X}", cert.fingerprint()),
"3E8877C877274692975189F5D03F6F865226FE8B");
assert_eq!(cert.userids.len(), 1, "number of userids");
assert_eq!(cert.userids[0].userid().value(),
&b"Testy McTestface <testy@example.org>"[..]);
assert_eq!(cert.userids[0].self_signatures.len(), 1);
assert_eq!(cert.userids[0].self_signatures[0].digest_prefix(),
&[ 0xc6, 0x8f ]);
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.subkeys.len(), 1, "number of subkeys");
assert_eq!(cert.subkeys[0].key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(cert.subkeys[0].self_signatures[0].digest_prefix(),
&[ 0xb7, 0xb9 ]);
let cert = parse_cert(crate::tests::key("testy-no-subkey.pgp"),
i == 0).unwrap();
assert_eq!(cert.primary.key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(format!("{:X}", cert.fingerprint()),
"3E8877C877274692975189F5D03F6F865226FE8B");
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.userids.len(), 1, "number of userids");
assert_eq!(cert.userids[0].userid().value(),
&b"Testy McTestface <testy@example.org>"[..]);
assert_eq!(cert.userids[0].self_signatures.len(), 1);
assert_eq!(cert.userids[0].self_signatures[0].digest_prefix(),
&[ 0xc6, 0x8f ]);
assert_eq!(cert.subkeys.len(), 0, "number of subkeys");
let cert = parse_cert(crate::tests::key("testy.asc"), i == 0).unwrap();
assert_eq!(format!("{:X}", cert.fingerprint()),
"3E8877C877274692975189F5D03F6F865226FE8B");
}
}
#[test]
fn only_a_public_key() {
let cert = Cert::from_bytes(crate::tests::key("testy-only-a-pk.pgp")).unwrap();
assert_eq!(cert.userids.len(), 0);
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.subkeys.len(), 0);
}
#[test]
fn merge() {
use crate::tests::key;
let cert_base = Cert::from_bytes(key("bannon-base.gpg")).unwrap();
let merged = cert_base.clone().merge_public_and_secret(cert_base.clone()).unwrap();
assert_eq!(cert_base, merged);
let cert_add_uid_1
= Cert::from_bytes(key("bannon-add-uid-1-whitehouse.gov.gpg"))
.unwrap();
let cert_add_uid_2
= Cert::from_bytes(key("bannon-add-uid-2-fox.com.gpg"))
.unwrap();
let cert_add_uid_3
= Cert::from_bytes(key("bannon-add-uid-3-whitehouse.gov-dup.gpg"))
.unwrap();
let cert_all_uids
= Cert::from_bytes(key("bannon-all-uids.gpg"))
.unwrap();
assert_eq!(cert_all_uids.userids.len(), 3);
let merged = cert_base.clone().merge_public_and_secret(cert_add_uid_1.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_2.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_3.clone()).unwrap();
assert_eq!(cert_all_uids, merged);
let merged = cert_base.clone()
.merge_public_and_secret(cert_add_uid_3.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_2.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_1.clone()).unwrap();
assert_eq!(cert_all_uids, merged);
let cert_add_subkey_1
= Cert::from_bytes(key("bannon-add-subkey-1.gpg")).unwrap();
let cert_add_subkey_2
= Cert::from_bytes(key("bannon-add-subkey-2.gpg")).unwrap();
let cert_add_subkey_3
= Cert::from_bytes(key("bannon-add-subkey-3.gpg")).unwrap();
let cert_all_subkeys
= Cert::from_bytes(key("bannon-all-subkeys.gpg")).unwrap();
let merged = cert_base.clone().merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_2.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_3.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let merged = cert_base.clone().merge_public_and_secret(cert_add_subkey_3.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_2.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let merged = cert_base.clone()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_3.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_2.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_3.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_3.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_2.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let cert_all
= Cert::from_bytes(key("bannon-all-uids-subkeys.gpg"))
.unwrap();
let merged = cert_all_subkeys.clone()
.merge_public_and_secret(cert_all_uids.clone()).unwrap();
assert_eq!(cert_all, merged);
let merged = cert_all_uids.clone()
.merge_public_and_secret(cert_all_subkeys.clone()).unwrap();
assert_eq!(cert_all, merged);
let merged = cert_base.clone()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_2.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_1.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_3.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_3.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_2.clone()).unwrap()
.merge_public_and_secret(cert_add_subkey_1.clone()).unwrap()
.merge_public_and_secret(cert_add_uid_2.clone()).unwrap();
assert_eq!(cert_all, merged);
let cert_donald_signs_base
= Cert::from_bytes(key("bannon-the-donald-signs-base.gpg"))
.unwrap();
let cert_donald_signs_all
= Cert::from_bytes(key("bannon-the-donald-signs-all-uids.gpg"))
.unwrap();
let cert_ivanka_signs_base
= Cert::from_bytes(key("bannon-ivanka-signs-base.gpg"))
.unwrap();
let cert_ivanka_signs_all
= Cert::from_bytes(key("bannon-ivanka-signs-all-uids.gpg"))
.unwrap();
assert!(cert_donald_signs_base.userids.len() == 1);
assert!(cert_donald_signs_base.userids[0].self_signatures.len() == 1);
assert!(cert_base.userids[0].certifications.len() == 0);
assert!(cert_donald_signs_base.userids[0].certifications.len() == 1);
let merged = cert_donald_signs_base.clone()
.merge_public_and_secret(cert_ivanka_signs_base.clone()).unwrap();
assert!(merged.userids.len() == 1);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 2);
let merged = cert_donald_signs_base.clone()
.merge_public_and_secret(cert_donald_signs_all.clone()).unwrap();
assert!(merged.userids.len() == 3);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 2);
assert!(merged.userids[1].certifications.len() == 1);
assert!(merged.userids[2].certifications.len() == 1);
let merged = cert_donald_signs_base.clone()
.merge_public_and_secret(cert_donald_signs_all.clone()).unwrap()
.merge_public_and_secret(cert_ivanka_signs_base.clone()).unwrap()
.merge_public_and_secret(cert_ivanka_signs_all.clone()).unwrap();
assert!(merged.userids.len() == 3);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 4);
assert!(merged.userids[1].certifications.len() == 2);
assert!(merged.userids[2].certifications.len() == 2);
let merged = cert_donald_signs_base.clone()
.merge_public_and_secret(cert_ivanka_signs_base.clone()).unwrap()
.merge_public_and_secret(cert_donald_signs_all.clone()).unwrap()
.merge_public_and_secret(cert_donald_signs_all.clone()).unwrap()
.merge_public_and_secret(cert_ivanka_signs_all.clone()).unwrap()
.merge_public_and_secret(cert_ivanka_signs_base.clone()).unwrap()
.merge_public_and_secret(cert_donald_signs_all.clone()).unwrap()
.merge_public_and_secret(cert_donald_signs_all.clone()).unwrap()
.merge_public_and_secret(cert_ivanka_signs_all.clone()).unwrap();
assert!(merged.userids.len() == 3);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 4);
assert!(merged.userids[1].certifications.len() == 2);
assert!(merged.userids[2].certifications.len() == 2);
}
#[test]
fn out_of_order_self_sigs_test() {
let cert = Cert::from_bytes(crate::tests::key("neal-sigs-out-of-order.pgp"))
.unwrap();
let mut userids = cert.userids()
.map(|u| String::from_utf8_lossy(u.value()).into_owned())
.collect::<Vec<String>>();
userids.sort();
assert_eq!(userids,
&[ "Neal H. Walfield <neal@g10code.com>",
"Neal H. Walfield <neal@gnupg.org>",
"Neal H. Walfield <neal@pep-project.org>",
"Neal H. Walfield <neal@pep.foundation>",
"Neal H. Walfield <neal@sequoia-pgp.org>",
"Neal H. Walfield <neal@walfield.org>",
]);
let mut subkeys = cert.subkeys()
.map(|sk| Some(sk.key().keyid()))
.collect::<Vec<Option<KeyID>>>();
subkeys.sort();
assert_eq!(subkeys,
&[ "7223B56678E02528".parse().ok(),
"A3506AFB820ABD08".parse().ok(),
"C2B819056C652598".parse().ok(),
]);
let cert =
Cert::from_bytes(crate::tests::key("dkg-sigs-out-of-order.pgp")).unwrap();
let mut userids = cert.userids()
.map(|u| String::from_utf8_lossy(u.value()).into_owned())
.collect::<Vec<String>>();
userids.sort();
assert_eq!(userids,
&[ "Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net>",
"Daniel Kahn Gillmor <dkg@aclu.org>",
"Daniel Kahn Gillmor <dkg@astro.columbia.edu>",
"Daniel Kahn Gillmor <dkg@debian.org>",
"Daniel Kahn Gillmor <dkg@fifthhorseman.net>",
"Daniel Kahn Gillmor <dkg@openflows.com>",
]);
assert_eq!(cert.user_attributes.len(), 1);
let mut subkeys = cert.subkeys()
.map(|sk| Some(sk.key().keyid()))
.collect::<Vec<Option<KeyID>>>();
subkeys.sort();
assert_eq!(subkeys,
&[ "1075 8EBD BD7C FAB5".parse().ok(),
"1258 68EA 4BFA 08E4".parse().ok(),
"1498 ADC6 C192 3237".parse().ok(),
"24EC FF5A FF68 370A".parse().ok(),
"3714 7292 14D5 DA70".parse().ok(),
"3B7A A7F0 14E6 9B5A".parse().ok(),
"5B58 DCF9 C341 6611".parse().ok(),
"A524 01B1 1BFD FA5C".parse().ok(),
"A70A 96E1 439E A852".parse().ok(),
"C61B D3EC 2148 4CFF".parse().ok(),
"CAEF A883 2167 5333".parse().ok(),
"DC10 4C4E 0CA7 57FB".parse().ok(),
"E3A3 2229 449B 0350".parse().ok(),
]);
}
#[test]
fn v3_packets() {
let dkg = crate::tests::key("dkg.gpg");
let lutz = crate::tests::key("lutz.gpg");
let cert = Cert::from_bytes(lutz);
assert_match!(Error::MalformedCert(_)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = Cert::from_bytes(dkg);
assert!(cert.is_ok(), "dkg.gpg: {:?}", cert);
}
#[test]
fn keyring_with_v3_public_keys() {
let dkg = crate::tests::key("dkg.gpg");
let lutz = crate::tests::key("lutz.gpg");
let cert = Cert::from_bytes(dkg);
assert!(cert.is_ok(), "dkg.gpg: {:?}", cert);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, true ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false ]);
let mut combined = vec![];
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ false, true ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false, true ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&lutz[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false, false ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false, false, true ]);
}
#[test]
fn merge_with_incomplete_update() {
let p = &P::new();
let cert = Cert::from_bytes(crate::tests::key("about-to-expire.expired.pgp"))
.unwrap();
cert.primary_key().with_policy(p, None).unwrap().alive().unwrap_err();
let update =
Cert::from_bytes(crate::tests::key("about-to-expire.update-no-uid.pgp"))
.unwrap();
let cert = cert.merge_public_and_secret(update).unwrap();
cert.primary_key().with_policy(p, None).unwrap().alive().unwrap();
}
#[test]
fn packet_pile_roundtrip() {
let cert = Cert::from_bytes(crate::tests::key("already-revoked.pgp")).unwrap();
let cert2
= Cert::try_from(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
let cert = Cert::from_bytes(
crate::tests::key("already-revoked-direct-revocation.pgp")).unwrap();
let cert2
= Cert::try_from(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
let cert = Cert::from_bytes(
crate::tests::key("already-revoked-userid-revocation.pgp")).unwrap();
let cert2
= Cert::try_from(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
let cert = Cert::from_bytes(
crate::tests::key("already-revoked-subkey-revocation.pgp")).unwrap();
let cert2
= Cert::try_from(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
}
#[test]
fn insert_packets() {
use crate::armor;
use crate::packet::Tag;
let cert = Cert::from_bytes(crate::tests::key("already-revoked.pgp")).unwrap();
let rev = crate::tests::key("already-revoked.rev");
let rev = PacketPile::from_reader(armor::Reader::new(&rev[..], None))
.unwrap();
let rev : Vec<Packet> = rev.into_children().collect();
assert_eq!(rev.len(), 1);
assert_eq!(rev[0].tag(), Tag::Signature);
let packets_pre_merge = cert.clone().into_packets().count();
let cert = cert.insert_packets(rev).unwrap();
let packets_post_merge = cert.clone().into_packets().count();
assert_eq!(packets_post_merge, packets_pre_merge + 1);
}
#[test]
fn set_validity_period() {
let p = &P::new();
let (cert, _) = CertBuilder::general_purpose(None, Some("Test"))
.generate().unwrap();
assert_eq!(cert.clone().into_packet_pile().children().count(),
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 );
let cert = check_set_validity_period(p, cert);
assert_eq!(cert.clone().into_packet_pile().children().count(),
1 + 1 + 2 + 1 + 1 + 2 + 1 + 1 + 1 + 1 );
}
#[test]
fn set_validity_period_two_uids() -> Result<()> {
use quickcheck::{Arbitrary, StdThreadGen};
let mut gen = StdThreadGen::new(16);
let p = &P::new();
let userid1 = UserID::arbitrary(&mut gen);
let mut userid2 = UserID::arbitrary(&mut gen);
while userid1 == userid2 {
userid2 = UserID::arbitrary(&mut gen);
}
let (cert, _) = CertBuilder::general_purpose(
None, Some(userid1))
.add_userid(userid2)
.generate()?;
let primary_uid = cert.with_policy(p, None)?.primary_userid()?.userid().clone();
assert_eq!(cert.clone().into_packet_pile().children().count(),
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 );
let cert = check_set_validity_period(p, cert);
assert_eq!(cert.clone().into_packet_pile().children().count(),
1 + 1 + 2 + 1 + 1 + 2 + 1 + 1 + 2 + 1 + 1 + 1 + 1 );
assert_eq!(&primary_uid, cert.with_policy(p, None)?.primary_userid()?.userid());
Ok(())
}
#[test]
fn set_validity_period_uidless() {
use crate::types::Duration;
let p = &P::new();
let (cert, _) = CertBuilder::new()
.set_validity_period(None) .set_validity_period(Some(Duration::weeks(52).unwrap().try_into().unwrap()))
.generate().unwrap();
assert_eq!(cert.clone().into_packet_pile().children().count(),
1 + 1 );
let cert = check_set_validity_period(p, cert);
assert_eq!(cert.clone().into_packet_pile().children().count(),
1 + 1 + 2 );
}
fn check_set_validity_period(policy: &dyn Policy, cert: Cert) -> Cert {
let now = cert.primary_key().creation_time();
let a_sec = time::Duration::new(1, 0);
let expiry_orig = cert.primary_key().with_policy(policy, now).unwrap()
.key_validity_period()
.expect("Keys expire by default.");
let mut keypair = cert.primary_key().key().clone().parts_into_secret()
.unwrap().into_keypair().unwrap();
let as_of1 = now + time::Duration::new(10, 0);
let cert = cert.set_validity_period_as_of(
policy, &mut keypair, None, as_of1).unwrap();
{
assert_eq!(cert.primary_key().with_policy(policy, now).unwrap()
.key_validity_period(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1 - a_sec).unwrap()
.key_validity_period(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1).unwrap()
.key_validity_period(),
None);
}
let expiry_new = expiry_orig - time::Duration::new(60 * 60, 0);
assert!(expiry_new > time::Duration::new(0, 0));
let as_of2 = as_of1 + time::Duration::new(10, 0);
let cert = cert.set_validity_period_as_of(
policy, &mut keypair, Some(expiry_new), as_of2).unwrap();
{
assert_eq!(cert.primary_key().with_policy(policy, now).unwrap()
.key_validity_period(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1 - a_sec).unwrap()
.key_validity_period(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1).unwrap()
.key_validity_period(),
None);
assert_eq!(cert.primary_key().with_policy(policy, as_of2 - a_sec).unwrap()
.key_validity_period(),
None);
assert_eq!(cert.primary_key().with_policy(policy, as_of2).unwrap()
.key_validity_period(),
Some(expiry_new));
}
cert
}
#[test]
fn direct_key_sig() {
use crate::types::SignatureType;
let p = &P::new();
let (cert1, _) = CertBuilder::new().generate().unwrap();
let mut buf = Vec::default();
cert1.serialize(&mut buf).unwrap();
let cert2 = Cert::from_bytes(&buf).unwrap();
assert_eq!(
cert2.primary_key().with_policy(p, None).unwrap()
.direct_key_signature().unwrap().typ(),
SignatureType::DirectKey);
assert_eq!(cert2.userids().count(), 0);
}
#[test]
fn revoked() {
fn check(cert: &Cert, direct_revoked: bool,
userid_revoked: bool, subkey_revoked: bool) {
let p = &P::new();
let typ = cert.primary_key().with_policy(p, None).unwrap()
.binding_signature().typ();
assert_eq!(typ, SignatureType::PositiveCertification,
"{:#?}", cert);
let revoked = cert.revocation_status(p, None);
if direct_revoked {
assert_match!(RevocationStatus::Revoked(_) = revoked,
"{:#?}", cert);
} else {
assert_eq!(revoked, RevocationStatus::NotAsFarAsWeKnow,
"{:#?}", cert);
}
for userid in cert.userids().with_policy(p, None) {
let typ = userid.binding_signature().typ();
assert_eq!(typ, SignatureType::PositiveCertification,
"{:#?}", cert);
let revoked = userid.revocation_status();
if userid_revoked {
assert_match!(RevocationStatus::Revoked(_) = revoked);
} else {
assert_eq!(RevocationStatus::NotAsFarAsWeKnow, revoked,
"{:#?}", cert);
}
}
for subkey in cert.subkeys() {
let typ = subkey.binding_signature(p, None).unwrap().typ();
assert_eq!(typ, SignatureType::SubkeyBinding,
"{:#?}", cert);
let revoked = subkey.revocation_status(p, None);
if subkey_revoked {
assert_match!(RevocationStatus::Revoked(_) = revoked);
} else {
assert_eq!(RevocationStatus::NotAsFarAsWeKnow, revoked,
"{:#?}", cert);
}
}
}
let cert = Cert::from_bytes(crate::tests::key("already-revoked.pgp")).unwrap();
check(&cert, false, false, false);
let d = Cert::from_bytes(
crate::tests::key("already-revoked-direct-revocation.pgp")).unwrap();
check(&d, true, false, false);
check(&cert.clone().merge_public_and_secret(d.clone()).unwrap(), true, false, false);
check(&d.clone().merge_public_and_secret(cert.clone()).unwrap(), true, false, false);
let u = Cert::from_bytes(
crate::tests::key("already-revoked-userid-revocation.pgp")).unwrap();
check(&u, false, true, false);
check(&cert.clone().merge_public_and_secret(u.clone()).unwrap(), false, true, false);
check(&u.clone().merge_public_and_secret(cert.clone()).unwrap(), false, true, false);
let k = Cert::from_bytes(
crate::tests::key("already-revoked-subkey-revocation.pgp")).unwrap();
check(&k, false, false, true);
check(&cert.clone().merge_public_and_secret(k.clone()).unwrap(), false, false, true);
check(&k.clone().merge_public_and_secret(cert.clone()).unwrap(), false, false, true);
check(&d.clone().merge_public_and_secret(u.clone()).unwrap(), true, true, false);
check(&u.clone().merge_public_and_secret(d.clone()).unwrap(), true, true, false);
check(&d.clone().merge_public_and_secret(k.clone()).unwrap(), true, false, true);
check(&k.clone().merge_public_and_secret(d.clone()).unwrap(), true, false, true);
check(&u.clone().merge_public_and_secret(k.clone()).unwrap(), false, true, true);
check(&k.clone().merge_public_and_secret(u.clone()).unwrap(), false, true, true);
check(&d.clone().merge_public_and_secret(u.clone().merge_public_and_secret(k.clone()).unwrap()).unwrap(),
true, true, true);
check(&d.clone().merge_public_and_secret(k.clone().merge_public_and_secret(u.clone()).unwrap()).unwrap(),
true, true, true);
}
#[test]
fn revoke() {
let p = &P::new();
let (cert, _) = CertBuilder::general_purpose(None, Some("Test"))
.generate().unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revocation_status(p, None));
let mut keypair = cert.primary_key().key().clone().parts_into_secret()
.unwrap().into_keypair().unwrap();
let sig = CertRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::KeyCompromised,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, None)
.unwrap();
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
assert_eq!(sig.issuers().collect::<Vec<_>>(),
vec![ &cert.keyid() ]);
assert_eq!(sig.issuer_fingerprints().collect::<Vec<_>>(),
vec![ &cert.fingerprint() ]);
let cert = cert.insert_packets(sig).unwrap();
assert_match!(RevocationStatus::Revoked(_) = cert.revocation_status(p, None));
let (other, _) = CertBuilder::general_purpose(None, Some("Test 2"))
.generate().unwrap();
let mut keypair = other.primary_key().key().clone().parts_into_secret()
.unwrap().into_keypair().unwrap();
let sig = CertRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::KeyCompromised,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, None)
.unwrap();
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
assert_eq!(sig.issuers().collect::<Vec<_>>(),
vec![ &other.keyid() ]);
assert_eq!(sig.issuer_fingerprints().collect::<Vec<_>>(),
vec![ &other.fingerprint() ]);
}
#[test]
fn revoke_subkey() {
let p = &P::new();
let (cert, _) = CertBuilder::new()
.add_transport_encryption_subkey()
.generate().unwrap();
let sig = {
let subkey = cert.subkeys().nth(0).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
subkey.revocation_status(p, None));
let mut keypair = cert.primary_key().key().clone().parts_into_secret()
.unwrap().into_keypair().unwrap();
SubkeyRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::UIDRetired,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, subkey.key(), None)
.unwrap()
};
assert_eq!(sig.typ(), SignatureType::SubkeyRevocation);
let cert = cert.insert_packets(sig).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revocation_status(p, None));
let subkey = cert.subkeys().nth(0).unwrap();
assert_match!(RevocationStatus::Revoked(_)
= subkey.revocation_status(p, None));
}
#[test]
fn revoke_uid() {
let p = &P::new();
let (cert, _) = CertBuilder::new()
.add_userid("Test1")
.add_userid("Test2")
.generate().unwrap();
let sig = {
let uid = cert.userids().with_policy(p, None).nth(1).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow, uid.revocation_status());
let mut keypair = cert.primary_key().key().clone().parts_into_secret()
.unwrap().into_keypair().unwrap();
UserIDRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::UIDRetired,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, uid.userid(), None)
.unwrap()
};
assert_eq!(sig.typ(), SignatureType::CertificationRevocation);
let cert = cert.insert_packets(sig).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revocation_status(p, None));
let uid = cert.userids().with_policy(p, None).nth(1).unwrap();
assert_match!(RevocationStatus::Revoked(_) = uid.revocation_status());
}
#[test]
fn key_revoked() {
use crate::types::Features;
use crate::packet::key::Key4;
use rand::{thread_rng, Rng, distributions::Open01};
let p = &P::new();
let t1 = time::UNIX_EPOCH + time::Duration::new(946681200, 0); let t2 = time::UNIX_EPOCH + time::Duration::new(978303600, 0); let t3 = time::UNIX_EPOCH + time::Duration::new(1009839600, 0); let t4 = time::UNIX_EPOCH + time::Duration::new(1041375600, 0);
let mut key: key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
key.set_creation_time(t1).unwrap();
let mut pair = key.clone().into_keypair().unwrap();
let (bind1, rev1, bind2, rev2) = {
let bind1 = signature::SignatureBuilder::new(SignatureType::DirectKey)
.set_features(Features::sequoia()).unwrap()
.set_key_flags(KeyFlags::empty()).unwrap()
.set_signature_creation_time(t1).unwrap()
.set_key_validity_period(Some(time::Duration::new(10 * 52 * 7 * 24 * 60 * 60, 0))).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.sign_direct_key(&mut pair, key.parts_as_public()).unwrap();
let rev1 = signature::SignatureBuilder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t2).unwrap()
.set_reason_for_revocation(ReasonForRevocation::KeySuperseded,
&b""[..]).unwrap()
.sign_direct_key(&mut pair, key.parts_as_public()).unwrap();
let bind2 = signature::SignatureBuilder::new(SignatureType::DirectKey)
.set_features(Features::sequoia()).unwrap()
.set_key_flags(KeyFlags::empty()).unwrap()
.set_signature_creation_time(t3).unwrap()
.set_key_validity_period(Some(time::Duration::new(10 * 52 * 7 * 24 * 60 * 60, 0))).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.sign_direct_key(&mut pair, key.parts_as_public()).unwrap();
let rev2 = signature::SignatureBuilder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t4).unwrap()
.set_reason_for_revocation(ReasonForRevocation::KeyCompromised,
&b""[..]).unwrap()
.sign_direct_key(&mut pair, key.parts_as_public()).unwrap();
(bind1, rev1, bind2, rev2)
};
let pk : key::PublicKey = key.into();
let cert = Cert::try_from(vec![
pk.into(),
bind1.into(),
bind2.into(),
rev1.into()
]).unwrap();
let f1: f32 = thread_rng().sample(Open01);
let f2: f32 = thread_rng().sample(Open01);
let f3: f32 = thread_rng().sample(Open01);
let f4: f32 = thread_rng().sample(Open01);
let te1 = t1 - time::Duration::new((60. * 60. * 24. * 300.0 * f1) as u64, 0);
let t12 = t1 + time::Duration::new((60. * 60. * 24. * 300.0 * f2) as u64, 0);
let t23 = t2 + time::Duration::new((60. * 60. * 24. * 300.0 * f3) as u64, 0);
let t34 = t3 + time::Duration::new((60. * 60. * 24. * 300.0 * f4) as u64, 0);
assert_eq!(cert.revocation_status(p, te1), RevocationStatus::NotAsFarAsWeKnow);
assert_eq!(cert.revocation_status(p, t12), RevocationStatus::NotAsFarAsWeKnow);
assert_match!(RevocationStatus::Revoked(_) = cert.revocation_status(p, t23));
assert_eq!(cert.revocation_status(p, t34), RevocationStatus::NotAsFarAsWeKnow);
let cert = cert.insert_packets(rev2).unwrap();
assert_match!(RevocationStatus::Revoked(_) = cert.revocation_status(p, te1));
assert_match!(RevocationStatus::Revoked(_) = cert.revocation_status(p, t12));
assert_match!(RevocationStatus::Revoked(_) = cert.revocation_status(p, t23));
assert_match!(RevocationStatus::Revoked(_) = cert.revocation_status(p, t34));
assert_match!(RevocationStatus::Revoked(_) = cert.revocation_status(p, t4));
assert_match!(RevocationStatus::Revoked(_)
= cert.revocation_status(p, time::SystemTime::now()));
}
#[test]
fn key_revoked2() {
tracer!(true, "cert_revoked2", 0);
let p = &P::new();
fn cert_revoked<T>(p: &dyn Policy, cert: &Cert, t: T) -> bool
where T: Into<Option<time::SystemTime>>
{
!matches!(
cert.revocation_status(p, t),
RevocationStatus::NotAsFarAsWeKnow
)
}
fn subkey_revoked<T>(p: &dyn Policy, cert: &Cert, t: T) -> bool
where T: Into<Option<time::SystemTime>>
{
!matches!(
cert.subkeys().nth(0).unwrap().bundle().revocation_status(p, t),
RevocationStatus::NotAsFarAsWeKnow
)
}
let tests : [(&str, Box<dyn Fn(&dyn Policy, &Cert, _) -> bool>); 2] = [
("cert", Box::new(cert_revoked)),
("subkey", Box::new(subkey_revoked)),
];
for (f, revoked) in tests.iter()
{
t!("Checking {} revocation", f);
t!("Normal key");
let cert = Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-0-public.pgp", f))).unwrap();
let selfsig0 = cert.primary_key().with_policy(p, None).unwrap()
.binding_signature().signature_creation_time().unwrap();
assert!(!revoked(p, &cert, Some(selfsig0)));
assert!(!revoked(p, &cert, None));
t!("Soft revocation");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-1-soft-revocation.pgp", f))
).unwrap()).unwrap();
assert!(!revoked(p, &cert, Some(selfsig0)));
assert!(revoked(p, &cert, None));
t!("New self signature");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-2-new-self-sig.pgp", f))
).unwrap()).unwrap();
assert!(!revoked(p, &cert, Some(selfsig0)));
assert!(!revoked(p, &cert, None));
t!("Hard revocation");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-3-hard-revocation.pgp", f))
).unwrap()).unwrap();
assert!(revoked(p, &cert, Some(selfsig0)));
assert!(revoked(p, &cert, None));
t!("New self signature");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-4-new-self-sig.pgp", f))
).unwrap()).unwrap();
assert!(revoked(p, &cert, Some(selfsig0)));
assert!(revoked(p, &cert, None));
}
}
#[test]
fn userid_revoked2() {
fn check_userids<T>(p: &dyn Policy, cert: &Cert, revoked: bool, t: T)
where T: Into<Option<time::SystemTime>>, T: Copy
{
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= cert.revocation_status(p, None));
let mut slim_shady = false;
let mut eminem = false;
for b in cert.userids().with_policy(p, t) {
if b.userid().value() == b"Slim Shady" {
assert!(!slim_shady);
slim_shady = true;
if revoked {
assert_match!(RevocationStatus::Revoked(_)
= b.revocation_status());
} else {
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= b.revocation_status());
}
} else {
assert!(!eminem);
eminem = true;
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= b.revocation_status());
}
}
assert!(slim_shady);
assert!(eminem);
}
fn check_uas<T>(p: &dyn Policy, cert: &Cert, revoked: bool, t: T)
where T: Into<Option<time::SystemTime>>, T: Copy
{
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= cert.revocation_status(p, None));
assert_eq!(cert.user_attributes().count(), 1);
let ua = cert.user_attributes().nth(0).unwrap();
if revoked {
assert_match!(RevocationStatus::Revoked(_)
= ua.revocation_status(p, t));
} else {
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= ua.revocation_status(p, t));
}
}
tracer!(true, "userid_revoked2", 0);
let p = &P::new();
let tests : [(&str, Box<dyn Fn(&dyn Policy, &Cert, bool, _)>); 2] = [
("userid", Box::new(check_userids)),
("user-attribute", Box::new(check_uas)),
];
for (f, check) in tests.iter()
{
t!("Checking {} revocation", f);
t!("Normal key");
let cert = Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-0-public.pgp", f))).unwrap();
let now = time::SystemTime::now();
let selfsig0
= cert.userids().with_policy(p, now).map(|b| {
b.binding_signature().signature_creation_time().unwrap()
})
.max().unwrap();
check(p, &cert, false, selfsig0);
check(p, &cert, false, now);
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-1-soft-revocation.pgp", f))
).unwrap()).unwrap();
check(p, &cert, false, selfsig0);
check(p, &cert, true, now);
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-2-new-self-sig.pgp", f))
).unwrap()).unwrap();
check(p, &cert, false, selfsig0);
check(p, &cert, false, now);
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-3-hard-revocation.pgp", f))
).unwrap()).unwrap();
check(p, &cert, false, selfsig0);
check(p, &cert, true, now);
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-4-new-self-sig.pgp", f))
).unwrap()).unwrap();
check(p, &cert, false, selfsig0);
check(p, &cert, false, now);
}
}
#[test]
fn unrevoked() {
let p = &P::new();
let cert =
Cert::from_bytes(crate::tests::key("un-revoked-userid.pgp")).unwrap();
for uid in cert.userids().with_policy(p, None) {
assert_eq!(uid.revocation_status(), RevocationStatus::NotAsFarAsWeKnow);
}
}
#[test]
fn is_tsk() {
let cert = Cert::from_bytes(
crate::tests::key("already-revoked.pgp")).unwrap();
assert!(! cert.is_tsk());
let cert = Cert::from_bytes(
crate::tests::key("already-revoked-private.pgp")).unwrap();
assert!(cert.is_tsk());
}
#[test]
fn export_only_exports_public_key() {
let cert = Cert::from_bytes(
crate::tests::key("testy-new-private.pgp")).unwrap();
assert!(cert.is_tsk());
let mut v = Vec::new();
cert.serialize(&mut v).unwrap();
let cert = Cert::from_bytes(&v).unwrap();
assert!(! cert.is_tsk());
}
#[test]
fn public_private_merge() {
let (tsk, _) = CertBuilder::general_purpose(None, Some("foo@example.com"))
.generate().unwrap();
assert!(tsk.primary.key().has_secret());
assert!(tsk.is_tsk());
let subkey_count = tsk.subkeys().len();
assert!(subkey_count > 0);
assert!(tsk.subkeys().all(|k| k.key().has_secret()));
let mut cert_bytes = Vec::new();
tsk.serialize(&mut cert_bytes).unwrap();
let cert = Cert::from_bytes(&cert_bytes[..]).unwrap();
assert!(! cert.primary.key().has_secret());
assert!(!cert.is_tsk());
assert!(cert.subkeys().all(|k| ! k.key().has_secret()));
let merge1 = cert.clone().merge_public_and_secret(tsk.clone()).unwrap();
assert!(merge1.is_tsk());
assert!(merge1.primary.key().has_secret());
assert_eq!(merge1.subkeys().len(), subkey_count);
assert!(merge1.subkeys().all(|k| k.key().has_secret()));
let merge2 = tsk.clone().merge_public_and_secret(cert.clone()).unwrap();
assert!(merge2.is_tsk());
assert!(merge2.primary.key().has_secret());
assert_eq!(merge2.subkeys().len(), subkey_count);
assert!(merge2.subkeys().all(|k| k.key().has_secret()));
}
#[test]
fn issue_120() {
let cert = "
-----BEGIN PGP ARMORED FILE-----
xcBNBFoVcvoBCACykTKOJddF8SSUAfCDHk86cNTaYnjCoy72rMgWJsrMLnz/V16B
J9M7l6nrQ0JMnH2Du02A3w+kNb5q97IZ/M6NkqOOl7uqjyRGPV+XKwt0G5mN/ovg
8630BZAYS3QzavYf3tni9aikiGH+zTFX5pynTNfYRXNBof3Xfzl92yad2bIt4ITD
NfKPvHRko/tqWbclzzEn72gGVggt1/k/0dKhfsGzNogHxg4GIQ/jR/XcqbDFR3RC
/JJjnTOUPGsC1y82Xlu8udWBVn5mlDyxkad5laUpWWg17anvczEAyx4TTOVItLSu
43iPdKHSs9vMXWYID0bg913VusZ2Ofv690nDABEBAAHNJFRlc3R5IE1jVGVzdGZh
Y2UgPHRlc3R5QGV4YW1wbGUub3JnPsLAlAQTAQgAPhYhBD6Id8h3J0aSl1GJ9dA/
b4ZSJv6LBQJaFXL6AhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJ
ENA/b4ZSJv6Lxo8H/1XMt+Nqa6e0SG/up3ypKe5nplA0p/9j/s2EIsP8S8uPUd+c
WS17XOmPwkNDmHeL3J6hzwL74NlYSLEtyf7WoOV74xAKQA9WkqaKPHCtpll8aFWA
ktQDLWTPeKuUuSlobAoRtO17ZmheSQzmm7JYt4Ahkxt3agqGT05OsaAey6nIKqpq
ArokvdHTZ7AFZeSJIWmuCoT9M1lo3LAtLnRGOhBMJ5dDIeOwflJwNBXlJVi4mDPK
+fumV0MbSPvZd1/ivFjSpQyudWWtv1R1nAK7+a4CPTGxPvAQkLtRsL/V+Q7F3BJG
jAn4QVx8p4t3NOPuNgcoZpLBE3sc4Nfs5/CphMLHwE0EWhVy+gEIALSpjYD+tuWC
rj6FGP6crQjQzVlH+7axoM1ooTwiPs4fzzt2iLw3CJyDUviM5F9ZBQTei635RsAR
a/CJTSQYAEU5yXXxhoe0OtwnuvsBSvVT7Fox3pkfNTQmwMvkEbodhfKpqBbDKCL8
f5A8Bb7aISsLf0XRHWDkHVqlz8LnOR3f44wEWiTeIxLc8S1QtwX/ExyW47oPsjs9
ShCmwfSpcngH/vGBRTO7WeI54xcAtKSm/20B/MgrUl5qFo17kUWot2C6KjuZKkHk
3WZmJwQz+6rTB11w4AXt8vKkptYQCkfat2FydGpgRO5dVg6aWNJefOJNkC7MmlzC
ZrrAK8FJ6jcAEQEAAcLAdgQYAQgAIBYhBD6Id8h3J0aSl1GJ9dA/b4ZSJv6LBQJa
FXL6AhsMAAoJENA/b4ZSJv6Lt7kH/jPr5wg8lcamuLj4lydYiLttvvTtDTlD1TL+
IfwVARB/ruoerlEDr0zX1t3DCEcvJDiZfOqJbXtHt70+7NzFXrYxfaNFmikMgSQT
XqHrMQho4qpseVOeJPWGzGOcrxCdw/ZgrWbkDlAU5KaIvk+M4wFPivjbtW2Ro2/F
J4I/ZHhJlIPmM+hUErHC103b08pBENXDQlXDma7LijH5kWhyfF2Ji7Ft0EjghBaW
AeGalQHjc5kAZu5R76Mwt06MEQ/HL1pIvufTFxkr/SzIv8Ih7Kexb0IrybmfD351
Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
=1Vzu
-----END PGP ARMORED FILE-----
";
assert!(Cert::from_bytes(cert).is_err());
}
#[test]
fn missing_uids() {
let (cert, _) = CertBuilder::new()
.add_userid("test1@example.com")
.add_userid("test2@example.com")
.add_transport_encryption_subkey()
.add_certification_subkey()
.generate().unwrap();
assert_eq!(cert.subkeys().len(), 2);
let pile = cert
.into_packet_pile()
.into_children()
.filter(|pkt| {
match pkt {
&Packet::PublicKey(_) | &Packet::PublicSubkey(_)
| &Packet::SecretKey(_) | &Packet::SecretSubkey(_) => true,
&Packet::Signature(ref sig) => {
sig.typ() == SignatureType::DirectKey
|| sig.typ() == SignatureType::SubkeyBinding
}
e => {
eprintln!("{:?}", e);
false
}
}
})
.collect::<Vec<_>>();
eprintln!("parse back");
let cert = Cert::try_from(pile).unwrap();
assert_eq!(cert.subkeys().len(), 2);
}
#[test]
fn signature_order() {
let p = &P::new();
let neal = Cert::from_bytes(crate::tests::key("neal.pgp")).unwrap();
let mut cmps = 0;
for uid in neal.userids() {
for sigs in [
uid.self_signatures().collect::<Vec<_>>(),
uid.certifications().collect::<Vec<_>>(),
uid.self_revocations().collect::<Vec<_>>(),
uid.other_revocations().collect::<Vec<_>>()
].iter() {
for sigs in sigs.windows(2) {
cmps += 1;
assert!(sigs[0].signature_creation_time()
>= sigs[1].signature_creation_time());
}
}
assert_eq!(uid.self_signatures().nth(0).unwrap(),
uid.binding_signature(p, None).unwrap());
}
assert!(cmps > 0);
}
#[test]
fn cert_reject_keyrings() {
let mut keyring = Vec::new();
keyring.extend_from_slice(crate::tests::key("neal.pgp"));
keyring.extend_from_slice(crate::tests::key("neal.pgp"));
assert!(Cert::from_bytes(&keyring).is_err());
}
#[test]
fn primary_userid() {
let p = &P::new();
let cert = Cert::from_bytes(
crate::tests::key("really-revoked-userid-0-public.pgp")).unwrap();
let now = time::SystemTime::now();
let selfsig0
= cert.userids().with_policy(p, now).map(|b| {
b.binding_signature().signature_creation_time().unwrap()
})
.max().unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-1-soft-revocation.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-2-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"Slim Shady");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-3-hard-revocation.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-4-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"Slim Shady");
let cert = Cert::from_bytes(
crate::tests::key("primary-key-0-public.pgp")).unwrap();
let selfsig0
= cert.userids().with_policy(p, now).map(|b| {
b.binding_signature().signature_creation_time().unwrap()
})
.max().unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("primary-key-1-add-userid-bbbbb.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"bbbbb");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("primary-key-2-make-aaaaa-primary.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("primary-key-3-make-bbbbb-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("primary-key-4-make-bbbbb-primary.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"bbbbb");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("primary-key-5-make-aaaaa-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"bbbbb");
let cert = cert.merge_public_and_secret(
Cert::from_bytes(
crate::tests::key("primary-key-6-revoked-aaaaa.pgp")
).unwrap()).unwrap();
assert_eq!(cert.with_policy(p, selfsig0).unwrap()
.primary_userid().unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.with_policy(p, now).unwrap()
.primary_userid().unwrap().userid().value(),
b"bbbbb");
}
#[test]
fn binding_signature_lookup() {
use crate::types::Features;
use crate::packet::key::Key4;
let p = &P::new();
let a_sec = time::Duration::new(1, 0);
let time_zero = time::UNIX_EPOCH;
let t1 = time::UNIX_EPOCH + time::Duration::new(946681200, 0); let t2 = time::UNIX_EPOCH + time::Duration::new(978303600, 0); let t3 = time::UNIX_EPOCH + time::Duration::new(1009839600, 0); let t4 = time::UNIX_EPOCH + time::Duration::new(1041375600, 0);
let mut key: key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
key.set_creation_time(t1).unwrap();
let mut pair = key.clone().into_keypair().unwrap();
let pk : key::PublicKey = key.clone().into();
let mut cert = Cert::try_from(vec![
pk.into(),
]).unwrap();
let uid: UserID = "foo@example.org".into();
let sig = uid.certify(&mut pair, &cert,
SignatureType::PositiveCertification,
None,
t1).unwrap();
cert = cert.insert_packets(
vec![Packet::from(uid), sig.into()]).unwrap();
const N: usize = 5;
for (t, offset) in &[ (t2, 0), (t4, 0), (t3, 1 * N), (t1, 3 * N) ] {
for i in 0..N {
let binding = signature::SignatureBuilder::new(SignatureType::DirectKey)
.set_features(Features::sequoia()).unwrap()
.set_key_flags(KeyFlags::empty()).unwrap()
.set_signature_creation_time(t1).unwrap()
.set_key_validity_period(Some(
time::Duration::new((1 + i as u64) * 24 * 60 * 60, 0)))
.unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.set_signature_creation_time(*t).unwrap()
.sign_direct_key(&mut pair, key.parts_as_public()).unwrap();
let binding : Packet = binding.into();
cert = cert.insert_packets(binding).unwrap();
let direct_signatures =
cert.primary_key().bundle().self_signatures();
assert_eq!(cert.primary_key().with_policy(p, *t).unwrap()
.direct_key_signature().ok(),
direct_signatures.get(*offset));
assert_eq!(cert.primary_key().with_policy(p, *t + a_sec).unwrap()
.direct_key_signature().ok(),
direct_signatures.get(*offset));
assert_eq!(cert.primary_key().with_policy(p, None).unwrap()
.direct_key_signature().ok(),
direct_signatures.get(0));
assert!(cert.primary_key().with_policy(p, time_zero).is_err());
}
}
}
#[test]
fn keysigning_party() {
use crate::packet::signature;
for cs in &[ CipherSuite::Cv25519,
CipherSuite::RSA3k,
CipherSuite::P256,
CipherSuite::P384,
CipherSuite::P521,
CipherSuite::RSA2k,
CipherSuite::RSA4k ]
{
let (alice, _) = CertBuilder::new()
.set_cipher_suite(*cs)
.add_userid("alice@foo.com")
.generate().unwrap();
let (bob, _) = CertBuilder::new()
.set_cipher_suite(*cs)
.add_userid("bob@bar.com")
.add_signing_subkey()
.generate().unwrap();
assert_eq!(bob.userids().len(), 1);
let bob_userid_binding = bob.userids().nth(0).unwrap();
assert_eq!(bob_userid_binding.userid().value(), b"bob@bar.com");
let sig_template
= signature::SignatureBuilder::new(SignatureType::GenericCertification)
.set_trust_signature(255, 120)
.unwrap();
let mut alice_certifies_bob
= bob_userid_binding.userid().bind(
&mut alice.primary_key().key().clone().parts_into_secret()
.unwrap().into_keypair().unwrap(),
&bob,
sig_template).unwrap();
let bob = bob.insert_packets(alice_certifies_bob.clone()).unwrap();
assert_eq!(bob.userids().len(), 1);
let bob_userid_binding = bob.userids().nth(0).unwrap();
assert_eq!(bob_userid_binding.userid().value(), b"bob@bar.com");
assert_eq!(bob_userid_binding.certifications().collect::<Vec<_>>(),
vec![&alice_certifies_bob]);
alice_certifies_bob
.verify_userid_binding(alice.primary_key().key(),
bob.primary_key().key(),
bob_userid_binding.userid()).unwrap();
}
}
#[test]
fn decrypt_secrets() {
let (cert, _) = CertBuilder::new()
.add_transport_encryption_subkey()
.set_password(Some(String::from("streng geheim").into()))
.generate().unwrap();
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 0);
let mut primary = cert.primary_key().key().clone()
.parts_into_secret().unwrap();
let algo = primary.pk_algo();
primary.secret_mut()
.decrypt_in_place(algo, &"streng geheim".into()).unwrap();
let cert = cert.insert_packets(
primary.parts_into_secret().unwrap().role_into_primary()).unwrap();
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 1);
}
#[test]
fn test_into_packets() -> Result<()> {
use crate::serialize::SerializeInto;
let dkg = Cert::from_bytes(crate::tests::key("dkg.gpg"))?;
let mut buf = Vec::new();
for p in dkg.clone().into_packets() {
p.serialize(&mut buf)?;
}
let dkg = dkg.to_vec()?;
if false && buf != dkg {
std::fs::write("/tmp/buf", &buf)?;
std::fs::write("/tmp/dkg", &dkg)?;
}
assert_eq!(buf, dkg);
Ok(())
}
#[test]
fn test_canonicalization() -> Result<()> {
let p = crate::policy::StandardPolicy::new();
let primary: Key<_, key::PrimaryRole> =
key::Key4::generate_ecc(true, Curve::Ed25519)?.into();
let mut primary_pair = primary.clone().into_keypair()?;
let cert = Cert::try_from(vec![primary.into()])?;
let uid = UserID::from("foo@example.org");
let cert = cert.insert_packets(uid)?;
assert_eq!(cert.userids().count(), 1);
assert_eq!(cert.userids().with_policy(&p, None).count(), 0);
use packet::user_attribute::{Subpacket, Image};
let ua = UserAttribute::new(&[
Subpacket::Image(
Image::Private(100, vec![0, 1, 2].into_boxed_slice())),
])?;
let cert = cert.insert_packets(ua)?;
assert_eq!(cert.user_attributes().count(), 1);
assert_eq!(cert.user_attributes().with_policy(&p, None).count(), 0);
let signing_subkey: Key<_, key::SubordinateRole> =
key::Key4::generate_ecc(true, Curve::Ed25519)?.into();
let _signing_subkey_pair = signing_subkey.clone().into_keypair()?;
let cert = cert.insert_packets(signing_subkey)?;
assert_eq!(cert.keys().subkeys().count(), 1);
assert_eq!(cert.keys().subkeys().with_policy(&p, None).count(), 0);
let mut fake_key = packet::Unknown::new(
packet::Tag::PublicSubkey, anyhow::anyhow!("fake key"));
fake_key.set_body("fake key".into());
let fake_binding = signature::SignatureBuilder::new(
SignatureType::Unknown(SignatureType::SubkeyBinding.into()))
.sign_standalone(&mut primary_pair)?;
let cert = cert.insert_packets(vec![Packet::from(fake_key),
fake_binding.clone().into()])?;
assert_eq!(cert.unknowns().count(), 1);
assert_eq!(cert.unknowns().nth(0).unwrap().unknown().tag(),
packet::Tag::PublicSubkey);
assert_eq!(cert.unknowns().nth(0).unwrap().self_signatures().collect::<Vec<_>>(),
vec![&fake_binding]);
Ok(())
}
#[test]
fn canonicalize_with_v3_sig() -> Result<()> {
let p = &P::new();
let sha1 =
p.hash_cutoff(
HashAlgorithm::SHA1, HashAlgoSecurity::CollisionResistance)
.unwrap();
let p = &P::at(sha1 - std::time::Duration::from_secs(1));
let cert = Cert::from_bytes(
crate::tests::key("eike-v3-v4.pgp"))?;
dbg!(&cert);
assert_eq!(cert.userids()
.with_policy(p, None)
.count(), 1);
Ok(())
}
#[test]
fn issue_215() {
let p = &P::new();
let cert = Cert::from_bytes(crate::tests::key(
"issue-215-expiration-on-direct-key-sig.pgp")).unwrap();
assert_match!(
Error::Expired(_)
= cert.with_policy(p, None).unwrap().alive()
.unwrap_err().downcast().unwrap());
assert_match!(
Error::Expired(_)
= cert.primary_key().with_policy(p, None).unwrap()
.alive().unwrap_err().downcast().unwrap());
}
#[test]
fn merge_keeps_secrets() -> Result<()> {
let primary_sec: Key<_, key::PrimaryRole> =
key::Key4::generate_ecc(true, Curve::Ed25519)?.into();
let primary_pub = primary_sec.clone().take_secret().0;
let cert_p =
Cert::try_from(vec![primary_pub.clone().into()])?;
let cert_s =
Cert::try_from(vec![primary_sec.clone().into()])?;
let cert = cert_p.merge_public_and_secret(cert_s)?;
assert!(cert.primary_key().has_secret());
let cert_p =
Cert::try_from(vec![primary_pub.clone().into()])?;
let cert_s =
Cert::try_from(vec![primary_sec.clone().into()])?;
let cert = cert_s.merge_public_and_secret(cert_p)?;
assert!(cert.primary_key().has_secret());
Ok(())
}
#[test]
fn canonicalizing_keeps_secrets() -> Result<()> {
let primary: Key<_, key::PrimaryRole> =
key::Key4::generate_ecc(true, Curve::Ed25519)?.into();
let mut primary_pair = primary.clone().into_keypair()?;
let cert = Cert::try_from(vec![primary.clone().into()])?;
let subkey_sec: Key<_, key::SubordinateRole> =
key::Key4::generate_ecc(false, Curve::Cv25519)?.into();
let subkey_pub = subkey_sec.clone().take_secret().0;
let builder = signature::SignatureBuilder::new(SignatureType::SubkeyBinding)
.set_key_flags(KeyFlags::empty()
.set_transport_encryption())?;
let binding = subkey_sec.bind(&mut primary_pair, &cert, builder)?;
let cert = Cert::try_from(vec![
primary.clone().into(),
subkey_pub.clone().into(),
binding.clone().into(),
subkey_sec.clone().into(),
binding.clone().into(),
])?;
assert_eq!(cert.keys().subkeys().count(), 1);
assert_eq!(cert.keys().unencrypted_secret().subkeys().count(), 1);
let cert = Cert::try_from(vec![
primary.clone().into(),
subkey_sec.clone().into(),
binding.clone().into(),
subkey_pub.clone().into(),
binding.clone().into(),
])?;
assert_eq!(cert.keys().subkeys().count(), 1);
assert_eq!(cert.keys().unencrypted_secret().subkeys().count(), 1);
Ok(())
}
#[test]
fn issue_361() -> Result<()> {
let (cert, _) = CertBuilder::new()
.add_transport_encryption_subkey()
.generate()?;
let p = &P::new();
let cert_at = cert.with_policy(p,
cert.primary_key().creation_time()
+ time::Duration::new(300, 0))
.unwrap();
assert_eq!(cert_at.userids().count(), 0);
assert_eq!(cert_at.keys().count(), 2);
let mut primary_pair = cert.primary_key().key().clone()
.parts_into_secret()?.into_keypair()?;
let uid: UserID = "foo@example.org".into();
let sig = uid.bind(
&mut primary_pair, &cert,
signature::SignatureBuilder::new(SignatureType::PositiveCertification))?;
let cert = cert.insert_packets(vec![
Packet::from(uid),
sig.into(),
])?;
let cert_at = cert.with_policy(p,
cert.primary_key().creation_time()
+ time::Duration::new(300, 0))
.unwrap();
assert_eq!(cert_at.userids().count(), 1);
assert_eq!(cert_at.keys().count(), 2);
Ok(())
}
#[test]
fn issue_321() -> Result<()> {
let cert = Cert::from_bytes(
crate::tests::file("contrib/pep/pEpkey-netpgp.asc"))?;
assert_eq!(cert.userids().count(), 1);
assert_eq!(cert.keys().count(), 1);
let mut p = P::new();
p.accept_hash(HashAlgorithm::SHA1);
let cert_at = cert.with_policy(&p, cert.primary_key().creation_time())
.unwrap();
assert_eq!(cert_at.userids().count(), 1);
assert_eq!(cert_at.keys().count(), 1);
Ok(())
}
#[test]
fn different_preferences() -> Result<()> {
use crate::cert::Preferences;
let p = &crate::policy::StandardPolicy::new();
let cert = Cert::from_bytes(
crate::tests::key("different-preferences.asc"))?;
assert_eq!(cert.userids().count(), 2);
if let Some(userid) = cert.userids().nth(0) {
assert_eq!(userid.userid().value(),
&b"Alice Confusion <alice@example.com>"[..]);
let userid = userid.with_policy(p, None).expect("valid");
use crate::types::SymmetricAlgorithm::*;
assert_eq!(userid.preferred_symmetric_algorithms(),
Some(&[ AES256, AES192, AES128, TripleDES ][..]));
use crate::types::HashAlgorithm::*;
assert_eq!(userid.preferred_hash_algorithms(),
Some(&[ SHA512, SHA384, SHA256, SHA224, SHA1 ][..]));
use crate::types::CompressionAlgorithm::*;
assert_eq!(userid.preferred_compression_algorithms(),
Some(&[ Zlib, BZip2, Zip ][..]));
assert_eq!(userid.preferred_aead_algorithms(), None);
assert_eq!(userid.features(),
Some(Features::new(&[]).set_mdc()));
} else {
panic!("two user ids");
}
if let Some(userid) = cert.userids().nth(0) {
assert_eq!(userid.userid().value(),
&b"Alice Confusion <alice@example.com>"[..]);
let userid = userid.with_policy(p, None).expect("valid");
use crate::types::SymmetricAlgorithm::*;
assert_eq!(userid.preferred_symmetric_algorithms(),
Some(&[ AES256, AES192, AES128, TripleDES ][..]));
use crate::types::HashAlgorithm::*;
assert_eq!(userid.preferred_hash_algorithms(),
Some(&[ SHA512, SHA384, SHA256, SHA224, SHA1 ][..]));
use crate::types::CompressionAlgorithm::*;
assert_eq!(userid.preferred_compression_algorithms(),
Some(&[ Zlib, BZip2, Zip ][..]));
assert_eq!(userid.preferred_aead_algorithms(), None);
assert_eq!(userid.key_server_preferences(),
Some(KeyServerPreferences::new(&[0x80])));
assert_eq!(userid.features(),
Some(Features::new(&[]).set_mdc()));
let cert = cert.with_policy(p, None).expect("valid");
assert_eq!(userid.preferred_symmetric_algorithms(),
cert.preferred_symmetric_algorithms());
assert_eq!(userid.preferred_hash_algorithms(),
cert.preferred_hash_algorithms());
assert_eq!(userid.preferred_compression_algorithms(),
cert.preferred_compression_algorithms());
assert_eq!(userid.preferred_aead_algorithms(),
cert.preferred_aead_algorithms());
assert_eq!(userid.key_server_preferences(),
cert.key_server_preferences());
assert_eq!(userid.features(),
cert.features());
} else {
panic!("two user ids");
}
if let Some(userid) = cert.userids().nth(1) {
assert_eq!(userid.userid().value(),
&b"Alice Confusion <alice@example.net>"[..]);
let userid = userid.with_policy(p, None).expect("valid");
use crate::types::SymmetricAlgorithm::*;
assert_eq!(userid.preferred_symmetric_algorithms(),
Some(&[ AES192, AES256, AES128, TripleDES ][..]));
use crate::types::HashAlgorithm::*;
assert_eq!(userid.preferred_hash_algorithms(),
Some(&[ SHA384, SHA512, SHA256, SHA224, SHA1 ][..]));
use crate::types::CompressionAlgorithm::*;
assert_eq!(userid.preferred_compression_algorithms(),
Some(&[ BZip2, Zlib, Zip ][..]));
assert_eq!(userid.preferred_aead_algorithms(), None);
assert_eq!(userid.key_server_preferences(),
Some(KeyServerPreferences::new(&[0x80])));
assert_eq!(userid.features(),
Some(Features::new(&[]).set_mdc()));
} else {
panic!("two user ids");
}
Ok(())
}
#[test]
fn unsigned_components() -> Result<()> {
let p = &crate::policy::StandardPolicy::new();
let cert = Cert::from_bytes(
crate::tests::key("certificate-with-unsigned-components.asc"))?;
assert_eq!(cert.userids().count(), 2);
assert_eq!(cert.userids().with_policy(p, None).count(), 1);
assert_eq!(cert.user_attributes().count(), 2);
assert_eq!(cert.user_attributes().with_policy(p, None).count(), 1);
assert_eq!(cert.keys().count(), 1 + 4);
assert_eq!(cert.keys().with_policy(p, None).count(), 1 + 2);
Ok(())
}
#[test]
fn issue_504() -> Result<()> {
let mut keyring = crate::tests::key("testy.pgp").to_vec();
keyring.extend_from_slice(crate::tests::key("testy-new.pgp"));
let pp = PacketPile::from_bytes(&keyring)?;
assert!(matches!(
Cert::try_from(pp.clone()).unwrap_err().downcast().unwrap(),
Error::MalformedCert(_)
));
let v: Vec<Packet> = pp.into();
assert!(matches!(
Cert::try_from(v.clone()).unwrap_err().downcast().unwrap(),
Error::MalformedCert(_)
));
assert!(matches!(
Cert::from_packets(v.into_iter()).unwrap_err().downcast().unwrap(),
Error::MalformedCert(_)
));
let ppr = PacketParser::from_bytes(&keyring)?;
assert!(matches!(
Cert::try_from(ppr).unwrap_err().downcast().unwrap(),
Error::MalformedCert(_)
));
Ok(())
}
#[test]
fn issue_531() -> Result<()> {
let cert =
Cert::from_bytes(crate::tests::key("peter-sha1-backsig.pgp"))?;
let p = &crate::policy::NullPolicy::new();
assert_eq!(cert.with_policy(p, None)?.keys().for_signing().count(), 1);
let mut p = crate::policy::StandardPolicy::new();
p.reject_hash(HashAlgorithm::SHA1);
assert_eq!(cert.with_policy(&p, None)?.keys().for_signing().count(), 0);
Ok(())
}
#[test]
fn issue_539() -> Result<()> {
let cert =
Cert::from_bytes(crate::tests::key("peter-expired-backsig.pgp"))?;
let p = &crate::policy::NullPolicy::new();
assert_eq!(cert.with_policy(p, None)?.keys().for_signing().count(), 0);
let p = &crate::policy::StandardPolicy::new();
assert_eq!(cert.with_policy(p, None)?.keys().for_signing().count(), 0);
Ok(())
}
#[test]
fn issue_568() -> Result<()> {
use crate::packet::signature::subpacket::*;
let (cert, _) = CertBuilder::general_purpose(
None, Some("alice@example.org")).generate().unwrap();
assert_eq!(cert.userids().count(), 1);
assert_eq!(cert.subkeys().count(), 2);
assert_eq!(cert.unknowns().count(), 0);
assert_eq!(cert.bad_signatures().count(), 0);
assert_eq!(cert.userids().nth(0).unwrap().self_signatures().count(), 1);
assert_eq!(cert.subkeys().nth(0).unwrap().self_signatures().count(), 1);
assert_eq!(cert.subkeys().nth(1).unwrap().self_signatures().count(), 1);
let cert_b = cert.clone();
let mut packets = crate::PacketPile::from(cert_b).into_children()
.collect::<Vec<_>>();
for p in packets.iter_mut() {
if let Packet::Signature(sig) = p {
assert_eq!(sig.hashed_area().subpackets(
SubpacketTag::IssuerFingerprint).count(),
1);
sig.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::Issuer("AAAA BBBB CCCC DDDD".parse()?),
false)?)?;
}
}
let cert_b = Cert::from_packets(packets.into_iter())?;
let cert = cert.merge_public_and_secret(cert_b)?;
assert_eq!(cert.userids().count(), 1);
assert_eq!(cert.subkeys().count(), 2);
assert_eq!(cert.unknowns().count(), 0);
assert_eq!(cert.bad_signatures().count(), 0);
assert_eq!(cert.userids().nth(0).unwrap().self_signatures().count(), 1);
assert_eq!(cert.subkeys().nth(0).unwrap().self_signatures().count(), 1);
assert_eq!(cert.subkeys().nth(1).unwrap().self_signatures().count(), 1);
Ok(())
}
#[test]
fn missing_backsig_is_bad() -> Result<()> {
use crate::packet::{
key::Key4,
signature::{
SignatureBuilder,
subpacket::{Subpacket, SubpacketValue},
},
};
let cert = crate::Cert::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))?;
let mut pp = crate::PacketPile::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))?;
assert_eq!(pp.children().count(), 5);
if let Some(Packet::Signature(sig)) = pp.path_ref_mut(&[4]) {
let key: key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519)?.into();
let mut pair = key.into_keypair()?;
sig.unhashed_area_mut().replace(Subpacket::new(
SubpacketValue::EmbeddedSignature(
SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
.sign_primary_key_binding(
&mut pair,
cert.primary_key().key(),
cert.keys().subkeys().nth(0).unwrap().key())?),
false)?)?;
} else {
panic!("expected a signature");
}
use std::convert::TryFrom;
let malicious_cert = Cert::try_from(pp)?;
let p = &crate::policy::StandardPolicy::new();
assert_eq!(malicious_cert.with_policy(p, None)?.keys().subkeys()
.for_signing().count(), 0);
assert_eq!(malicious_cert.bad_signatures().count(), 1);
Ok(())
}
#[test]
fn multiple_embedded_signatures() -> Result<()> {
use crate::packet::{
key::Key4,
signature::{
SignatureBuilder,
subpacket::{Subpacket, SubpacketValue},
},
};
let cert = crate::Cert::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))?;
let key: key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519)?.into();
let mut pair = key.into_keypair()?;
let mut pp = crate::PacketPile::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))?;
assert_eq!(pp.children().count(), 5);
if let Some(Packet::Signature(sig)) = pp.path_ref_mut(&[4]) {
let backsig = sig.embedded_signatures().nth(0).unwrap().clone();
sig.unhashed_area_mut().replace(Subpacket::new(
SubpacketValue::EmbeddedSignature(
SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
.sign_primary_key_binding(
&mut pair,
cert.primary_key().key(),
cert.keys().subkeys().nth(0).unwrap().key())?),
false)?)?;
sig.unhashed_area_mut().add(Subpacket::new(
SubpacketValue::EmbeddedSignature(backsig), false)?)?;
} else {
panic!("expected a signature");
}
use std::convert::TryFrom;
let malicious_cert = Cert::try_from(pp)?;
let p = &crate::policy::StandardPolicy::new();
assert_eq!(malicious_cert.with_policy(p, None)?.keys().subkeys()
.for_signing().count(), 1);
assert_eq!(malicious_cert.bad_signatures().count(), 0);
let merged = cert.clone().merge_public_and_secret(malicious_cert.clone())?;
assert_eq!(merged.with_policy(p, None)?.keys().subkeys()
.for_signing().count(), 1);
let sig = merged.with_policy(p, None)?.keys().subkeys()
.for_signing().nth(0).unwrap().binding_signature();
assert_eq!(sig.embedded_signatures().count(), 2);
let merged = malicious_cert.clone().merge_public_and_secret(cert.clone())?;
assert_eq!(merged.with_policy(p, None)?.keys().subkeys()
.for_signing().count(), 1);
let sig = merged.with_policy(p, None)?.keys().subkeys()
.for_signing().nth(0).unwrap().binding_signature();
assert_eq!(sig.embedded_signatures().count(), 2);
Ok(())
}
#[test]
fn issue_579() -> Result<()> {
use std::convert::TryFrom;
use crate::packet::signature::subpacket::SubpacketTag;
let mut pp = crate::PacketPile::from_bytes(crate::tests::key(
"emmelie-dorothea-dina-samantha-awina-ed25519.pgp"))?;
assert_eq!(pp.children().count(), 5);
if let Some(Packet::Signature(sig)) = pp.path_ref_mut(&[2]) {
sig.unhashed_area_mut().remove_all(SubpacketTag::Issuer);
} else {
panic!("expected a signature");
}
if let Some(Packet::Signature(sig)) = pp.path_ref_mut(&[4]) {
sig.unhashed_area_mut().remove_all(SubpacketTag::Issuer);
} else {
panic!("expected a signature");
}
let cert = Cert::try_from(pp)?;
assert_eq!(cert.clone().merge_public_and_secret(cert.clone())?, cert);
assert_eq!(
cert.userids().nth(0).unwrap().self_signatures().nth(0).unwrap()
.unhashed_area().subpackets(SubpacketTag::Issuer).count(),
1);
assert_eq!(
cert.keys().subkeys().nth(0).unwrap().self_signatures().nth(0).unwrap()
.unhashed_area().subpackets(SubpacketTag::Issuer).count(),
1);
Ok(())
}
#[test]
fn merge_public() -> Result<()> {
let cert =
Cert::from_bytes(crate::tests::key("testy-new.pgp"))?;
let key =
Cert::from_bytes(crate::tests::key("testy-new-private.pgp"))?;
assert!(! cert.is_tsk());
assert!(key.is_tsk());
let merged = cert.clone().merge_public(key.clone())?;
assert!(! merged.is_tsk());
assert_eq!(merged, cert);
let merged = key.clone().merge_public(cert.clone())?;
assert!(merged.is_tsk());
assert_eq!(merged, key);
Ok(())
}
}