diff options
author | Neal H. Walfield <neal@pep.foundation> | 2020-04-07 16:55:34 +0200 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2020-04-07 22:02:47 +0200 |
commit | a1b06c5bc4b755c3e9d7a2e79ff20c216258fdcc (patch) | |
tree | 74582a46ff2a2208aa787c8881f285259f8c8bde /openpgp | |
parent | a67e57dcd9ce176bc03d7699b2e4decf137d3f9c (diff) |
openpgp: Support setting designated revocations on new keys
- Add CertBuilder::set_revocation_keys to add designated revokers to
the certificate.
Diffstat (limited to 'openpgp')
-rw-r--r-- | openpgp/src/cert/builder.rs | 82 |
1 files changed, 78 insertions, 4 deletions
diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs index e727881e..5d74d727 100644 --- a/openpgp/src/cert/builder.rs +++ b/openpgp/src/cert/builder.rs @@ -18,6 +18,7 @@ use crate::types::{ KeyFlags, SignatureType, SymmetricAlgorithm, + RevocationKey, }; /// Groups symmetric and asymmetric algorithms @@ -117,6 +118,7 @@ pub struct CertBuilder { userids: Vec<packet::UserID>, user_attributes: Vec<packet::UserAttribute>, password: Option<Password>, + revocation_keys: Option<Vec<RevocationKey>>, } impl CertBuilder { @@ -129,7 +131,7 @@ impl CertBuilder { /// `CertBuilder::add_transport_encryption_subkey`, for instance), and user /// ids (using `CertBuilder::add_userid`). pub fn new() -> Self { - CertBuilder{ + CertBuilder { creation_time: None, ciphersuite: CipherSuite::default(), primary: KeyBlueprint{ @@ -141,6 +143,7 @@ impl CertBuilder { userids: vec![], user_attributes: vec![], password: None, + revocation_keys: None, } } @@ -176,6 +179,7 @@ impl CertBuilder { userids: userids.into_iter().map(|x| x.into()).collect(), user_attributes: vec![], password: None, + revocation_keys: None, } } @@ -279,6 +283,12 @@ impl CertBuilder { self } + /// Sets designated revokers. + pub fn set_revocation_keys(mut self, revocation_keys: Vec<RevocationKey>) -> Self { + self.revocation_keys = Some(revocation_keys); + self + } + /// Generates the actual Cert. pub fn generate(mut self) -> Result<(Cert, Signature)> { use crate::{PacketPile, Packet}; @@ -309,12 +319,18 @@ impl CertBuilder { })); packets.push(sig.clone().into()); + let sig = signature::Builder::from(sig.clone()); + + // Remove subpackets that needn't be copied into the binding + // signatures. + let sig = sig.set_revocation_key(vec![])?; + let mut cert = Cert::from_packet_pile(PacketPile::from(packets))?; // Sign UserIDs. for uid in self.userids.into_iter() { - let builder = signature::Builder::from(sig.clone()) + let builder = sig.clone() .set_type(SignatureType::PositiveCertification) // GnuPG wants at least a 512-bit hash for P521 keys. .set_hash_algo(HashAlgorithm::SHA512); @@ -324,7 +340,7 @@ impl CertBuilder { // Sign UserAttributes. for ua in self.user_attributes.into_iter() { - let builder = signature::Builder::from(sig.clone()) + let builder = sig.clone() .set_type(SignatureType::PositiveCertification) // GnuPG wants at least a 512-bit hash for P521 keys. .set_hash_algo(HashAlgorithm::SHA512); @@ -408,7 +424,7 @@ impl CertBuilder { .unwrap_or(self.ciphersuite) .generate_key(&KeyFlags::default().set_certification(true))?; key.set_creation_time(creation_time)?; - let sig = signature::Builder::new(SignatureType::DirectKey) + let mut sig = signature::Builder::new(SignatureType::DirectKey) // GnuPG wants at least a 512-bit hash for P521 keys. .set_hash_algo(HashAlgorithm::SHA512) .set_features(&Features::sequoia())? @@ -419,6 +435,10 @@ impl CertBuilder { .set_issuer(key.keyid())? .set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512])?; + 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)?; @@ -429,7 +449,10 @@ impl CertBuilder { #[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; @@ -663,4 +686,55 @@ mod tests { .signature_creation_time().unwrap(), UNIX_EPOCH); } } + + #[test] + fn designated_revokers() -> Result<()> { + 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().collect::<Vec<_>>(), + revokers.iter().collect::<Vec<_>>()); + + // The designated revokers on the direct signature should also + // be returned when querying components for designated + // revokers. + assert_eq!(cert.primary_key().revocation_keys().collect::<Vec<_>>(), + revokers.iter().collect::<Vec<_>>()); + assert_eq!(cert.primary_userid()?.revocation_keys().collect::<Vec<_>>(), + revokers.iter().collect::<Vec<_>>()); + + + // Do it again, with a key that has no User IDs. + 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().collect::<Vec<_>>(), + revokers.iter().collect::<Vec<_>>()); + + // The designated revokers on the direct signature should also + // be returned when querying components for designated + // revokers. + assert_eq!(cert.primary_key().revocation_keys().collect::<Vec<_>>(), + revokers.iter().collect::<Vec<_>>()); + Ok(()) + } } |