summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2020-02-13 13:13:54 +0100
committerNeal H. Walfield <neal@pep.foundation>2020-02-13 14:54:18 +0100
commitd01bda82e6304128140393499a22b55d238b2d52 (patch)
tree6a9b254e538294824d665ec63ed73e84e75e45e1
parentd8918d1789d639370df6f7dea83a16e0e5df2ea9 (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.rs12
-rw-r--r--openpgp/src/policy.rs127
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(())
+ }
}