summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-07-06 18:24:59 +0200
committerJustus Winter <justus@sequoia-pgp.org>2023-07-06 20:22:58 +0200
commit9cde4b27309582715edc0692501a5df9d62c9f17 (patch)
treef909f071e9660316f94f04f41da5f6dac18630d5
parent00a2fc56ed639ae0210e44f53ba4fe474fdb79c0 (diff)
openpgp: Fix hashing v3 signatures.
- The high-level hashing functions are implemented on SignatureFields (so that we can use them from the SignatureBuilder). Unfortunately, when those functions invoke SignatureFields::hash, the type encoding the packet version has been erased. - Recover the version at runtime and dispatch to the right hashing function.
-rw-r--r--openpgp/src/cert.rs28
-rw-r--r--openpgp/src/crypto/hash.rs42
-rw-r--r--openpgp/tests/data/keys/pgp5-dsa-elg-v3-subkey-binding.pgpbin0 -> 701 bytes
3 files changed, 61 insertions, 9 deletions
diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs
index 23050580..0170b0e8 100644
--- a/openpgp/src/cert.rs
+++ b/openpgp/src/cert.rs
@@ -7247,4 +7247,32 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
Ok(())
}
+
+ /// Tests v3 binding signatures.
+ #[test]
+ fn v3_binding_signature() -> Result<()> {
+ if ! crate::types::PublicKeyAlgorithm::DSA.is_supported() {
+ eprintln!("Skipping because DSA is not supported");
+ return Ok(());
+ }
+
+ let c = Cert::from_bytes(
+ crate::tests::key("pgp5-dsa-elg-v3-subkey-binding.pgp"))?;
+ assert_eq!(c.bad_signatures().count(), 0);
+
+ let np = crate::policy::NullPolicy::new();
+
+ // The subkey is interesting because it is bound using a v3
+ // signature.
+ let vcert = c.with_policy(&np, None)?;
+ assert_eq!(vcert.keys().subkeys().count(), 1);
+
+ // XXX: Unfortunately, it being a v3 signature, the subkey has
+ // no keyflags, limiting its usefulness for now.
+
+ // The subkey is interesting because it is bound using a v3
+ // signature.
+ assert_eq!(c.keys().subkeys().with_policy(&np, None).count(), 1);
+ Ok(())
+ }
}
diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs
index 532e3d41..d22b0377 100644
--- a/openpgp/src/crypto/hash.rs
+++ b/openpgp/src/crypto/hash.rs
@@ -420,18 +420,28 @@ impl Hash for Signature {
impl Hash for Signature3 {
fn hash(&self, hash: &mut dyn Digest) {
+ Self::hash_signature(self, hash);
+ }
+}
+
+impl Signature3 {
+ /// Hashes this signature.
+ ///
+ /// Because we need to call this from SignatureFields::hash, we
+ /// provide this as associated method.
+ fn hash_signature(f: &signature::SignatureFields, hash: &mut dyn Digest) {
// XXX: Annoyingly, we have no proper way of handling errors
// here.
let mut buffer = [0u8; 5];
// Signature type.
- buffer[0] = u8::from(self.typ());
+ buffer[0] = u8::from(f.typ());
// Creation time.
let creation_time: u32 =
Timestamp::try_from(
- self.signature_creation_time()
+ f.signature_creation_time()
.unwrap_or(std::time::UNIX_EPOCH))
.unwrap_or_else(|_| Timestamp::from(0))
.into();
@@ -447,17 +457,21 @@ impl Hash for Signature3 {
impl Hash for Signature4 {
fn hash(&self, hash: &mut dyn Digest) {
- self.fields.hash(hash);
+ Self::hash_signature(self, hash);
}
}
-impl Hash for signature::SignatureFields {
- fn hash(&self, hash: &mut dyn Digest) {
+impl Signature4 {
+ /// Hashes this signature.
+ ///
+ /// Because we need to call this from SignatureFields::hash, we
+ /// provide this as associated method.
+ fn hash_signature(f: &signature::SignatureFields, hash: &mut dyn Digest) {
use crate::serialize::MarshalInto;
// XXX: Annoyingly, we have no proper way of handling errors
// here.
- let hashed_area = self.hashed_area().to_vec()
+ let hashed_area = f.hashed_area().to_vec()
.unwrap_or_else(|_| Vec::new());
// A version 4 signature packet is laid out as follows:
@@ -474,9 +488,9 @@ impl Hash for signature::SignatureFields {
// Version.
header[0] = 4;
- header[1] = self.typ().into();
- header[2] = self.pk_algo().into();
- header[3] = self.hash_algo().into();
+ header[1] = f.typ().into();
+ header[2] = f.pk_algo().into();
+ header[3] = f.hash_algo().into();
// The length of the hashed area, as a 16-bit big endian number.
let len = hashed_area.len() as u16;
@@ -509,6 +523,16 @@ impl Hash for signature::SignatureFields {
}
}
+impl Hash for signature::SignatureFields {
+ fn hash(&self, hash: &mut dyn Digest) {
+ match self.version() {
+ 3 => Signature3::hash_signature(self, hash),
+ 4 => Signature4::hash_signature(self, hash),
+ _ => (),
+ }
+ }
+}
+
/// Hashing-related functionality.
///
/// <a id="hashing-functions"></a>
diff --git a/openpgp/tests/data/keys/pgp5-dsa-elg-v3-subkey-binding.pgp b/openpgp/tests/data/keys/pgp5-dsa-elg-v3-subkey-binding.pgp
new file mode 100644
index 00000000..7a51c1db
--- /dev/null
+++ b/openpgp/tests/data/keys/pgp5-dsa-elg-v3-subkey-binding.pgp
Binary files differ