diff options
author | Neal H. Walfield <neal@pep.foundation> | 2020-02-13 13:13:54 +0100 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2020-02-13 14:54:18 +0100 |
commit | d01bda82e6304128140393499a22b55d238b2d52 (patch) | |
tree | 6a9b254e538294824d665ec63ed73e84e75e45e1 | |
parent | d8918d1789d639370df6f7dea83a16e0e5df2ea9 (diff) |
openpgp: Provide a hook to allow the user to validate keys.
- Add the `key` method to the `Policy` trait to allow users to
impose policy on valid keys.
- This method is called when a `KeyAmalgamation` (or similiar) is
converted to a `ValidKeyAmalgamation` (or similar).
-rw-r--r-- | openpgp/src/cert/key_amalgamation.rs | 12 | ||||
-rw-r--r-- | openpgp/src/policy.rs | 127 |
2 files changed, 136 insertions, 3 deletions
diff --git a/openpgp/src/cert/key_amalgamation.rs b/openpgp/src/cert/key_amalgamation.rs index 77c6c85a..aa225deb 100644 --- a/openpgp/src/cert/key_amalgamation.rs +++ b/openpgp/src/cert/key_amalgamation.rs @@ -10,8 +10,9 @@ use crate::{ }, Error, packet::key, - packet::key::SecretKeyMaterial, packet::Key, + packet::key::SecretKeyMaterial, + packet::key::KeyParts, packet::Signature, policy::Policy, Result, @@ -157,12 +158,17 @@ impl<'a, P: 'a + key::KeyParts> KeyAmalgamation<'a, P> { { let time = time.into().unwrap_or_else(SystemTime::now); if let Some(binding_signature) = self.binding_signature(policy, time) { - Ok(ValidKeyAmalgamation { + let ka = ValidKeyAmalgamation { a: self, policy: policy, time: time, binding_signature: binding_signature, - }) + }; + policy.key( + key::PublicParts::convert_valid_amalgamation_ref( + (&ka).mark_parts_unspecified_ref()) + .expect("secret key amalgamations contain secret keys"))?; + Ok(ka) } else { Err(Error::NoBindingSignature(time).into()) } diff --git a/openpgp/src/policy.rs b/openpgp/src/policy.rs index 20ae9b24..49b33064 100644 --- a/openpgp/src/policy.rs +++ b/openpgp/src/policy.rs @@ -32,6 +32,8 @@ use std::u32; use failure::ResultExt; use crate::{ + cert::components::ValidKeyAmalgamation, + packet::key, packet::Signature, Result, types::HashAlgorithm, @@ -65,6 +67,27 @@ pub trait Policy : fmt::Debug { fn signature(&self, _sig: &Signature) -> Result<()> { Ok(()) } + + /// Returns an error if the key violates the policy. + /// + /// This function performs one of the last checks before a + /// `KeyAmalgamation` or a related data structures is turned into + /// a `ValidKeyAmalgamation`, or similar. + /// + /// Internally, the library always does this before using a key. + /// The sole exception is when creating a key using `CertBuilder`. + /// In that case, the primary key is not validated before it is + /// used to create any binding signatures. + /// + /// Thus, you can prevent keys that make use of insecure + /// algoriths, don't have a sufficiently high security margin + /// (e.g., 1024-bit RSA keys), are on a bad list, etc. from being + /// used here. + fn key(&self, _ka: &ValidKeyAmalgamation<key::PublicParts>) + -> Result<()> + { + Ok(()) + } } /// The standard policy. @@ -854,4 +877,108 @@ mod test { Ok(()) } + + #[test] + fn key() -> Result<()> { + use crate::cert::CipherSuite; + use crate::types::Curve; + + let p = &P::new(); + + #[derive(Debug)] + struct NoRsa; + impl Policy for NoRsa { + fn key(&self, ka: &ValidKeyAmalgamation<key::PublicParts>) + -> Result<()> + { + use crate::types::PublicKeyAlgorithm::*; + + eprintln!("algo: {}", ka.key().pk_algo()); + if ka.key().pk_algo() == RSAEncryptSign { + Err(format_err!("RSA!")) + } else { + Ok(()) + } + } + } + let norsa = &NoRsa {}; + + // Generate a certificate with an RSA primary and two RSA + // subkeys. + let (cert,_) = CertBuilder::new() + .set_cipher_suite(CipherSuite::RSA4k) + .add_signing_subkey() + .add_signing_subkey() + .generate()?; + assert_eq!(cert.keys().with_policy(p, None).count(), 3); + assert_eq!(cert.keys().with_policy(norsa, None).count(), 0); + assert!(cert.primary_key().with_policy(p, None).is_ok()); + assert!(cert.primary_key().with_policy(norsa, None).is_err()); + + use crate::packet::key::Key4; + use crate::packet::signature; + use crate::types::KeyFlags; + + // Generate a certificate with an ECC primary, an ECC subkey, + // and an RSA subkey. + let (cert,_) = CertBuilder::new() + .set_cipher_suite(CipherSuite::Cv25519) + .add_signing_subkey() + .generate()?; + + let pk = cert.primary_key().key().mark_parts_secret_ref()?; + let subkey: key::SecretSubkey + = Key4::generate_rsa(4096)?.into(); + let binding = signature::Builder::new(SignatureType::SubkeyBinding) + .set_key_flags(&KeyFlags::default().set_transport_encryption(true))? + .set_issuer_fingerprint(cert.fingerprint())? + .set_issuer(cert.keyid())? + .sign_subkey_binding(&mut pk.clone().into_keypair()?, + &pk, &subkey)?; + + let cert = cert.merge_packets(vec![ subkey.into(), binding.into() ])?; + + assert_eq!(cert.keys().with_policy(p, None).count(), 3); + assert_eq!(cert.keys().with_policy(norsa, None).count(), 2); + assert!(cert.primary_key().with_policy(p, None).is_ok()); + assert!(cert.primary_key().with_policy(norsa, None).is_ok()); + + // Generate a certificate with an RSA primary, an RSA subkey, + // and an ECC subkey. + let (cert,_) = CertBuilder::new() + .set_cipher_suite(CipherSuite::RSA4k) + .add_signing_subkey() + .generate()?; + + let pk = cert.primary_key().key().mark_parts_secret_ref()?; + let subkey: key::SecretSubkey + = key::Key4::generate_ecc(true, Curve::Ed25519)?.into(); + let binding = signature::Builder::new(SignatureType::SubkeyBinding) + .set_key_flags(&KeyFlags::default().set_transport_encryption(true))? + .set_issuer_fingerprint(cert.fingerprint())? + .set_issuer(cert.keyid())? + .sign_subkey_binding(&mut pk.clone().into_keypair()?, + &pk, &subkey)?; + + let cert = cert.merge_packets(vec![ subkey.into(), binding.into() ])?; + + assert_eq!(cert.keys().with_policy(p, None).count(), 3); + assert_eq!(cert.keys().with_policy(norsa, None).count(), 0); + assert!(cert.primary_key().with_policy(p, None).is_ok()); + assert!(cert.primary_key().with_policy(norsa, None).is_err()); + + // Generate a certificate with an ECC primary and two ECC + // subkeys. + let (cert,_) = CertBuilder::new() + .set_cipher_suite(CipherSuite::Cv25519) + .add_signing_subkey() + .add_signing_subkey() + .generate()?; + assert_eq!(cert.keys().with_policy(p, None).count(), 3); + assert_eq!(cert.keys().with_policy(norsa, None).count(), 3); + assert!(cert.primary_key().with_policy(p, None).is_ok()); + assert!(cert.primary_key().with_policy(norsa, None).is_ok()); + + Ok(()) + } } |