use std::time;
use std::marker::PhantomData;
use crate::packet;
use crate::packet::{
Key,
key::Key4,
key::KeyRole,
key::SecretKey as KeySecretKey,
key::SecretParts as KeySecretParts,
};
use crate::Result;
use crate::packet::Signature;
use crate::packet::signature::{
self,
SignatureBuilder,
subpacket::SubpacketTag,
};
use crate::cert::prelude::*;
use crate::Error;
use crate::crypto::{Password, Signer};
use crate::types::{
Features,
HashAlgorithm,
KeyFlags,
SignatureType,
SymmetricAlgorithm,
RevocationKey,
};
mod key;
pub use key::{
KeyBuilder,
SubkeyBuilder,
};
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub enum CipherSuite {
Cv25519,
RSA3k,
P256,
P384,
P521,
RSA2k,
RSA4k,
}
assert_send_and_sync!(CipherSuite);
impl Default for CipherSuite {
fn default() -> Self {
CipherSuite::Cv25519
}
}
impl CipherSuite {
pub fn is_supported(&self) -> Result<()> {
use crate::types::{Curve, PublicKeyAlgorithm};
use CipherSuite::*;
macro_rules! check_pk {
($pk: expr) => {
if ! $pk.is_supported() {
return Err(Error::UnsupportedPublicKeyAlgorithm($pk)
.into());
}
}
}
macro_rules! check_curve {
($curve: expr) => {
if ! $curve.is_supported() {
return Err(Error::UnsupportedEllipticCurve($curve)
.into());
}
}
}
match self {
Cv25519 => {
check_pk!(PublicKeyAlgorithm::EdDSA);
check_curve!(Curve::Ed25519);
check_pk!(PublicKeyAlgorithm::ECDH);
check_curve!(Curve::Cv25519);
},
RSA2k | RSA3k | RSA4k => {
check_pk!(PublicKeyAlgorithm::RSAEncryptSign);
},
P256 => {
check_pk!(PublicKeyAlgorithm::ECDSA);
check_curve!(Curve::NistP256);
check_pk!(PublicKeyAlgorithm::ECDH);
},
P384 => {
check_pk!(PublicKeyAlgorithm::ECDSA);
check_curve!(Curve::NistP384);
check_pk!(PublicKeyAlgorithm::ECDH);
},
P521 => {
check_pk!(PublicKeyAlgorithm::ECDSA);
check_curve!(Curve::NistP521);
check_pk!(PublicKeyAlgorithm::ECDH);
},
}
Ok(())
}
fn generate_key<K, R>(self, flags: K)
-> Result<Key<KeySecretParts, R>>
where R: KeyRole,
K: AsRef<KeyFlags>,
{
use crate::types::Curve;
match self {
CipherSuite::RSA2k =>
Key4::generate_rsa(2048),
CipherSuite::RSA3k =>
Key4::generate_rsa(3072),
CipherSuite::RSA4k =>
Key4::generate_rsa(4096),
CipherSuite::Cv25519 | CipherSuite::P256 |
CipherSuite::P384 | CipherSuite::P521 => {
let flags = flags.as_ref();
let sign = flags.for_certification() || flags.for_signing()
|| flags.for_authentication();
let encrypt = flags.for_transport_encryption()
|| flags.for_storage_encryption();
let curve = match self {
CipherSuite::Cv25519 if sign => Curve::Ed25519,
CipherSuite::Cv25519 if encrypt => Curve::Cv25519,
CipherSuite::Cv25519 => {
return Err(Error::InvalidOperation(
"No key flags set".into())
.into());
}
CipherSuite::P256 => Curve::NistP256,
CipherSuite::P384 => Curve::NistP384,
CipherSuite::P521 => Curve::NistP521,
_ => unreachable!(),
};
match (sign, encrypt) {
(true, false) => Key4::generate_ecc(true, curve),
(false, true) => Key4::generate_ecc(false, curve),
(true, true) =>
Err(Error::InvalidOperation(
"Can't use key for encryption and signing".into())
.into()),
(false, false) =>
Err(Error::InvalidOperation(
"No key flags set".into())
.into()),
}
},
}.map(|key| key.into())
}
}
#[derive(Clone, Debug)]
pub struct KeyBlueprint {
flags: KeyFlags,
validity: Option<time::Duration>,
ciphersuite: Option<CipherSuite>,
}
assert_send_and_sync!(KeyBlueprint);
pub struct CertBuilder<'a> {
creation_time: Option<std::time::SystemTime>,
ciphersuite: CipherSuite,
primary: KeyBlueprint,
subkeys: Vec<(Option<SignatureBuilder>, KeyBlueprint)>,
userids: Vec<(Option<SignatureBuilder>, packet::UserID)>,
user_attributes: Vec<(Option<SignatureBuilder>, packet::UserAttribute)>,
password: Option<Password>,
revocation_keys: Option<Vec<RevocationKey>>,
phantom: PhantomData<&'a ()>,
}
assert_send_and_sync!(CertBuilder<'_>);
#[allow(clippy::new_without_default)]
impl CertBuilder<'_> {
pub fn new() -> Self {
CertBuilder {
creation_time: None,
ciphersuite: CipherSuite::default(),
primary: KeyBlueprint{
flags: KeyFlags::empty().set_certification(),
validity: None,
ciphersuite: None,
},
subkeys: vec![],
userids: vec![],
user_attributes: vec![],
password: None,
revocation_keys: None,
phantom: PhantomData,
}
}
pub fn general_purpose<C, U>(ciphersuite: C, userid: Option<U>) -> Self
where C: Into<Option<CipherSuite>>,
U: Into<packet::UserID>
{
let mut builder = Self::new()
.set_cipher_suite(ciphersuite.into().unwrap_or_default())
.set_primary_key_flags(KeyFlags::empty().set_certification())
.set_validity_period(
time::Duration::new(3 * 52 * 7 * 24 * 60 * 60, 0))
.add_signing_subkey()
.add_subkey(KeyFlags::empty()
.set_transport_encryption()
.set_storage_encryption(), None, None);
if let Some(u) = userid.map(Into::into) {
builder = builder.add_userid(u);
}
builder
}
pub fn set_creation_time<T>(mut self, creation_time: T) -> Self
where T: Into<Option<std::time::SystemTime>>,
{
self.creation_time = creation_time.into();
self
}
pub fn creation_time(&self) -> Option<std::time::SystemTime>
{
self.creation_time
}
pub fn set_cipher_suite(mut self, cs: CipherSuite) -> Self {
self.ciphersuite = cs;
self
}
pub fn add_userid<U>(mut self, uid: U) -> Self
where U: Into<packet::UserID>
{
self.userids.push((None, uid.into()));
self
}
pub fn add_userid_with<U, B>(mut self, uid: U, builder: B)
-> Result<Self>
where U: Into<packet::UserID>,
B: Into<SignatureBuilder>,
{
let builder = builder.into();
match builder.typ() {
SignatureType::GenericCertification
| SignatureType::PersonaCertification
| SignatureType::CasualCertification
| SignatureType::PositiveCertification =>
{
self.userids.push((Some(builder), uid.into()));
Ok(self)
},
t =>
Err(Error::InvalidArgument(format!(
"Signature type is not a certification: {}", t)).into()),
}
}
pub fn add_user_attribute<U>(mut self, ua: U) -> Self
where U: Into<packet::UserAttribute>
{
self.user_attributes.push((None, ua.into()));
self
}
pub fn add_user_attribute_with<U, B>(mut self, ua: U, builder: B)
-> Result<Self>
where U: Into<packet::UserAttribute>,
B: Into<SignatureBuilder>,
{
let builder = builder.into();
match builder.typ() {
SignatureType::GenericCertification
| SignatureType::PersonaCertification
| SignatureType::CasualCertification
| SignatureType::PositiveCertification =>
{
self.user_attributes.push((Some(builder), ua.into()));
Ok(self)
},
t =>
Err(Error::InvalidArgument(format!(
"Signature type is not a certification: {}", t)).into()),
}
}
pub fn add_signing_subkey(self) -> Self {
self.add_subkey(KeyFlags::empty().set_signing(), None, None)
}
pub fn add_transport_encryption_subkey(self) -> Self {
self.add_subkey(KeyFlags::empty().set_transport_encryption(),
None, None)
}
pub fn add_storage_encryption_subkey(self) -> Self {
self.add_subkey(KeyFlags::empty().set_storage_encryption(),
None, None)
}
pub fn add_certification_subkey(self) -> Self {
self.add_subkey(KeyFlags::empty().set_certification(), None, None)
}
pub fn add_authentication_subkey(self) -> Self {
self.add_subkey(KeyFlags::empty().set_authentication(), None, None)
}
pub fn add_subkey<T, C>(mut self, flags: KeyFlags, validity: T, cs: C)
-> Self
where T: Into<Option<time::Duration>>,
C: Into<Option<CipherSuite>>,
{
self.subkeys.push((None, KeyBlueprint {
flags,
validity: validity.into(),
ciphersuite: cs.into(),
}));
self
}
pub fn add_subkey_with<T, C, B>(mut self, flags: KeyFlags, validity: T,
cs: C, builder: B) -> Result<Self>
where T: Into<Option<time::Duration>>,
C: Into<Option<CipherSuite>>,
B: Into<SignatureBuilder>,
{
let builder = builder.into();
match builder.typ() {
SignatureType::SubkeyBinding => {
self.subkeys.push((Some(builder), KeyBlueprint {
flags,
validity: validity.into(),
ciphersuite: cs.into(),
}));
Ok(self)
},
t =>
Err(Error::InvalidArgument(format!(
"Signature type is not a subkey binding: {}", t)).into()),
}
}
pub fn set_primary_key_flags(mut self, flags: KeyFlags) -> Self {
self.primary.flags = flags;
self
}
pub fn set_password(mut self, password: Option<Password>) -> Self {
self.password = password;
self
}
pub fn set_validity_period<T>(mut self, validity: T) -> Self
where T: Into<Option<time::Duration>>
{
self.primary.validity = validity.into();
self
}
pub fn set_revocation_keys(mut self, revocation_keys: Vec<RevocationKey>)
-> Self
{
self.revocation_keys = Some(revocation_keys);
self
}
pub fn generate(self) -> Result<(Cert, Signature)> {
use crate::Packet;
use crate::types::ReasonForRevocation;
let creation_time =
self.creation_time.unwrap_or_else(|| {
use crate::packet::signature::SIG_BACKDATE_BY;
crate::now() -
time::Duration::new(SIG_BACKDATE_BY, 0)
});
let (primary, sig, mut signer) = self.primary_key(creation_time)?;
let mut cert = Cert::try_from(vec![
Packet::SecretKey({
let mut primary = primary.clone();
if let Some(ref password) = self.password {
primary.secret_mut().encrypt_in_place(password)?;
}
primary
}),
sig.into(),
])?;
let have_primary_user_thing = {
let is_primary = |osig: &Option<SignatureBuilder>| -> bool {
osig.as_ref().and_then(|s| s.primary_userid()).unwrap_or(false)
};
self.userids.iter().map(|(s, _)| s).any(is_primary)
|| self.user_attributes.iter().map(|(s, _)| s).any(is_primary)
};
let mut emitted_primary_user_thing = false;
for (template, uid) in self.userids.into_iter() {
let sig = template.unwrap_or_else(
|| SignatureBuilder::new(SignatureType::PositiveCertification));
let sig = Self::signature_common(sig, creation_time)?;
let mut sig = Self::add_primary_key_metadata(sig, &self.primary)?;
if emitted_primary_user_thing {
sig = sig.modify_hashed_area(|mut a| {
a.remove_all(SubpacketTag::PrimaryUserID);
Ok(a)
})?;
} else if have_primary_user_thing {
emitted_primary_user_thing |=
sig.primary_userid().unwrap_or(false);
} else {
sig = sig.set_primary_userid(true)?;
emitted_primary_user_thing = true;
}
let signature = uid.bind(&mut signer, &cert, sig)?;
cert = cert.insert_packets(
vec![Packet::from(uid), signature.into()])?;
}
for (template, ua) in self.user_attributes.into_iter() {
let sig = template.unwrap_or_else(
|| SignatureBuilder::new(SignatureType::PositiveCertification));
let sig = Self::signature_common(sig, creation_time)?;
let mut sig = Self::add_primary_key_metadata(sig, &self.primary)?;
if emitted_primary_user_thing {
sig = sig.modify_hashed_area(|mut a| {
a.remove_all(SubpacketTag::PrimaryUserID);
Ok(a)
})?;
} else if have_primary_user_thing {
emitted_primary_user_thing |=
sig.primary_userid().unwrap_or(false);
} else {
sig = sig.set_primary_userid(true)?;
emitted_primary_user_thing = true;
}
let signature = ua.bind(&mut signer, &cert, sig)?;
cert = cert.insert_packets(
vec![Packet::from(ua), signature.into()])?;
}
for (template, blueprint) in self.subkeys {
let flags = &blueprint.flags;
let mut subkey = blueprint.ciphersuite
.unwrap_or(self.ciphersuite)
.generate_key(flags)?;
subkey.set_creation_time(creation_time)?;
let sig = template.unwrap_or_else(
|| SignatureBuilder::new(SignatureType::SubkeyBinding));
let sig = Self::signature_common(sig, creation_time)?;
let mut builder = sig
.set_key_flags(flags.clone())?
.set_key_validity_period(blueprint.validity.or(self.primary.validity))?;
if flags.for_certification() || flags.for_signing() {
let mut subkey_signer = subkey.clone().into_keypair().unwrap();
let backsig =
signature::SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
.set_signature_creation_time(creation_time)?
.set_hash_algo(HashAlgorithm::SHA512)
.sign_primary_key_binding(&mut subkey_signer, &primary,
&subkey)?;
builder = builder.set_embedded_signature(backsig)?;
}
let signature = subkey.bind(&mut signer, &cert, builder)?;
if let Some(ref password) = self.password {
subkey.secret_mut().encrypt_in_place(password)?;
}
cert = cert.insert_packets(vec![Packet::SecretSubkey(subkey),
signature.into()])?;
}
let revocation = CertRevocationBuilder::new()
.set_signature_creation_time(creation_time)?
.set_reason_for_revocation(
ReasonForRevocation::Unspecified, b"Unspecified")?
.build(&mut signer, &cert, None)?;
assert!(cert.bad.is_empty());
assert!(cert.unknowns.is_empty());
Ok((cert, revocation))
}
fn primary_key(&self, creation_time: std::time::SystemTime)
-> Result<(KeySecretKey, Signature, Box<dyn Signer>)>
{
let mut key = self.primary.ciphersuite
.unwrap_or(self.ciphersuite)
.generate_key(KeyFlags::empty().set_certification())?;
key.set_creation_time(creation_time)?;
let sig = SignatureBuilder::new(SignatureType::DirectKey);
let sig = Self::signature_common(sig, creation_time)?;
let mut sig = Self::add_primary_key_metadata(sig, &self.primary)?;
if let Some(ref revocation_keys) = self.revocation_keys {
sig = sig.set_revocation_key(revocation_keys.clone())?;
}
let mut signer = key.clone().into_keypair()
.expect("key generated above has a secret");
let sig = sig.sign_direct_key(&mut signer, key.parts_as_public())?;
Ok((key, sig, Box::new(signer)))
}
fn signature_common(builder: SignatureBuilder,
creation_time: time::SystemTime)
-> Result<SignatureBuilder>
{
builder
.set_hash_algo(HashAlgorithm::SHA512)
.set_signature_creation_time(creation_time)
}
fn add_primary_key_metadata(builder: SignatureBuilder,
primary: &KeyBlueprint)
-> Result<SignatureBuilder>
{
builder
.set_features(Features::sequoia())?
.set_key_flags(primary.flags.clone())?
.set_key_validity_period(primary.validity)?
.set_preferred_hash_algorithms(vec![
HashAlgorithm::SHA512,
HashAlgorithm::SHA256,
])?
.set_preferred_symmetric_algorithms(vec![
SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES128,
])
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
use crate::Fingerprint;
use crate::packet::signature::subpacket::{SubpacketTag, SubpacketValue};
use crate::types::PublicKeyAlgorithm;
use crate::policy::StandardPolicy as P;
#[test]
fn all_opts() {
let p = &P::new();
let (cert, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.add_userid("test1@example.com")
.add_userid("test2@example.com")
.add_signing_subkey()
.add_transport_encryption_subkey()
.add_certification_subkey()
.generate().unwrap();
let mut userids = cert.userids().with_policy(p, None)
.map(|u| String::from_utf8_lossy(u.userid().value()).into_owned())
.collect::<Vec<String>>();
userids.sort();
assert_eq!(userids,
&[ "test1@example.com",
"test2@example.com",
][..]);
assert_eq!(cert.subkeys().count(), 3);
}
#[test]
fn direct_key_sig() {
let p = &P::new();
let (cert, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.add_signing_subkey()
.add_transport_encryption_subkey()
.add_certification_subkey()
.generate().unwrap();
assert_eq!(cert.userids().count(), 0);
assert_eq!(cert.subkeys().count(), 3);
let sig =
cert.primary_key().with_policy(p, None).unwrap().binding_signature();
assert_eq!(sig.typ(), crate::types::SignatureType::DirectKey);
assert!(sig.features().unwrap().supports_mdc());
}
#[test]
fn setter() {
let (cert1, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.set_cipher_suite(CipherSuite::RSA3k)
.set_cipher_suite(CipherSuite::Cv25519)
.generate().unwrap();
assert_eq!(cert1.primary_key().pk_algo(), PublicKeyAlgorithm::EdDSA);
let (cert2, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::RSA3k)
.add_userid("test2@example.com")
.add_transport_encryption_subkey()
.generate().unwrap();
assert_eq!(cert2.primary_key().pk_algo(),
PublicKeyAlgorithm::RSAEncryptSign);
assert_eq!(cert2.subkeys().next().unwrap().key().pk_algo(),
PublicKeyAlgorithm::RSAEncryptSign);
}
#[test]
fn defaults() {
let p = &P::new();
let (cert1, _) = CertBuilder::new()
.add_userid("test2@example.com")
.generate().unwrap();
assert_eq!(cert1.primary_key().pk_algo(),
PublicKeyAlgorithm::EdDSA);
assert!(cert1.subkeys().next().is_none());
assert!(cert1.with_policy(p, None).unwrap().primary_userid().unwrap()
.binding_signature().features().unwrap().supports_mdc());
}
#[test]
fn not_always_certify() {
let p = &P::new();
let (cert1, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.set_primary_key_flags(KeyFlags::empty())
.add_transport_encryption_subkey()
.generate().unwrap();
assert!(! cert1.primary_key().with_policy(p, None).unwrap().for_certification());
assert_eq!(cert1.keys().subkeys().count(), 1);
}
#[test]
fn gen_wired_subkeys() {
let (cert1, _) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.set_primary_key_flags(KeyFlags::empty())
.add_subkey(KeyFlags::empty().set_certification(), None, None)
.generate().unwrap();
let sig_pkts = cert1.subkeys().next().unwrap().bundle().self_signatures[0].hashed_area();
match sig_pkts.subpacket(SubpacketTag::KeyFlags).unwrap().value() {
SubpacketValue::KeyFlags(ref ks) => assert!(ks.for_certification()),
v => panic!("Unexpected subpacket: {:?}", v),
}
assert_eq!(cert1.subkeys().count(), 1);
}
#[test]
fn generate_revocation_certificate() {
let p = &P::new();
use crate::types::RevocationStatus;
let (cert, revocation) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.generate().unwrap();
assert_eq!(cert.revocation_status(p, None),
RevocationStatus::NotAsFarAsWeKnow);
let cert = cert.insert_packets(revocation.clone()).unwrap();
assert_eq!(cert.revocation_status(p, None),
RevocationStatus::Revoked(vec![ &revocation ]));
}
#[test]
fn builder_roundtrip() {
use std::convert::TryFrom;
let (cert,_) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.add_signing_subkey()
.generate().unwrap();
let pile = cert.clone().into_packet_pile().into_children().collect::<Vec<_>>();
let exp = Cert::try_from(pile).unwrap();
assert_eq!(cert, exp);
}
#[test]
fn encrypted_secrets() {
let (cert,_) = CertBuilder::new()
.set_cipher_suite(CipherSuite::Cv25519)
.set_password(Some(String::from("streng geheim").into()))
.generate().unwrap();
assert!(cert.primary_key().optional_secret().unwrap().is_encrypted());
}
#[test]
fn all_ciphersuites() {
use self::CipherSuite::*;
for cs in vec![Cv25519, RSA3k, P256, P384, P521, RSA2k, RSA4k]
.into_iter().filter(|cs| cs.is_supported().is_ok())
{
assert!(CertBuilder::new()
.set_cipher_suite(cs)
.generate().is_ok());
}
}
#[test]
fn validity_periods() {
let p = &P::new();
let now = crate::now();
let s = std::time::Duration::new(1, 0);
let (cert,_) = CertBuilder::new()
.set_creation_time(now)
.set_validity_period(600 * s)
.add_subkey(KeyFlags::empty().set_signing(),
300 * s, None)
.add_subkey(KeyFlags::empty().set_authentication(),
None, None)
.generate().unwrap();
let key = cert.primary_key().key();
let sig = &cert.primary_key().bundle().self_signatures()[0];
assert!(sig.key_alive(key, now).is_ok());
assert!(sig.key_alive(key, now + 590 * s).is_ok());
assert!(! sig.key_alive(key, now + 610 * s).is_ok());
let ka = cert.keys().with_policy(p, now).alive().revoked(false)
.for_signing().next().unwrap();
assert!(ka.alive().is_ok());
assert!(ka.clone().with_policy(p, now + 290 * s).unwrap().alive().is_ok());
assert!(! ka.clone().with_policy(p, now + 310 * s).unwrap().alive().is_ok());
let ka = cert.keys().with_policy(p, now).alive().revoked(false)
.for_authentication().next().unwrap();
assert!(ka.alive().is_ok());
assert!(ka.clone().with_policy(p, now + 590 * s).unwrap().alive().is_ok());
assert!(! ka.clone().with_policy(p, now + 610 * s).unwrap().alive().is_ok());
}
#[test]
fn creation_time() {
let p = &P::new();
use std::time::UNIX_EPOCH;
let (cert, rev) = CertBuilder::new()
.set_creation_time(UNIX_EPOCH)
.set_cipher_suite(CipherSuite::Cv25519)
.add_userid("foo")
.add_signing_subkey()
.generate().unwrap();
assert_eq!(cert.primary_key().creation_time(), UNIX_EPOCH);
assert_eq!(cert.primary_key().with_policy(p, None).unwrap()
.binding_signature()
.signature_creation_time().unwrap(), UNIX_EPOCH);
assert_eq!(cert.primary_key().with_policy(p, None).unwrap()
.direct_key_signature().unwrap()
.signature_creation_time().unwrap(), UNIX_EPOCH);
assert_eq!(rev.signature_creation_time().unwrap(), UNIX_EPOCH);
assert_eq!(cert.keys().with_policy(p, None).count(), 2);
for ka in cert.keys().with_policy(p, None) {
assert_eq!(ka.key().creation_time(), UNIX_EPOCH);
assert_eq!(ka.binding_signature()
.signature_creation_time().unwrap(), UNIX_EPOCH);
}
assert_eq!(cert.userids().count(), 1);
for ui in cert.userids().with_policy(p, None) {
assert_eq!(ui.binding_signature()
.signature_creation_time().unwrap(), UNIX_EPOCH);
}
}
#[test]
fn designated_revokers() -> Result<()> {
use std::collections::HashSet;
let p = &P::new();
let fpr1 = "C03F A641 1B03 AE12 5764 6118 7223 B566 78E0 2528";
let fpr2 = "50E6 D924 308D BF22 3CFB 510A C2B8 1905 6C65 2598";
let revokers = vec![
RevocationKey::new(PublicKeyAlgorithm::RSAEncryptSign,
Fingerprint::from_str(fpr1)?,
false),
RevocationKey::new(PublicKeyAlgorithm::ECDSA,
Fingerprint::from_str(fpr2)?,
false)
];
let (cert,_)
= CertBuilder::general_purpose(None, Some("alice@example.org"))
.set_revocation_keys(revokers.clone())
.generate()?;
let cert = cert.with_policy(p, None)?;
assert_eq!(cert.revocation_keys(None).collect::<HashSet<_>>(),
revokers.iter().collect::<HashSet<_>>());
let (cert,_) = CertBuilder::new()
.set_revocation_keys(revokers.clone())
.generate()?;
let cert = cert.with_policy(p, None)?;
assert!(cert.primary_userid().is_err());
assert_eq!(cert.revocation_keys(None).collect::<HashSet<_>>(),
revokers.iter().collect::<HashSet<_>>());
let now = crate::types::Timestamp::now();
let then = now.checked_add(crate::types::Duration::days(1)?).unwrap();
let (cert,_) = CertBuilder::new()
.set_revocation_keys(revokers.clone())
.set_creation_time(now)
.generate()?;
use crate::crypto::hash::Hash;
let mut hash = HashAlgorithm::SHA512.context()?;
cert.primary_key().hash(&mut hash);
let mut primary_signer =
cert.primary_key().key().clone().parts_into_secret()?
.into_keypair()?;
let sig = signature::SignatureBuilder::new(SignatureType::DirectKey)
.set_signature_creation_time(then)?
.sign_hash(&mut primary_signer, hash)?;
let cert = cert.insert_packets(sig)?;
assert!(cert.with_policy(p, then)?.primary_userid().is_err());
assert_eq!(cert.revocation_keys(p).collect::<HashSet<_>>(),
revokers.iter().collect::<HashSet<_>>());
Ok(())
}
#[test]
fn primary_user_things() -> Result<()> {
fn count_primary_user_things(c: Cert) -> usize {
c.into_packets().map(|p| match p {
Packet::Signature(s) if s.primary_userid().unwrap_or(false)
=> 1,
_ => 0,
}).sum()
}
use crate::packet::{prelude::*, user_attribute::Subpacket};
let ua_foo =
UserAttribute::new(&[Subpacket::Unknown(7, vec![7; 7].into())])?;
let ua_bar =
UserAttribute::new(&[Subpacket::Unknown(11, vec![11; 11].into())])?;
let p = &P::new();
let positive = SignatureType::PositiveCertification;
let (c, _) = CertBuilder::new().generate()?;
assert_eq!(count_primary_user_things(c), 0);
let (c, _) = CertBuilder::new()
.add_userid("foo")
.generate()?;
assert_eq!(count_primary_user_things(c), 1);
let (c, _) = CertBuilder::new()
.add_userid("foo")
.add_userid("bar")
.generate()?;
assert_eq!(count_primary_user_things(c), 1);
let (c, _) = CertBuilder::new()
.add_user_attribute(ua_foo.clone())
.generate()?;
assert_eq!(count_primary_user_things(c), 1);
let (c, _) = CertBuilder::new()
.add_user_attribute(ua_foo.clone())
.add_user_attribute(ua_bar.clone())
.generate()?;
assert_eq!(count_primary_user_things(c), 1);
let (c, _) = CertBuilder::new()
.add_userid("foo")
.add_user_attribute(ua_foo.clone())
.generate()?;
let vc = c.with_policy(p, None)?;
assert_eq!(vc.primary_userid()?.binding_signature().primary_userid(),
Some(true));
assert_eq!(vc.primary_user_attribute()?.binding_signature().primary_userid(),
None);
assert_eq!(count_primary_user_things(c), 1);
let (c, _) = CertBuilder::new()
.add_user_attribute(ua_foo.clone())
.add_userid("foo")
.generate()?;
let vc = c.with_policy(p, None)?;
assert_eq!(vc.primary_userid()?.binding_signature().primary_userid(),
Some(true));
assert_eq!(vc.primary_user_attribute()?.binding_signature().primary_userid(),
None);
assert_eq!(count_primary_user_things(c), 1);
let (c, _) = CertBuilder::new()
.add_userid("foo")
.add_userid_with(
"buz",
SignatureBuilder::new(positive).set_primary_userid(false)?)?
.add_userid_with(
"bar",
SignatureBuilder::new(positive).set_primary_userid(true)?)?
.add_userid_with(
"baz",
SignatureBuilder::new(positive).set_primary_userid(true)?)?
.generate()?;
let vc = c.with_policy(p, None)?;
assert_eq!(vc.primary_userid()?.value(), b"bar");
assert_eq!(count_primary_user_things(c), 1);
Ok(())
}
}