summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2020-09-15 15:57:01 +0200
committerJustus Winter <justus@sequoia-pgp.org>2020-09-15 16:27:57 +0200
commitf0f206c04c394caf752feede0450891fc45b1f92 (patch)
tree14dd6bcbe89547c3f443c50dba320a9f19aed2de
parente388a0b127e0ce3f27136bf6e13d758e5a09337f (diff)
openpgp: Ensure signatures created from templates take precedence.
- If a signature is created from a template, make sure the new signature has a newer creation time than the original one, while still being valid (i.e. not in the future). This makes it easy to robustly update binding signatures. - Fixes #488.
-rw-r--r--openpgp/src/packet/signature/mod.rs108
1 files changed, 100 insertions, 8 deletions
diff --git a/openpgp/src/packet/signature/mod.rs b/openpgp/src/packet/signature/mod.rs
index dadd69f9..ec2cc35d 100644
--- a/openpgp/src/packet/signature/mod.rs
+++ b/openpgp/src/packet/signature/mod.rs
@@ -305,12 +305,21 @@ impl SignatureFields {
/// from both the hashed subpacket area and the unhashed subpacket
/// area when converting a `Signature` to a `SignatureBuilder`, and
/// when the `SignatureBuilder` is finalized, we automatically insert
-/// a `Signature Creation Time` subpacket with the current time into
-/// the hashed subpacket area unless the `Signature Creation Time`
-/// subpacket has been set using the [`set_signature_creation_time`]
-/// method or preserved using the [`preserve_signature_creation_time`]
-/// method or suppressed using the
-/// [`suppress_signature_creation_time`] method.
+/// a `Signature Creation Time` subpacket into the hashed subpacket
+/// area unless the `Signature Creation Time` subpacket has been set
+/// using the [`set_signature_creation_time`] method or preserved
+/// using the [`preserve_signature_creation_time`] method or
+/// suppressed using the [`suppress_signature_creation_time`] method.
+///
+/// If the `SignatureBuilder` has been created from scratch, the
+/// current time is used as signature creation time. If it has been
+/// created from a template, we make sure that the generated signature
+/// is newer. If that is not possible (i.e. the generated signature
+/// would have a future creation time), the signing operation fails.
+/// This ensures that binding signatures can be updated by deriving a
+/// `SignatureBuilder` from the existing binding. To disable this,
+/// explicitly set a signature creation time, or preserve the original
+/// one, or suppress the insertion of a timestamp.
///
/// [Section 5.2.3.4 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.2.3.4
/// [`Signature Creation Time`]: https://tools.ietf.org/html/rfc4880#section-5.2.3.4
@@ -1513,12 +1522,29 @@ impl SignatureBuilder {
}
fn pre_sign(mut self, signer: &dyn Signer) -> Result<Self> {
+ use std::time;
self.pk_algo = signer.public().pk_algo();
// Set the creation time.
if ! self.overrode_creation_time {
- self = self.set_signature_creation_time(
- std::time::SystemTime::now())?;
+ self =
+ // See if we want to backdate the signature.
+ if let Some(oct) = self.original_creation_time.clone() {
+ let t =
+ (oct + time::Duration::new(1, 0)).max(
+ time::SystemTime::now() -
+ time::Duration::new(SIG_BACKDATE_BY, 0));
+
+ if t > time::SystemTime::now() {
+ return Err(Error::InvalidOperation(
+ "Cannot create valid signature newer than template"
+ .into()).into());
+ }
+
+ self.set_signature_creation_time(t)?
+ } else {
+ self.set_signature_creation_time(time::SystemTime::now())?
+ };
}
// Make sure we have an issuer packet.
@@ -2891,4 +2917,70 @@ mod test {
}
Ok(())
}
+
+ /// Checks that binding signatures of newly created certificates
+ /// can be conveniently and robustly be overwritten without
+ /// fiddling with creation timestamps.
+ #[test]
+ fn binding_signatures_are_overrideable() -> Result<()> {
+ use crate::packet::signature::subpacket::NotationDataFlags;
+ let notation_key = "override-test@sequoia-pgp.org";
+ let p = &P::new();
+
+ // Create a certificate and try to update the userid's binding
+ // signature.
+ let (mut alice, _) =
+ CertBuilder::general_purpose(None, Some("alice@example.org"))
+ .generate()?;
+ let mut primary_signer = alice.primary_key().key().clone()
+ .parts_into_secret()?.into_keypair()?;
+ assert_eq!(alice.userids().len(), 1);
+ assert_eq!(alice.userids().nth(0).unwrap().self_signatures().len(), 1);
+ let creation_time =
+ alice.userids().nth(0).unwrap().self_signatures()[0]
+ .signature_creation_time().unwrap();
+
+ for i in 0..2 * SIG_BACKDATE_BY {
+ assert_eq!(alice.userids().nth(0).unwrap().self_signatures().len(),
+ 1 + i as usize);
+
+ // Get the binding signature so that we can modify it.
+ let sig = alice.with_policy(p, None)?.userids().nth(0).unwrap()
+ .binding_signature().clone();
+ assert_eq!(sig.signature_creation_time().unwrap(),
+ creation_time + std::time::Duration::new(i, 0));
+
+ let new_sig = match
+ SignatureBuilder::from(sig)
+ .set_notation(notation_key,
+ i.to_string().as_bytes(),
+ NotationDataFlags::empty().set_human_readable(),
+ false)?
+ .sign_userid_binding(&mut primary_signer,
+ &alice.primary_key(),
+ &alice.userids().nth(0).unwrap()) {
+ Ok(v) => v,
+ Err(e) => if i < SIG_BACKDATE_BY {
+ return Err(e); // Not cool.
+ } else {
+ assert!(e.to_string().contains(
+ "Cannot create valid signature newer than \
+ template"));
+ return Ok(()); // Cool.
+ },
+ };
+
+ // Merge it and check that the new binding signature is
+ // the current one.
+ alice = alice.merge_packets(new_sig.clone())?;
+ let sig = alice.with_policy(p, None)?.userids().nth(0).unwrap()
+ .binding_signature();
+ assert_eq!(sig, &new_sig);
+ }
+
+ panic!("We were unexpectedly able to update binding signatures {} \
+ times. This is either a very slow build environment, or \
+ there is a bug. Please get in contact.",
+ 2 * SIG_BACKDATE_BY);
+ }
}