summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-02-13 16:57:48 +0100
committerJustus Winter <justus@sequoia-pgp.org>2023-06-26 13:24:45 +0200
commit99247e3aed358fb6d073fed3b0503d74dc212184 (patch)
treea135f571ac0cb8a1d0b95493a21878966ddc6a51
parent9dbe8f192d4e04cb87706fc7dd4ddfd4174ac737 (diff)
openpgp: Add support for v6 signature packets.
todo: - check salt lengths on verification - salt length in arbitrary - streaming signing
-rw-r--r--openpgp/src/cert.rs22
-rw-r--r--openpgp/src/crypto/hash.rs204
-rw-r--r--openpgp/src/packet/mod.rs18
-rw-r--r--openpgp/src/packet/prelude.rs1
-rw-r--r--openpgp/src/packet/signature.rs155
-rw-r--r--openpgp/src/packet/signature/v6.rs282
-rw-r--r--openpgp/src/parse.rs49
-rw-r--r--openpgp/src/serialize.rs96
-rw-r--r--openpgp/src/types/mod.rs14
-rw-r--r--openpgp/tests/data/crypto-refresh/v6-minimal-cert.key12
-rw-r--r--openpgp/tests/data/crypto-refresh/v6-minimal-secret.key14
11 files changed, 835 insertions, 32 deletions
diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs
index 819b9245..0553b444 100644
--- a/openpgp/src/cert.rs
+++ b/openpgp/src/cert.rs
@@ -7237,4 +7237,26 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
Ok(())
}
+
+ #[test]
+ fn v6_minimal_cert() -> Result<()> {
+ let p = &crate::policy::StandardPolicy::new();
+ let t = None; // XXX
+ let cert = Cert::from_bytes(
+ crate::tests::file("crypto-refresh/v6-minimal-cert.key"))?;
+ assert_eq!(cert.userids().count(), 0);
+ let vcert = cert.with_policy(p, t)?;
+ assert_eq!(vcert.keys().count(), 2);
+ assert_eq!(vcert.keys().for_signing().count(), 1);
+ assert_eq!(vcert.keys().for_transport_encryption().count(), 1);
+
+ let cert = Cert::from_bytes(
+ crate::tests::file("crypto-refresh/v6-minimal-secret.key")).unwrap();
+ assert_eq!(cert.userids().count(), 0);
+ let vcert = cert.with_policy(p, t)?;
+ assert_eq!(vcert.keys().count(), 2);
+ assert_eq!(vcert.keys().for_signing().count(), 1);
+ assert_eq!(vcert.keys().for_transport_encryption().count(), 1);
+ Ok(())
+ }
}
diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs
index 765a6211..bb905b63 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, Key6};
use crate::packet::Signature;
-use crate::packet::signature::{self, Signature3, Signature4};
+use crate::packet::signature::{self, Signature3, Signature4, Signature6};
use crate::Result;
use crate::types::Timestamp;
@@ -468,6 +468,7 @@ impl Hash for Signature {
match self {
Signature::V3(sig) => sig.hash(hash),
Signature::V4(sig) => sig.hash(hash),
+ Signature::V6(sig) => sig.hash(hash),
}
}
}
@@ -501,17 +502,17 @@ impl Hash for Signature3 {
impl Hash for Signature4 {
fn hash(&self, hash: &mut dyn Digest) {
- self.fields.hash(hash);
+ Self::hash_fields(hash, &self.fields);
}
}
-impl Hash for signature::SignatureFields {
- fn hash(&self, hash: &mut dyn Digest) {
+impl Signature4 {
+ fn hash_fields(hash: &mut dyn Digest, sig: &signature::SignatureFields) {
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 = sig.hashed_area().to_vec()
.unwrap_or_else(|_| Vec::new());
// A version 4 signature packet is laid out as follows:
@@ -528,9 +529,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] = sig.typ().into();
+ header[2] = sig.pk_algo().into();
+ header[3] = sig.hash_algo().into();
// The length of the hashed area, as a 16-bit big endian number.
let len = hashed_area.len() as u16;
@@ -563,13 +564,91 @@ impl Hash for signature::SignatureFields {
}
}
+impl Hash for Signature6 {
+ fn hash(&self, hash: &mut dyn Digest) {
+ Self::hash_fields(hash, &self.fields);
+ }
+}
+
+impl Signature6 {
+ fn hash_fields(hash: &mut dyn Digest, sig: &signature::SignatureFields) {
+ use crate::serialize::MarshalInto;
+
+ // XXX: Annoyingly, we have no proper way of handling errors
+ // here.
+ let hashed_area = sig.hashed_area().to_vec()
+ .unwrap_or_else(|_| Vec::new());
+
+ // A version 6 signature packet is laid out as follows:
+ //
+ // version - 1 byte \
+ // type - 1 byte \
+ // pk_algo - 1 byte \
+ // hash_algo - 1 byte Included in the hash
+ // hashed_area_len - 4 bytes (big endian)/
+ // hashed_area _/
+ // ... <- Not included in the hash
+
+ let mut header = [0u8; 8];
+
+ // Version.
+ header[0] = 6;
+ header[1] = sig.typ().into();
+ header[2] = sig.pk_algo().into();
+ header[3] = sig.hash_algo().into();
+
+ // The length of the hashed area, as a 16-bit big endian number.
+ let len = hashed_area.len() as u32;
+ header[4..8].copy_from_slice(&len.to_be_bytes());
+
+ hash.update(&header[..]);
+ hash.update(&hashed_area);
+
+ // A version 6 signature trailer is:
+ //
+ // version - 1 byte
+ // 0xFF (constant) - 1 byte
+ // amount - 4 bytes (big endian)
+ //
+ // The amount field is the amount of hashed from this
+ // packet (this excludes the message content, and this
+ // trailer) modulo 2**32.
+ //
+ // See https://tools.ietf.org/html/rfc4880#section-5.2.4
+ let mut trailer = [0u8; 6];
+
+ trailer[0] = 6;
+ trailer[1] = 0xff;
+ // The signature packet's length, not including the previous
+ // two bytes and the length modulo 2**32.
+ let len = header.len() + hashed_area.len();
+ trailer[2..6].copy_from_slice(&(len as u32).to_be_bytes());
+
+ hash.update(&trailer[..]);
+ }
+}
+
+impl Hash for signature::SignatureBuilder {
+ fn hash(&self, hash: &mut dyn Digest) {
+ match self.version {
+ signature::SBVersion::V4 {} =>
+ Signature4::hash_fields(hash, &self.fields),
+ signature::SBVersion::V6 { .. } =>
+ Signature6::hash_fields(hash, &self.fields),
+ }
+ }
+}
+
/// Hashing-related functionality.
///
/// <a id="hashing-functions"></a>
-impl signature::SignatureFields {
+impl signature::SignatureBuilder {
/// Hashes this standalone signature.
pub fn hash_standalone(&self, hash: &mut dyn Digest)
{
+ if let Some(salt) = self.prefix_salt() {
+ hash.update(salt);
+ }
self.hash(hash);
}
@@ -585,6 +664,9 @@ impl signature::SignatureFields {
key: &Key<P, key::PrimaryRole>)
where P: key::KeyParts,
{
+ if let Some(salt) = self.prefix_salt() {
+ hash.update(salt);
+ }
key.hash(hash);
self.hash(hash);
}
@@ -597,6 +679,9 @@ impl signature::SignatureFields {
where P: key::KeyParts,
Q: key::KeyParts,
{
+ if let Some(salt) = self.prefix_salt() {
+ hash.update(salt);
+ }
key.hash(hash);
subkey.hash(hash);
self.hash(hash);
@@ -610,6 +695,9 @@ impl signature::SignatureFields {
where P: key::KeyParts,
Q: key::KeyParts,
{
+ if let Some(salt) = self.prefix_salt() {
+ hash.update(salt);
+ }
self.hash_subkey_binding(hash, key, subkey);
}
@@ -620,6 +708,9 @@ impl signature::SignatureFields {
userid: &UserID)
where P: key::KeyParts,
{
+ if let Some(salt) = self.prefix_salt() {
+ hash.update(salt);
+ }
key.hash(hash);
userid.hash(hash);
self.hash(hash);
@@ -635,6 +726,9 @@ impl signature::SignatureFields {
ua: &UserAttribute)
where P: key::KeyParts,
{
+ if let Some(salt) = self.prefix_salt() {
+ hash.update(salt);
+ }
key.hash(hash);
ua.hash(hash);
self.hash(hash);
@@ -645,12 +739,104 @@ impl signature::SignatureFields {
///
/// <a id="hashing-functions"></a>
impl Signature {
+ /// Hashes this standalone signature.
+ pub fn hash_standalone(&self, hash: &mut dyn Digest)
+ {
+ if let Some(salt) = self.salt() {
+ hash.update(salt);
+ }
+ self.hash(hash);
+ }
+
+ /// Hashes this timestamp signature.
+ pub fn hash_timestamp(&self, hash: &mut dyn Digest)
+ {
+ self.hash_standalone(hash);
+ }
+
+ /// Hashes this direct key signature over the specified primary
+ /// key, and the primary key.
+ pub fn hash_direct_key<P>(&self, hash: &mut dyn Digest,
+ key: &Key<P, key::PrimaryRole>)
+ where P: key::KeyParts,
+ {
+ if let Some(salt) = self.salt() {
+ hash.update(salt);
+ }
+ key.hash(hash);
+ self.hash(hash);
+ }
+
+ /// Hashes this subkey binding over the specified primary key and
+ /// subkey, the primary key, and the subkey.
+ pub fn hash_subkey_binding<P, Q>(&self, hash: &mut dyn Digest,
+ key: &Key<P, key::PrimaryRole>,
+ subkey: &Key<Q, key::SubordinateRole>)
+ where P: key::KeyParts,
+ Q: key::KeyParts,
+ {
+ if let Some(salt) = self.salt() {
+ hash.update(salt);
+ }
+ key.hash(hash);
+ subkey.hash(hash);
+ self.hash(hash);
+ }
+
+ /// Hashes this primary key binding over the specified primary key
+ /// and subkey, the primary key, and the subkey.
+ pub fn hash_primary_key_binding<P, Q>(&self, hash: &mut dyn Digest,
+ key: &Key<P, key::PrimaryRole>,
+ subkey: &Key<Q, key::SubordinateRole>)
+ where P: key::KeyParts,
+ Q: key::KeyParts,
+ {
+ if let Some(salt) = self.salt() {
+ hash.update(salt);
+ }
+ self.hash_subkey_binding(hash, key, subkey);
+ }
+
+ /// Hashes this user ID binding over the specified primary key and
+ /// user ID, the primary key, and the userid.
+ pub fn hash_userid_binding<P>(&self, hash: &mut dyn Digest,
+ key: &Key<P, key::PrimaryRole>,
+ userid: &UserID)
+ where P: key::KeyParts,
+ {
+ if let Some(salt) = self.salt() {
+ hash.update(salt);
+ }
+ key.hash(hash);
+ userid.hash(hash);
+ self.hash(hash);
+ }
+
+ /// Hashes this user attribute binding over the specified primary
+ /// key and user attribute, the primary key, and the user
+ /// attribute.
+ pub fn hash_user_attribute_binding<P>(
+ &self,
+ hash: &mut dyn Digest,
+ key: &Key<P, key::PrimaryRole>,
+ ua: &UserAttribute)
+ where P: key::KeyParts,
+ {
+ if let Some(salt) = self.salt() {
+ hash.update(salt);
+ }
+ key.hash(hash);
+ ua.hash(hash);
+ self.hash(hash);
+ }
+
/// Hashes this signature for use in a Third-Party Confirmation
/// 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),
+ Signature::V6(s) => s.hash_for_confirmation(hash),
}
}
}
diff --git a/openpgp/src/packet/mod.rs b/openpgp/src/packet/mod.rs
index 513c40ad..3d72846c 100644
--- a/openpgp/src/packet/mod.rs
+++ b/openpgp/src/packet/mod.rs
@@ -993,6 +993,9 @@ pub enum Signature {
/// Signature packet version 4.
V4(self::signature::Signature4),
+
+ /// Signature packet version 6.
+ V6(self::signature::Signature6),
}
assert_send_and_sync!(Signature);
@@ -1002,6 +1005,7 @@ impl Signature {
match self {
Signature::V3(_) => 3,
Signature::V4(_) => 4,
+ Signature::V6(_) => 6,
}
}
}
@@ -1012,6 +1016,18 @@ impl From<Signature> for Packet {
}
}
+impl Signature {
+ /// Gets the salt, if any.
+ pub fn salt(&self) -> Option<&[u8]> {
+ match self {
+ Signature::V3(_) => None,
+ Signature::V4(_) => None,
+ Signature::V6(s) => Some(s.salt()),
+ }
+ }
+
+}
+
// Trivial forwarder for singleton enum.
impl Deref for Signature {
type Target = signature::Signature4;
@@ -1020,6 +1036,7 @@ impl Deref for Signature {
match self {
Signature::V3(sig) => &sig.intern,
Signature::V4(sig) => sig,
+ Signature::V6(sig) => &sig.common,
}
}
}
@@ -1030,6 +1047,7 @@ impl DerefMut for Signature {
match self {
Signature::V3(ref mut sig) => &mut sig.intern,
Signature::V4(ref mut sig) => sig,
+ Signature::V6(ref mut sig) => &mut sig.common,
}
}
}
diff --git a/openpgp/src/packet/prelude.rs b/openpgp/src/packet/prelude.rs
index cd69d3dd..5c3d7782 100644
--- a/openpgp/src/packet/prelude.rs
+++ b/openpgp/src/packet/prelude.rs
@@ -52,6 +52,7 @@ pub use crate::packet::{
seip::SEIP1,
signature,
signature::Signature4,
+ signature::Signature6,
signature::SignatureBuilder,
skesk::SKESK4,
skesk::SKESK5,
diff --git a/openpgp/src/packet/signature.rs b/openpgp/src/packet/signature.rs
index c5f3b019..d09ea346 100644
--- a/openpgp/src/packet/signature.rs
+++ b/openpgp/src/packet/signature.rs
@@ -154,7 +154,7 @@ use crate::types::Timestamp;
#[cfg(test)]
/// Like quickcheck::Arbitrary, but bounded.
-trait ArbitraryBounded {
+pub(crate) trait ArbitraryBounded {
/// Generates an arbitrary value, but only recurses if `depth >
/// 0`.
fn arbitrary_bounded(g: &mut Gen, depth: usize) -> Self;
@@ -178,6 +178,8 @@ macro_rules! impl_arbitrary_with_bound {
}
pub mod subpacket;
+mod v6;
+pub use v6::Signature6;
/// How many seconds to backdate signatures.
///
@@ -285,6 +287,20 @@ impl SignatureFields {
}
}
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub(crate) enum SBVersion {
+ V4 {},
+ V6 {
+ salt: Vec<u8>,
+ },
+}
+
+impl Default for SBVersion {
+ fn default() -> Self {
+ SBVersion::V4 {}
+ }
+}
+
/// A Signature builder.
///
/// The `SignatureBuilder` is used to create [`Signature`]s. Although
@@ -451,7 +467,8 @@ pub struct SignatureBuilder {
reference_time: Option<SystemTime>,
overrode_creation_time: bool,
original_creation_time: Option<SystemTime>,
- fields: SignatureFields,
+ pub(crate) fields: SignatureFields,
+ pub(crate) version: SBVersion,
}
assert_send_and_sync!(SignatureBuilder);
@@ -482,7 +499,8 @@ impl SignatureBuilder {
pk_algo: PublicKeyAlgorithm::Unknown(0),
hash_algo: HashAlgorithm::default(),
subpackets: SubpacketAreas::default(),
- }
+ },
+ version: SBVersion::default(),
}
}
@@ -1702,7 +1720,24 @@ impl SignatureBuilder {
/// # Ok(()) }
/// ```
pub fn pre_sign(mut self, signer: &dyn Signer) -> Result<Self> {
- self.pk_algo = signer.public().pk_algo();
+ let pk = signer.public();
+ self.pk_algo = pk.pk_algo();
+
+ // Set the version. A Key6 will create a Signature6, a key4
+ // will create a Signature4. If necessary, generate a salt.
+ // If the salt has been explicitly set, it is not changed.
+ self.version = match (self.version, pk.version()) {
+ (SBVersion::V4 {}, 4) => SBVersion::V4 {},
+ (SBVersion::V6 { .. }, 4) => SBVersion::V4 {},
+ (SBVersion::V4 {}, 6) => {
+ let mut salt = vec![0; self.fields.hash_algo().salt_size()?];
+ crate::crypto::random(&mut salt);
+ SBVersion::V6 { salt }
+ },
+ (SBVersion::V6 { salt }, 6) => SBVersion::V6 { salt },
+ (_, n) => return Err(Error::InvalidOperation(
+ format!("Unsupported key version {}", n)).into()),
+ };
// Set the creation time.
if ! self.overrode_creation_time {
@@ -1711,31 +1746,83 @@ impl SignatureBuilder {
}
}
- // Make sure we have an issuer packet.
- if self.issuers().next().is_none()
- && self.issuer_fingerprints().next().is_none()
- {
- self = self.set_issuer(signer.public().keyid())?
- .set_issuer_fingerprint(signer.public().fingerprint())?;
- }
+ match &self.version {
+ SBVersion::V4 {} => {
+ // Make sure we have an issuer packet.
+ if self.issuers().next().is_none()
+ && self.issuer_fingerprints().next().is_none()
+ {
+ self = self.set_issuer(signer.public().keyid())?
+ .set_issuer_fingerprint(signer.public().fingerprint())?;
+ }
+
+ // Add a salt to v4 signatures to make the signature
+ // unpredictable.
+ let mut salt = [0; 32];
+ crate::crypto::random(&mut salt);
+ self = self.set_notation("salt@notations.sequoia-pgp.org",
+ salt, None, false)?;
+ },
+ SBVersion::V6 { .. } => {
+ // Make sure we have an issuer fingerprint packet.
+ if self.issuer_fingerprints().next().is_none() {
+ self = self
+ .set_issuer_fingerprint(signer.public().fingerprint())?;
+ }
- // Add a salt to make the signature unpredictable.
- let mut salt = [0; 32];
- crate::crypto::random(&mut salt);
- self = self.set_notation("salt@notations.sequoia-pgp.org",
- salt, None, false)?;
+ // In v6 signatures, we have a proper prefix salt.
+ },
+ }
self.sort();
Ok(self)
}
+ /// Returns the prefix salt.
+ ///
+ /// In OpenPGP v6 signatures, a salt is prefixed to the data
+ /// stream hashed in the signature. If a v6 signature is
+ /// generated by using a v6 key, then a salt will be added
+ /// automatically. If a salt has been set explicitly (see
+ /// [`SignatureBuilder::set_prefix_salt`]), this function will
+ /// return the salt.
+ pub fn prefix_salt(&self) -> Option<&[u8]> {
+ match &self.version {
+ SBVersion::V4 {} => None,
+ SBVersion::V6 { salt } => Some(salt),
+ }
+ }
+
+ /// Explicitly sets the prefix salt.
+ ///
+ /// In OpenPGP v6 signatures, a salt is prefixed to the data
+ /// stream hashed in the signature. If a v6 signature is
+ /// generated by using a v6 key, then a salt will be added
+ /// automatically. In general, you do not need to call this
+ /// function.
+ ///
+ /// When streaming a signed message, it is useful to explicitly
+ /// set the salt using this function.
+ pub fn set_prefix_salt(mut self, new_salt: Vec<u8>) -> (Self, Option<Vec<u8>>) {
+ let mut old = None;
+
+ self.version = match std::mem::take(&mut self.version) {
+ SBVersion::V4 {} => SBVersion::V6 { salt: new_salt },
+ SBVersion::V6 { salt } => {
+ old = Some(salt);
+ SBVersion::V6 { salt: new_salt }
+ },
+ };
+
+ (self, old)
+ }
+
fn sign(self, signer: &mut dyn Signer, digest: Vec<u8>)
-> Result<Signature>
{
let mpis = signer.sign(self.hash_algo, &digest)?;
-
- Ok(Signature4 {
+ let v4 = Signature4 {
common: Default::default(),
fields: self.fields,
digest_prefix: [digest[0], digest[1]],
@@ -1743,7 +1830,13 @@ impl SignatureBuilder {
computed_digest: Some(digest),
level: 0,
additional_issuers: Vec::with_capacity(0),
- }.into())
+ };
+
+ match self.version {
+ SBVersion::V4 {} => Ok(v4.into()),
+ SBVersion::V6 { salt } =>
+ Ok(Signature6::from_common(v4, salt)?.into())
+ }
}
}
@@ -1752,6 +1845,7 @@ impl From<Signature> for SignatureBuilder {
match sig {
Signature::V3(sig) => sig.into(),
Signature::V4(sig) => sig.into(),
+ Signature::V6(sig) => sig.into(),
}
}
}
@@ -1777,10 +1871,17 @@ impl From<Signature4> for SignatureBuilder {
overrode_creation_time: false,
original_creation_time: creation_time,
fields,
+ version: Default::default(),
}
}
}
+impl From<Signature6> for SignatureBuilder {
+ fn from(sig: Signature6) -> Self {
+ SignatureBuilder::from(sig.common)
+ }
+}
+
/// Holds a v4 Signature packet.
///
/// This holds a [version 4] Signature packet. Normally, you won't
@@ -2699,6 +2800,13 @@ impl Signature {
"Signature has no creation time subpacket".into()).into());
}
+ // XXX if hash_algo.salt_size().map(|expected| expected != salt_len)
+ // XXX .unwrap_or(false)
+ // XXX {
+ // XXX return php.fail(format!("bad salt length, expected {} got {}",
+ // XXX );
+ // XXX }
+
let result = key.verify(self.mpis(), self.hash_algo(), digest.as_ref());
if result.is_ok() {
// Mark information in this signature as authenticated.
@@ -3378,10 +3486,11 @@ impl From<Signature4> for super::Signature {
#[cfg(test)]
impl ArbitraryBounded for super::Signature {
fn arbitrary_bounded(g: &mut Gen, depth: usize) -> Self {
- if bool::arbitrary(g) {
- Signature3::arbitrary_bounded(g, depth).into()
- } else {
- Signature4::arbitrary_bounded(g, depth).into()
+ match u8::arbitrary(g) % 3 {
+ 0 => Signature3::arbitrary_bounded(g, depth).into(),
+ 1 => Signature4::arbitrary_bounded(g, depth).into(),
+ 2 => Signature6::arbitrary_bounded(g, depth).into(),
+ _ => unreachable!(),
}
}
}
diff --git a/openpgp/src/packet/signature/v6.rs b/openpgp/src/packet/signature/v6.rs
new file mode 100644
index 00000000..d0993397
--- /dev/null
+++ b/openpgp/src/packet/signature/v6.rs
@@ -0,0 +1,282 @@
+//! OpenPGP v6 signature implementation.
+
+use std::cmp::Ordering;
+use std::convert::TryFrom;
+use std::fmt;
+use std::ops::{Deref, DerefMut};
+
+use crate::{
+ Error,
+ HashAlgorithm,
+ Packet,
+ PublicKeyAlgorithm,
+ Result,
+ SignatureType,
+ crypto::mpi,
+ packet::{
+ Signature,
+ signature::{
+ Signature4,
+ subpacket::{
+ SubpacketArea,
+ },
+ },
+ },
+};
+
+/// Holds a v6 Signature packet.
+///
+/// This holds a [version 6] 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 6]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-08.html#name-version-4-and-6-signature-p
+/// [`Signature`]: super::Signature
+#[derive(Clone)]
+pub struct Signature6 {
+ pub(crate) common: Signature4,
+ salt: Vec<u8>,
+}
+assert_send_and_sync!(Signature6);
+
+impl TryFrom<Signature> for Signature6 {
+ type Error = anyhow::Error;
+
+ fn try_from(sig: Signature) -> Result<Self> {
+ match sig {
+ Signature::V6(sig) => Ok(sig),
+ sig => Err(
+ Error::InvalidArgument(
+ format!(
+ "Got a v{}, require a v6 signature",
+ sig.version()))
+ .into()),
+ }
+ }
+}
+
+// Yes, Signature6 derefs to Signature4. This is because Signature
+// derefs to Signature4 so this is the only way to add support for v6
+// sigs without breaking the semver.
+impl Deref for Signature6 {
+ type Target = Signature4;
+
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+}
+
+impl DerefMut for Signature6 {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.common
+ }
+}
+
+impl fmt::Debug for Signature6 {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Signature6")
+ .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)