diff options
author | Neal H. Walfield <neal@pep.foundation> | 2022-11-11 09:32:25 +0100 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2022-11-11 21:01:46 +0100 |
commit | 53474e58493cc10a9e021fde5ca1048a86859f2b (patch) | |
tree | 38bf36070d7aa5fb87d0e790809bad1003f5c5f2 /openpgp/src/serialize.rs | |
parent | 813f3f00a967eea210641103b5e64e0d31299ae6 (diff) |
openpgp: Add support for verifying v3 signatures.
- RFC 4880 explicitly allows the use of v3 signatures, but adds:
> Implementations SHOULD accept V3 signatures. Implementations
> SHOULD generate V4 signatures.
- In practice, rpm-based distributions are generating v3 signatures,
and it will be awhile before we can actually stop supporting them.
https://bugzilla.redhat.com/show_bug.cgi?id=2141686#c20
- Add support for parsing, verifying, and serializing v3
signatures (but not v3 certificates, and not generating v3
signatures!).
Diffstat (limited to 'openpgp/src/serialize.rs')
-rw-r--r-- | openpgp/src/serialize.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs index 7588fb4b..531b3d35 100644 --- a/openpgp/src/serialize.rs +++ b/openpgp/src/serialize.rs @@ -152,6 +152,7 @@ use crate::packet::signature::subpacket::{ SubpacketArea, Subpacket, SubpacketValue, SubpacketLength }; use crate::packet::prelude::*; +use crate::packet::signature::Signature3; use crate::seal; use crate::types::{ RevocationKey, @@ -1591,12 +1592,14 @@ impl seal::Sealed for Signature {} impl Marshal for Signature { fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { match self { + Signature::V3(ref s) => s.serialize(o), Signature::V4(ref s) => s.serialize(o), } } fn export(&self, o: &mut dyn std::io::Write) -> Result<()> { match self { + Signature::V3(ref s) => s.export(o), Signature::V4(ref s) => s.export(o), } } @@ -1605,29 +1608,173 @@ impl Marshal for Signature { impl MarshalInto for Signature { fn serialized_len(&self) -> usize { match self { + Signature::V3(ref s) => s.serialized_len(), Signature::V4(ref s) => s.serialized_len(), } } fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> { match self { + Signature::V3(ref s) => s.serialize_into(buf), Signature::V4(ref s) => s.serialize_into(buf), } } fn export_into(&self, buf: &mut [u8]) -> Result<usize> { match self { + Signature::V3(ref s) => s.export_into(buf), Signature::V4(ref s) => s.export_into(buf), } } fn export_to_vec(&self) -> Result<Vec<u8>> { match self { + Signature::V3(ref s) => s.export_to_vec(), Signature::V4(ref s) => s.export_to_vec(), } } } +impl NetLength for Signature { + fn net_len(&self) -> usize { + match self { + Signature::V3(sig) => sig.net_len(), + Signature::V4(sig) => sig.net_len(), + } + } +} + +impl seal::Sealed for Signature3 {} +impl Marshal for Signature3 { + /// Writes a serialized version of the specified `Signature` + /// packet to `o`. + /// + /// # Errors + /// + /// Returns [`Error::InvalidArgument`] if `self` does not contain + /// a valid v3 signature. Because v3 signature support was added + /// late in the 1.x release cycle, `Signature3` is just a thin + /// wrapper around a `Signature4`. As such, it is possible to add + /// v4 specific data to a `Signature3`. In general, this isn't a + /// significnat problem as generating v3 is deprecated. + /// + /// [`Error::InvalidArgument`]: Error::InvalidArgument + fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { + use crate::packet::signature::subpacket::SubpacketTag; + + assert_eq!(self.version(), 3); + write_byte(o, self.version())?; + // hashed length. + write_byte(o, 5)?; + write_byte(o, self.typ().into())?; + if let Some(SubpacketValue::SignatureCreationTime(ct)) + = self.hashed_area().subpacket( + SubpacketTag::SignatureCreationTime) + .map(|sp| sp.value()) + { + write_be_u32(o, u32::from(*ct))?; + } else { + return Err(Error::InvalidArgument( + "Invalid v3 signature, missing creation time.".into()).into()); + } + + // Only one signature creation time subpacket is allowed in + // the hashed area. + let mut iter = self.hashed_area().iter(); + let _ = iter.next(); + if iter.next().is_some() { + return Err(Error::InvalidArgument( + format!("Invalid v3 signature: \ + subpackets are not allowed: {}", + self.hashed_area().iter().map(|sp| { + format!("{}: {:?}", sp.tag(), sp.value()) + }).collect::<Vec<String>>().join(", "))).into()); + } + + if let Some(SubpacketValue::Issuer(keyid)) + = self.unhashed_area().subpacket(SubpacketTag::Issuer) + .map(|sp| sp.value()) + { + match keyid { + KeyID::V4(bytes) => { + assert_eq!(bytes.len(), 8); + o.write_all(&bytes[..])?; + } + KeyID::Invalid(_) => { + return Err(Error::InvalidArgument( + "Invalid v3 signature, invalid issuer.".into()).into()); + } + } + } else { + return Err(Error::InvalidArgument( + "Invalid v3 signature, missing issuer.".into()).into()); + } + + // Only one issuer subpacket is allowed in the unhashed area. + let mut iter = self.unhashed_area().iter(); + let _ = iter.next(); + if iter.next().is_some() { + return Err(Error::InvalidArgument( + format!("Invalid v3 signature: \ + subpackets are not allowed: {}", + self.unhashed_area().iter().map(|sp| { + format!("{}: {:?}", sp.tag(), sp.value()) + }).collect::<Vec<String>>().join(", "))).into()); + } + + write_byte(o, self.pk_algo().into())?; + write_byte(o, self.hash_algo().into())?; + + write_byte(o, self.digest_prefix()[0])?; + write_byte(o, self.digest_prefix()[1])?; + + self.mpis().serialize(o)?; + + Ok(()) + } + + fn export(&self, o: &mut dyn std::io::Write) -> Result<()> { + self.exportable()?; + self.serialize(o) + } +} + +impl NetLength for Signature3 { + fn net_len(&self) -> usize { + assert_eq!(self.version(), 3); + + 1 // Version. + + 1 // Hashed length. + + 1 // Signature type. + + 4 // Creation time. + + 8 // Issuer. + + 1 // PK algorithm. + + 1 // Hash algorithm. + + 2 // Hash prefix. + + self.mpis().serialized_len() + } +} + +impl MarshalInto for Signature3 { + fn serialized_len(&self) -> usize { + self.net_len() + } + + fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> { + generic_serialize_into(self, MarshalInto::serialized_len(self), buf) + } + + fn export_into(&self, buf: &mut [u8]) -> Result<usize> { + self.exportable()?; + self.serialize_into(buf) + } + + fn export_to_vec(&self) -> Result<Vec<u8>> { + self.exportable()?; + self.to_vec() + } +} + impl seal::Sealed for Signature4 {} impl Marshal for Signature4 { /// Writes a serialized version of the specified `Signature` @@ -1678,6 +1825,8 @@ impl Marshal for Signature4 { impl NetLength for Signature4 { fn net_len(&self) -> usize { + assert_eq!(self.version(), 4); + 1 // Version. + 1 // Signature type. + 1 // PK algorithm. |