diff options
Diffstat (limited to 'openpgp/src/cert')
-rw-r--r-- | openpgp/src/cert/bindings.rs | 365 | ||||
-rw-r--r-- | openpgp/src/cert/builder.rs | 665 | ||||
-rw-r--r-- | openpgp/src/cert/keyiter.rs | 501 | ||||
-rw-r--r-- | openpgp/src/cert/mod.rs | 3115 | ||||
-rw-r--r-- | openpgp/src/cert/parser/low_level/grammar.lalrpop | 297 | ||||
-rw-r--r-- | openpgp/src/cert/parser/low_level/grammar.rs | 2 | ||||
-rw-r--r-- | openpgp/src/cert/parser/low_level/lexer.rs | 156 | ||||
-rw-r--r-- | openpgp/src/cert/parser/low_level/mod.rs | 80 | ||||
-rw-r--r-- | openpgp/src/cert/parser/mod.rs | 931 | ||||
-rw-r--r-- | openpgp/src/cert/revoke.rs | 485 |
10 files changed, 6597 insertions, 0 deletions
diff --git a/openpgp/src/cert/bindings.rs b/openpgp/src/cert/bindings.rs new file mode 100644 index 00000000..032c7ba2 --- /dev/null +++ b/openpgp/src/cert/bindings.rs @@ -0,0 +1,365 @@ +use std::time; + +use crate::Error; +use crate::Result; +use crate::Cert; +use crate::types::{HashAlgorithm, SignatureType}; +use crate::conversions::Time; +use crate::crypto::Signer; +use crate::packet::{UserID, UserAttribute, key, Key, signature, Signature}; + +impl<P: key::KeyParts> Key<P, key::SubordinateRole> { + /// Creates a binding signature. + /// + /// The signature binds this userid to `cert`. `signer` will be used + /// to create a signature using `signature` as builder. + /// The`hash_algo` defaults to SHA512, `creation_time` to the + /// current time. + /// + /// This function adds a creation time subpacket, a issuer + /// fingerprint subpacket, and a issuer subpacket to the + /// signature. + /// + /// # Example + /// + /// This example demonstrates how to bind this key to a Cert. Note + /// that in general, the `CertBuilder` is a better way to add + /// subkeys to a Cert. + /// + /// ``` + /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*}; + /// # f().unwrap(); + /// # fn f() -> Result<()> { + /// // Generate a Cert, and create a keypair from the primary key. + /// let (cert, _) = CertBuilder::new().generate()?; + /// let mut keypair = cert.primary().clone() + /// .mark_parts_secret()?.into_keypair()?; + /// + /// // Let's add an encryption subkey. + /// let flags = KeyFlags::default().set_encrypt_at_rest(true); + /// assert_eq!(cert.keys_valid().key_flags(flags.clone()).count(), 0); + /// + /// // Generate a subkey and a binding signature. + /// let subkey : key::SecretSubkey + /// = Key4::generate_ecc(false, Curve::Cv25519)?.into(); + /// let builder = signature::Builder::new(SignatureType::SubkeyBinding) + /// .set_key_flags(&flags)?; + /// let binding = subkey.bind(&mut keypair, &cert, builder, None)?; + /// + /// // Now merge the key and binding signature into the Cert. + /// let cert = cert.merge_packets(vec![subkey.into(), + /// binding.into()])?; + /// + /// // Check that we have an encryption subkey. + /// assert_eq!(cert.keys_valid().key_flags(flags).count(), 1); + /// # Ok(()) } + pub fn bind<T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert, + signature: signature::Builder, + creation_time: T) + -> Result<Signature> + where T: Into<Option<time::SystemTime>>, + R: key::KeyRole + { + signature + .set_signature_creation_time( + creation_time.into().unwrap_or_else(|| { + time::SystemTime::now().canonicalize() + }))? + .set_issuer_fingerprint(signer.public().fingerprint())? + .set_issuer(signer.public().keyid())? + .sign_subkey_binding(signer, cert.primary(), self) + } +} + +impl UserID { + /// Creates a binding signature. + /// + /// The signature binds this userid to `cert`. `signer` will be used + /// to create a signature using `signature` as builder. + /// The`hash_algo` defaults to SHA512, `creation_time` to the + /// current time. + /// + /// This function adds a creation time subpacket, a issuer + /// fingerprint subpacket, and a issuer subpacket to the + /// signature. + /// + /// # Example + /// + /// This example demonstrates how to bind this userid to a Cert. + /// Note that in general, the `CertBuilder` is a better way to add + /// userids to a Cert. + /// + /// ``` + /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*}; + /// # f().unwrap(); + /// # fn f() -> Result<()> { + /// // Generate a Cert, and create a keypair from the primary key. + /// let (cert, _) = CertBuilder::new().generate()?; + /// let mut keypair = cert.primary().clone() + /// .mark_parts_secret()?.into_keypair()?; + /// assert_eq!(cert.userids().len(), 0); + /// + /// // Generate a userid and a binding signature. + /// let userid = UserID::from("test@example.org"); + /// let builder = + /// signature::Builder::new(SignatureType::PositiveCertificate); + /// let binding = userid.bind(&mut keypair, &cert, builder, None)?; + /// + /// // Now merge the userid and binding signature into the Cert. + /// let cert = cert.merge_packets(vec![userid.into(), binding.into()])?; + /// + /// // Check that we have a userid. + /// assert_eq!(cert.userids().len(), 1); + /// # Ok(()) } + pub fn bind<T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert, + signature: signature::Builder, + creation_time: T) + -> Result<Signature> + where T: Into<Option<time::SystemTime>>, + R: key::KeyRole + { + signature + .set_signature_creation_time( + creation_time.into().unwrap_or_else(|| { + time::SystemTime::now().canonicalize() + }))? + .set_issuer_fingerprint(signer.public().fingerprint())? + .set_issuer(signer.public().keyid())? + .sign_userid_binding( + signer, cert.primary(), self) + } + + /// Returns a certificate for the user id. + /// + /// The signature binds this userid to `cert`. `signer` will be + /// used to create a certification signature of type + /// `signature_type`. `signature_type` defaults to + /// `SignatureType::GenericCertificate`, `hash_algo` to SHA512, + /// `creation_time` to the current time. + /// + /// This function adds a creation time subpacket, a issuer + /// fingerprint subpacket, and a issuer subpacket to the + /// signature. + /// + /// # Errors + /// + /// Returns `Error::InvalidArgument` if `signature_type` is not + /// one of `SignatureType::{Generic, Persona, Casual, + /// Positive}Certificate` + /// + /// # Example + /// + /// This example demonstrates how to certify a userid. + /// + /// ``` + /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*}; + /// # f().unwrap(); + /// # fn f() -> Result<()> { + /// // Generate a Cert, and create a keypair from the primary key. + /// let (alice, _) = CertBuilder::new() + /// .primary_keyflags(KeyFlags::default().set_certify(true)) + /// .add_userid("alice@example.org") + /// .generate()?; + /// let mut keypair = alice.primary().clone() + /// .mark_parts_secret()?.into_keypair()?; + /// + /// // Generate a Cert for Bob. + /// let (bob, _) = CertBuilder::new() + /// .primary_keyflags(KeyFlags::default().set_certify(true)) + /// .add_userid("bob@example.org") + /// .generate()?; + /// + /// // Alice now certifies the binding between `bob@example.org` and `bob`. + /// let certificate = + /// bob.userids().nth(0).unwrap().userid() + /// .certify(&mut keypair, &bob, SignatureType::PositiveCertificate, + /// None, None)?; + /// + /// // `certificate` can now be used, e.g. by merging it into `bob`. + /// let bob = bob.merge_packets(vec![certificate.into()])?; + /// + /// // Check that we have a certification on the userid. + /// assert_eq!(bob.userids().nth(0).unwrap().certifications().len(), 1); + /// # Ok(()) } + pub fn certify<S, H, T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert, + signature_type: S, + hash_algo: H, creation_time: T) + -> Result<Signature> + where S: Into<Option<SignatureType>>, + H: Into<Option<HashAlgorithm>>, + T: Into<Option<time::SystemTime>>, + R: key::KeyRole + { + let typ = signature_type.into(); + let typ = match typ { + Some(SignatureType::GenericCertificate) + | Some(SignatureType::PersonaCertificate) + | Some(SignatureType::CasualCertificate) + | Some(SignatureType::PositiveCertificate) => typ.unwrap(), + Some(t) => return Err(Error::InvalidArgument( + format!("Invalid signature type: {}", t)).into()), + None => SignatureType::GenericCertificate, + }; + let mut sig = signature::Builder::new(typ); + if let Some(algo) = hash_algo.into() { + sig = sig.set_hash_algo(algo); + } + self.bind(signer, cert, sig, + // Unwrap arguments to prevent further + // monomorphization of bind(). + creation_time.into().unwrap_or_else(|| { + time::SystemTime::now().canonicalize() + })) + } +} + +impl UserAttribute { + /// Creates a binding signature. + /// + /// The signature binds this user attribute to `cert`. `signer` + /// will be used to create a signature using `signature` as + /// builder. The`hash_algo` defaults to SHA512, `creation_time` + /// to the current time. + /// + /// This function adds a creation time subpacket, a issuer + /// fingerprint subpacket, and a issuer subpacket to the + /// signature. + /// + /// # Example + /// + /// This example demonstrates how to bind this user attribute to a + /// Cert. Note that in general, the `CertBuilder` is a better way + /// to add userids to a Cert. + /// + /// ``` + /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*, + /// packet::user_attribute::*}; + /// # f().unwrap(); + /// # fn f() -> Result<()> { + /// // Generate a Cert, and create a keypair from the primary key. + /// let (cert, _) = CertBuilder::new() + /// .generate()?; + /// let mut keypair = cert.primary().clone() + /// .mark_parts_secret()?.into_keypair()?; + /// assert_eq!(cert.userids().len(), 0); + /// + /// // Generate a user attribute and a binding signature. + /// let user_attr = UserAttribute::new(&[ + /// Subpacket::Image( + /// Image::Private(100, vec![0, 1, 2].into_boxed_slice())), + /// ])?; + /// let builder = + /// signature::Builder::new(SignatureType::PositiveCertificate); + /// let binding = user_attr.bind(&mut keypair, &cert, builder, None)?; + /// + /// // Now merge the user attribute and binding signature into the Cert. + /// let cert = cert.merge_packets(vec![user_attr.into(), binding.into()])?; + /// + /// // Check that we have a user attribute. + /// assert_eq!(cert.user_attributes().len(), 1); + /// # Ok(()) } + pub fn bind<T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert, + signature: signature::Builder, + creation_time: T) + -> Result<Signature> + where T: Into<Option<time::SystemTime>>, + R: key::KeyRole + { + signature + .set_signature_creation_time( + creation_time.into().unwrap_or_else(|| { + time::SystemTime::now().canonicalize() + }))? + .set_issuer_fingerprint(signer.public().fingerprint())? + .set_issuer(signer.public().keyid())? + .sign_user_attribute_binding(signer, cert.primary(), self) + } + + /// Returns a certificate for the user attribute. + /// + /// The signature binds this user attribute to `cert`. `signer` will be + /// used to create a certification signature of type + /// `signature_type`. `signature_type` defaults to + /// `SignatureType::GenericCertificate`, `hash_algo` to SHA512, + /// `creation_time` to the current time. + /// + /// This function adds a creation time subpacket, a issuer + /// fingerprint subpacket, and a issuer subpacket to the + /// signature. + /// + /// # Errors + /// + /// Returns `Error::InvalidArgument` if `signature_type` is not + /// one of `SignatureType::{Generic, Persona, Casual, + /// Positive}Certificate` + /// + /// # Example + /// + /// This example demonstrates how to certify a userid. + /// + /// ``` + /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*, + /// packet::user_attribute::*}; + /// # f().unwrap(); + /// # fn f() -> Result<()> { + /// // Generate a Cert, and create a keypair from the primary key. + /// let (alice, _) = CertBuilder::new() + /// .add_userid("alice@example.org") + /// .generate()?; + /// let mut keypair = alice.primary().clone() + /// .mark_parts_secret()?.into_keypair()?; + /// + /// // Generate a Cert for Bob. + /// let user_attr = UserAttribute::new(&[ + /// Subpacket::Image( + /// Image::Private(100, vec![0, 1, 2].into_boxed_slice())), + /// ])?; + /// let (bob, _) = CertBuilder::new() + /// .primary_keyflags(KeyFlags::default().set_certify(true)) + /// .add_user_attribute(user_attr) + /// .generate()?; + /// + /// // Alice now certifies the binding between `bob@example.org` and `bob`. + /// let certificate = + /// bob.user_attributes().nth(0).unwrap().user_attribute() + /// .certify(&mut keypair, &bob, SignatureType::PositiveCertificate, + /// None, None)?; + /// + /// // `certificate` can now be used, e.g. by merging it into `bob`. + /// let bob = bob.merge_packets(vec![certificate.into()])?; + /// + /// // Check that we have a certification on the userid. + /// assert_eq!(bob.user_attributes().nth(0).unwrap().certifications().len(), + /// 1); + /// # Ok(()) } + pub fn certify<S, H, T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert, + signature_type: S, + hash_algo: H, creation_time: T) + -> Result<Signature> + where S: Into<Option<SignatureType>>, + H: Into<Option<HashAlgorithm>>, + T: Into<Option<time::SystemTime>>, + R: key::KeyRole + { + let typ = signature_type.into(); + let typ = match typ { + Some(SignatureType::GenericCertificate) + | Some(SignatureType::PersonaCertificate) + | Some(SignatureType::CasualCertificate) + | Some(SignatureType::PositiveCertificate) => typ.unwrap(), + Some(t) => return Err(Error::InvalidArgument( + format!("Invalid signature type: {}", t)).into()), + None => SignatureType::GenericCertificate, + }; + let mut sig = signature::Builder::new(typ); + if let Some(algo) = hash_algo.into() { + sig = sig.set_hash_algo(algo); + } + self.bind(signer, cert, sig, + // Unwrap arguments to prevent further + // monomorphization of bind(). + creation_time.into().unwrap_or_else(|| { + time::SystemTime::now().canonicalize() + })) + } +} diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs new file mode 100644 index 00000000..fe8b6ccf --- /dev/null +++ b/openpgp/src/cert/builder.rs @@ -0,0 +1,665 @@ +use std::time; + +use crate::packet; +use crate::packet::{ + key, + Key, + key::Key4, +}; +use crate::Result; +use crate::packet::Signature; +use crate::packet::signature; +use crate::Cert; +use crate::cert::CertRevocationBuilder; +use crate::Error; +use crate::conversions::Time; +use crate::crypto::Password; +use crate::autocrypt::Autocrypt; +use crate::types::{ + Features, + HashAlgorithm, + KeyFlags, + SignatureType, + SymmetricAlgorithm, +}; + +/// Groups symmetric and asymmetric algorithms +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)] +pub enum CipherSuite { + /// EdDSA and ECDH over Curve25519 with SHA512 and AES256 + Cv25519, + /// 3072 bit RSA with SHA512 and AES256 + RSA3k, + /// EdDSA and ECDH over NIST P-256 with SHA256 and AES256 + P256, + /// EdDSA and ECDH over NIST P-384 with SHA384 and AES256 + P384, + /// EdDSA and ECDH over NIST P-521 with SHA512 and AES256 + P521, + /// 2048 bit RSA with SHA512 and AES256 + RSA2k, + /// 4096 bit RSA with SHA512 and AES256 + RSA4k, +} + +impl Default for CipherSuite { + fn default() -> Self { + CipherSuite::Cv25519 + } +} + +impl CipherSuite { + fn generate_key<R>(self, flags: &KeyFlags) + -> Result<Key<key::SecretParts, R>> + where R: key::KeyRole + { + 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 sign = flags.can_certify() || flags.can_sign() + || flags.can_authenticate(); + let encrypt = flags.can_encrypt_for_transport() + || flags.can_encrypt_at_rest(); + 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, + expiration: Option<time::Duration>, +} + +/// Simplifies generation of Keys. +/// +/// Builder to generate complex Cert hierarchies with multiple user IDs. +#[derive(Clone, Debug)] +pub struct CertBuilder { + ciphersuite: CipherSuite, + primary: KeyBlueprint, + subkeys: Vec<KeyBlueprint>, + userids: Vec<packet::UserID>, + user_attributes: Vec<packet::UserAttribute>, + password: Option<Password>, +} + +impl CertBuilder { + /// Returns a new CertBuilder. + /// + /// The returned CertBuilder is setup to only create a + /// certification-capable primary key using the default cipher + /// suite. You'll almost certainly want to add subkeys (using + /// `CertBuilder::add_signing_subkey`, or + /// `CertBuilder::add_encryption_subkey`, for instance), and user + /// ids (using `CertBuilder::add_userid`). + pub fn new() -> Self { + CertBuilder{ + ciphersuite: CipherSuite::default(), + primary: KeyBlueprint{ + flags: KeyFlags::default().set_certify(true), + expiration: None, + }, + subkeys: vec![], + userids: vec![], + user_attributes: vec![], + password: None, + } + } + + /// Generates a general-purpose key. + /// + /// The key's primary key is certification- and signature-capable. + /// The key has one subkey, an encryption-capable subkey. + pub fn general_purpose<C, U>(ciphersuite: C, userids: Option<U>) -> Self + where C: Into<Option<CipherSuite>>, + U: Into<packet::UserID> + { + CertBuilder { + ciphersuite: ciphersuite.into().unwrap_or(Default::default()), + primary: KeyBlueprint { + flags: KeyFlags::default() + .set_certify(true) + .set_sign(true), + expiration: Some( + time::Duration::new(3 * 52 * 7 * 24 * 60 * 60, 0)), + }, + subkeys: vec![ + KeyBlueprint { + flags: KeyFlags::default() + .set_encrypt_for_transport(true) + .set_encrypt_at_rest(true), + expiration: None, + } + ], + userids: userids.into_iter().map(|x| x.into()).collect(), + user_attributes: vec![], + password: None, + } + } + + /// Generates a key compliant to + /// [Autocrypt](https://autocrypt.org/). + /// + /// If no version is given the latest one is used. + /// + /// The autocrypt specification requires a UserID. However, + /// because it can be useful to add the UserID later, it is + /// permitted to be none. + pub fn autocrypt<'a, V, U>(version: V, userid: Option<U>) + -> Self + where V: Into<Option<Autocrypt>>, + U: Into<packet::UserID> + { + let builder = CertBuilder{ + ciphersuite: match version.into().unwrap_or(Default::default()) { + Autocrypt::V1 => CipherSuite::RSA3k, + Autocrypt::V1_1 => CipherSuite::Cv25519, + }, + primary: KeyBlueprint { + flags: KeyFlags::default() + .set_certify(true) + .set_sign(true), + expiration: Some( + time::Duration::new(3 * 52 * 7 * 24 * 60 * 60, 0)), + }, + subkeys: vec![ + KeyBlueprint { + flags: KeyFlags::default() + .set_encrypt_for_transport(true) + .set_encrypt_at_rest(true), + expiration: None, + } + ], + userids: vec![], + user_attributes: vec![], + password: None, + }; + + if let Some(userid) = userid { + builder.add_userid(userid.into()) + } else { + builder + } + } + + /// Sets the encryption and signature algorithms for primary and all subkeys. + pub fn set_cipher_suite(mut self, cs: CipherSuite) -> Self { + self.ciphersuite = cs; + self + } + + /// Adds a new user ID. The first user ID added will be the primary user ID. + pub fn add_userid<'a, U>(mut self, uid: U) -> Self + where U: Into<packet::UserID> + { + self.userids.push(uid.into()); + self + } + + /// Adds a new user attribute. + pub fn add_user_attribute<'a, U>(mut self, ua: U) -> Self + where U: Into<packet::UserAttribute> + { + self.user_attributes.push(ua.into()); + self + } + + /// Adds a signing capable subkey. + pub fn add_signing_subkey(self) -> Self { + self.add_subkey(KeyFlags::default().set_sign(true), None) + } + + /// Adds an encryption capable subkey. + pub fn add_encryption_subkey(self) -> Self { + self.add_subkey(KeyFlags::default() + .set_encrypt_for_transport(true) + .set_encrypt_at_rest(true), None) + } + + /// Adds an certification capable subkey. + pub fn add_certification_subkey(self) -> Self { + self.add_subkey(KeyFlags::default().set_certify(true), None) + } + + /// Adds an authentication capable subkey. + pub fn add_authentication_subkey(self) -> Self { + self.add_subkey(KeyFlags::default().set_authenticate(true), None) + } + + /// Adds a custom subkey. + /// + /// If `expiration` is `None`, the subkey uses the same expiration + /// time as the primary key. + pub fn add_subkey<T>(mut self, flags: KeyFlags, expiration: T) -> Self + where T: Into<Option<time::Duration>> + { + self.subkeys.push(KeyBlueprint { + flags: flags, + expiration: expiration.into(), + }); + self + } + + /// Sets the capabilities of the primary key. The function automatically + /// makes the primary key certification capable if subkeys are added. + pub fn primary_keyflags(mut self, flags: KeyFlags) -> Self { + self.primary.flags = flags; + self + } + + /// Sets a password to encrypt the secret keys with. + pub fn set_password(mut self, password: Option<Password>) -> Self { + self.password = password; + self + } + + /// Sets the expiration time. + /// + /// A value of None means never. + pub fn set_expiration<T>(mut self, expiration: T) -> Self + where T: Into<Option<time::Duration>> + { + self.primary.expiration = expiration.into(); + self + } + + /// Generates the actual Cert. + pub fn generate(mut self) -> Result<(Cert, Signature)> { + use crate::{PacketPile, Packet}; + use crate::types::ReasonForRevocation; + + let mut packets = Vec::<Packet>::with_capacity( + 1 + 1 + self.subkeys.len() + self.userids.len() + + self.user_attributes.len()); + + // make sure the primary key can sign subkeys + if !self.subkeys.is_empty() { + self.primary.flags = self.primary.flags.set_certify(true); + } + + // Generate & and self-sign primary key. + let (primary, sig) = self.primary_key()?; + let mut signer = primary.clone().mark_parts_secret().unwrap() + .into_keypair().unwrap(); + + packets.push(Packet::PublicKey({ + let mut primary = primary.clone(); + if let Some(ref password) = self.password { + primary.secret_mut().unwrap().encrypt_in_place(password)?; + } + primary + })); + packets.push(sig.clone().into()); + + let mut cert = + Cer |