use std::io;
use std::cmp;
use std::cmp::Ordering;
use std::path::Path;
use std::slice;
use std::mem;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::time;
use crate::{
crypto::{hash::Hash, Signer},
Error,
Result,
RevocationStatus,
SignatureType,
HashAlgorithm,
packet,
packet::Signature,
packet::signature,
packet::Key,
packet::key,
packet::UserID,
packet::UserAttribute,
packet::Unknown,
Packet,
PacketPile,
KeyID,
Fingerprint,
};
use crate::parse::{Parse, PacketParserResult, PacketParser};
use crate::types::{
ReasonForRevocation,
RevocationType,
};
mod builder;
mod bindings;
mod keyiter;
mod key_amalgamation;
mod parser;
mod revoke;
pub use self::builder::{CertBuilder, CipherSuite};
pub use keyiter::KeyIter;
pub use key_amalgamation::KeyAmalgamation;
pub use parser::{
KeyringValidity,
KeyringValidator,
CertParser,
CertValidity,
CertValidator,
};
pub use revoke::{
SubkeyRevocationBuilder,
CertRevocationBuilder,
UserAttributeRevocationBuilder,
UserIDRevocationBuilder,
};
const TRACE : bool = false;
fn canonical_signature_order(a: Option<time::SystemTime>, b: Option<time::SystemTime>)
-> Ordering {
a.cmp(&b).reverse()
}
fn sig_cmp(a: &Signature, b: &Signature) -> Ordering {
match canonical_signature_order(a.signature_creation_time(),
b.signature_creation_time()) {
Ordering::Equal => a.mpis().cmp(b.mpis()),
r => r
}
}
pub type KeyBinding<KeyPart, KeyRole> = ComponentBinding<Key<KeyPart, KeyRole>>;
impl<K: key::KeyParts, R: key::KeyRole> KeyBinding<K, R>
{
pub(crate) fn secret(&self)
-> Option<&crate::packet::key::SecretKeyMaterial> {
self.key().secret()
}
}
type PrimaryKeyBinding<KeyPart> = KeyBinding<KeyPart, key::PrimaryRole>;
pub type SubkeyBinding<KeyPart> = KeyBinding<KeyPart, key::SubordinateRole>;
#[allow(dead_code)]
type GenericKeyBinding
= ComponentBinding<Key<key::UnspecifiedParts, key::UnspecifiedRole>>;
pub type UserIDBinding = ComponentBinding<UserID>;
pub type UserAttributeBinding = ComponentBinding<UserAttribute>;
pub type UnknownBinding = ComponentBinding<Unknown>;
#[derive(Debug, Clone, PartialEq)]
pub struct ComponentBinding<C> {
component: C,
self_signatures: Vec<Signature>,
certifications: Vec<Signature>,
self_revocations: Vec<Signature>,
other_revocations: Vec<Signature>,
}
impl<C> ComponentBinding<C> {
pub fn component(&self) -> &C {
&self.component
}
fn component_mut(&mut self) -> &mut C {
&mut self.component
}
pub fn binding_signature<T>(&self, t: T) -> Option<&Signature>
where T: Into<Option<time::SystemTime>>
{
let t = t.into().unwrap_or_else(|| time::SystemTime::now());
let i =
if Some(t) >= self.self_signatures.get(0)
.and_then(|s| s.signature_creation_time())
{
0
} else {
match self.self_signatures.binary_search_by(
|s| canonical_signature_order(
s.signature_creation_time(), Some(t)))
{
Ok(mut i) => {
while i > 0
&& self.self_signatures[i - 1].signature_creation_time()
.cmp(&Some(t)) == Ordering::Equal
{
i -= 1;
}
i
}
Err(i) => i,
}
};
self.self_signatures[i..].iter().filter(|s| {
s.signature_alive(t, time::Duration::new(0, 0)).is_ok()
}).nth(0)
}
pub fn self_signatures(&self) -> &[Signature] {
&self.self_signatures
}
pub fn certifications(&self) -> &[Signature] {
&self.certifications
}
pub fn self_revocations(&self) -> &[Signature] {
&self.self_revocations
}
pub fn other_revocations(&self) -> &[Signature] {
&self.other_revocations
}
fn _revoked<'a, T>(&'a self, hard_revocations_are_final: bool,
selfsig: Option<&Signature>, t: T)
-> RevocationStatus<'a>
where T: Into<Option<time::SystemTime>>
{
let time_zero = || time::UNIX_EPOCH;
let t = t.into()
.unwrap_or_else(|| time::SystemTime::now());
let selfsig_creation_time
= selfsig.and_then(|s| s.signature_creation_time())
.unwrap_or_else(time_zero);
tracer!(TRACE, "ComponentBinding::_revoked", 0);
t!("hard_revocations_are_final: {}, selfsig: {:?}, t: {:?}",
hard_revocations_are_final,
selfsig_creation_time,
t);
if let Some(selfsig) = selfsig {
assert!(
selfsig.signature_alive(t, time::Duration::new(0, 0)).is_ok());
}
macro_rules! check {
($revs:expr) => ({
let revs = $revs.iter().filter_map(|rev| {
if hard_revocations_are_final
&& rev.reason_for_revocation()
.map(|(r, _)| {
r.revocation_type() == RevocationType::Hard
})
.unwrap_or(true)
{
t!(" got a hard revocation: {:?}, {:?}",
rev.signature_creation_time()
.unwrap_or_else(time_zero),
rev.reason_for_revocation()
.map(|r| (r.0, String::from_utf8_lossy(r.1))));
Some(rev)
} else if selfsig_creation_time
> rev.signature_creation_time()
.unwrap_or_else(time_zero)
{
t!(" ignoring out of date revocation ({:?})",
rev.signature_creation_time()
.unwrap_or_else(time_zero));
None
} else if
! rev.signature_alive(t, time::Duration::new(0, 0))
.is_ok()
{
t!(" ignoring revocation that is not alive ({:?} - {:?})",
rev.signature_creation_time()
.unwrap_or_else(time_zero),
rev.signature_expiration_time()
.unwrap_or_else(|| time::Duration::new(0, 0)));
None
} else {
t!(" got a revocation: {:?} ({:?})",
rev.signature_creation_time()
.unwrap_or_else(time_zero),
rev.reason_for_revocation()
.map(|r| (r.0, String::from_utf8_lossy(r.1))));
Some(rev)
}
}).collect::<Vec<&Signature>>();
if revs.len() == 0 {
None
} else {
Some(revs)
}
})
}
if let Some(revs) = check!(&self.self_revocations) {
RevocationStatus::Revoked(revs)
} else if let Some(revs) = check!(&self.other_revocations) {
RevocationStatus::CouldBe(revs)
} else {
RevocationStatus::NotAsFarAsWeKnow
}
}
fn into_packets<'a>(self) -> impl Iterator<Item=Packet>
where Packet: From<C>
{
let p : Packet = self.component.into();
std::iter::once(p)
.chain(self.self_signatures.into_iter().map(|s| s.into()))
.chain(self.certifications.into_iter().map(|s| s.into()))
.chain(self.self_revocations.into_iter().map(|s| s.into()))
.chain(self.other_revocations.into_iter().map(|s| s.into()))
}
fn sort_and_dedup(&mut self)
{
self.self_signatures.sort_by(sig_cmp);
self.self_signatures.dedup();
self.certifications.sort_by(sig_cmp);
self.certifications.dedup();
self.self_revocations.sort_by(sig_cmp);
self.self_revocations.dedup();
self.other_revocations.sort_by(sig_cmp);
self.other_revocations.dedup();
}
}
impl<P: key::KeyParts, R: key::KeyRole> ComponentBinding<Key<P, R>> {
pub fn key(&self) -> &Key<P, R> {
self.component()
}
fn key_mut(&mut self) -> &mut Key<P, R> {
self.component_mut()
}
}
impl<P: key::KeyParts> ComponentBinding<Key<P, key::SubordinateRole>> {
pub fn revoked<T>(&self, t: T)
-> RevocationStatus
where T: Into<Option<time::SystemTime>>
{
let t = t.into();
self._revoked(true, self.binding_signature(t), t)
}
}
impl ComponentBinding<UserID> {
pub fn userid(&self) -> &UserID {
self.component()
}
pub fn revoked<T>(&self, t: T)
-> RevocationStatus
where T: Into<Option<time::SystemTime>>
{
let t = t.into();
self._revoked(false, self.binding_signature(t), t)
}
}
impl ComponentBinding<UserAttribute> {
pub fn user_attribute(&self) -> &UserAttribute {
self.component()
}
pub fn revoked<T>(&self, t: T)
-> RevocationStatus
where T: Into<Option<time::SystemTime>>
{
let t = t.into();
self._revoked(false, self.binding_signature(t), t)
}
}
impl ComponentBinding<Unknown> {
pub fn unknown(&self) -> &Unknown {
self.component()
}
}
impl fmt::Display for Cert {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.primary().fingerprint())
}
}
pub struct ComponentBindingIter<'a, C> {
iter: Option<slice::Iter<'a, ComponentBinding<C>>>,
}
pub type KeyBindingIter<'a, P, R> = ComponentBindingIter<'a, Key<P, R>>;
pub type UserIDBindingIter<'a> = ComponentBindingIter<'a, UserID>;
pub type UserAttributeBindingIter<'a> = ComponentBindingIter<'a, UserAttribute>;
pub type UnknownBindingIter<'a> = ComponentBindingIter<'a, Unknown>;
impl<'a, C> Iterator for ComponentBindingIter<'a, C>
{
type Item = &'a ComponentBinding<C>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter {
Some(ref mut iter) => iter.next(),
None => None,
}
}
}
impl<'a, C> ExactSizeIterator for ComponentBindingIter<'a, C>
{
fn len(&self) -> usize {
match self.iter {
Some(ref iter) => iter.len(),
None => 0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
struct ComponentBindings<C>
where ComponentBinding<C>: cmp::PartialEq
{
bindings: Vec<ComponentBinding<C>>,
}
impl<C> Deref for ComponentBindings<C>
where ComponentBinding<C>: cmp::PartialEq
{
type Target = Vec<ComponentBinding<C>>;
fn deref(&self) -> &Self::Target {
&self.bindings
}
}
impl<C> DerefMut for ComponentBindings<C>
where ComponentBinding<C>: cmp::PartialEq
{
fn deref_mut(&mut self) -> &mut Vec<ComponentBinding<C>> {
&mut self.bindings
}
}
impl<C> Into<Vec<ComponentBinding<C>>> for ComponentBindings<C>
where ComponentBinding<C>: cmp::PartialEq
{
fn into(self) -> Vec<ComponentBinding<C>> {
self.bindings
}
}
impl<C> IntoIterator for ComponentBindings<C>
where ComponentBinding<C>: cmp::PartialEq
{
type Item = ComponentBinding<C>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.bindings.into_iter()
}
}
impl<C> ComponentBindings<C>
where ComponentBinding<C>: cmp::PartialEq
{
fn new() -> Self {
Self { bindings: vec![] }
}
}
impl<C> ComponentBindings<C>
where ComponentBinding<C>: cmp::PartialEq
{
fn sort_and_dedup<F, F2>(&mut self, cmp: F, merge: F2)
where F: Fn(&C, &C) -> Ordering,
F2: Fn(&mut C, &mut C)
{
self.bindings.sort_unstable_by(
|a, b| cmp(&a.component, &b.component));
self.bindings.dedup_by(|a, b| {
if cmp(&a.component, &b.component) == Ordering::Equal {
merge(&mut a.component, &mut b.component);
b.self_signatures.append(&mut a.self_signatures);
b.certifications.append(&mut a.certifications);
b.self_revocations.append(&mut a.self_revocations);
b.other_revocations.append(&mut a.self_revocations);
true
} else {
false
}
});
for b in self.bindings.iter_mut() {
b.sort_and_dedup();
}
}
}
type KeyBindings<KeyPart, KeyRole> = ComponentBindings<Key<KeyPart, KeyRole>>;
type SubkeyBindings<KeyPart> = KeyBindings<KeyPart, key::SubordinateRole>;
#[allow(dead_code)]
type GenericKeyBindings
= ComponentBindings<Key<key::UnspecifiedParts, key::UnspecifiedRole>>;
type UserIDBindings = ComponentBindings<UserID>;
type UserAttributeBindings = ComponentBindings<UserAttribute>;
type UnknownBindings = ComponentBindings<Unknown>;
#[derive(Debug, Clone, PartialEq)]
pub struct Cert {
primary: PrimaryKeyBinding<key::PublicParts>,
userids: UserIDBindings,
user_attributes: UserAttributeBindings,
subkeys: SubkeyBindings<key::PublicParts>,
unknowns: UnknownBindings,
bad: Vec<packet::Signature>,
}
impl std::str::FromStr for Cert {
type Err = failure::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Self::from_bytes(s.as_bytes())
}
}
impl<'a> Parse<'a, Cert> for Cert {
fn from_reader<R: io::Read>(reader: R) -> Result<Self> {
Cert::from_packet_parser(PacketParser::from_reader(reader)?)
}
fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
Cert::from_packet_parser(PacketParser::from_file(path)?)
}
fn from_bytes<D: AsRef<[u8]> + ?Sized>(data: &'a D) -> Result<Self> {
Cert::from_packet_parser(PacketParser::from_bytes(data)?)
}
}
impl Cert {
pub fn primary(&self) -> &key::PublicKey {
&self.primary.key()
}
pub fn primary_userid<T>(&self, t: T) -> Option<&UserIDBinding>
where T: Into<Option<time::SystemTime>>
{
self.primary_userid_full(t).map(|r| r.0)
}
pub fn primary_userid_full<T>(&self, t: T)
-> Option<(&UserIDBinding, &Signature, RevocationStatus)>
where T: Into<Option<time::SystemTime>>
{
let t = t.into()
.unwrap_or_else(|| time::SystemTime::now());
self.userids()
.filter_map(|b| {
let selfsig = b.binding_signature(t)?;
if !selfsig.signature_alive(t, time::Duration::new(0, 0)).is_ok() {
return None;
}
let revoked = b.revoked(t);
let primary = selfsig.primary_userid().unwrap_or(false);
let signature_creation_time = selfsig.signature_creation_time()?;
Some(((b, selfsig, revoked), primary, signature_creation_time))
})
.max_by(|(a, a_primary, a_signature_creation_time),
(b, b_primary, b_signature_creation_time)| {
match (destructures_to!(RevocationStatus::Revoked(_) = &a.2),
destructures_to!(RevocationStatus::Revoked(_) = &b.2)) {
(true, false) => return Ordering::Less,
(false, true) => return Ordering::Greater,
_ => (),
}
match (a_primary, b_primary) {
(true, false) => return Ordering::Greater,
(false, true) => return Ordering::Less,
_ => (),
}
match a_signature_creation_time.cmp(&b_signature_creation_time) {
Ordering::Less => return Ordering::Less,
Ordering::Greater => return Ordering::Greater,
Ordering::Equal => (),
}
match a.0.userid().value().cmp(&b.0.userid().value()) {
Ordering::Less => return Ordering::Greater,
Ordering::Greater => return Ordering::Less,
Ordering::Equal =>
panic!("non-canonicalized Cert (duplicate User IDs)"),
}
})
.map(|b| b.0)
}
pub fn primary_key_signature_full<T>(&self, t: T)
-> Option<(&Signature, Option<(&UserIDBinding, RevocationStatus)>)>
where T: Into<Option<time::SystemTime>>
{
let t = t.into()
.unwrap_or_else(|| time::SystemTime::now());
let primary_userid = self.primary_userid_full(t);
if let Some((ref u, ref s, ref r)) = primary_userid {
if !destructures_to!(RevocationStatus::Revoked(_) = r) {
return Some((s, Some((u, r.clone()))));
}
}
if let Some(s) = self.primary.binding_signature(t) {
return Some((s, None));
}
if let Some((u, s, r)) = primary_userid {
assert!(destructures_to!(RevocationStatus::Revoked(_) = &r));
return Some((s, Some((u, r))));
}
None
}
pub fn primary_key_signature<T>(&self, t: T) -> Option<&Signature>
where T: Into<Option<time::SystemTime>>
{
if let Some((sig, _)) = self.primary_key_signature_full(t) {
Some(sig)
} else {
None
}
}
pub fn direct_signatures(&self) -> &[Signature] {
&self.primary.self_signatures
}
pub fn certifications(&self) -> &[Signature] {
&self.primary.certifications
}
pub fn self_revocations(&self) -> &[Signature] {
&self.primary.self_revocations
}
pub fn other_revocations(&self) -> &[Signature] {
&self.primary.other_revocations
}
pub fn revoked<T>(&self, t: T) -> RevocationStatus
where T: Into<Option<time::SystemTime>>
{
let t = t.into();
self.primary._revoked(true, self.primary_key_signature(t), t)
}
pub fn revoke_in_place(self, primary_signer: &mut dyn Signer,
code: ReasonForRevocation, reason: &[u8])
-> Result<Cert>
{
let sig = CertRevocationBuilder::new()
.set_reason_for_revocation(code, reason)?
.build(primary_signer, &self, None)?;
self.merge_packets(vec![sig.into()])
}
pub fn alive<T>(&self, t: T) -> Result<()>
where T: Into<Option<time::SystemTime>>
{
let t = t.into();
if let Some(sig) = self.primary_key_signature(t) {
sig.key_alive(self.primary(), t)
} else {
Err(Error::MalformedCert("No primary key signature".into()).into())
}
}
fn set_expiry_as_of(self, primary_signer: &mut dyn Signer,
expiration: Option<time::Duration>,
now: time::SystemTime)
-> Result<Cert>
{
let sig = {
let (template, userid) = self
.primary_key_signature_full(Some(now))
.ok_or(Error::MalformedCert("No self-signature".into()))?;
let hash_algo = HashAlgorithm::SHA512;
let mut hash = hash_algo.context()?;
self.primary().hash(&mut hash);
if let Some((userid, _)) = userid {
userid.userid().hash(&mut hash);
} else {
assert_eq!(template.typ(), SignatureType::DirectKey);
}
signature::Builder::from(template.clone())
.set_key_expiration_time(expiration)?
.set_signature_creation_time(now)?
.sign_hash(primary_signer, hash)?
};
self.merge_packets(vec![sig.into()])
}
pub fn set_expiry(self, primary_signer: &mut dyn Signer,
expiration: Option<time::Duration>)
-> Result<Cert>
{
self.set_expiry_as_of(primary_signer, expiration,
time::SystemTime::now())
}
pub fn userids(&self) -> UserIDBindingIter {
UserIDBindingIter { iter: Some(self.userids.iter()) }
}
pub fn user_attributes(&self) -> UserAttributeBindingIter {
UserAttributeBindingIter { iter: Some(self.user_attributes.iter()) }
}
pub fn subkeys(&self) -> KeyBindingIter<key::PublicParts,
key::SubordinateRole>
{
KeyBindingIter { iter: Some(self.subkeys.iter()) }
}
pub fn unknowns(&self) -> UnknownBindingIter {
UnknownBindingIter { iter: Some(self.unknowns.iter()) }
}
pub fn bad_signatures(&self) -> &[Signature] {
&self.bad
}
pub fn keys(&self) -> KeyIter<key::PublicParts, key::UnspecifiedRole>
{
KeyIter::new(self)
}
pub fn from_packet_parser(ppr: PacketParserResult) -> Result<Self> {
let mut parser = parser::CertParser::from_packet_parser(ppr);
if let Some(cert_result) = parser.next() {
if parser.next().is_some() {
Err(Error::MalformedCert(
"Additional packets found, is this a keyring?".into()
).into())
} else {
cert_result
}
} else {
Err(Error::MalformedCert("No data".into()).into())
}
}
pub fn from_packet_pile(p: PacketPile) -> Result<Self> {
let mut i = parser::CertParser::from_iter(p.into_children());
match i.next() {
Some(Ok(cert)) => Ok(cert),
Some(Err(err)) => Err(err),
None => Err(Error::MalformedCert("No data".into()).into()),
}
}
fn canonicalize(mut self) -> Self {
tracer!(TRACE, "canonicalize", 0);
macro_rules! check {
($desc:expr, $binding:expr, $sigs:ident,
$verify_method:ident, $($verify_args:expr),*) => ({
t!("check!({}, {}, {:?}, {}, ...)",
$desc, stringify!($binding), $binding.$sigs,
stringify!($verify_method));
for sig in mem::replace(&mut $binding.$sigs, Vec::new())
.into_iter()
{
if let Ok(true) = sig.$verify_method(self.primary.key(),
self.primary.key(),
$($verify_args),*) {
$binding.$sigs.push(sig);
} else {
t!("Sig {:02X}{:02X}, type = {} doesn't belong to {}",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ(), $desc);
self.bad.push(sig);
}
}
});
($desc:expr, $binding:expr, $sigs:ident,
$verify_method:ident) => ({
check!($desc, $binding, $sigs, $verify_method,)
});
}
macro_rules! check_3rd_party {
($desc:expr,
$binding:expr,
$sigs:ident,
$lookup_fn:expr,
$verify_method:ident,
$hash_method:ident,
$($verify_args:expr),*
) => ({
t!("check_3rd_party!({}, {}, {:?}, {}, {}, ...)",
$desc, stringify!($binding), $binding.$sigs,
stringify!($verify_method), stringify!($hash_method));
for sig in mem::replace(&mut $binding.$sigs, Vec::new())
.into_iter()
{
if let Ok(hash) = Signature::$hash_method(
&sig, self.primary.key(), $($verify_args),*) {
if &sig.digest_prefix()[..] == &hash[..2] {
if let Some(key) = $lookup_fn(&sig) {
if let Ok(true) = sig.$verify_method(
&key, self.primary.key(), $($verify_args),*)
{
$binding.$sigs.push(sig);
} else {
t!("Sig {:02X}{:02X}, type = {} \
doesn't belong to {}",
sig.digest_prefix()[0],
sig.digest_prefix()[1],
sig.typ(), $desc);
self.bad.push(sig);
}
} else {
$binding.$sigs.push(sig);
}
} else {
t!("Sig {:02X}{:02X}, type = {} \
doesn't belong to {}",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ(), $desc);
self.bad.push(sig);
}
} else {
t!("Sig {:02X}{:02X}, type = {}: \
Hashing failed",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ());
self.bad.push(sig);
}
}
});
($desc:expr, $binding:expr, $sigs:ident, $lookup_fn:expr,
$verify_method:ident, $hash_method:ident) => ({
check_3rd_party!($desc, $binding, $sigs, $lookup_fn,
$verify_method, $hash_method, )
});
}
fn lookup_fn(_: &Signature)
-> Option<Key<key::PublicParts, key::UnspecifiedRole>> {
None
}
check!("primary key",
self.primary, self_signatures, verify_direct_key);
check!("primary key",
self.primary, self_revocations, verify_primary_key_revocation);
check_3rd_party!("primary key",
self.primary, certifications, lookup_fn,
verify_direct_key, hash_direct_key);
check_3rd_party!("primary key",
self.primary, other_revocations, lookup_fn,
verify_primary_key_revocation, hash_direct_key);
for binding in self.userids.iter_mut() {
check!(format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, self_signatures, verify_userid_binding,
binding.userid());
check!(format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, self_revocations, verify_userid_revocation,
binding.userid());
check_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, certifications, lookup_fn,
verify_userid_binding, hash_userid_binding,
binding.userid());
check_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(binding.userid().value())),
binding, other_revocations, lookup_fn,
verify_userid_revocation, hash_userid_binding,
binding.userid());
}
for binding in self.user_attributes.iter_mut() {
check!("user attribute",
binding, self_signatures, verify_user_attribute_binding,
binding.user_attribute());
check!("user attribute",
binding, self_revocations, verify_user_attribute_revocation,
binding.user_attribute());
check_3rd_party!(
"user attribute",
binding, certifications, lookup_fn,
verify_user_attribute_binding, hash_user_attribute_binding,
binding.user_attribute());
check_3rd_party!(
"user attribute",
binding, other_revocations, lookup_fn,
verify_user_attribute_revocation, hash_user_attribute_binding,
binding.user_attribute());
}
for binding in self.subkeys.iter_mut() {
check!(format!("subkey {}", binding.key().keyid()),
binding, self_signatures, verify_subkey_binding,
binding.key());
check!(format!("subkey {}", binding.key().keyid()),
binding, self_revocations, verify_subkey_revocation,
binding.key());
check_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding, certifications, lookup_fn,
verify_subkey_binding, hash_subkey_binding,
binding.key());
check_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding, other_revocations, lookup_fn,
verify_subkey_revocation, hash_subkey_binding,
binding.key());
}
'outer: for sig in mem::replace(&mut self.bad, Vec::new()) {
macro_rules! check_one {
($desc:expr, $sigs:expr, $sig:expr,
$verify_method:ident, $($verify_args:expr),*) => ({
t!("check_one!({}, {:?}, {:?}, {}, ...)",
$desc, $sigs, $sig,
stringify!($verify_method));
if let Ok(true)
= $sig.$verify_method(self.primary.key(),
self.primary.key(),
$($verify_args),*)
{
t!("Sig {:02X}{:02X}, {:?} \
was out of place. Belongs to {}.",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
$sigs.push($sig);
continue 'outer;
}
});
($desc:expr, $sigs:expr, $sig:expr,
$verify_method:ident) => ({
check_one!($desc, $sigs, $sig, $verify_method,)
});
}
macro_rules! check_one_3rd_party {
($desc:expr,
$sigs:expr,
$sig:ident,
$lookup_fn:expr,
$verify_method:ident,
$hash_method:ident,
$($verify_args:expr),*
) => ({
t!("check_one_3rd_party!({}, {}, {:?}, {}, {}, ...)",
$desc, stringify!($sigs), $sig,
stringify!($verify_method), stringify!($hash_method));
if let Some(key) = $lookup_fn(&sig) {
if let Ok(true) = sig.$verify_method(&key,
self.primary.key(),
$($verify_args),*)
{
t!("Sig {:02X}{:02X}, {:?} \
was out of place. Belongs to {}.",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
$sigs.push($sig);
continue 'outer;
}
} else {
if let Ok(hash) = Signature::$hash_method(
&sig, self.primary.key(), $($verify_args),*) {
if &sig.digest_prefix()[..] == &hash[..2] {
t!("Sig {:02X}{:02X}, {:?} \
was out of place. Likely belongs to {}.",
$sig.digest_prefix()[0],
$sig.digest_prefix()[1],
$sig.typ(), $desc);
$sigs.push($sig);
continue 'outer;
}
}
}
});
($desc:expr, $sigs:expr, $sig:ident, $lookup_fn:expr,
$verify_method:ident, $hash_method:ident) => ({
check_one_3rd_party!($desc, $sigs, $sig, $lookup_fn,
$verify_method, $hash_method, )
});
}
use SignatureType::*;
match sig.typ() {
DirectKey => {
check_one!("primary key", self.primary.self_signatures,
sig, verify_direct_key);
check_one_3rd_party!(
"primary key", self.primary.certifications, sig,
lookup_fn,
verify_direct_key, hash_direct_key);
},
KeyRevocation => {
check_one!("primary key", self.primary.self_revocations,
sig, verify_primary_key_revocation);
check_one_3rd_party!(
"primary key", self.primary.other_revocations, sig,
lookup_fn, verify_primary_key_revocation,
hash_direct_key);
},
GenericCertification | PersonaCertification
| CasualCertification | PositiveCertification =>
{
for binding in self.userids.iter_mut() {
check_one!(format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.self_signatures, sig,
verify_userid_binding, binding.userid());
check_one_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.certifications, sig, lookup_fn,
verify_userid_binding, hash_userid_binding,
binding.userid());
}
for binding in self.user_attributes.iter_mut() {
check_one!("user attribute",
binding.self_signatures, sig,
verify_user_attribute_binding,
binding.user_attribute());
check_one_3rd_party!(
"user attribute",
binding.certifications, sig, lookup_fn,
verify_user_attribute_binding,
hash_user_attribute_binding,
binding.user_attribute());
}
},
CertificationRevocation => {
for binding in self.userids.iter_mut() {
check_one!(format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.self_revocations, sig,
verify_userid_revocation,
binding.userid());
check_one_3rd_party!(
format!("userid \"{}\"",
String::from_utf8_lossy(
binding.userid().value())),
binding.other_revocations, sig, lookup_fn,
verify_userid_revocation, hash_userid_binding,
binding.userid());
}
for binding in self.user_attributes.iter_mut() {
check_one!("user attribute",
binding.self_revocations, sig,
verify_user_attribute_revocation,
binding.user_attribute());
check_one_3rd_party!(
"user attribute",
binding.other_revocations, sig, lookup_fn,
verify_user_attribute_revocation,
hash_user_attribute_binding,
binding.user_attribute());
}
},
SubkeyBinding => {
for binding in self.subkeys.iter_mut() {
check_one!(format!("subkey {}", binding.key().keyid()),
binding.self_signatures, sig,
verify_subkey_binding, binding.key());
check_one_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding.certifications, sig, lookup_fn,
verify_subkey_binding, hash_subkey_binding,
binding.key());
}
},
SubkeyRevocation => {
for binding in self.subkeys.iter_mut() {
check_one!(format!("subkey {}", binding.key().keyid()),
binding.self_revocations, sig,
verify_subkey_revocation, binding.key());
check_one_3rd_party!(
format!("subkey {}", binding.key().keyid()),
binding.other_revocations, sig, lookup_fn,
verify_subkey_revocation, hash_subkey_binding,
binding.key());
}
},
typ => {
t!("Odd signature type: {:?}", typ);
},
}
t!("Self-sig {:02X}{:02X}, {:?} doesn't belong \
to any known component or is bad.",
sig.digest_prefix()[0], sig.digest_prefix()[1],
sig.typ());
self.bad.push(sig);
}
if self.bad.len() > 0 {
t!("{}: ignoring {} bad self-signatures",
self.primary().keyid(), self.bad.len());
}
self.userids.retain(|userid| {
userid.self_signatures.len() > 0 || userid.self_revocations.len() > 0
});
t!("Retained {} userids", self.userids.len());
self.user_attributes.retain(|ua| {
ua.self_signatures.len() > 0 || ua.self_revocations.len() > 0
});
t!("Retained {} user_attributes", self.user_attributes.len());
self.subkeys.retain(|subkey| {
subkey.self_signatures.len() > 0 || subkey.self_revocations.len() > 0
});
t!("Retained {} subkeys", self.subkeys.len());
self.primary.sort_and_dedup();
self.bad.sort_by(sig_cmp);
self.bad.dedup();
self.userids.sort_and_dedup(UserID::cmp, |_, _| {});
self.user_attributes.sort_and_dedup(UserAttribute::cmp, |_, _| {});
self.subkeys.sort_and_dedup(Key::public_cmp,
|ref mut a, ref mut b| {
if b.secret().is_none() && a.secret().is_some() {
b.set_secret(a.set_secret(None));
}
});
self.unknowns.sort_and_dedup(Unknown::best_effort_cmp, |_, _| {});
self
}
pub fn fingerprint(&self) -> Fingerprint {
self.primary().fingerprint()
}
pub fn keyid(&self) -> KeyID {
self.primary().keyid()
}
pub fn into_packets(self) -> impl Iterator<Item=Packet> {
self.primary.into_packets()
.chain(self.userids.into_iter().flat_map(|b| b.into_packets()))
.chain(self.user_attributes.into_iter().flat_map(|b| b.into_packets()))
.chain(self.subkeys.into_iter().flat_map(|b| b.into_packets()))
}
pub fn into_packet_pile(self) -> PacketPile {
PacketPile::from(self.into_packets().collect::<Vec<Packet>>())
}
pub fn merge(mut self, mut other: Cert) -> Result<Self> {
if self.primary().fingerprint()
!= other.primary().fingerprint()
{
return Err(Error::InvalidArgument(
"Primary key mismatch".into()).into());
}
if self.primary.key().secret().is_none() && other.primary.key().secret().is_some() {
self.primary.key_mut().set_secret(other.primary.key_mut().set_secret(None));
}
self.primary.self_signatures.append(
&mut other.primary.self_signatures);
self.primary.certifications.append(
&mut other.primary.certifications);
self.primary.self_revocations.append(
&mut other.primary.self_revocations);
self.primary.other_revocations.append(
&mut other.primary.other_revocations);
self.userids.append(&mut other.userids);
self.user_attributes.append(&mut other.user_attributes);
self.subkeys.append(&mut other.subkeys);
self.bad.append(&mut other.bad);
Ok(self.canonicalize())
}
pub fn merge_packets(self, packets: Vec<Packet>) -> Result<Self> {
let mut combined = self.into_packets().collect::<Vec<_>>();
fn replace_or_push<P, R>(acc: &mut Vec<Packet>, k: Key<P, R>)
where P: key::KeyParts,
R: key::KeyRole,
Packet: From<packet::Key<P, R>>,
{
for q in acc.iter_mut() {
let replace = match q {
Packet::PublicKey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
Packet::SecretKey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
Packet::PublicSubkey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
Packet::SecretSubkey(k_) =>
k_.public_cmp(&k) == Ordering::Equal,
_ => false,
};
if replace {
*q = k.into();
return;
}
}
acc.push(k.into());
};
for p in packets.into_iter() {
match p {
Packet::PublicKey(k) => replace_or_push(&mut combined, k),
Packet::SecretKey(k) => replace_or_push(&mut combined, k),
Packet::PublicSubkey(k) => replace_or_push(&mut combined, k),
Packet::SecretSubkey(k) => replace_or_push(&mut combined, k),
p => combined.push(p),
}
}
Cert::from_packet_pile(PacketPile::from(combined))
}
pub fn is_tsk(&self) -> bool {
if self.primary().secret().is_some() {
return true;
}
self.subkeys().any(|sk| {
sk.binding_signature(None).is_some() && sk.key().secret().is_some()
})
}
}
#[cfg(test)]
mod test {
use crate::serialize::Serialize;
use super::*;
use crate::{
KeyID,
types::KeyFlags,
};
fn parse_cert(data: &[u8], as_message: bool) -> Result<Cert> {
if as_message {
let pile = PacketPile::from_bytes(data).unwrap();
Cert::from_packet_pile(pile)
} else {
Cert::from_bytes(data)
}
}
#[test]
fn broken() {
use crate::types::Timestamp;
for i in 0..2 {
let cert = parse_cert(crate::tests::key("testy-broken-no-pk.pgp"),
i == 0);
assert_match!(Error::MalformedCert(_)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = parse_cert(crate::tests::key("testy-broken-no-uid.pgp"),
i == 0);
assert!(cert.is_ok());
let cert = parse_cert(crate::tests::key("testy-broken-no-sig-on-subkey.pgp"),
i == 0).unwrap();
assert_eq!(cert.primary.key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(cert.userids.len(), 1);
assert_eq!(cert.userids[0].userid().value(),
&b"Testy McTestface <testy@example.org>"[..]);
assert_eq!(cert.userids[0].self_signatures.len(), 1);
assert_eq!(cert.userids[0].self_signatures[0].digest_prefix(),
&[ 0xc6, 0x8f ]);
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.subkeys.len(), 0);
}
}
#[test]
fn basics() {
use crate::types::Timestamp;
for i in 0..2 {
let cert = parse_cert(crate::tests::key("testy.pgp"),
i == 0).unwrap();
assert_eq!(cert.primary.key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(cert.fingerprint().to_hex(),
"3E8877C877274692975189F5D03F6F865226FE8B");
assert_eq!(cert.userids.len(), 1, "number of userids");
assert_eq!(cert.userids[0].userid().value(),
&b"Testy McTestface <testy@example.org>"[..]);
assert_eq!(cert.userids[0].self_signatures.len(), 1);
assert_eq!(cert.userids[0].self_signatures[0].digest_prefix(),
&[ 0xc6, 0x8f ]);
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.subkeys.len(), 1, "number of subkeys");
assert_eq!(cert.subkeys[0].key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(cert.subkeys[0].self_signatures[0].digest_prefix(),
&[ 0xb7, 0xb9 ]);
let cert = parse_cert(crate::tests::key("testy-no-subkey.pgp"),
i == 0).unwrap();
assert_eq!(cert.primary.key().creation_time(),
Timestamp::from(1511355130).into());
assert_eq!(cert.fingerprint().to_hex(),
"3E8877C877274692975189F5D03F6F865226FE8B");
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.userids.len(), 1, "number of userids");
assert_eq!(cert.userids[0].userid().value(),
&b"Testy McTestface <testy@example.org>"[..]);
assert_eq!(cert.userids[0].self_signatures.len(), 1);
assert_eq!(cert.userids[0].self_signatures[0].digest_prefix(),
&[ 0xc6, 0x8f ]);
assert_eq!(cert.subkeys.len(), 0, "number of subkeys");
let cert = parse_cert(crate::tests::key("testy.asc"), i == 0).unwrap();
assert_eq!(cert.fingerprint().to_hex(),
"3E8877C877274692975189F5D03F6F865226FE8B");
}
}
#[test]
fn only_a_public_key() {
let cert = Cert::from_bytes(crate::tests::key("testy-only-a-pk.pgp")).unwrap();
assert_eq!(cert.userids.len(), 0);
assert_eq!(cert.user_attributes.len(), 0);
assert_eq!(cert.subkeys.len(), 0);
}
#[test]
fn merge() {
use crate::tests::key;
let cert_base = Cert::from_bytes(key("bannon-base.gpg")).unwrap();
let merged = cert_base.clone().merge(cert_base.clone()).unwrap();
assert_eq!(cert_base, merged);
let cert_add_uid_1
= Cert::from_bytes(key("bannon-add-uid-1-whitehouse.gov.gpg"))
.unwrap();
let cert_add_uid_2
= Cert::from_bytes(key("bannon-add-uid-2-fox.com.gpg"))
.unwrap();
let cert_add_uid_3
= Cert::from_bytes(key("bannon-add-uid-3-whitehouse.gov-dup.gpg"))
.unwrap();
let cert_all_uids
= Cert::from_bytes(key("bannon-all-uids.gpg"))
.unwrap();
assert_eq!(cert_all_uids.userids.len(), 3);
let merged = cert_base.clone().merge(cert_add_uid_1.clone()).unwrap()
.merge(cert_add_uid_2.clone()).unwrap()
.merge(cert_add_uid_3.clone()).unwrap();
assert_eq!(cert_all_uids, merged);
let merged = cert_base.clone()
.merge(cert_add_uid_3.clone()).unwrap()
.merge(cert_add_uid_2.clone()).unwrap()
.merge(cert_add_uid_1.clone()).unwrap();
assert_eq!(cert_all_uids, merged);
let cert_add_subkey_1
= Cert::from_bytes(key("bannon-add-subkey-1.gpg")).unwrap();
let cert_add_subkey_2
= Cert::from_bytes(key("bannon-add-subkey-2.gpg")).unwrap();
let cert_add_subkey_3
= Cert::from_bytes(key("bannon-add-subkey-3.gpg")).unwrap();
let cert_all_subkeys
= Cert::from_bytes(key("bannon-all-subkeys.gpg")).unwrap();
let merged = cert_base.clone().merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let merged = cert_base.clone().merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let merged = cert_base.clone()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap();
assert_eq!(cert_all_subkeys, merged);
let cert_all
= Cert::from_bytes(key("bannon-all-uids-subkeys.gpg"))
.unwrap();
let merged = cert_all_subkeys.clone()
.merge(cert_all_uids.clone()).unwrap();
assert_eq!(cert_all, merged);
let merged = cert_all_uids.clone()
.merge(cert_all_subkeys.clone()).unwrap();
assert_eq!(cert_all, merged);
let merged = cert_base.clone()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_uid_2.clone()).unwrap()
.merge(cert_add_uid_1.clone()).unwrap()
.merge(cert_add_subkey_3.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_uid_3.clone()).unwrap()
.merge(cert_add_subkey_2.clone()).unwrap()
.merge(cert_add_subkey_1.clone()).unwrap()
.merge(cert_add_uid_2.clone()).unwrap();
assert_eq!(cert_all, merged);
let cert_donald_signs_base
= Cert::from_bytes(key("bannon-the-donald-signs-base.gpg"))
.unwrap();
let cert_donald_signs_all
= Cert::from_bytes(key("bannon-the-donald-signs-all-uids.gpg"))
.unwrap();
let cert_ivanka_signs_base
= Cert::from_bytes(key("bannon-ivanka-signs-base.gpg"))
.unwrap();
let cert_ivanka_signs_all
= Cert::from_bytes(key("bannon-ivanka-signs-all-uids.gpg"))
.unwrap();
assert!(cert_donald_signs_base.userids.len() == 1);
assert!(cert_donald_signs_base.userids[0].self_signatures.len() == 1);
assert!(cert_base.userids[0].certifications.len() == 0);
assert!(cert_donald_signs_base.userids[0].certifications.len() == 1);
let merged = cert_donald_signs_base.clone()
.merge(cert_ivanka_signs_base.clone()).unwrap();
assert!(merged.userids.len() == 1);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 2);
let merged = cert_donald_signs_base.clone()
.merge(cert_donald_signs_all.clone()).unwrap();
assert!(merged.userids.len() == 3);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 2);
assert!(merged.userids[1].certifications.len() == 1);
assert!(merged.userids[2].certifications.len() == 1);
let merged = cert_donald_signs_base.clone()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_ivanka_signs_base.clone()).unwrap()
.merge(cert_ivanka_signs_all.clone()).unwrap();
assert!(merged.userids.len() == 3);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 4);
assert!(merged.userids[1].certifications.len() == 2);
assert!(merged.userids[2].certifications.len() == 2);
let merged = cert_donald_signs_base.clone()
.merge(cert_ivanka_signs_base.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_ivanka_signs_all.clone()).unwrap()
.merge(cert_ivanka_signs_base.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_donald_signs_all.clone()).unwrap()
.merge(cert_ivanka_signs_all.clone()).unwrap();
assert!(merged.userids.len() == 3);
assert!(merged.userids[0].self_signatures.len() == 1);
assert!(merged.userids[0].certifications.len() == 4);
assert!(merged.userids[1].certifications.len() == 2);
assert!(merged.userids[2].certifications.len() == 2);
}
#[test]
fn out_of_order_self_sigs_test() {
let cert = Cert::from_bytes(crate::tests::key("neal-sigs-out-of-order.pgp"))
.unwrap();
let mut userids = cert.userids()
.map(|u| String::from_utf8_lossy(u.userid().value()).into_owned())
.collect::<Vec<String>>();
userids.sort();
assert_eq!(userids,
&[ "Neal H. Walfield <neal@g10code.com>",
"Neal H. Walfield <neal@gnupg.org>",
"Neal H. Walfield <neal@pep-project.org>",
"Neal H. Walfield <neal@pep.foundation>",
"Neal H. Walfield <neal@sequoia-pgp.org>",
"Neal H. Walfield <neal@walfield.org>",
]);
let mut subkeys = cert.subkeys()
.map(|sk| Some(sk.key().keyid()))
.collect::<Vec<Option<KeyID>>>();
subkeys.sort();
assert_eq!(subkeys,
&[ KeyID::from_hex("7223B56678E02528").ok(),
KeyID::from_hex("A3506AFB820ABD08").ok(),
KeyID::from_hex("C2B819056C652598").ok(),
]);
let cert =
Cert::from_bytes(crate::tests::key("dkg-sigs-out-of-order.pgp")).unwrap();
let mut userids = cert.userids()
.map(|u| String::from_utf8_lossy(u.userid().value()).into_owned())
.collect::<Vec<String>>();
userids.sort();
assert_eq!(userids,
&[ "Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net>",
"Daniel Kahn Gillmor <dkg@aclu.org>",
"Daniel Kahn Gillmor <dkg@astro.columbia.edu>",
"Daniel Kahn Gillmor <dkg@debian.org>",
"Daniel Kahn Gillmor <dkg@fifthhorseman.net>",
"Daniel Kahn Gillmor <dkg@openflows.com>",
]);
assert_eq!(cert.user_attributes.len(), 1);
let mut subkeys = cert.subkeys()
.map(|sk| Some(sk.key().keyid()))
.collect::<Vec<Option<KeyID>>>();
subkeys.sort();
assert_eq!(subkeys,
&[ KeyID::from_hex(&"1075 8EBD BD7C FAB5"[..]).ok(),
KeyID::from_hex(&"1258 68EA 4BFA 08E4"[..]).ok(),
KeyID::from_hex(&"1498 ADC6 C192 3237"[..]).ok(),
KeyID::from_hex(&"24EC FF5A FF68 370A"[..]).ok(),
KeyID::from_hex(&"3714 7292 14D5 DA70"[..]).ok(),
KeyID::from_hex(&"3B7A A7F0 14E6 9B5A"[..]).ok(),
KeyID::from_hex(&"5B58 DCF9 C341 6611"[..]).ok(),
KeyID::from_hex(&"A524 01B1 1BFD FA5C"[..]).ok(),
KeyID::from_hex(&"A70A 96E1 439E A852"[..]).ok(),
KeyID::from_hex(&"C61B D3EC 2148 4CFF"[..]).ok(),
KeyID::from_hex(&"CAEF A883 2167 5333"[..]).ok(),
KeyID::from_hex(&"DC10 4C4E 0CA7 57FB"[..]).ok(),
KeyID::from_hex(&"E3A3 2229 449B 0350"[..]).ok(),
]);
}
#[test]
fn v3_packets() {
let dkg = crate::tests::key("dkg.gpg");
let lutz = crate::tests::key("lutz.gpg");
let cert = Cert::from_bytes(lutz);
assert_match!(Error::MalformedCert(_)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = Cert::from_bytes(dkg);
assert!(cert.is_ok(), "dkg.gpg: {:?}", cert);
}
#[test]
fn keyring_with_v3_public_keys() {
let dkg = crate::tests::key("dkg.gpg");
let lutz = crate::tests::key("lutz.gpg");
let cert = Cert::from_bytes(dkg);
assert!(cert.is_ok(), "dkg.gpg: {:?}", cert);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, true ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false ]);
let mut combined = vec![];
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ false, true ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false, true ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&lutz[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false, false ]);
let mut combined = vec![];
combined.extend_from_slice(&dkg[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&lutz[..]);
combined.extend_from_slice(&dkg[..]);
let certs = CertParser::from_bytes(&combined[..]).unwrap()
.map(|certr| certr.is_ok())
.collect::<Vec<bool>>();
assert_eq!(certs, &[ true, false, false, true ]);
}
#[test]
fn merge_with_incomplete_update() {
let cert = Cert::from_bytes(crate::tests::key("about-to-expire.expired.pgp"))
.unwrap();
assert!(! cert.primary_key_signature(None).unwrap()
.key_alive(cert.primary(), None).is_ok());
let update =
Cert::from_bytes(crate::tests::key("about-to-expire.update-no-uid.pgp"))
.unwrap();
let cert = cert.merge(update).unwrap();
assert!(cert.primary_key_signature(None).unwrap()
.key_alive(cert.primary(), None).is_ok());
}
#[test]
fn packet_pile_roundtrip() {
let cert = Cert::from_bytes(crate::tests::key("already-revoked.pgp")).unwrap();
let cert2
= Cert::from_packet_pile(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
let cert = Cert::from_bytes(
crate::tests::key("already-revoked-direct-revocation.pgp")).unwrap();
let cert2
= Cert::from_packet_pile(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
let cert = Cert::from_bytes(
crate::tests::key("already-revoked-userid-revocation.pgp")).unwrap();
let cert2
= Cert::from_packet_pile(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
let cert = Cert::from_bytes(
crate::tests::key("already-revoked-subkey-revocation.pgp")).unwrap();
let cert2
= Cert::from_packet_pile(cert.clone().into_packet_pile()).unwrap();
assert_eq!(cert, cert2);
}
#[test]
fn merge_packets() {
use crate::armor;
use crate::packet::Tag;
let cert = Cert::from_bytes(crate::tests::key("already-revoked.pgp")).unwrap();
let rev = crate::tests::key("already-revoked.rev");
let rev = PacketPile::from_reader(armor::Reader::new(&rev[..], None))
.unwrap();
let rev : Vec<Packet> = rev.into_children().collect();
assert_eq!(rev.len(), 1);
assert_eq!(rev[0].tag(), Tag::Signature);
let packets_pre_merge = cert.clone().into_packets().count();
let cert = cert.merge_packets(rev).unwrap();
let packets_post_merge = cert.clone().into_packets().count();
assert_eq!(packets_post_merge, packets_pre_merge + 1);
}
#[test]
fn set_expiry() {
let (cert, _) = CertBuilder::autocrypt(None, Some("Test"))
.generate().unwrap();
let now = cert.primary().creation_time();
let a_sec = time::Duration::new(1, 0);
let expiry_orig = cert.primary_key_signature(None).unwrap()
.key_expiration_time()
.expect("Keys expire by default.");
let mut keypair = cert.primary().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap();
let as_of1 = now + time::Duration::new(10, 0);
let cert = cert.set_expiry_as_of(
&mut keypair,
None,
as_of1).unwrap();
{
assert_eq!(cert.primary_key_signature(now).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key_signature(as_of1 - a_sec).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key_signature(as_of1).unwrap()
.key_expiration_time(),
None);
}
let expiry_new = expiry_orig - time::Duration::new(60 * 60, 0);
assert!(expiry_new > time::Duration::new(0, 0));
let as_of2 = as_of1 + time::Duration::new(10, 0);
let cert = cert.set_expiry_as_of(
&mut keypair,
Some(expiry_new),
as_of2).unwrap();
{
assert_eq!(cert.primary_key_signature(now).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key_signature(as_of1 - a_sec).unwrap()
.key_expiration_time(),
Some(expiry_orig));
assert_eq!(cert.primary_key_signature(as_of1).unwrap()
.key_expiration_time(),
None);
assert_eq!(cert.primary_key_signature(as_of2 - a_sec).unwrap()
.key_expiration_time(),
None);
assert_eq!(cert.primary_key_signature(as_of2).unwrap()
.key_expiration_time(),
Some(expiry_new));
}
}
#[test]
fn direct_key_sig() {
use crate::types::SignatureType;
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_signature(None).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 typ = cert.primary_key_signature(None).unwrap().typ();
assert_eq!(typ, SignatureType::PositiveCertification,
"{:#?}", cert);
let revoked = cert.revoked(None);
if direct_revoked {
assert_match!(RevocationStatus::Revoked(_) = revoked,
"{:#?}", cert);
} else {
assert_eq!(revoked, RevocationStatus::NotAsFarAsWeKnow,
"{:#?}", cert);
}
for userid in cert.userids() {
let typ = userid.binding_signature(None).unwrap().typ();
assert_eq!(typ, SignatureType::PositiveCertification,
"{:#?}", cert);
let revoked = userid.revoked(None);
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(None).unwrap().typ();
assert_eq!(typ, SignatureType::SubkeyBinding,
"{:#?}", cert);
let revoked = subkey.revoked(None);
if subkey_revoked {
assert_match!(RevocationStatus::Revoked(_) = revoked);
} else {
assert_eq!(RevocationStatus::NotAsFarAsWeKnow, revoked,
"{:#?}", cert);
}
}
}
let cert = Cert::from_bytes(crate::tests::key("already-revoked.pgp")).unwrap();
check(&cert, false, false, false);
let d = Cert::from_bytes(
crate::tests::key("already-revoked-direct-revocation.pgp")).unwrap();
check(&d, true, false, false);
check(&cert.clone().merge(d.clone()).unwrap(), true, false, false);
check(&d.clone().merge(cert.clone()).unwrap(), true, false, false);
let u = Cert::from_bytes(
crate::tests::key("already-revoked-userid-revocation.pgp")).unwrap();
check(&u, false, true, false);
check(&cert.clone().merge(u.clone()).unwrap(), false, true, false);
check(&u.clone().merge(cert.clone()).unwrap(), false, true, false);
let k = Cert::from_bytes(
crate::tests::key("already-revoked-subkey-revocation.pgp")).unwrap();
check(&k, false, false, true);
check(&cert.clone().merge(k.clone()).unwrap(), false, false, true);
check(&k.clone().merge(cert.clone()).unwrap(), false, false, true);
check(&d.clone().merge(u.clone()).unwrap(), true, true, false);
check(&u.clone().merge(d.clone()).unwrap(), true, true, false);
check(&d.clone().merge(k.clone()).unwrap(), true, false, true);
check(&k.clone().merge(d.clone()).unwrap(), true, false, true);
check(&u.clone().merge(k.clone()).unwrap(), false, true, true);
check(&k.clone().merge(u.clone()).unwrap(), false, true, true);
check(&d.clone().merge(u.clone().merge(k.clone()).unwrap()).unwrap(),
true, true, true);
check(&d.clone().merge(k.clone().merge(u.clone()).unwrap()).unwrap(),
true, true, true);
}
#[test]
fn revoke() {
let (cert, _) = CertBuilder::autocrypt(None, Some("Test"))
.generate().unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revoked(None));
let mut keypair = cert.primary().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap();
let sig = CertRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::KeyCompromised,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, None)
.unwrap();
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
assert_eq!(sig.issuer(), Some(&cert.primary().keyid()));
assert_eq!(sig.issuer_fingerprint(),
Some(&cert.primary().fingerprint()));
let cert = cert.merge_packets(vec![sig.into()]).unwrap();
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(None));
let (other, _) = CertBuilder::autocrypt(None, Some("Test 2"))
.generate().unwrap();
let mut keypair = other.primary().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap();
let sig = CertRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::KeyCompromised,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, None)
.unwrap();
assert_eq!(sig.typ(), SignatureType::KeyRevocation);
assert_eq!(sig.issuer(), Some(&other.primary().keyid()));
assert_eq!(sig.issuer_fingerprint(),
Some(&other.primary().fingerprint()));
}
#[test]
fn revoke_subkey() {
use std::{thread, time};
let (cert, _) = CertBuilder::new()
.add_transport_encryption_subkey()
.generate().unwrap();
thread::sleep(time::Duration::from_secs(2));
let sig = {
let subkey = cert.subkeys().nth(0).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow, subkey.revoked(None));
let mut keypair = cert.primary().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap();
SubkeyRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::UIDRetired,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, subkey.key(), None)
.unwrap()
};
assert_eq!(sig.typ(), SignatureType::SubkeyRevocation);
let cert = cert.merge_packets(vec![sig.into()]).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revoked(None));
let subkey = cert.subkeys().nth(0).unwrap();
assert_match!(RevocationStatus::Revoked(_) = subkey.revoked(None));
}
#[test]
fn revoke_uid() {
use std::{thread, time};
let (cert, _) = CertBuilder::new()
.add_userid("Test1")
.add_userid("Test2")
.generate().unwrap();
thread::sleep(time::Duration::from_secs(2));
let sig = {
let uid = cert.userids().skip(1).next().unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow, uid.revoked(None));
let mut keypair = cert.primary().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap();
UserIDRevocationBuilder::new()
.set_reason_for_revocation(
ReasonForRevocation::UIDRetired,
b"It was the maid :/").unwrap()
.build(&mut keypair, &cert, uid.userid(), None)
.unwrap()
};
assert_eq!(sig.typ(), SignatureType::CertificationRevocation);
let cert = cert.merge_packets(vec![sig.into()]).unwrap();
assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
cert.revoked(None));
let uid = cert.userids().skip(1).next().unwrap();
assert_match!(RevocationStatus::Revoked(_) = uid.revoked(None));
}
#[test]
fn key_revoked() {
use crate::types::Features;
use crate::packet::key::Key4;
use crate::types::Curve;
use rand::{thread_rng, Rng, distributions::Open01};
let t1 = time::UNIX_EPOCH + time::Duration::new(946681200, 0);
let t2 = time::UNIX_EPOCH + time::Duration::new(978303600, 0);
let t3 = time::UNIX_EPOCH + time::Duration::new(1009839600, 0);
let t4 = time::UNIX_EPOCH + time::Duration::new(1041375600, 0);
let mut key: key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
key.set_creation_time(t1).unwrap();
let mut pair = key.clone().into_keypair().unwrap();
let (bind1, rev1, bind2, rev2) = {
let bind1 = signature::Builder::new(SignatureType::DirectKey)
.set_features(&Features::sequoia()).unwrap()
.set_key_flags(&KeyFlags::default()).unwrap()
.set_signature_creation_time(t1).unwrap()
.set_key_expiration_time(Some(time::Duration::new(10 * 52 * 7 * 24 * 60 * 60, 0))).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.sign_direct_key(&mut pair).unwrap();
let rev1 = signature::Builder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t2).unwrap()
.set_reason_for_revocation(ReasonForRevocation::KeySuperseded,
&b""[..]).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.sign_direct_key(&mut pair).unwrap();
let bind2 = signature::Builder::new(SignatureType::DirectKey)
.set_features(&Features::sequoia()).unwrap()
.set_key_flags(&KeyFlags::default()).unwrap()
.set_signature_creation_time(t3).unwrap()
.set_key_expiration_time(Some(time::Duration::new(10 * 52 * 7 * 24 * 60 * 60, 0))).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.sign_direct_key(&mut pair).unwrap();
let rev2 = signature::Builder::new(SignatureType::KeyRevocation)
.set_signature_creation_time(t4).unwrap()
.set_reason_for_revocation(ReasonForRevocation::KeyCompromised,
&b""[..]).unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.sign_direct_key(&mut pair).unwrap();
(bind1, rev1, bind2, rev2)
};
let pk : key::PublicKey = key.into();
let cert = Cert::from_packet_pile(PacketPile::from(vec![
pk.into(),
bind1.into(),
bind2.into(),
rev1.into()
])).unwrap();
let f1: f32 = thread_rng().sample(Open01);
let f2: f32 = thread_rng().sample(Open01);
let f3: f32 = thread_rng().sample(Open01);
let 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 * f3) as u64, 0);
assert_eq!(cert.revoked(te1), RevocationStatus::NotAsFarAsWeKnow);
assert_eq!(cert.revoked(t12), RevocationStatus::NotAsFarAsWeKnow);
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(t23));
assert_eq!(cert.revoked(t34), RevocationStatus::NotAsFarAsWeKnow);
let cert = cert.merge_packets(vec![ rev2.into() ]).unwrap();
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(te1));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(t12));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(t23));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(t34));
assert_match!(RevocationStatus::Revoked(_) = cert.revoked(t4));
assert_match!(RevocationStatus::Revoked(_)
= cert.revoked(time::SystemTime::now()));
}
#[test]
fn key_revoked2() {
tracer!(true, "cert_revoked2", 0);
fn cert_revoked<T>(cert: &Cert, t: T) -> bool
where T: Into<Option<time::SystemTime>>
{
!destructures_to!(RevocationStatus::NotAsFarAsWeKnow
= cert.revoked(t))
}
fn subkey_revoked<T>(cert: &Cert, t: T) -> bool
where T: Into<Option<time::SystemTime>>
{
!destructures_to!(RevocationStatus::NotAsFarAsWeKnow
= cert.subkeys().nth(0).unwrap().revoked(t))
}
let tests : [(&str, Box<dyn Fn(&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_signature(None).unwrap()
.signature_creation_time().unwrap();
assert!(!revoked(&cert, Some(selfsig0)));
assert!(!revoked(&cert, None));
t!("Soft revocation");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-1-soft-revocation.pgp", f))
).unwrap()).unwrap();
assert!(!revoked(&cert, Some(selfsig0)));
assert!(revoked(&cert, None));
t!("New self signature");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-2-new-self-sig.pgp", f))
).unwrap()).unwrap();
assert!(!revoked(&cert, Some(selfsig0)));
assert!(!revoked(&cert, None));
t!("Hard revocation");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-3-hard-revocation.pgp", f))
).unwrap()).unwrap();
assert!(revoked(&cert, Some(selfsig0)));
assert!(revoked(&cert, None));
t!("New self signature");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-4-new-self-sig.pgp", f))
).unwrap()).unwrap();
assert!(revoked(&cert, Some(selfsig0)));
assert!(revoked(&cert, None));
}
}
#[test]
fn userid_revoked2() {
fn check_userids<T>(cert: &Cert, revoked: bool, t: T)
where T: Into<Option<time::SystemTime>>, T: Copy
{
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= cert.revoked(None));
let mut slim_shady = false;
let mut eminem = false;
for b in cert.userids() {
if b.userid().value() == b"Slim Shady" {
assert!(!slim_shady);
slim_shady = true;
if revoked {
assert_match!(RevocationStatus::Revoked(_)
= b.revoked(t));
} else {
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= b.revoked(t));
}
} else {
assert!(!eminem);
eminem = true;
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= b.revoked(t));
}
}
assert!(slim_shady);
assert!(eminem);
}
fn check_uas<T>(cert: &Cert, revoked: bool, t: T)
where T: Into<Option<time::SystemTime>>, T: Copy
{
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= cert.revoked(None));
assert_eq!(cert.user_attributes().count(), 1);
let ua = cert.user_attributes().nth(0).unwrap();
if revoked {
assert_match!(RevocationStatus::Revoked(_)
= ua.revoked(t));
} else {
assert_match!(RevocationStatus::NotAsFarAsWeKnow
= ua.revoked(t));
}
}
tracer!(true, "userid_revoked2", 0);
let tests : [(&str, Box<dyn Fn(&Cert, bool, _)>); 2] = [
("userid", Box::new(check_userids)),
("user-attribute", Box::new(check_uas)),
];
for (f, check) in tests.iter()
{
t!("Checking {} revocation", f);
t!("Normal key");
let cert = Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-0-public.pgp", f))).unwrap();
let now = time::SystemTime::now();
let selfsig0
= cert.userids().map(|b| {
b.binding_signature(now).unwrap()
.signature_creation_time().unwrap()
})
.max().unwrap();
check(&cert, false, selfsig0);
check(&cert, false, now);
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-1-soft-revocation.pgp", f))
).unwrap()).unwrap();
check(&cert, false, selfsig0);
check(&cert, true, now);
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-2-new-self-sig.pgp", f))
).unwrap()).unwrap();
check(&cert, false, selfsig0);
check(&cert, false, now);
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-3-hard-revocation.pgp", f))
).unwrap()).unwrap();
check(&cert, false, selfsig0);
check(&cert, true, now);
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key(
&format!("really-revoked-{}-4-new-self-sig.pgp", f))
).unwrap()).unwrap();
check(&cert, false, selfsig0);
check(&cert, false, now);
}
}
#[test]
fn unrevoked() {
let cert =
Cert::from_bytes(crate::tests::key("un-revoked-userid.pgp")).unwrap();
for uid in cert.userids() {
assert_eq!(uid.revoked(None), 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::autocrypt(None, Some("foo@example.com"))
.generate().unwrap();
assert!(tsk.primary.key().secret().is_some());
assert!(tsk.is_tsk());
let subkey_count = tsk.subkeys().len();
assert!(subkey_count > 0);
assert!(tsk.subkeys().all(|k| k.key().secret().is_some()));
let mut cert_bytes = Vec::new();
tsk.serialize(&mut cert_bytes).unwrap();
let cert = Cert::from_bytes(&cert_bytes[..]).unwrap();
assert!(cert.primary.key().secret().is_none());
assert!(!cert.is_tsk());
assert!(cert.subkeys().all(|k| k.key().secret().is_none()));
let merge1 = cert.clone().merge(tsk.clone()).unwrap();
assert!(merge1.is_tsk());
assert!(merge1.primary.key().secret().is_some());
assert_eq!(merge1.subkeys().len(), subkey_count);
assert!(merge1.subkeys().all(|k| k.key().secret().is_some()));
let merge2 = tsk.clone().merge(cert.clone()).unwrap();
assert!(merge2.is_tsk());
assert!(merge2.primary.key().secret().is_some());
assert_eq!(merge2.subkeys().len(), subkey_count);
assert!(merge2.subkeys().all(|k| k.key().secret().is_some()));
}
#[test]
fn issue_120() {
let cert = "
-----BEGIN PGP ARMORED FILE-----
xcBNBFoVcvoBCACykTKOJddF8SSUAfCDHk86cNTaYnjCoy72rMgWJsrMLnz/V16B
J9M7l6nrQ0JMnH2Du02A3w+kNb5q97IZ/M6NkqOOl7uqjyRGPV+XKwt0G5mN/ovg
8630BZAYS3QzavYf3tni9aikiGH+zTFX5pynTNfYRXNBof3Xfzl92yad2bIt4ITD
NfKPvHRko/tqWbclzzEn72gGVggt1/k/0dKhfsGzNogHxg4GIQ/jR/XcqbDFR3RC
/JJjnTOUPGsC1y82Xlu8udWBVn5mlDyxkad5laUpWWg17anvczEAyx4TTOVItLSu
43iPdKHSs9vMXWYID0bg913VusZ2Ofv690nDABEBAAHNJFRlc3R5IE1jVGVzdGZh
Y2UgPHRlc3R5QGV4YW1wbGUub3JnPsLAlAQTAQgAPhYhBD6Id8h3J0aSl1GJ9dA/
b4ZSJv6LBQJaFXL6AhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJ
ENA/b4ZSJv6Lxo8H/1XMt+Nqa6e0SG/up3ypKe5nplA0p/9j/s2EIsP8S8uPUd+c
WS17XOmPwkNDmHeL3J6hzwL74NlYSLEtyf7WoOV74xAKQA9WkqaKPHCtpll8aFWA
ktQDLWTPeKuUuSlobAoRtO17ZmheSQzmm7JYt4Ahkxt3agqGT05OsaAey6nIKqpq
ArokvdHTZ7AFZeSJIWmuCoT9M1lo3LAtLnRGOhBMJ5dDIeOwflJwNBXlJVi4mDPK
+fumV0MbSPvZd1/ivFjSpQyudWWtv1R1nAK7+a4CPTGxPvAQkLtRsL/V+Q7F3BJG
jAn4QVx8p4t3NOPuNgcoZpLBE3sc4Nfs5/CphMLHwE0EWhVy+gEIALSpjYD+tuWC
rj6FGP6crQjQzVlH+7axoM1ooTwiPs4fzzt2iLw3CJyDUviM5F9ZBQTei635RsAR
a/CJTSQYAEU5yXXxhoe0OtwnuvsBSvVT7Fox3pkfNTQmwMvkEbodhfKpqBbDKCL8
f5A8Bb7aISsLf0XRHWDkHVqlz8LnOR3f44wEWiTeIxLc8S1QtwX/ExyW47oPsjs9
ShCmwfSpcngH/vGBRTO7WeI54xcAtKSm/20B/MgrUl5qFo17kUWot2C6KjuZKkHk
3WZmJwQz+6rTB11w4AXt8vKkptYQCkfat2FydGpgRO5dVg6aWNJefOJNkC7MmlzC
ZrrAK8FJ6jcAEQEAAcLAdgQYAQgAIBYhBD6Id8h3J0aSl1GJ9dA/b4ZSJv6LBQJa
FXL6AhsMAAoJENA/b4ZSJv6Lt7kH/jPr5wg8lcamuLj4lydYiLttvvTtDTlD1TL+
IfwVARB/ruoerlEDr0zX1t3DCEcvJDiZfOqJbXtHt70+7NzFXrYxfaNFmikMgSQT
XqHrMQho4qpseVOeJPWGzGOcrxCdw/ZgrWbkDlAU5KaIvk+M4wFPivjbtW2Ro2/F
J4I/ZHhJlIPmM+hUErHC103b08pBENXDQlXDma7LijH5kWhyfF2Ji7Ft0EjghBaW
AeGalQHjc5kAZu5R76Mwt06MEQ/HL1pIvufTFxkr/SzIv8Ih7Kexb0IrybmfD351
Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
=1Vzu
-----END PGP ARMORED FILE-----
";
assert!(Cert::from_bytes(cert).is_err());
}
#[test]
fn missing_uids() {
let (cert, _) = CertBuilder::new()
.add_userid("test1@example.com")
.add_userid("test2@example.com")
.add_transport_encryption_subkey()
.add_certification_subkey()
.generate().unwrap();
assert_eq!(cert.subkeys().len(), 2);
let pile = cert
.into_packet_pile()
.into_children()
.filter(|pkt| {
match pkt {
&Packet::PublicKey(_) | &Packet::PublicSubkey(_) => true,
&Packet::Signature(ref sig) => {
sig.typ() == SignatureType::DirectKey
|| sig.typ() == SignatureType::SubkeyBinding
}
e => {
eprintln!("{:?}", e);
false
}
}
})
.collect::<Vec<_>>();
eprintln!("parse back");
let cert = Cert::from_packet_pile(PacketPile::from(pile)).unwrap();
assert_eq!(cert.subkeys().len(), 2);
}
#[test]
fn signature_order() {
let 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(),
uid.certifications(),
uid.self_revocations(),
uid.other_revocations()
].iter() {
for sigs in sigs.windows(2) {
cmps += 1;
assert!(sigs[0].signature_creation_time()
>= sigs[1].signature_creation_time());
}
}
assert_eq!(uid.self_signatures().first().unwrap(),
uid.binding_signature(None).unwrap());
}
assert!(cmps > 0);
}
#[test]
fn cert_reject_keyrings() {
let mut keyring = Vec::new();
keyring.extend_from_slice(crate::tests::key("neal.pgp"));
keyring.extend_from_slice(crate::tests::key("neal.pgp"));
assert!(Cert::from_bytes(&keyring).is_err());
}
#[test]
fn cert_is_send_and_sync() {
fn f<T: Send + Sync>(_: T) {}
f(Cert::from_bytes(crate::tests::key("testy-new.pgp")).unwrap());
}
#[test]
fn primary_userid() {
let cert = Cert::from_bytes(
crate::tests::key("really-revoked-userid-0-public.pgp")).unwrap();
let now = time::SystemTime::now();
let selfsig0
= cert.userids().map(|b| {
b.binding_signature(now).unwrap()
.signature_creation_time().unwrap()
})
.max().unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"Eminem");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"Eminem");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-1-soft-revocation.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"Eminem");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"Eminem");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-2-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"Eminem");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"Slim Shady");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-3-hard-revocation.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"Eminem");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"Eminem");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("really-revoked-userid-4-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"Eminem");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"Slim Shady");
let cert = Cert::from_bytes(
crate::tests::key("primary-key-0-public.pgp")).unwrap();
let selfsig0
= cert.userids().map(|b| {
b.binding_signature(now).unwrap()
.signature_creation_time().unwrap()
})
.max().unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"aaaaa");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"aaaaa");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-1-add-userid-bbbbb.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"aaaaa");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"bbbbb");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-2-make-aaaaa-primary.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"aaaaa");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"aaaaa");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-3-make-bbbbb-new-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"aaaaa");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"aaaaa");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-4-make-bbbbb-primary.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"aaaaa");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"bbbbb");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-5-make-aaaaa-self-sig.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"aaaaa");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"bbbbb");
let cert = cert.merge(
Cert::from_bytes(
crate::tests::key("primary-key-6-revoked-aaaaa.pgp")
).unwrap()).unwrap();
assert_eq!(cert.primary_userid(selfsig0).unwrap().userid().value(), b"aaaaa");
assert_eq!(cert.primary_userid(now).unwrap().userid().value(), b"bbbbb");
}
#[test]
fn binding_signature_lookup() {
use crate::types::Features;
use crate::packet::key::Key4;
use crate::types::Curve;
let 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 key: key::SecretKey
= Key4::generate_ecc(true, Curve::Ed25519).unwrap().into();
let mut pair = key.clone().into_keypair().unwrap();
let pk : key::PublicKey = key.clone().into();
let mut cert = Cert::from_packet_pile(PacketPile::from(vec![
pk.into(),
])).unwrap();
for (t, offset) in &[ (t2, 0), (t4, 0), (t3, 100), (t1, 300) ] {
for i in 0..100 {
let binding = signature::Builder::new(SignatureType::DirectKey)
.set_features(&Features::sequoia()).unwrap()
.set_key_flags(&KeyFlags::default()).unwrap()
.set_signature_creation_time(t1).unwrap()
.set_key_expiration_time(Some(
time::Duration::new((1 + i) * 24 * 60 * 60, 0)))
.unwrap()
.set_issuer_fingerprint(key.fingerprint()).unwrap()
.set_issuer(key.keyid()).unwrap()
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512]).unwrap()
.set_signature_creation_time(*t).unwrap()
.sign_direct_key(&mut pair).unwrap();
let binding : Packet = binding.into();
cert = cert.merge_packets(vec![ binding ]).unwrap();
assert_eq!(cert.primary_key_signature(*t),
cert.direct_signatures().get(*offset));
assert_eq!(cert.primary_key_signature(*t + a_sec),
cert.direct_signatures().get(*offset));
assert_eq!(cert.primary_key_signature(None),
cert.direct_signatures().get(0));
assert_eq!(cert.primary_key_signature(time_zero), None);
}
}
}
#[test]
fn keysigning_party() {
use crate::cert::packet::signature;
for cs in &[ CipherSuite::Cv25519,
CipherSuite::RSA3k,
CipherSuite::P256,
CipherSuite::P384,
CipherSuite::P521,
CipherSuite::RSA2k,
CipherSuite::RSA4k ]
{
let (alice, _) = CertBuilder::new()
.set_cipher_suite(*cs)
.add_userid("alice@foo.com")
.generate().unwrap();
let (bob, _) = CertBuilder::new()
.set_cipher_suite(*cs)
.add_userid("bob@bar.com")
.add_signing_subkey()
.generate().unwrap();
assert_eq!(bob.userids().len(), 1);
let bob_userid_binding = bob.userids().nth(0).unwrap();
assert_eq!(bob_userid_binding.userid().value(), b"bob@bar.com");
let sig_template
= signature::Builder::new(SignatureType::GenericCertification)
.set_trust_signature(255, 120)
.unwrap();
let alice_certifies_bob
= bob_userid_binding.userid().bind(
&mut alice.primary().clone().mark_parts_secret()
.unwrap().into_keypair().unwrap(),
&bob,
sig_template,
None).unwrap();
let bob
= bob.merge_packets(vec![ alice_certifies_bob.clone().into() ])
.unwrap();
assert_eq!(bob.userids().len(), 1);
let bob_userid_binding = bob.userids().nth(0).unwrap();
assert_eq!(bob_userid_binding.userid().value(), b"bob@bar.com");
assert_eq!(bob_userid_binding.certifications(),
&[ alice_certifies_bob.clone() ]);
assert_eq!(
alice_certifies_bob
.verify_userid_binding(&alice.primary().clone(),
&bob.primary().clone(),
bob_userid_binding.userid())
.unwrap(),
true);
}
}
#[test]
fn decrypt_secrets() {
let (cert, _) = CertBuilder::new()
.add_transport_encryption_subkey()
.set_password(Some(String::from("streng geheim").into()))
.generate().unwrap();
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 0);
let mut primary = cert.primary().clone();
let algo = primary.pk_algo();
primary.secret_mut().unwrap()
.decrypt_in_place(algo, &"streng geheim".into()).unwrap();
let cert = cert.merge_packets(vec![
primary.mark_parts_secret().unwrap().mark_role_primary().into()
]).unwrap();
assert_eq!(cert.keys().secret().count(), 2);
assert_eq!(cert.keys().unencrypted_secret().count(), 1);
}
}