use std::io;
use std::collections::btree_map::BTreeMap;
use std::collections::btree_map::Entry;
use std::collections::hash_map::DefaultHasher;
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::hash::Hasher;
use std::path::Path;
use std::mem;
use std::fmt;
use std::time;
use buffered_reader::BufferedReader;
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::{Cookie, 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;
use bundle::{
ComponentBundles,
UserIDBundles,
UserAttributeBundles,
SubkeyBundles,
UnknownBundles,
};
mod lazysigs;
mod parser;
pub mod raw;
mod revoke;
pub use self::builder::{CertBuilder, CipherSuite, KeyBuilder, SubkeyBuilder};
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())
}
}
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]>;
#[deprecated]
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>;
fn policy_uri(&self) -> Option<&'a [u8]>;
}
#[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_buffered_reader<R>(reader: R) -> Result<Cert>
where
R: BufferedReader<Cookie> + 'a,
{
Cert::try_from(PacketParser::from_buffered_reader(reader)?)
}
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(crate::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.primary.bad_signatures()
.chain(self.userids.iter().flat_map(|u| u.bad_signatures()))
.chain(self.user_attributes.iter().flat_map(|u| u.bad_signatures()))
.chain(self.subkeys.iter().flat_map(|u| u.bad_signatures()))
.chain(self.unknowns.iter().flat_map(|u| u.bad_signatures()))
.chain(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())
}
#[deprecated(
since = "1.18.0",
note = "Use Cert::into_packets2() to strip secret key material \
or cert.into_tsk().into_packets() to serialize any \
secret key material")]
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 into_packets2(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) =>
Packet::PublicKey(k.take_secret().0),
Packet::PublicSubkey(k) =>
Packet::PublicSubkey(k.take_secret().0),
_ => 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 sort_and_dedup(&mut self) {
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 a.has_secret() {
std::mem::swap(a, b);
}
});
self.unknowns.sort_and_dedup(Unknown::best_effort_cmp, |_, _| {});
}
fn canonicalize(mut self) -> Self {
tracer!(TRACE, "canonicalize", 0);
use SignatureType::*;
self.sort_and_dedup();
macro_rules! check {
($desc:expr, $binding:expr, $sigs:ident,
$hash_method:ident, // method to hash the signature
$sig_type_pat:pat, // pattern to test signature types against
$($hash_args:expr),* // additional arguments to pass to hash_method
) => ({
let sigs = $binding.$sigs.take();
t!("check!({}, {}, {} ({:?}), {}, ...)",
$desc, stringify!($binding), sigs.len(), sigs,
stringify!($hash_method));
for sig in sigs.into_iter() {
let key = self.primary.key();
match sig.hash_algo().context().and_then(|mut ctx| {
if matches!(sig.typ(), $sig_type_pat) {
sig.$hash_method(&mut ctx, key, $($hash_args),*);
ctx.into_digest()
} else {
Err(Error::UnsupportedSignatureType(sig.typ()).into())
}
}) {
Ok(hash) => {
if &sig.digest_prefix()[..] == &hash[..2] {
sig.set_computed_digest(Some(hash));
$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,
$hash_method:ident, $sig_type_pat:pat) => ({
check!($desc, $binding, $sigs, $hash_method, $sig_type_pat, )
});
}
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
$sig_type_pat:pat, // pattern to test signature types against
$($verify_args:expr),* // additional arguments to pass to the above
) => ({
let sigs = mem::take(&mut $binding.$sigs);
t!("check_3rd_party!({}, {}, {} ({:?}_, {}, {}, ...)",
$desc, stringify!($binding), sigs.len(), sigs,
stringify!($verify_method), stringify!($hash_method));
for sig in sigs {
let key = self.primary.key();
match sig.hash_algo().context().and_then(|mut ctx| {
if matches!(sig.typ(), $sig_type_pat) {
sig.$hash_method(&mut ctx, key, $($verify_args),*);
ctx.into_digest()
} else {
Err(Error::UnsupportedSignatureType(sig.typ()).into())
}
}) {
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 {
sig.set_computed_digest(Some(hash));
$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, $sig_type_pat:pat) => ({
check_3rd_party!($desc, $binding, $sigs, $lookup_fn,
$verify_method, $hash_method, $sig_type_pat, )
});
}
fn lookup_fn(_: &Signature)
-> Option<Key<key::PublicParts, key::UnspecifiedRole>> {
None
}
check!("primary key",
self.primary, self_signatures, hash_direct_key, DirectKey);
check!("primary key",
self.primary, self_revocations, hash_direct_key, KeyRevocation);
check_3rd_party!("primary key",
self.primary, certifications, lookup_fn,
verify_direct_key, hash_direct_key, DirectKey);
check_3rd_party!("primary key",
self.primary, other_revocations, lookup_fn,
verify_primary_key_revocation, hash_direct_key,
KeyRevocation);
self.bad.append(&mut self.primary.attestations.take());
for ua in self.userids.iter_mut() {
check!(format!("userid \"{}\"",
String::from_utf8_lossy(ua.userid().value())),
ua, self_signatures, hash_userid_binding,
GenericCertification | PersonaCertification
| CasualCertification | PositiveCertification,
ua.userid());
check!(format!("userid \"{}\"",
String::from_utf8_lossy(ua.userid().value())),
ua, self_revocations, hash_userid_binding,
CertificationRevocation,
ua.userid());
check!(format!("userid \"{}\"",
String::from_utf8_lossy(ua.userid().value())),
ua, attestations, hash_userid_binding,
AttestationKey,
ua.userid());
check_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(ua.userid().value())),
ua, certifications, lookup_fn,
verify_userid_binding, hash_userid_binding,
GenericCertification | PersonaCertification
| CasualCertification | PositiveCertification,
ua.userid());
check_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(ua.userid().value())),
ua, other_revocations, lookup_fn,
verify_userid_revocation, hash_userid_binding,
CertificationRevocation,
ua.userid());
}
for binding in self.user_attributes.iter_mut() {
check!("user attribute",
binding, self_signatures, hash_user_attribute_binding,
GenericCertification | PersonaCertification
| CasualCertification | PositiveCertification,
binding.user_attribute());
check!("user attribute",
binding, self_revocations, hash_user_attribute_binding,
CertificationRevocation,
binding.user_attribute());
check!("user attribute",
binding, attestations, hash_user_attribute_binding,
AttestationKey,
binding.user_attribute());
check_3rd_party!(
"user attribute",
binding, certifications, lookup_fn,
verify_user_attribute_binding, hash_user_attribute_binding,
GenericCertification | PersonaCertification
| CasualCertification | PositiveCertification,
binding.user_attribute());
check_3rd_party!(
"user attribute",
binding, other_revocations, lookup_fn,
verify_user_attribute_revocation, hash_user_attribute_binding,
CertificationRevocation,
binding.user_attribute());
}
for binding in self.subkeys.iter_mut() {
check!(format!("subkey {}", binding.key().keyid()),
binding, self_signatures, hash_subkey_binding,
SubkeyBinding,
binding.key());
check!(format!("subkey {}", binding.key().keyid()),
binding, self_revocations, hash_subkey_binding,
SubkeyRevocation,
binding.key());
check_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding, certifications, lookup_fn,
verify_subkey_binding, hash_subkey_binding,
SubkeyBinding,
binding.key());
check_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding, other_revocations, lookup_fn,
verify_subkey_revocation, hash_subkey_binding,
SubkeyRevocation,
binding.key());
self.bad.append(&mut binding.attestations.take());
}
let mut bad_sigs: Vec<(Option<usize>, Signature)> =
std::mem::take(&mut self.bad).into_iter()
.map(|sig| {
t!("We're going to reconsider bad signature {:?}", sig);
(None, sig)
})
.collect();
t!("Attempting to reorder {} signatures", bad_sigs.len());
for (i, c) in self.unknowns.iter_mut().enumerate() {
for sig in
c.self_signatures.take().into_iter()
.chain(
std::mem::take(&mut c.certifications).into_iter())
.chain(
c.attestations.take().into_iter())
.chain(
c.self_revocations.take().into_iter())
.chain(
std::mem::take(&mut c.other_revocations).into_iter())
{
t!("We're going to reconsider {:?} on unknown component #{}",
sig, i);
bad_sigs.push((Some(i), sig));
}
}
let primary_fp: KeyHandle = self.key_handle();
'outer: for (unknown_idx, sig) in bad_sigs {
let mut found_component = false;
let issuers =
sig.get_issuers();
let is_selfsig =
issuers.is_empty()
|| issuers.iter().any(|kh| kh.aliases(&primary_fp));
macro_rules! check_one {
($desc:expr, // a description of the component
$sigs:expr, // where to put $sig if successful
$sig:ident, // the signature to check
$hash_method:ident, // the method to compute the hash
$($verify_args:expr),* // additional arguments for the above
) => ({
if is_selfsig {
t!("check_one!({}, {:?}, {:?}/{}, {}, ...)",
$desc, $sigs, $sig, $sig.typ(),
stringify!($hash_method));
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({
let sig = $sig.clone();
sig.set_computed_digest(Some(hash));
sig
});
t!("Will keep trying to match this sig to \
other components (found before? {:?})...",
found_component);
found_component = true;
} else {
t!("Sig {:02X}{:02X}, {:?} \
does not belong to {}: \
hash prefix mismatch",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
}
} else {
t!("Sig {:02X}{:02X}, type = {} \
doesn't use a supported hash algorithm: \
{:?} unsupported",
$sig.digest_prefix()[0], $sig.digest_prefix()[1],
$sig.typ(), $sig.hash_algo());
}
}
});
($desc:expr, $sigs:expr, $sig:ident,
$hash_method:ident) => ({
check_one!($desc, $sigs, $sig, $hash_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) {
match $sig.$verify_method(&key,
self.primary.key(),
$($verify_args),*)
{
Ok(()) => {
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;
},
Err(err) => {
t!("Sig {:02X}{:02X}, type = {} \
doesn't belong to {}: {:?}",
$sig.digest_prefix()[0], $sig.digest_prefix()[1],
$sig.typ(), $desc, err);
},
}
} 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({
let sig = $sig.clone();
sig.set_computed_digest(Some(hash));
sig
});
t!("Will keep trying to match this sig to \
other components (found before? {:?})...",
found_component);
found_component = true;
} else {
t!("Sig {:02X}{:02X}, {:?} \
does not belong to {}: \
hash prefix mismatch",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
}
} else {
t!("Sig {:02X}{:02X}, type = {} \
doesn't use a supported hash algorithm: \
{:?} unsupported",
$sig.digest_prefix()[0], $sig.digest_prefix()[1],
$sig.typ(), $sig.hash_algo());
}
}
}
});
($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, )
});
}
match sig.typ() {
DirectKey => {
check_one!("primary key", self.primary.self_signatures,
sig, hash_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, hash_direct_key);
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,
hash_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,
hash_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());
}
},
crate::types::SignatureType::AttestationKey => {
for binding in self.userids.iter_mut() {
check_one!(format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.attestations, sig,
hash_userid_binding, binding.userid());
}
for binding in self.user_attributes.iter_mut() {
check_one!("user attribute",
binding.attestations, sig,
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,
hash_userid_binding,
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,
hash_user_attribute_binding,
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,
hash_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,
hash_subkey_binding, 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}, {:?}, originally found on {:?} \
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(), unknown_idx);
if let Some(i) = unknown_idx {
let is_revocation = match sig.typ() {
CertificationRevocation | KeyRevocation | SubkeyRevocation
=> true,
_ => false,
};
match (is_selfsig, is_revocation) {
(false, false) =>
self.unknowns[i].certifications.push(sig),
(false, true) =>
self.unknowns[i].other_revocations.push(sig),
(true, false) =>
self.unknowns[i].self_signatures.push(sig),
(true, true) =>
self.unknowns[i].self_revocations.push(sig),
}
} else {
self.bad.push(sig);
}
}
if !self.bad.is_empty() {
t!("{}: ignoring {} bad self signatures",
self.keyid(), self.bad.len());
}
self.sort_and_dedup();
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 = other.strip_secret_key_material();
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 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.attestations.append(
&mut other.primary.attestations);
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()),
}
}
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())
}
_ => Ok(()),
}
}
pub fn insert_packets2<I>(self, packets: I)
-> Result<(Self, bool)>
where I: IntoIterator,
I::Item: Into<Packet>,
{
self.insert_packets_merge(packets, |_old, new| Ok(new))
}
pub fn insert_packets_merge<P, I>(self, packets: P, merge: I)
-> Result<(Self, bool)>
where P: IntoIterator,
P::Item: Into<Packet>,
I: FnMut(Option<Packet>, Packet) -> Result<Packet>,
{
self.insert_packets_(&mut packets.into_iter().map(Into::into),
Box::new(merge))
}
fn insert_packets_<'a>(self,
packets: &mut dyn Iterator<Item = Packet>,
mut merge: Box<dyn FnMut(Option<Packet>, Packet)
-> Result<Packet> + 'a>)
-> Result<(Self, bool)>
{
let mut changed = false;
let mut combined =
self.as_tsk().into_packets().collect::<Vec<_>>();
let hash_packet = |p: &Packet| -> u64 {
let mut hasher = DefaultHasher::new();
p.normalized_hash(&mut hasher);
hasher.finish()
};
let mut packet_map: BTreeMap<u64, Vec<usize>> = BTreeMap::new();
for (i, p) in combined.iter().enumerate() {
match packet_map.entry(hash_packet(p)) {
Entry::Occupied(mut oe) => {
oe.get_mut().push(i)
}
Entry::Vacant(ve) => {
ve.insert(vec![ i ]);
}
}
}
enum Action {
Drop,
Overwrite(usize),
Insert,
}
use Action::*;
for p in packets {
Cert::valid_packet(&p)?;
let hash = hash_packet(&p);
let mut action = Insert;
if let Some(combined_i) = packet_map.get(&hash) {
for i in combined_i {
let i: usize = *i;
let (same, identical) = match (&p, &combined[i]) {
(Packet::PublicKey(a), Packet::PublicKey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
a == b),
(Packet::SecretKey(a), Packet::SecretKey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
a == b),
(Packet::PublicKey(a), Packet::SecretKey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
false),
(Packet::SecretKey(a), Packet::PublicKey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
false),
(Packet::PublicSubkey(a), Packet::PublicSubkey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
a == b),
(Packet::SecretSubkey(a), Packet::SecretSubkey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
a == b),
(Packet::PublicSubkey(a), Packet::SecretSubkey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
false),
(Packet::SecretSubkey(a), Packet::PublicSubkey(b)) =>
(a.public_cmp(b) == Ordering::Equal,
false),
(Packet::Signature(a), Packet::Signature(b)) =>
(a.normalized_eq(b),
a == b),
(a, b) => {
let identical = a == b;
(identical, identical)
}
};
if same {
if identical {
action = Drop;
} else {
action = Overwrite(i);
}
break;
}
}
}
match action {
Drop => (),
Overwrite(i) => {
let existing =
std::mem::replace(&mut combined[i],
Packet::Marker(Default::default()));
let merged = merge(Some(existing), p)?;
let merged_hash = hash_packet(&merged);
if hash != merged_hash {
return Err(Error::InvalidOperation(
format!("merge function changed packet hash \
(expected: {}, got: {})",
hash, merged_hash)).into());
}
combined[i] = merged;
changed = true;
},
Insert => {
let merged = merge(None, p)?;
let merged_hash = hash_packet(&merged);
if hash != merged_hash {
return Err(Error::InvalidOperation(
format!("merge function changed packet hash \
(expected: {}, got: {})",
hash, merged_hash)).into());
}
combined.push(merged);
changed = true;
let i = combined.len() - 1;
match packet_map.entry(hash) {
Entry::Occupied(mut oe) => {
oe.get_mut().push(i)
}
Entry::Vacant(ve) => {
ve.insert(vec![ i ]);
}
}
}
}
}
Cert::try_from(combined).map(|cert| (cert, changed))
}
pub fn insert_packets<I>(self, packets: I)
-> Result<Self>
where I: IntoIterator,
I::Item: Into<Packet>,
{
self.insert_packets2(packets).map(|(cert, _)| cert)
}
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 {
self.primary.key_mut().steal_secret();
self.subkeys.iter_mut().for_each(|sk| {
sk.key_mut().steal_secret();
});
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 keep = keep.iter();
self.userids.retain(|_| *keep.next().unwrap());
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 keep = keep.iter();
self.user_attributes.retain(|_| *keep.next().unwrap());
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 keep = keep.iter();
self.subkeys.retain(|_| *keep.next().unwrap());
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(crate::now);
self.primary_key().with_policy(policy, time)?;
Ok(ValidCert {
cert: self,
policy,
time,
})
}
}
use crate::serialize::TSK;
impl<'a> TSK<'a> {
pub fn into_packets(self) -> impl Iterator<Item=Packet> + 'a {
fn rewrite<'a>(
filter: &Box<dyn Fn(&key::UnspecifiedSecret) -> bool + 'a>,
emit_secret_key_stubs: bool,
mut p: impl Iterator<Item=Packet> + Send + Sync)
-> impl Iterator<Item=Packet> + Send + Sync
{
let k: Packet = match p.next().unwrap() {
Packet::PublicKey(mut k) => {
if ! k.role_as_unspecified().parts_as_secret()
.map(|k| (filter)(k))
.unwrap_or(false)
{
k = k.take_secret().0;
}
if ! k.has_secret() && emit_secret_key_stubs {
k = TSK::add_stub(k).into();
}
if k.has_secret() {
Packet::SecretKey(k.parts_into_secret().unwrap())
} else {
Packet::PublicKey(k)
}
}
Packet::PublicSubkey(mut k) => {
if ! k.role_as_unspecified().parts_as_secret()
.map(|k| (filter)(k))
.unwrap_or(false)
{
k = k.take_secret().0;
}
if ! k.has_secret() && emit_secret_key_stubs {
k = TSK::add_stub(k).into();
}
if k.has_secret() {
Packet::SecretSubkey(k.parts_into_secret().unwrap())
} else {
Packet::PublicSubkey(k)
}
}
_ => unreachable!(),
};
std::iter::once(k).chain(p)
}
let (cert, filter, emit_secret_key_stubs) = self.decompose();
let cert = cert.into_owned();
rewrite(&filter, emit_secret_key_stubs, cert.primary.into_packets())
.chain(cert.userids.into_iter().flat_map(|b| b.into_packets()))
.chain(cert.user_attributes.into_iter().flat_map(|b| b.into_packets()))
.chain(cert.subkeys.into_iter().flat_map(
move |b| rewrite(&filter, emit_secret_key_stubs, b.into_packets())))
.chain(cert.unknowns.into_iter().flat_map(|b| b.into_packets()))
.chain(cert.bad.into_iter().map(|s| s.into()))
}
}
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> {
Cert::from_packets(std::iter::once(p))
}
}
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 {
#[allow(deprecated)]
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 {
#[allow(deprecated)]
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)
}
pub fn revocation_keys<P>(&self, policy: P)
-> Box<dyn Iterator<Item = &'a RevocationKey> + 'a>
where
P: Into<Option<&'a dyn Policy>>,
{
self.cert.revocation_keys(
policy.into().unwrap_or_else(|| self.policy()))
}
}
macro_rules! impl_pref {
($subpacket:ident, $rt:ty) => {
#[allow(deprecated)]
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!(policy_uri, &'a [u8]);
impl_pref!(features, Features);
}
#[cfg(test)]
mod test {
use std::convert::TryInto;
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_signatures2().count(), 1);
assert_eq!(cert.userids[0].self_signatures2().next().unwrap()
.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_signatures2().count(), 1);
assert_eq!(cert.userids[0].self_signatures2().next().unwrap()
.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_signatures2().next().unwrap()
.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_signatures2().count(), 1);
assert_eq!(cert.userids[0].self_signatures2().next().unwrap()
.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_signatures2().count() == 1);
assert!(cert_base.userids[0].certifications.is_empty());
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_signatures2().count() == 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_signatures2().count() == 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_signatures2().count() == 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_signatures2().count() == 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 cert = Cert::from_bytes(crate::tests::key("john-v3.pgp"));
assert_match!(Error::UnsupportedCert2(..)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = Cert::from_bytes(crate::tests::key("john-v3-secret.pgp"));
assert_match!(Error::UnsupportedCert2(..)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = Cert::from_bytes(crate::tests::key("lutz.gpg"));
assert_match!(Error::UnsupportedCert2(..)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = Cert::from_bytes(crate::tests::key("dkg.gpg"));
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_add_sig() {
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::from_reader(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_packets2().count();
let cert = cert.insert_packets(rev).unwrap();
let packets_post_merge = cert.clone().into_packets2().count();
assert_eq!(packets_post_merge, packets_pre_merge + 1);
}
#[test]
fn insert_packets_update_sig() -> Result<()> {
use std::time::Duration;
use crate::packet::signature::subpacket::Subpacket;
use crate::packet::signature::subpacket::SubpacketValue;
let (cert, _) = CertBuilder::general_purpose(None, Some("Test"))
.generate()?;
let packets = cert.clone().into_packets2().count();
let sig = cert.primary_key().self_signatures().next()
.expect("binding signature");
let a = Subpacket::new(
SubpacketValue::SignatureExpirationTime(
Duration::new(1, 0).try_into()?),
false)?;
let b = Subpacket::new(
SubpacketValue::SignatureExpirationTime(
Duration::new(2, 0).try_into()?),
false)?;
let mut sig_a = sig.clone();
sig_a.unhashed_area_mut().add(a)?;
let mut sig_b = sig.clone();
sig_b.unhashed_area_mut().add(b)?;
let cert2 = cert.clone().insert_packets(sig_a.clone())?;
let mut sigs = cert2.primary_key().self_signatures();
assert_eq!(sigs.next(), Some(&sig_a));
assert!(sigs.next().is_none());
assert_eq!(cert2.clone().into_packets2().count(), packets);
let cert2 = cert.clone().insert_packets(sig_b.clone())?;
let mut sigs = cert2.primary_key().self_signatures();
assert_eq!(sigs.next(), Some(&sig_b));
assert!(sigs.next().is_none());
assert_eq!(cert2.clone().into_packets2().count(), packets);
let cert2 = cert.clone().insert_packets(
vec![ sig_a.clone(), sig_b.clone() ])?;
let mut sigs = cert2.primary_key().self_signatures();
assert_eq!(sigs.next(), Some(&sig_b));
assert!(sigs.next().is_none());
assert_eq!(cert2.clone().into_packets2().count(), packets);
let cert2 = cert.clone().insert_packets(
vec![ sig_b.clone(), sig_a.clone() ])?;
let mut sigs = cert2.primary_key().self_signatures();
assert_eq!(sigs.next(), Some(&sig_a));
assert!(sigs.next().is_none());
assert_eq!(cert2.clone().into_packets2().count(), packets);
Ok(())
}
#[test]
fn insert_packets_add_userid() -> Result<()> {
let (cert, _) = CertBuilder::general_purpose(None, Some("a"))
.generate()?;
let packets = cert.clone().into_packets2().count();
let uid_a = UserID::from("a");
let uid_b = UserID::from("b");
let cert2 = cert.clone().insert_packets(uid_a.clone())?;
let mut uids = cert2.userids();
assert_eq!(uids.next().unwrap().userid(), &uid_a);
assert!(uids.next().is_none());
assert_eq!(cert2.clone().into_packets2().count(), packets);
let cert2 = cert.clone().insert_packets(uid_b.clone())?;
let mut uids: Vec<UserID>
= cert2.userids().map(|ua| ua.userid().clone()).collect();
uids.sort();
let mut uids = uids.iter();
assert_eq!(uids.next().unwrap(), &uid_a);
assert_eq!(uids.next().unwrap(), &uid_b);
assert!(uids.next().is_none());
assert_eq!(cert2.clone().into_packets2().count(), packets + 1);
Ok(())
}
#[test]
fn insert_packets_update_key() -> Result<()> {
use crate::crypto::Password;
let (cert, _) = CertBuilder::new().generate()?;
let packets = cert.clone().into_packets2().count();
assert_eq!(cert.keys().count(), 1);
let key = cert.keys().secret().next().unwrap().key();
assert!(key.has_secret());
let key_a = key.clone().encrypt_secret(&Password::from("a"))?
.role_into_primary();
let key_b = key.clone().encrypt_secret(&Password::from("b"))?
.role_into_primary();
let cert2 = cert.clone().insert_packets(key_a.clone())?;
assert_eq!(cert2.primary_key().key().parts_as_secret().unwrap(),
&key_a);
assert_eq!(cert2.clone().into_packets2().count(), packets);
let cert2 = cert.clone().insert_packets(key_b.clone())?;
assert_eq!(cert2.primary_key().key().parts_as_secret().unwrap(),
&key_b);
assert_eq!(cert2.clone().into_packets2().count(), packets);
let cert2 = cert.clone().insert_packets(
vec![ key_a.clone(), key_b.clone() ])?;
assert_eq!(cert2.primary_key().key().parts_as_secret().unwrap(),
&key_b);
assert_eq!(cert2.clone().into_packets2().count(), packets);
let cert2 = cert.clone().insert_packets(
vec![ key_b.clone(), key_a.clone() ])?;
assert_eq!(cert2.primary_key().key().parts_as_secret().unwrap(),
&key_a);
assert_eq!(cert2.clone().into_packets2().count(), packets);
Ok(())
}
#[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, Gen};
let mut gen = Gen::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().next().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().next().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, crate::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().next().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().next().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 = crate::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().next().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 = crate::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_signatures2()
.collect::<Vec<_>>();
assert_eq!(cert.primary_key().with_policy(p, *t).unwrap()
.direct_key_signature().ok(),
direct_signatures.get(*offset).cloned());
assert_eq!(cert.primary_key().with_policy(p, *t + a_sec).unwrap()
.direct_key_signature().ok(),
direct_signatures.get(*offset).cloned());
assert_eq!(cert.primary_key().with_policy(p, None).unwrap()
.direct_key_signature().ok(),
direct_signatures.get(0).cloned());
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::P256,
CipherSuite::P384,
CipherSuite::P521,
CipherSuite::RSA2k ]
{
if cs.is_supported().is_err() {
eprintln!("Skipping {:?} because it is not supported.", cs);
continue;
}
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().next().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 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().next().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_encrypt_secrets() -> Result<()> {
let p: crate::crypto::Password = "streng geheim".into();
let (mut cert, _) = CertBuilder::new()
.add_transport_encryption_subkey()
.set_password(Some(p.clone()))
.generate()?;
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 0);
for (i, ka) in cert.clone().keys().secret().enumerate() {
let key = ka.key().clone().decrypt_secret(&p)?;
cert = if i == 0 {
cert.insert_packets(key.role_into_primary())?
} else {
cert.insert_packets(key.role_into_subordinate())?
};
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), i + 1);
}
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 2);
for (i, ka) in cert.clone().keys().secret().enumerate() {
let key = ka.key().clone().encrypt_secret(&p)?;
cert = if i == 0 {
cert.insert_packets(key.role_into_primary())?
} else {
cert.insert_packets(key.role_into_subordinate())?
};
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 2 - 1 - i);
}
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 0);
Ok(())
}
#[test]
fn test_into_packets2() -> 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_packets2() {
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().next().unwrap().unknown().tag(),
packet::Tag::PublicSubkey);
assert_eq!(cert.unknowns().next().unwrap().self_signatures().collect::<Vec<_>>(),
vec![&fake_binding]);
Ok(())
}
#[test]
fn canonicalize_with_v3_sig() -> Result<()> {
if ! crate::types::PublicKeyAlgorithm::DSA.is_supported() {
eprintln!("Skipping because DSA is not supported");
return Ok(());
}
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 (cert_s, _) =
CertBuilder::general_purpose(None, Some("uid")).generate()?;
let cert_p = cert_s.clone().strip_secret_key_material();
let cert = cert_p.clone().merge_public_and_secret(cert_s.clone())?;
assert!(cert.keys().all(|ka| ka.has_secret()));
let cert = cert_s.clone().merge_public_and_secret(cert_p.clone())?;
assert!(cert.keys().all(|ka| ka.has_secret()));
Ok(())
}
#[test]
fn merge_prefers_merged_in_secrets() -> Result<()> {
let pw: crate::crypto::Password = "foo".into();
let (cert_encrypted_secrets, _) =
CertBuilder::general_purpose(None, Some("uid"))
.set_password(Some(pw.clone()))
.generate()?;
let mut cert_plain_secrets = cert_encrypted_secrets.clone();
for ka in cert_encrypted_secrets.keys().secret() {
assert!(! ka.has_unencrypted_secret());
let key = ka.key().clone().decrypt_secret(&pw)?;
assert!(key.has_unencrypted_secret());
let key: Packet = if ka.primary() {
key.role_into_primary().into()
} else {
key.role_into_subordinate().into()
};
cert_plain_secrets =
cert_plain_secrets.insert_packets(vec![key])?;
}
assert!(
cert_plain_secrets.keys().all(|ka| ka.has_unencrypted_secret()));
let cert = cert_encrypted_secrets.clone().merge_public_and_secret(
cert_plain_secrets.clone())?;
assert!(cert.keys().all(|ka| ka.has_unencrypted_secret()));
let cert = cert_plain_secrets.clone().merge_public_and_secret(
cert_encrypted_secrets.clone())?;
assert!(cert.keys().all(|ka| ka.has_secret()
&& ! ka.has_unencrypted_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 policy_uri_some() -> Result<()> {
use crate::packet::prelude::SignatureBuilder;
use crate::policy::StandardPolicy;
let p = &StandardPolicy::new();
let (alice, _) = CertBuilder::new().add_userid("Alice").generate()?;
let sig = SignatureBuilder::from(
alice
.with_policy(p, None)?
.direct_key_signature().expect("Direct key signature")
.clone()
)
.set_policy_uri("https://example.org/~alice/signing-policy.txt")?;
assert_eq!(sig.policy_uri(), Some("https://example.org/~alice/signing-policy.txt".as_bytes()));
Ok(())
}
#[test]
fn policy_uri_none() -> Result<()> {
use crate::packet::prelude::SignatureBuilder;
use crate::policy::StandardPolicy;
let p = &StandardPolicy::new();
let (alice, _) = CertBuilder::new().add_userid("Alice").generate()?;
let sig = SignatureBuilder::from(
alice
.with_policy(p, None)?
.direct_key_signature().expect("Direct key signature")
.clone()
);
assert_eq!(sig.policy_uri(), None);
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().next() {
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 ][..]));
#[allow(deprecated)] {
assert_eq!(userid.preferred_aead_algorithms(), None);
}
assert_eq!(userid.features(),
Some(Features::new(&[]).set_seipdv1()));
} else {
panic!("two user ids");
}
if let Some(userid) = cert.userids().next() {
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 ][..]));
#[allow(deprecated)] {
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_seipdv1()));
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());
#[allow(deprecated)] {
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 ][..]));
#[allow(deprecated)] {
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_seipdv1()));
} 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().next().unwrap().self_signatures().count(), 1);
assert_eq!(cert.subkeys().next().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().next().unwrap().self_signatures().count(), 1);
assert_eq!(cert.subkeys().next().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().next().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().next().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().next().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().next().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().next().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);
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(())
}
#[test]
fn primary_key_is_subkey() -> Result<()> {
let p = &crate::policy::StandardPolicy::new();
let cert =
Cert::from_bytes(crate::tests::key("primary-key-is-also-subkey.pgp"))?;
assert_eq!(cert.keys().count(), 3);
assert!(cert.keys().subkeys().any(|k| {
k.fingerprint() == cert.primary_key().fingerprint()
}));
assert_eq!(cert.keys().count(), 3);
let vc = cert.with_policy(p, None)?;
assert!(vc.keys().subkeys().any(|k| {
k.fingerprint() == vc.primary_key().fingerprint()
}));
Ok(())
}
#[test]
fn attested_key_signatures() -> Result<()> {
use crate::{
packet::signature::SignatureBuilder,
types::*,
};
let p = &crate::policy::StandardPolicy::new();
let (alice, _) = CertBuilder::new()
.add_userid("alice@foo.com")
.generate()?;
let mut alice_signer =
alice.primary_key().key().clone().parts_into_secret()?
.into_keypair()?;
let (bob, _) = CertBuilder::new()
.add_userid("bob@bar.com")
.generate()?;
let mut bob_signer =
bob.primary_key().key().clone().parts_into_secret()?
.into_keypair()?;
let bob_pristine = bob.clone();
let alice_certifies_bob
= bob.userids().next().unwrap().userid().bind(
&mut alice_signer, &bob,
SignatureBuilder::new(SignatureType::GenericCertification))?;
let bob = bob.insert_packets(vec![
alice_certifies_bob.clone(),
])?;
assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap()
.certifications().count(), 1);
assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap()
.attested_certifications().count(), 0);
#[allow(deprecated)]
let attestations =
bob.userids().next().unwrap().attest_certifications(
p,
&mut bob_signer,
vec![&alice_certifies_bob])?;
assert_eq!(attestations.len(), 1);
let attestation = attestations[0].clone();
let bob = bob.insert_packets(vec![
attestation.clone(),
])?;
assert_eq!(bob.bad_signatures().count(), 0);
assert_eq!(bob.userids().next().unwrap().certifications().next(),
Some(&alice_certifies_bob));
assert_eq!(bob.userids().next().unwrap().bundle().attestations().next().unwrap(),
&attestation);
assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap()
.certifications().count(), 1);
assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap()
.attested_certifications().count(), 1);
let bob_ = bob.clone().merge_public(bob_pristine.clone())?;
assert_eq!(bob_.bad_signatures().count(), 0);
assert_eq!(bob_.userids().next().unwrap().certifications().next(),
Some(&alice_certifies_bob));
assert_eq!(bob_.userids().next().unwrap().bundle().attestations().next().unwrap(),
&attestation);
assert_eq!(bob_.with_policy(p, None)?.userids().next().unwrap()
.attested_certifications().count(), 1);
let bob_ = bob_pristine.clone().merge_public(bob.clone())?;
assert_eq!(bob_.bad_signatures().count(), 0);
assert_eq!(bob_.userids().next().unwrap().certifications().next(),
Some(&alice_certifies_bob));
assert_eq!(bob_.userids().next().unwrap().bundle().attestations().next().unwrap(),
&attestation);
assert_eq!(bob_.with_policy(p, None)?.userids().next().unwrap()
.attested_certifications().count(), 1);
#[allow(deprecated)]
let attestations =
bob.userids().next().unwrap().attest_certifications(
p,
&mut bob_signer,
&[])?;
assert_eq!(attestations.len(), 1);
let attestation = attestations[0].clone();
let bob = bob.insert_packets(vec![
attestation.clone(),
])?;
assert_eq!(bob.bad_signatures().count(), 0);
assert_eq!(bob.userids().next().unwrap().certifications().next(),
Some(&alice_certifies_bob));
assert_eq!(bob.userids().next().unwrap().bundle().attestations().next().unwrap(),
&attestation);
assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap()
.certifications().count(), 1);
assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap()
.attested_certifications().count(), 0);
Ok(())
}
#[test]
fn attested_key_signatures_dkgpg() -> Result<()> {
const DUMP: bool = false;
use crate::{
crypto::hash::Digest,
};
let p = &crate::policy::StandardPolicy::new();
let test = Cert::from_bytes(crate::tests::key("1pa3pc-dkgpg.pgp"))?;
assert_eq!(test.bad_signatures().count(), 0);
assert_eq!(test.userids().next().unwrap().certifications().count(),
1);
assert_eq!(test.userids().next().unwrap().bundle().attestations().count(),
1);
let attestation =
test.userids().next().unwrap().bundle().attestations().next().unwrap();
if DUMP {
for (i, d) in attestation.attested_certifications()?.enumerate() {
crate::fmt::hex::Dumper::new(std::io::stderr(), "")
.write(d, format!("expected digest {}", i))?;
}
}
let digests: std::collections::HashSet<_> =
attestation.attested_certifications()?.collect();
for (i, certification) in
test.userids().next().unwrap().certifications().enumerate()
{
let mut h = attestation.hash_algo().context()?;
certification.hash_for_confirmation(&mut h);
let digest = h.into_digest()?;
if DUMP {
crate::fmt::hex::Dumper::new(std::io::stderr(), "")
.write(&digest, format!("computed digest {}", i))?;
}
assert!(digests.contains(&digest[..]));
}
assert_eq!(test.with_policy(p, None)?.userids().next().unwrap()
.certifications().count(), 1);
assert_eq!(test.with_policy(p, None)?.userids().next().unwrap()
.attested_certifications().count(), 1);
Ok(())
}
#[test]
fn attested_key_signature_out_of_order() -> Result<()> {
let p = &crate::policy::StandardPolicy::new();
let (alice, _) = CertBuilder::general_purpose(
None, Some("alice@example.org")).generate().unwrap();
assert!(alice.keys().subkeys().count() > 0);
let mut alice_signer =
alice.primary_key().key().clone().parts_into_secret()?
.into_keypair()?;
let mut attestation_signatures = Vec::new();
for uid in alice.userids() {
#[allow(deprecated)]
attestation_signatures.append(&mut uid.attest_certifications(
p,
&mut alice_signer,
uid.certifications(),
)?);
}
let alice2 = alice.insert_packets(attestation_signatures)?;
assert_eq!(alice2.bad_signatures().count(), 0);
assert!(alice2.keys().all(|ka| ka.attestations().count() == 0));
let ua = alice2.userids().next().unwrap();
assert_eq!(ua.attestations().count(), 1);
Ok(())
}
#[test]
fn marker_packets() -> Result<()> {
let cert = Cert::from_bytes(crate::tests::key("neal.pgp"))?;
let mut buf = Vec::new();
Packet::Marker(Default::default()).serialize(&mut buf)?;
cert.serialize(&mut buf)?;
let cert_ = Cert::from_bytes(&buf)?;
assert_eq!(cert, cert_);
Ok(())
}
#[test]
fn issue_486() -> Result<()> {
use crate::{
crypto::mpi,
types::RevocationStatus::*,
packet::signature::Signature4,
policy::StandardPolicy,
};
let p = &StandardPolicy::new();
let (cert, revocation) = CertBuilder::new().generate()?;
let c = cert.clone().insert_packets(Some(revocation.clone()))?;
if let Revoked(_) = c.revocation_status(p, None) {
} else {
panic!("Should be revoked, but is not: {:?}",
c.revocation_status(p, None));
}
let c = cert.clone().insert_packets(Some(
Signature4::new(
revocation.typ(),
revocation.pk_algo(),
revocation.hash_algo(),
revocation.hashed_area().clone(),
revocation.unhashed_area().clone(),
*revocation.digest_prefix(),
mpi::Signature::RSA {
s: mpi::MPI::from(vec![1, 2, 3])
})))?;
if let NotAsFarAsWeKnow = c.revocation_status(p, None) {
assert_eq!(c.bad_signatures().count(), 1);
} else {
panic!("Should not be revoked, but is: {:?}",
c.revocation_status(p, None));
}
let c = cert.clone().insert_packets(Some(
Signature4::new(
revocation.typ(),
revocation.pk_algo(),
revocation.hash_algo(),
revocation.hashed_area().clone(),
revocation.unhashed_area().clone(),
[0, 1],
mpi::Signature::RSA {
s: mpi::MPI::from(vec![1, 2, 3])
})))?;
if let NotAsFarAsWeKnow = c.revocation_status(p, None) {
assert_eq!(c.bad_signatures().count(), 1);
} else {
panic!("Should not be revoked, but is: {:?}",
c.revocation_status(p, None));
}
Ok(())
}
#[test]
fn v3_binding_signature() -> Result<()> {
if ! crate::types::PublicKeyAlgorithm::DSA.is_supported() {
eprintln!("Skipping because DSA is not supported");
return Ok(());
}
let c = Cert::from_bytes(
crate::tests::key("pgp5-dsa-elg-v3-subkey-binding.pgp"))?;
assert_eq!(c.bad_signatures().count(), 0);
let np = crate::policy::NullPolicy::new();
let vcert = c.with_policy(&np, None)?;
assert_eq!(vcert.keys().subkeys().count(), 1);
assert_eq!(vcert.keys().for_signing().count(), 1);
assert_eq!(vcert.keys().for_transport_encryption().count(), 1);
assert_eq!(c.keys().subkeys().with_policy(&np, None).count(), 1);
assert_eq!(c.keys().with_policy(&np, None).for_signing().count(), 1);
assert_eq!(c.keys().with_policy(&np, None)
.for_transport_encryption().count(), 1);
Ok(())
}
#[test]
fn v3_revocation_signature() -> Result<()> {
if ! crate::types::PublicKeyAlgorithm::ECDSA.is_supported()
|| ! crate::types::Curve::NistP521.is_supported()
{
eprintln!("Skipping because ECDSA/NistP521 is not supported");
return Ok(());
}
let c = Cert::from_bytes(
crate::tests::key("v4-revoked-by-v3.pgp"))?;
assert_eq!(c.bad_signatures().count(), 0);
let sp = crate::policy::StandardPolicy::new();
assert!(matches!(c.revocation_status(&sp, None),
RevocationStatus::Revoked(_)));
Ok(())
}
}