use std::io;
use std::cmp;
use std::cmp::Ordering;
use std::path::Path;
use std::mem;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::time;
use failure::ResultExt;
use crate::{
crypto::{hash::Hash, Signer},
Error,
Result,
RevocationStatus,
SignatureType,
packet,
packet::Signature,
packet::signature,
packet::Key,
packet::key,
packet::UserID,
packet::UserAttribute,
packet::Unknown,
Packet,
PacketPile,
KeyID,
Fingerprint,
KeyHandle,
policy::Policy,
};
use crate::parse::{Parse, PacketParserResult, PacketParser};
use crate::types::{
AEADAlgorithm,
CompressionAlgorithm,
Features,
HashAlgorithm,
KeyServerPreferences,
ReasonForRevocation,
SymmetricAlgorithm,
};
mod amalgamation;
mod builder;
mod bindings;
pub mod components;
use components::{
Amalgamation,
ComponentBundle,
PrimaryKeyBundle,
UnfilteredKeyBundleIter,
UnknownBundleIter,
ValidComponentAmalgamation,
};
mod component_iter;
use component_iter::{
ComponentIter,
};
mod keyiter;
mod key_amalgamation;
mod parser;
mod revoke;
pub use self::builder::{CertBuilder, CipherSuite};
use keyiter::KeyIter;
use key_amalgamation::{
KeyAmalgamation,
PrimaryKeyAmalgamation,
ValidKeyAmalgamation,
};
pub use parser::{
KeyringValidity,
KeyringValidator,
CertParser,
CertValidity,
CertValidator,
};
pub use revoke::{
SubkeyRevocationBuilder,
CertRevocationBuilder,
UserAttributeRevocationBuilder,
UserIDRevocationBuilder,
};
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
{
bindings: 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.bindings
}
}
impl<C> DerefMut for ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
fn deref_mut(&mut self) -> &mut Vec<ComponentBundle<C>> {
&mut self.bindings
}
}
impl<C> Into<Vec<ComponentBundle<C>>> for ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
fn into(self) -> Vec<ComponentBundle<C>> {
self.bindings
}
}
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.bindings.into_iter()
}
}
impl<C> ComponentBundles<C>
where ComponentBundle<C>: cmp::PartialEq
{
fn new() -> Self {
Self { bindings: 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.bindings.sort_unstable_by(
|a, b| cmp(&a.component, &b.component));
self.bindings.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.self_revocations);
true
} else {
false
}
});
for b in self.bindings.iter_mut() {
b.sort_and_dedup();
}
}
}
type KeyBundles<KeyPart, KeyRole> = ComponentBundles<Key<KeyPart, KeyRole>>;
type SubkeyBindings<KeyPart> = KeyBundles<KeyPart, key::SubordinateRole>;
#[allow(dead_code)]
type GenericKeyBindings
= ComponentBundles<Key<key::UnspecifiedParts, key::UnspecifiedRole>>;
type UserIDBindings = ComponentBundles<UserID>;
type UserAttributeBindings = ComponentBundles<UserAttribute>;
type UnknownBindings = ComponentBundles<Unknown>;
pub trait Preferences<'a>: components::Amalgamation<'a> {
fn preferred_symmetric_algorithms(&self)
-> Option<&'a [SymmetricAlgorithm]> {
self.map(|s| s.preferred_symmetric_algorithms())
}
fn preferred_hash_algorithms(&self) -> Option<&'a [HashAlgorithm]> {
self.map(|s| s.preferred_hash_algorithms())
}
fn preferred_compression_algorithms(&self)
-> Option<&'a [CompressionAlgorithm]> {
self.map(|s| s.preferred_compression_algorithms())
}
fn preferred_aead_algorithms(&self) -> Option<&'a [AEADAlgorithm]> {
self.map(|s| s.preferred_aead_algorithms())
}
fn key_server_preferences(&self) -> Option<KeyServerPreferences> {
self.map(|s| s.key_server_preferences())
}
fn preferred_key_server(&self) -> Option<&'a [u8]> {
self.map(|s| s.preferred_key_server())
}
fn features(&self) -> Option<Features> {
self.map(|s| s.features())
}
}
pub use def::Cert;
mod def {
use super::*;
#[derive(Debug, Clone, PartialEq)]
pub struct Cert {
pub(super)
primary: PrimaryKeyBundle<key::PublicParts>,
pub(super)
userids: UserIDBindings,
pub(super)
user_attributes: UserAttributeBindings,
pub(super)
subkeys: SubkeyBindings<key::PublicParts>,
pub(super)
unknowns: UnknownBindings,
pub(super)
bad: Vec<packet::Signature>,
}
}
impl std::str::FromStr for Cert {
type Err = failure::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>(reader: R) -> Result<Self> {
Cert::from_packet_parser(PacketParser::from_reader(reader)?)
}
fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
Cert::from_packet_parser(PacketParser::from_file(path)?)
}
fn from_bytes<D: AsRef<[u8]> + ?Sized>(data: &'a D) -> Result<Self> {
Cert::from_packet_parser(PacketParser::from_bytes(data)?)
}
}
impl Cert {
pub fn primary_key(&self) -> PrimaryKeyAmalgamation<key::PublicParts>
{
PrimaryKeyAmalgamation::new(
self.keys().nth(0).expect("primary key"))
}
pub fn revoked<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())
{
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()._revoked(policy, t, true, sig)
}
pub fn revoke_in_place(self, primary_signer: &mut dyn Signer,
code: ReasonForRevocation, reason: &[u8])
-> Result<Cert>
{
let sig = CertRevocationBuilder::new()
.set_reason_for_revocation(code, reason)?
.build(primary_signer, &self, None)?;
self.merge_packets(vec![sig.into()])
}
pub fn alive<T>(&self, policy: &dyn Policy, t: T) -> Result<()>
where T: Into<Option<time::SystemTime>>
{
let t = t.into();
self.primary_key().with_policy(policy, t).context("Primary key")?.alive()
}
fn set_expiration_time_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 mut sigs = Vec::new();
let binding = primary.binding_signature();
for template in [
if binding.typ() != SignatureType::DirectKey {
Some(binding)
} else {
None
},
primary.direct_key_signature(),
].iter().filter_map(|&x| x) {
let hash_algo = HashAlgorithm::SHA512;
let mut hash = hash_algo.context()?;
self.primary_key().hash(&mut hash);
match template.typ() {
SignatureType::DirectKey =>
(),
SignatureType::GenericCertification
| SignatureType::PersonaCertification
| SignatureType::CasualCertification
| SignatureType::PositiveCertification =>
self.primary_userid(policy, now)
.expect("this type must be from a userid binding, \
hence there must be a userid valid at `now`")
.userid().hash(&mut hash),
_ => unreachable!(),
}
sigs.push(signature::Builder::from(template.clone())
.set_key_expiration_time(expiration)?
.set_signature_creation_time(now)?
.sign_hash(primary_signer, hash)?.into());
}
self.merge_packets(sigs)
}
pub fn set_expiration_time(self, policy: &dyn Policy,
primary_signer: &mut dyn Signer,
expiration: Option<time::Duration>)
-> Result<Cert>
{
self.set_expiration_time_as_of(policy, primary_signer, expiration,
time::SystemTime::now())
}
pub fn primary_userid<'a, T>(&'a self, policy: &'a dyn Policy, t: T)
-> Option<ValidComponentAmalgamation<'a, UserID>>
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)
}
pub fn userids(&self) -> ComponentIter<UserID> {
ComponentIter::new(self, self.userids.iter())
}
pub fn primary_user_attribute<'a, T>(&'a self, policy: &'a dyn Policy, t: T)
-> Option<ValidComponentAmalgamation<'a, UserAttribute>>
where T: Into<Option<std::time::SystemTime>>
{
let t = t.into().unwrap_or_else(std::time::SystemTime::now);
ValidComponentAmalgamation::primary(self, self.user_attributes.iter(),
policy, t)
}
pub fn user_attributes(&self) -> ComponentIter<UserAttribute> {
ComponentIter::new(self, self.user_attributes.iter())
}
pub(crate) fn subkeys(&self) -> UnfilteredKeyBundleIter<key::PublicParts,
key::SubordinateRole>
{
UnfilteredKeyBundleIter { iter: Some(self.subkeys.iter()) }
}
pub fn unknowns(&self) -> UnknownBundleIter {
UnknownBundleIter { iter: Some(self.unknowns.iter()) }
}
pub fn bad_signatures(&self) -> &[Signature] {
&self.bad
}
pub fn keys(&self) -> KeyIter<key::PublicParts>
{
KeyIter::new(self)
}
pub fn from_packet_parser(ppr: PacketParserResult) -> Result<Self> {
let mut parser = parser::CertParser::from_packet_parser(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())
}
}
pub fn from_packet_pile(p: PacketPile) -> Result<Self> {
let mut i = parser::CertParser::from_iter(p.into_children());
match i.next() {
Some(Ok(cert)) => Ok(cert),
Some(Err(err)) => Err(err),
None => Err(Error::MalformedCert("No data".into()).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 sig in mem::replace(&mut $binding.$sigs, Vec::new())
.into_iter()
{
if let Ok(()) = sig.$verify_method(self.primary.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);
}
}
});
($desc:expr, $binding:expr, $sigs:ident,
$verify_method:ident) => ({
check!($desc, $binding, $sigs, $verify_method,)
});
}
macro_rules! check_3rd_party {
($desc:expr,
$binding:expr,
$sigs:ident,
$lookup_fn:expr,
$verify_method:ident,
$hash_method:ident,
$($verify_args:expr),*
) => ({
t!("check_3rd_party!({}, {}, {:?}, {}, {}, ...)",
$desc, stringify!($binding), $binding.$sigs,
stringify!($verify_method), stringify!($hash_method));
for sig in mem::replace(&mut $binding.$sigs, Vec::new())
.into_iter()
{
if let Ok(hash) = Signature::$hash_method(
&sig, self.primary.key(), $($verify_args),*) {
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 {}",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ(), $desc);
self.bad.push(sig);
}
} else {
t!("Sig {:02X}{:02X}, type = {}: \
Hashing failed",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ());
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));
}
}
'outer: for (unknown_idx, sig) in bad_sigs {
let mut found_component = false;
macro_rules! check_one {
($desc:expr, $sigs:expr, $sig:expr,
$verify_method:ident, $($verify_args:expr),*) => ({
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,
$sigs:expr,
$sig:ident,
$lookup_fn:expr,
$verify_method:ident,
$hash_method:ident,
$($verify_args:expr),*
) => ({
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 {
if let Ok(hash) = Signature::$hash_method(
&sig, self.primary.key(), $($verify_args),*) {
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!("Self-sig {:02X}{:02X}, {:?} doesn't belong \
to any known component or is bad.",
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(sig_cmp);
self.bad.dedup();
self.userids.sort_and_dedup(UserID::cmp, |_, _| {});
self.user_attributes.sort_and_dedup(UserAttribute::cmp, |_, _| {});
self.subkeys.sort_and_dedup(Key::public_cmp,
|ref mut a, ref mut b| {
if b.secret().is_none() && a.secret().is_some() {
b.set_secret(a.set_secret(None));
}
});
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 into_packets(self) -> impl Iterator<Item=Packet> {
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| 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 into_packet_pile(self) -> PacketPile {
PacketPile::from(self.into_packets().collect::<Vec<Packet>>())
}
pub fn merge(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().secret().is_none() && other.primary.key().secret().is_some() {
self.primary.key_mut().set_secret(other.primary.key_mut().set_secret(None));
}
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())
}
pub fn merge_packets(self, packets: Vec<Packet>) -> Result<Self> {
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());
};
for p in packets.into_iter() {
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),
p => combined.push(p),
}
}
Cert::from_packet_pile(PacketPile::from(combined))
}
pub fn is_tsk(&self) -> bool {
if self.primary_key().secret().is_some() {
return true;
}
self.subkeys().any(|sk| {
sk.key().secret().is_some()
})
}
}
#[cfg(test)]
mod test {
use crate::serialize::Serialize;
use super::components::Amalgamation;
use crate::policy::StandardPolicy as P;
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::from_packet_pile(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!(cert.fingerprint().to_hex(),
"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!(cert.fingerprint().to_hex(),
"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!(cert.fingerprint().to_hex(),
"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(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(cert_add_uid_1.clone()).unwrap()
.merge(cert_add_uid_2.clone()).unwrap()
.merge(cert_add_uid_3.clone()).unwrap();
assert_eq!(cert_all_uids, merged);
let merged = cert_base.clone()
.merge(cert_add_uid_3.clone()).unwrap()
.merge(cert_add_uid_2.clone()).unwrap()
.merge(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(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let merged = cert_base.clone().merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let merged = cert_base.clone()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(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(cert_all_uids.clone()).unwrap();
assert_eq!(cert_all, merged);
let merged = cert_all_uids.clone()
.merge(cert_all_subkeys.clone()).unwrap();
assert_eq!(cert_all, merged);
let merged = cert_base.clone()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_uid_2.clone()).unwrap()
.merge(cert_add_uid_1.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_uid_3.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(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(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(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(cert_donald_signs_all.clone()).unwrap()
.merge(cert_ivanka_signs_base.clone()).unwrap()
.merge(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(cert_ivanka_signs_base.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_ivanka_signs_all.clone()).unwrap()
.merge(cert_ivanka_signs_base.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(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,
&[ KeyID::from_hex("7223B56678E02528").ok(),
KeyID::from_hex("A3506AFB820ABD08").ok(),
KeyID::from_hex("C2B819056C652598").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,
&[ KeyID::from_hex(&"1075 8EBD BD7C FAB5"[..]).ok(),
KeyID::from_hex(&"1258 68EA 4BFA 08E4"[..]).ok(),
KeyID::from_hex(&"1498 ADC6 C192 3237"[..]).ok(),
KeyID::from_hex(&"24EC FF5A FF68 370A"[..]).ok(),
KeyID::from_hex(&"3714 7292 14D5 DA70"[..]).ok(),
KeyID::from_hex(&"3B7A A7F0 14E6 9B5A"[..]).ok(),
KeyID::from_hex(&"5B58 DCF9 C341 6611"[..]).ok(),
KeyID::from_hex(&"A524 01B1 1BFD FA5C"[..]).ok(),
KeyID::from_hex(&"A70A 96E1 439E A852"[..]).ok(),
KeyID::from_hex(&"C61B D3EC 2148 4CFF"[..]).ok(),
KeyID::from_hex(&"CAEF A883 2167 5333"[..]).ok(),
KeyID::from_hex(&"DC10 4C4E 0CA7 57FB"[..]).ok(),
KeyID::from_hex(&"E3A3 2229 449B 0350"[..]).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(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::from_packet_pile(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::from_packet_pile(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::from_packet_pile(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::from_packet_pile(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
}
#[test]
fn merge_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.merge_packets(rev).unwrap();
let packets_post_merge = cert.clone().into_packets().count();
assert_eq!(packets_post_merge, packets_pre_merge + 1);
}
#[test]
fn set_expiration_time() {
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
);
let cert = check_set_expiration_time(p, cert);
assert_eq!(cert.clone().into_packet_pile().children().count(),
1
+ 1
+ 2
+ 1
+ 1
+ 2
+ 1
+ 1
);
}
#[test]
fn set_expiration_time_uidless() {
let p = &P::new();
let (cert, _) = CertBuilder::new()
.set_expiration_time(None)
.set_expiration_time(
Some(crate::types::Duration::weeks(52).unwrap().into()))
.generate().unwrap();
assert_eq!(cert.clone().into_packet_pile().children().count(),
1
+ 1
);
let cert = check_set_expiration_time(p, cert);
assert_eq!(cert.clone().into_packet_pile().children().count(),
1
+ 1
+ 2
);
}
fn check_set_expiration_time(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_expiration_time()
.expect("Keys expire by default.");
let mut keypair = cert.primary_key().key().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap();
let as_of1 = now + time::Duration::new(10, 0);
let cert = cert.set_expiration_time_as_of(
policy, &mut keypair, None, as_of1).unwrap();
{
assert_eq!(cert.primary_key().with_policy(policy, now).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1 - a_sec).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1).unwrap()
.key_expiration_time(),
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_expiration_time_as_of(
policy, &mut keypair, Some(expiry_new), as_of2).unwrap();
{
assert_eq!(cert.primary_key().with_policy(policy, now).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1 - a_sec).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key().with_policy(policy, as_of1).unwrap()
.key_expiration_time(),
None);
assert_eq!(cert.primary_key().with_policy(policy, as_of2 - a_sec).unwrap()
.key_expiration_time(),
None);
assert_eq!(cert.primary_key().with_policy(policy, as_of2).unwrap()
.key_expiration_time(),
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.revoked(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.revoked();
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.revoked(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(d.clone()).unwrap(), true, false, false);
check(&d.clone().merge(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(u.clone()).unwrap(), false, true, false);
check(&u.clone().merge(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(k.clone()).unwrap(), false, false, true);
check(&k.clone().merge(cert.clone()).unwrap(), false, false, true);
check(&d.clone().merge(u.clone()).unwrap(), true, true, false);
check(&u.clone().merge(d.clone()).unwrap(), true, true, false);
check(&d.clone().merge(k.clone()).unwrap(), true, false, true);
check(&k.clone().merge(d.clone()).unwrap(), true, false, true);
check(&u.clone().merge(k.clone()).unwrap(), false, true, true);
check(&k.clone().merge(u.clone()).unwrap(), false, true, true);
check(&d.clone().merge(u.clone().merge(k.clone()).unwrap()).unwrap(),
true, true, true);
check(&d.clone().merge(k.clone().merge(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.revoked(p, None));
let mut keypair = cert.primary_key().key().clone().mark_parts_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.issuer(), Some(&cert.keyid()));
assert_eq!(sig.issuer_fingerprint(),
Some(&cert.fingerprint()));
let cert = cert.merge_packets(vec![sig.into()]).unwrap();
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(p, None));
let (other, _) = CertBuilder::general_purpose(None, Some("Test 2"))
.generate().unwrap();
let mut keypair = other.primary_key().key().clone().mark_parts_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.issuer(), Some(&other.keyid()));
assert_eq!(sig.issuer_fingerprint(),
Some(&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.revoked(p, None));
let mut keypair = cert.primary_key().key().clone().mark_parts_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.merge_packets(vec![sig.into()]).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revoked(p, None));
let subkey = cert.subkeys().nth(0).unwrap();
assert_match!(RevocationStatus::Revoked(_)
= subkey.revoked(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.revoked());
let mut keypair = cert.primary_key().key().clone().mark_parts_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.merge_packets(vec![sig.into()]).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revoked(p, None));
let uid = cert.userids().with_policy(p, None).nth(1).unwrap();
assert_match!(RevocationStatus::Revoked(_) = uid.revoked());
}
#[test]
fn key_revoked() {
use crate::types::Features;
use crate::packet::key::Key4;
use crate::types::Curve;
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::Builder::new(SignatureType::DirectKey)
.set_features(&Features::sequoia()).unwrap()
.set_key_flags(&KeyFlags::default()).unwrap()
.set_signature_creation_time(t1).unwrap()
.set_key_expiration_time(Some(time::Duration::new(10 * 52 * 7 * 24 * 60 * 60, 0))).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.sign_direct_key(&mut pair).unwrap();
let rev1 = signature::Builder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t2).unwrap()
.set_reason_for_revocation(ReasonForRevocation::KeySuperseded,
&b""[..]).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.sign_direct_key(&mut pair).unwrap();
let bind2 = signature::Builder::new(SignatureType::DirectKey)
.set_features(&Features::sequoia()).unwrap()
.set_key_flags(&KeyFlags::default()).unwrap()
.set_signature_creation_time(t3).unwrap()
.set_key_expiration_time(Some(time::Duration::new(10 * 52 * 7 * 24 * 60 * 60, 0))).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.sign_direct_key(&mut pair).unwrap();
let rev2 = signature::Builder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t4).unwrap()
.set_reason_for_revocation(ReasonForRevocation::KeyCompromised,
&b""[..]).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.sign_direct_key(&mut pair).unwrap();
(bind1, rev1, bind2, rev2)
};
let pk : key::PublicKey = key.into();
let cert = Cert::from_packet_pile(PacketPile::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.revoked(p, te1), RevocationStatus::NotAsFarAsWeKnow);
assert_eq!(cert.revoked(p, t12), RevocationStatus::NotAsFarAsWeKnow);
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(p, t23));
assert_eq!(cert.revoked(p, t34), RevocationStatus::NotAsFarAsWeKnow);
let cert = cert.merge_packets(vec![ rev2.into() ]).unwrap();
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(p, te1));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(p, t12));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(p, t23));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(p, t34));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(p, t4));
assert_match!(RevocationStatus::Revoked(_)
= cert.revoked(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>>
{
!destructures_to!(RevocationStatus::NotAsFarAsWeKnow
= cert.revoked(p, t))
}
fn subkey_revoked<T>(p: &dyn Policy, cert: &Cert, t: T) -> bool
where T: Into<Option<time::SystemTime>>
{
!destructures_to!(RevocationStatus::NotAsFarAsWeKnow
= cert.subkeys().nth(0).unwrap().revoked(p, t))
}
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(
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(
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(
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(
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.revoked(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.revoked());
} else {
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= b.revoked());
}
} else {
assert!(!eminem);
eminem = true;
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= b.revoked());
}
}
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.revoked(p, None));
assert_eq!(cert.user_attributes().count(), 1);
let ua = cert.user_attributes().bundles().nth(0).unwrap();
if revoked {
assert_match!(RevocationStatus::Revoked(_)
= ua.revoked(p, t));
} else {
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= ua.revoked(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(
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(
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(
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(
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.revoked(), 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().secret().is_some());
assert!(tsk.is_tsk());
let subkey_count = tsk.subkeys().len();
assert!(subkey_count > 0);
assert!(tsk.subkeys().all(|k| k.key().secret().is_some()));
let mut cert_bytes = Vec::new();
tsk.serialize(&mut cert_bytes).unwrap();
let cert = Cert::from_bytes(&cert_bytes[..]).unwrap();
assert!(cert.primary.key().secret().is_none());
assert!(!cert.is_tsk());
assert!(cert.subkeys().all(|k| k.key().secret().is_none()));
let merge1 = cert.clone().merge(tsk.clone()).unwrap();
assert!(merge1.is_tsk());
assert!(merge1.primary.key().secret().is_some());
assert_eq!(merge1.subkeys().len(), subkey_count);
assert!(merge1.subkeys().all(|k| k.key().secret().is_some()));
let merge2 = tsk.clone().merge(cert.clone()).unwrap();
assert!(merge2.is_tsk());
assert!(merge2.primary.key().secret().is_some());
assert_eq!(merge2.subkeys().len(), subkey_count);
assert!(merge2.subkeys().all(|k| k.key().secret().is_some()));
}
#[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(_) => 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::from_packet_pile(PacketPile::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().bundles() {
for sigs in [
uid.self_signatures(),
uid.certifications(),
uid.self_revocations(),
uid.other_revocations()
].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().first().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 cert_is_send_and_sync() {
fn f<T: Send + Sync>(_: T) {}
f(Cert::from_bytes(crate::tests::key("testy-new.pgp")).unwrap());
}
#[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.primary_userid(p, selfsig0).unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"Eminem");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-1-soft-revocation.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"Eminem");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-2-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"Slim Shady");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-3-hard-revocation.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"Eminem");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-4-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"Eminem");
assert_eq!(cert.primary_userid(p, now).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.primary_userid(p, selfsig0).unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"aaaaa");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-1-add-userid-bbbbb.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"bbbbb");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-2-make-aaaaa-primary.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"aaaaa");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-3-make-bbbbb-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"aaaaa");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-4-make-bbbbb-primary.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"bbbbb");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-5-make-aaaaa-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"bbbbb");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-6-revoked-aaaaa.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(p, selfsig0).unwrap().userid().value(),
b"aaaaa");
assert_eq!(cert.primary_userid(p, now).unwrap().userid().value(),
b"bbbbb");
}
#[test]
fn binding_signature_lookup() {
use crate::types::Features;
use crate::packet::key::Key4;
use crate::types::Curve;
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::from_packet_pile(PacketPile::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.merge_packets(vec![uid.into(), 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::Builder::new(SignatureType::DirectKey)
.set_features(&Features::sequoia()).unwrap()
.set_key_flags(&KeyFlags::default()).unwrap()
.set_signature_creation_time(t1).unwrap()
.set_key_expiration_time(Some(
time::Duration::new((1 + i as u64) * 24 * 60 * 60, 0)))
.unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.set_signature_creation_time(*t).unwrap()
.sign_direct_key(&mut pair).unwrap();
let binding : Packet = binding.into();
cert = cert.merge_packets(vec![ binding ]).unwrap();
let direct_signatures =
cert.primary_key().bundle().self_signatures();
assert_eq!(cert.primary_key().with_policy(p, *t).unwrap()
.direct_key_signature(),
direct_signatures.get(*offset));
assert_eq!(cert.primary_key().with_policy(p, *t + a_sec).unwrap()
.direct_key_signature(),
direct_signatures.get(*offset));
assert_eq!(cert.primary_key().with_policy(p, None).unwrap()
.direct_key_signature(),
direct_signatures.get(0));
assert!(cert.primary_key().with_policy(p, time_zero).is_err());
}
}
}
#[test]
fn keysigning_party() {
use crate::cert::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().bundles().nth(0).unwrap();
assert_eq!(bob_userid_binding.userid().value(), b"bob@bar.com");
let sig_template
= signature::Builder::new(SignatureType::GenericCertification)
.set_trust_signature(255, 120)
.unwrap();
let alice_certifies_bob
= bob_userid_binding.userid().bind(
&mut alice.primary_key().key().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap(),
&bob,
sig_template).unwrap();
let bob
= bob.merge_packets(vec![ alice_certifies_bob.clone().into() ])
.unwrap();
assert_eq!(bob.userids().len(), 1);
let bob_userid_binding = bob.userids().bundles().nth(0).unwrap();
assert_eq!(bob_userid_binding.userid().value(), b"bob@bar.com");
assert_eq!(bob_userid_binding.certifications(),
&[ alice_certifies_bob.clone() ]);
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();
let algo = primary.pk_algo();
primary.secret_mut().unwrap()
.decrypt_in_place(algo, &"streng geheim".into()).unwrap();
let cert = cert.merge_packets(vec![
primary.mark_parts_secret().unwrap().mark_role_primary().into()
]).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<()> {
use crate::types::Curve;
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::from_packet_pile(vec![primary.into()].into())?;
let uid = UserID::from("foo@example.org");
let cert = cert.merge_packets(vec![uid.into()])?;
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.merge_packets(vec![ua.into()])?;
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.merge_packets(vec![signing_subkey.into()])?;
assert_eq!(cert.keys().skip_primary().count(), 1);
assert_eq!(cert.keys().skip_primary().with_policy(&p, None).count(), 0);
let mut fake_key = packet::Unknown::new(
packet::Tag::PublicSubkey, failure::err_msg("fake key"));
fake_key.set_body("fake key".into());
let fake_binding = signature::Builder::new(SignatureType::SubkeyBinding)
.set_issuer(primary_pair.public().keyid())?
.set_issuer_fingerprint(primary_pair.public().fingerprint())?
.sign_standalone(&mut primary_pair)?;
let cert = cert.merge_packets(vec![fake_key.into(),
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(),
&[fake_binding]);
Ok(())
}
#[test]
fn canonicalize_with_v3_sig() -> Result<()> {
let cert = Cert::from_bytes(
crate::tests::key("eike-v3-v4.pgp"))?;
dbg!(&cert);
assert_eq!(cert.userids()
.with_policy(&crate::policy::StandardPolicy::new(), None)
.count(), 1);
Ok(())
}
#[test]
fn issue_215() {
let p = crate::policy::StandardPolicy::new();
let cert = Cert::from_bytes(crate::tests::key(
"issue-215-expiration-on-direct-key-sig.pgp")).unwrap();
assert_match!(
Error::Expired(_)
= cert.alive(&p, None).unwrap_err().downcast().unwrap());
assert_match!(
Error::Expired(_)
= cert.primary_key().with_policy(&p, None).unwrap()
.alive().unwrap_err().downcast().unwrap());
}
}