summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2022-11-11 09:32:25 +0100
committerNeal H. Walfield <neal@pep.foundation>2022-11-11 21:01:46 +0100
commit53474e58493cc10a9e021fde5ca1048a86859f2b (patch)
tree38bf36070d7aa5fb87d0e790809bad1003f5c5f2
parent813f3f00a967eea210641103b5e64e0d31299ae6 (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!).
-rw-r--r--openpgp/NEWS2
-rw-r--r--openpgp/src/crypto/hash.rs31
-rw-r--r--openpgp/src/packet/mod.rs6
-rw-r--r--openpgp/src/packet/signature.rs288
-rw-r--r--openpgp/src/packet/signature/subpacket.rs12
-rw-r--r--openpgp/src/parse.rs130
-rw-r--r--openpgp/src/serialize.rs149
-rw-r--r--openpgp/tests/data/messages/a-cypherpunks-manifesto.txt.dennis-simon-anton-v3.sig6
8 files changed, 578 insertions, 46 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS
index 9b1cf413..6d17339d 100644
--- a/openpgp/NEWS
+++ b/openpgp/NEWS
@@ -5,6 +5,8 @@
* Changes in 1.11.0
* New functionality
- AsymmetricAlgorithm implements PartialEq, Eq, and Copy.
+ - Signature3 implements support for parsing, verifying, and
+ reserializing version 3 signature packages.
* Changes in 1.10.0
** New functionality
- Cert::insert_packets2
diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs
index 9af3de07..c4e7c4c8 100644
--- a/openpgp/src/crypto/hash.rs
+++ b/openpgp/src/crypto/hash.rs
@@ -40,7 +40,7 @@ use crate::packet::UserAttribute;
use crate::packet::key;
use crate::packet::key::Key4;
use crate::packet::Signature;
-use crate::packet::signature::{self, Signature4};
+use crate::packet::signature::{self, Signature3, Signature4};
use crate::Result;
use crate::types::Timestamp;
@@ -411,11 +411,39 @@ impl<P, R> Hash for Key4<P, R>
impl Hash for Signature {
fn hash(&self, hash: &mut dyn Digest) {
match self {
+ Signature::V3(sig) => sig.hash(hash),
Signature::V4(sig) => sig.hash(hash),
}
}
}
+impl Hash for Signature3 {
+ fn hash(&self, 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());
+
+ // Creation time.
+ let creation_time: u32 =
+ Timestamp::try_from(
+ self.signature_creation_time()
+ .unwrap_or(std::time::UNIX_EPOCH))
+ .unwrap_or_else(|_| Timestamp::from(0))
+ .into();
+
+ buffer[1] = (creation_time >> 24) as u8;
+ buffer[2] = (creation_time >> 16) as u8;
+ buffer[3] = (creation_time >> 8) as u8;
+ buffer[4] = (creation_time ) as u8;
+
+ hash.update(&buffer[..]);
+ }
+}
+
impl Hash for Signature4 {
fn hash(&self, hash: &mut dyn Digest) {
self.fields.hash(hash);
@@ -566,6 +594,7 @@ impl Signature {
/// signature.
pub fn hash_for_confirmation(&self, hash: &mut dyn Digest) {
match self {
+ Signature::V3(s) => s.hash_for_confirmation(hash),
Signature::V4(s) => s.hash_for_confirmation(hash),
}
}
diff --git a/openpgp/src/packet/mod.rs b/openpgp/src/packet/mod.rs
index b112eaf2..1a3da9ab 100644
--- a/openpgp/src/packet/mod.rs
+++ b/openpgp/src/packet/mod.rs
@@ -987,6 +987,9 @@ fn packet_path_iter() {
#[non_exhaustive]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
pub enum Signature {
+ /// Signature packet version 3.
+ V3(self::signature::Signature3),
+
/// Signature packet version 4.
V4(self::signature::Signature4),
}
@@ -996,6 +999,7 @@ impl Signature {
/// Gets the version.
pub fn version(&self) -> u8 {
match self {
+ Signature::V3(_) => 3,
Signature::V4(_) => 4,
}
}
@@ -1013,6 +1017,7 @@ impl Deref for Signature {
fn deref(&self) -> &Self::Target {
match self {
+ Signature::V3(sig) => &sig.intern,
Signature::V4(sig) => sig,
}
}
@@ -1022,6 +1027,7 @@ impl Deref for Signature {
impl DerefMut for Signature {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
+ Signature::V3(ref mut sig) => &mut sig.intern,
Signature::V4(ref mut sig) => sig,
}
}
diff --git a/openpgp/src/packet/signature.rs b/openpgp/src/packet/signature.rs
index 782ad526..84910e25 100644
--- a/openpgp/src/packet/signature.rs
+++ b/openpgp/src/packet/signature.rs
@@ -113,6 +113,7 @@
//! [its documentation]: subpacket::SubpacketAreas
use std::cmp::Ordering;
+use std::convert::TryFrom;
use std::fmt;
use std::hash::Hasher;
use std::ops::{Deref, DerefMut};
@@ -128,6 +129,7 @@ use crate::crypto::{
hash::{self, Hash, Digest},
Signer,
};
+use crate::KeyID;
use crate::KeyHandle;
use crate::HashAlgorithm;
use crate::PublicKeyAlgorithm;
@@ -148,6 +150,7 @@ use crate::packet::signature::subpacket::{
SubpacketTag,
SubpacketValue,
};
+use crate::types::Timestamp;
#[cfg(test)]
/// Like quickcheck::Arbitrary, but bounded.
@@ -1742,6 +1745,7 @@ impl SignatureBuilder {
impl From<Signature> for SignatureBuilder {
fn from(sig: Signature) -> Self {
match sig {
+ Signature::V3(sig) => sig.into(),
Signature::V4(sig) => sig.into(),
}
}
@@ -2010,6 +2014,204 @@ impl Signature4 {
}
}
+impl From<Signature3> for SignatureBuilder {
+ fn from(sig: Signature3) -> Self {
+ SignatureBuilder::from(sig.intern)
+ }
+}
+
+/// Holds a v3 Signature packet.
+///
+/// This holds a [version 3] Signature packet. Normally, you won't
+/// directly work with this data structure, but with the [`Signature`]
+/// enum, which is version agnostic. An exception is when you need to
+/// do version-specific operations. But currently, there aren't any
+/// version-specific methods.
+///
+/// [version 3]: https://tools.ietf.org/html/rfc4880#section-5.2
+/// [`Signature`]: super::Signature
+///
+/// Note: Per RFC 4880, v3 signatures should not be generated, but
+/// they should be accepted. As such, support for version 3
+/// signatures is limited to verifying them, but not generating them.
+#[derive(Clone)]
+pub struct Signature3 {
+ pub(crate) intern: Signature4,
+}
+assert_send_and_sync!(Signature3);
+
+impl TryFrom<Signature> for Signature3 {
+ type Error = anyhow::Error;
+
+ fn try_from(sig: Signature) -> Result<Self> {
+ match sig {
+ Signature::V3(sig) => Ok(sig),
+ sig => Err(
+ Error::InvalidArgument(
+ format!(
+ "Got a v{}, require a v3 signature",
+ sig.version()))
+ .into()),
+ }
+ }
+}
+
+// Yes, Signature3 derefs to Signature4. This is because Signature
+// derefs to Signature4 so this is the only way to add support for v3
+// sigs without breaking the semver.
+impl Deref for Signature3 {
+ type Target = Signature4;
+
+ fn deref(&self) -> &Self::Target {
+ &self.intern
+ }
+}
+
+impl DerefMut for Signature3 {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.intern
+ }
+}
+
+impl fmt::Debug for Signature3 {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Signature3")
+ .field("version", &self.version())
+ .field("typ", &self.typ())
+ .field("pk_algo", &self.pk_algo())
+ .field("hash_algo", &self.hash_algo())
+ .field("hashed_area", self.hashed_area())
+ .field("unhashed_area", self.unhashed_area())
+ .field("additional_issuers", &self.additional_issuers)
+ .field("digest_prefix",
+ &crate::fmt::to_hex(&self.digest_prefix, false))
+ .field(
+ "computed_digest",
+ &self
+ .computed_digest
+ .as_ref()
+ .map(|hash| crate::fmt::to_hex(&hash[..], false)),
+ )
+ .field("level", &self.level)
+ .field("mpis", &self.mpis)
+ .finish()
+ }
+}
+
+impl PartialEq for Signature3 {
+ /// This method tests for self and other values to be equal, and
+ /// is used by ==.
+ ///
+ /// This method compares the serialized version of the two
+ /// packets. Thus, the computed values are ignored ([`level`],
+ /// [`computed_digest`]).
+ ///
+ /// Note: because this function also compares the unhashed
+ /// subpacket area, it is possible for a malicious party to take
+ /// valid signatures, add subpackets to the unhashed area,
+ /// yielding valid but distinct signatures. If you want to ignore
+ /// the unhashed area, you should instead use the
+ /// [`Signature::normalized_eq`] method.
+ ///
+ /// [`level`]: Signature3::level()
+ /// [`computed_digest`]: Signature3::computed_digest()
+ fn eq(&self, other: &Signature3) -> bool {
+ self.cmp(other) == Ordering::Equal
+ }
+}
+
+impl Eq for Signature3 {}
+
+impl PartialOrd for Signature3 {
+ fn partial_cmp(&self, other: &Signature3) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Signature3 {
+ fn cmp(&self, other: &Signature3) -> Ordering {
+ self.intern.cmp(&other.intern)
+ }
+}
+
+impl std::hash::Hash for Signature3 {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ use std::hash::Hash as StdHash;
+ StdHash::hash(&self.intern, state);
+ }
+}
+
+impl Signature3 {
+ /// Creates a new signature packet.
+ ///
+ /// If you want to sign something, consider using the [`SignatureBuilder`]
+ /// interface.
+ ///
+ pub fn new(typ: SignatureType, creation_time: Timestamp,
+ issuer: KeyID,
+ pk_algo: PublicKeyAlgorithm,
+ hash_algo: HashAlgorithm,
+ digest_prefix: [u8; 2],
+ mpis: mpi::Signature) -> Self {
+ let hashed_area = SubpacketArea::new(vec![
+ Subpacket::new(
+ SubpacketValue::SignatureCreationTime(creation_time),
+ true).expect("fits"),
+ ]).expect("fits");
+ let unhashed_area = SubpacketArea::new(vec![
+ Subpacket::new(
+ SubpacketValue::Issuer(issuer),
+ false).expect("fits"),
+ ]).expect("fits");
+
+ let mut sig = Signature4::new(typ,
+ pk_algo, hash_algo,
+ hashed_area, unhashed_area,
+ digest_prefix, mpis);
+ sig.version = 3;
+
+ Signature3 {
+ intern: sig,
+ }
+ }
+
+ /// Gets the public key algorithm.
+ // SigantureFields::pk_algo is private, because we don't want it
+ // available on SignatureBuilder, which also derefs to
+ // &SignatureFields.
+ pub fn pk_algo(&self) -> PublicKeyAlgorithm {
+ self.fields.pk_algo()
+ }
+
+ /// Gets the hash prefix.
+ pub fn digest_prefix(&self) -> &[u8; 2] {
+ &self.digest_prefix
+ }
+
+ /// Gets the signature packet's MPIs.
+ pub fn mpis(&self) -> &mpi::Signature {
+ &self.mpis
+ }
+
+ /// Gets the computed hash value.
+ ///
+ /// This is set by the [`PacketParser`] when parsing the message.
+ ///
+ /// [`PacketParser`]: crate::parse::PacketParser
+ pub fn computed_digest(&self) -> Option<&[u8]> {
+ self.computed_digest.as_ref().map(|d| &d[..])
+ }
+
+ /// Gets the signature level.
+ ///
+ /// A level of 0 indicates that the signature is directly over the
+ /// data, a level of 1 means that the signature is a notarization
+ /// over all level 0 signatures and the data, and so on.
+ pub fn level(&self) -> usize {
+ self.level
+ }
+}
+
impl crate::packet::Signature {
/// Returns the value of any Issuer and Issuer Fingerprint subpackets.
///
@@ -3152,6 +3354,18 @@ impl Signature {
}
}
+impl From<Signature3> for Packet {
+ fn from(s: Signature3) -> Self {
+ Packet::Signature(s.into())
+ }
+}
+
+impl From<Signature3> for super::Signature {
+ fn from(s: Signature3) -> Self {
+ super::Signature::V3(s)
+ }
+}
+
impl From<Signature4> for Packet {
fn from(s: Signature4) -> Self {
Packet::Signature(s.into())
@@ -3167,7 +3381,11 @@ impl From<Signature4> for super::Signature {
#[cfg(test)]
impl ArbitraryBounded for super::Signature {
fn arbitrary_bounded(g: &mut Gen, depth: usize) -> Self {
- Signature4::arbitrary_bounded(g, depth).into()
+ if bool::arbitrary(g) {
+ Signature3::arbitrary_bounded(g, depth).into()
+ } else {
+ Signature4::arbitrary_bounded(g, depth).into()
+ }
}
}
@@ -3222,6 +3440,52 @@ impl ArbitraryBounded for Signature4 {
impl_arbitrary_with_bound!(Signature4);
#[cfg(test)]
+impl ArbitraryBounded for Signature3 {
+ fn arbitrary_bounded(g: &mut Gen, _depth: usize) -> Self {
+ use mpi::MPI;
+ use PublicKeyAlgorithm::*;
+
+ let pk_algo = PublicKeyAlgorithm::arbitrary_for_signing(g);
+
+ #[allow(deprecated)]
+ let mpis = match pk_algo {
+ RSAEncryptSign | RSASign => mpi::Signature::RSA {
+ s: MPI::arbitrary(g),
+ },
+
+ DSA => mpi::Signature::DSA {
+ r: MPI::arbitrary(g),
+ s: MPI::arbitrary(g),
+ },
+
+ EdDSA => mpi::Signature::EdDSA {
+ r: MPI::arbitrary(g),
+ s: MPI::arbitrary(g),
+ },
+
+ ECDSA => mpi::Signature::ECDSA {
+ r: MPI::arbitrary(g),
+ s: MPI::arbitrary(g),
+ },
+
+ _ => unreachable!(),
+ };
+
+ Signature3::new(
+ SignatureType::arbitrary(g),
+ Timestamp::arbitrary(g),
+ KeyID::arbitrary(g),
+ pk_algo,
+ HashAlgorithm::arbitrary(g),
+ [Arbitrary::arbitrary(g), Arbitrary::arbitrary(g)],
+ mpis)
+ }
+}
+
+#[cfg(test)]
+impl_arbitrary_with_bound!(Signature3);
+
+#[cfg(test)]
mod test {
use super::*;
use crate::KeyID;
@@ -3491,6 +3755,28 @@ mod test {
}
#[test]
+ fn verify_v3_sig() {
+ if ! PublicKeyAlgorithm::DSA.is_supported() {
+ return;
+ }
+
+ let cert = Cert::from_bytes(crate::tests::key(
+ "dennis-simon-anton-private.pgp")).unwrap();
+ let msg = crate::tests::manifesto();
+ let p = Packet::from_bytes(
+ crate::tests::message("a-cypherpunks-manifesto.txt.dennis-simon-anton-v3.sig"))
+ .unwrap();
+ let mut sig = if let Packet::Signature(s) = p {
+ assert_eq!(s.version(), 3);
+ s
+ } else {
+ panic!("Expected a Signature, got: {:?}", p);
+ };
+
+ sig.verify_message(cert.primary_key().key(), msg).unwrap();
+ }
+
+ #[test]
fn sign_with_short_ed25519_secret_key() {
// 20 byte sec key
let secret_key = [
diff --git a/openpgp/src/packet/signature/subpacket.rs b/openpgp/src/packet/signature/subpacket.rs
index 065111eb..50e56271 100644
--- a/openpgp/src/packet/signature/subpacket.rs
+++ b/openpgp/src/packet/signature/subpacket.rs
@@ -3730,12 +3730,12 @@ impl TryFrom<Signature> for Signature4 {
fn try_from(sig: Signature) -> Result<Self> {
match sig {
Signature::V4(sig) => Ok(sig),
- // XXX: Once there are more signature variants:
- //sig => Err(
- // Error::InvalidArgument(
- // format!("Got a v{}, require a v4 signature", sig.version())
- // .into())
- // .into()),
+ sig => Err(
+ Error::InvalidArgument(
+ format!(
+ "Got a v{}, require a v4 signature",
+ sig.version()))
+ .into()),
}
}
}
diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs
index 79467efe..fe421c71 100644
--- a/openpgp/src/parse.rs
+++ b/openpgp/src/parse.rs
@@ -205,6 +205,7 @@ use crate::{
Container,
Header,
},
+ packet::signature::Signature3,
packet::signature::Signature4,
packet::prelude::*,
Packet,
@@ -1344,6 +1345,7 @@ impl Signature {
let version = php_try!(php.parse_u8("version"));
match version {
+ 3 => Signature3::parse(php),
4 => Signature4::parse(php),
_ => {
t!("Ignoring version {} packet.", version);
@@ -1358,48 +1360,14 @@ impl Signature {
-> Result<()> {
Signature4::plausible(bio, header)
}
-}
-impl Signature4 {
- // Parses a signature packet.
- fn parse<'a, T: 'a + BufferedReader<Cookie>>(mut php: PacketHeaderParser<T>)
- -> Result<PacketParser<'a>>
+ fn parse_finish(indent: isize, mut pp: PacketParser,
+ typ: SignatureType, hash_algo: HashAlgorithm)
+ -> Result<PacketParser>
{
- let indent = php.recursion_depth();
- tracer!(TRACE, "Signature4::parse", indent);
+ tracer!(TRACE, "Signature::parse_finish", indent);
- make_php_try!(php);
-
- let typ = php_try!(php.parse_u8("type"));
- let pk_algo: PublicKeyAlgorithm = php_try!(php.parse_u8("pk_algo")).into();
- let hash_algo: HashAlgorithm =
- php_try!(php.parse_u8("hash_algo")).into();
- let hashed_area_len = php_try!(php.parse_be_u16("hashed_area_len"));
- let hashed_area
- = php_try!(SubpacketArea::parse(&mut php,
- hashed_area_len as usize,
- hash_algo));
- let unhashed_area_len = php_try!(php.parse_be_u16("unhashed_area_len"));
- let unhashed_area
- = php_try!(SubpacketArea::parse(&mut php,
- unhashed_area_len as usize,
- hash_algo));
- let digest_prefix1 = php_try!(php.parse_u8("digest_prefix1"));
- let digest_prefix2 = php_try!(php.parse_u8("digest_prefix2"));
- if ! pk_algo.for_signing() {
- return php.fail("not a signature algorithm");
- }
- let mpis = php_try!(
- crypto::mpi::Signature::_parse(pk_algo, &mut php));
-
- let typ = typ.into();
let need_hash = HashingMode::for_signature(hash_algo, typ);
- let mut pp = php.ok(Packet::Signature(Signature4::new(
- typ, pk_algo, hash_algo,
- hashed_area,
- unhashed_area,
- [digest_prefix1, digest_prefix2],
- mpis).into()))?;
// Locate the corresponding HashedReader and extract the
// computed hash.
@@ -1480,6 +1448,50 @@ impl Signature4 {
Ok(pp)
}
+}
+
+impl Signature4 {
+ // Parses a signature packet.
+ fn parse<'a, T: 'a + BufferedReader<Cookie>>(mut php: PacketHeaderParser<T>)
+ -> Result<PacketParser<'a>>
+ {
+ let indent = php.recursion_depth();
+ tracer!(TRACE, "Signature4::parse", indent);
+
+ make_php_try!(php);
+
+ let typ = php_try!(php.parse_u8("type"));
+ let pk_algo: PublicKeyAlgorithm = php_try!(php.parse_u8("pk_algo")).into();
+ let hash_algo: HashAlgorithm =
+ php_try!(php.parse_u8("hash_algo")).into();
+ let hashed_area_len = php_try!(php.parse_be_u16("hashed_area_len"));
+ let hashed_area
+ = php_try!(SubpacketArea::parse(&mut php,
+ hashed_area_len as usize,
+ hash_algo));
+ let unhashed_area_len = php_try!(php.parse_be_u16("unhashed_area_len"));
+ let unhashed_area
+ = php_try!(SubpacketArea::parse(&mut php,
+ unhashed_area_len as usize,
+ hash_algo));
+ let digest_prefix1 = php_try!(php.parse_u8("digest_prefix1"));
+ let digest_prefix2 = php_try!(php.parse_u8("digest_prefix2"));
+ if ! pk_algo.for_signing() {
+ return php.fail("not a signature algorithm");
+ }
+ let mpis = php_try!(
+ crypto::mpi::Signature::_parse(pk_algo, &mut php));
+
+ let typ = typ.into();
+ let pp = php.ok(Packet::Signature(Signature4::new(
+ typ, pk_algo, hash_algo,
+ hashed_area,
+ unhashed_area,
+ [digest_prefix1, digest_prefix2],
+ mpis).into()))?;
+
+ Signature::parse_finish(indent, pp, typ, hash_algo)
+ }
/// Returns whether the data appears to be a signature (no promises).
fn plausible<T: BufferedReader<Cookie>>(
@@ -1527,6 +1539,48 @@ impl Signature4 {
}
}
+impl Signature3 {
+ // Parses a v3 signature packet.
+ fn parse<'a, T: 'a + BufferedReader<Cookie>>(mut php: PacketHeaderParser<T>)
+ -> Result<PacketParser<'a>>
+ {
+ let indent = php.recursion_depth();
+ tracer!(TRACE, "Signature3::parse", indent);
+
+ make_php_try!(php);
+
+ let len = php_try!(php.parse_u8("hashed length"));
+ if len != 5 {
+ return php.fail("invalid length \
+ (a v3 sig has 5 bytes of hashed data)");
+ }
+ let typ = php_try!(php.parse_u8("type"));
+ let creation_time: Timestamp
+ = php_try!(php.parse_be_u32("creation_time")).into();
+ let issuer: KeyID
+ = KeyID::from_bytes(&php_try!(php.parse_bytes("issuer", 8))[..]);
+ let pk_algo: PublicKeyAlgorithm
+ = php_try!(php.parse_u8("pk_algo")).into();
+ let hash_algo: HashAlgorithm =
+ php_try!(php.parse_u8("hash_algo")).into();
+ let digest_prefix1 = php_try!(php.parse_u8("digest_prefix1"));
+ let digest_prefix2 = php_try!(php.parse_u8("digest_prefix2"));
+ if ! pk_algo.for_signing() {
+ return php.fail("not a signature algorithm");
+ }
+ let mpis = php_try!(
+ crypto::mpi::Signature::_parse(pk_algo, &mut php));
+
+ let typ = typ.into();
+ let pp = php.ok(Packet::Signature(Signature3::new(
+ typ, creation_time, issuer, pk_algo, hash_algo,
+ [digest_prefix1, digest_prefix2],
+ mpis).into()))?;
+
+ Signature::parse_finish(indent, pp, typ, hash_algo)
+ }
+}
+
impl_parse_generic_packet!(Signature);
#[test]
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()).