sequoia_openpgp/packet/signature/
v6.rs

1//! OpenPGP v6 signature implementation.
2
3use std::cmp::Ordering;
4use std::convert::TryFrom;
5use std::fmt;
6use std::ops::{Deref, DerefMut};
7
8use crate::{
9    Error,
10    HashAlgorithm,
11    Packet,
12    PublicKeyAlgorithm,
13    Result,
14    SignatureType,
15    crypto::mpi,
16    packet::{
17        Signature,
18        signature::{
19            Signature4,
20            subpacket::{
21                SubpacketArea,
22            },
23        },
24    },
25};
26
27/// Holds a v6 Signature packet.
28///
29/// This holds a [version 6] Signature packet.  Normally, you won't
30/// directly work with this data structure, but with the [`Signature`]
31/// enum, which is version agnostic.  An exception is when you need to
32/// do version-specific operations.  But currently, there aren't any
33/// version-specific methods.
34///
35///   [version 6]: https://www.rfc-editor.org/rfc/rfc9580.html#name-versions-4-and-6-signature-
36///   [`Signature`]: super::Signature
37#[derive(Clone)]
38pub struct Signature6 {
39    pub(crate) common: Signature4,
40    salt: Vec<u8>,
41}
42assert_send_and_sync!(Signature6);
43
44impl TryFrom<Signature> for Signature6 {
45    type Error = anyhow::Error;
46
47    fn try_from(sig: Signature) -> Result<Self> {
48        match sig {
49            Signature::V6(sig) => Ok(sig),
50            sig => Err(
51                Error::InvalidArgument(
52                    format!(
53                        "Got a v{}, require a v6 signature",
54                        sig.version()))
55                    .into()),
56        }
57    }
58}
59
60// Yes, Signature6 derefs to Signature4.  This is because Signature
61// derefs to Signature4 so this is the only way to add support for v6
62// sigs without breaking the semver.
63impl Deref for Signature6 {
64    type Target = Signature4;
65
66    fn deref(&self) -> &Self::Target {
67        &self.common
68    }
69}
70
71impl DerefMut for Signature6 {
72    fn deref_mut(&mut self) -> &mut Self::Target {
73        &mut self.common
74    }
75}
76
77impl fmt::Debug for Signature6 {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        f.debug_struct("Signature6")
80            .field("version", &self.version())
81            .field("typ", &self.typ())
82            .field("pk_algo", &self.pk_algo())
83            .field("hash_algo", &self.hash_algo())
84            .field("hashed_area", self.hashed_area())
85            .field("unhashed_area", self.unhashed_area())
86            .field("additional_issuers", &self.additional_issuers)
87            .field("digest_prefix",
88                   &crate::fmt::to_hex(&self.digest_prefix, false))
89            .field("salt", &crate::fmt::hex::encode(&self.salt))
90            .field(
91                "computed_digest",
92                &self
93                    .computed_digest
94                    .get()
95                    .map(|hash| crate::fmt::to_hex(&hash[..], false)),
96            )
97            .field("level", &self.level)
98            .field("mpis", &self.mpis)
99            .finish()
100    }
101}
102
103impl PartialEq for Signature6 {
104    /// This method tests for self and other values to be equal, and
105    /// is used by ==.
106    ///
107    /// This method compares the serialized version of the two
108    /// packets.  Thus, the computed values are ignored ([`level`],
109    /// [`computed_digest`]).
110    ///
111    /// [`level`]: Signature6::level()
112    /// [`computed_digest`]: Signature6::computed_digest()
113    fn eq(&self, other: &Signature6) -> bool {
114        self.cmp(other) == Ordering::Equal
115    }
116}
117
118impl Eq for Signature6 {}
119
120impl PartialOrd for Signature6 {
121    fn partial_cmp(&self, other: &Signature6) -> Option<Ordering> {
122        Some(self.cmp(other))
123    }
124}
125
126impl Ord for Signature6 {
127    fn cmp(&self, other: &Signature6) -> Ordering {
128        self.common.cmp(&other.common)
129            .then_with(|| self.salt.cmp(&other.salt))
130    }
131}
132
133impl std::hash::Hash for Signature6 {
134    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
135        use std::hash::Hash as StdHash;
136        StdHash::hash(&self.common, state);
137        StdHash::hash(&self.salt, state);
138    }
139}
140
141impl Signature6 {
142    /// Creates a new signature packet from common fields and salt.
143    pub(crate) fn from_common(mut common: Signature4, salt: Vec<u8>)
144                              -> Result<Self>
145    {
146        common.fields.version = 6;
147        Ok(Signature6 { common, salt })
148    }
149
150    /// Creates a new signature packet.
151    ///
152    /// If you want to sign something, consider using the
153    /// [`SignatureBuilder`] interface.
154    ///
155    /// [`SignatureBuilder`]: crate::packet::signature::SignatureBuilder
156    pub fn new(typ: SignatureType, pk_algo: PublicKeyAlgorithm,
157               hash_algo: HashAlgorithm, hashed_area: SubpacketArea,
158               unhashed_area: SubpacketArea,
159               digest_prefix: [u8; 2],
160               salt: Vec<u8>,
161               mpis: mpi::Signature) -> Result<Self> {
162        Signature6::from_common(
163            Signature4::new(typ, pk_algo, hash_algo,
164                            hashed_area, unhashed_area,
165                            digest_prefix, mpis),
166            salt)
167    }
168
169    /// Gets the public key algorithm.
170    // SigantureFields::pk_algo is private, because we don't want it
171    // available on SignatureBuilder, which also derefs to
172    // &SignatureFields.
173    pub fn pk_algo(&self) -> PublicKeyAlgorithm {
174        self.fields.pk_algo()
175    }
176
177    /// Gets the hash prefix.
178    pub fn digest_prefix(&self) -> &[u8; 2] {
179        &self.digest_prefix
180    }
181
182    /// Sets the hash prefix.
183    #[allow(dead_code)]
184    pub(crate) fn set_digest_prefix(&mut self, prefix: [u8; 2]) -> [u8; 2] {
185        ::std::mem::replace(&mut self.digest_prefix, prefix)
186    }
187
188    /// Gets the salt.
189    pub fn salt(&self) -> &[u8] {
190        &self.salt
191    }
192
193    /// Sets the salt.
194    #[allow(dead_code)]
195    pub(crate) fn set_salt(&mut self, salt: Vec<u8>) -> Vec<u8> {
196        ::std::mem::replace(&mut self.salt, salt)
197    }
198
199    /// Gets the signature packet's MPIs.
200    pub fn mpis(&self) -> &mpi::Signature {
201        &self.mpis
202    }
203
204    /// Sets the signature packet's MPIs.
205    #[allow(dead_code)]
206    pub(crate) fn set_mpis(&mut self, mpis: mpi::Signature) -> mpi::Signature
207    {
208        ::std::mem::replace(&mut self.mpis, mpis)
209    }
210
211    /// Gets the computed hash value.
212    ///
213    /// This is set by the [`PacketParser`] when parsing the message.
214    ///
215    /// [`PacketParser`]: crate::parse::PacketParser
216    pub fn computed_digest(&self) -> Option<&[u8]> {
217        self.computed_digest.get().map(|d| &d[..])
218    }
219
220    /// Gets the signature level.
221    ///
222    /// A level of 0 indicates that the signature is directly over the
223    /// data, a level of 1 means that the signature is a notarization
224    /// over all level 0 signatures and the data, and so on.
225    pub fn level(&self) -> usize {
226        self.level
227    }
228
229    /// Returns whether this signature should be exported.
230    ///
231    /// This checks whether the [`Exportable Certification`] subpacket
232    /// is absent or present and 1, and that the signature does not
233    /// include any sensitive [`Revocation Key`] (designated revokers)
234    /// subpackets.
235    ///
236    ///   [`Exportable Certification`]: https://www.rfc-editor.org/rfc/rfc9580.html#name-exportable-certification
237    ///   [`Revocation Key`]: https://www.rfc-editor.org/rfc/rfc9580.html#name-revocation-key-deprecated
238    pub fn exportable(&self) -> Result<()> {
239        if ! self.exportable_certification().unwrap_or(true) {
240            return Err(Error::InvalidOperation(
241                "Cannot export non-exportable certification".into()).into());
242        }
243
244        if self.revocation_keys().any(|r| r.sensitive()) {
245            return Err(Error::InvalidOperation(
246                "Cannot export signature with sensitive designated revoker"
247                    .into()).into());
248        }
249
250        Ok(())
251    }
252}
253
254impl From<Signature6> for Packet {
255    fn from(s: Signature6) -> Self {
256        Packet::Signature(s.into())
257    }
258}
259
260impl From<Signature6> for super::Signature {
261    fn from(s: Signature6) -> Self {
262        super::Signature::V6(s)
263    }
264}
265
266#[cfg(test)]
267use quickcheck::{Arbitrary, Gen};
268#[cfg(test)]
269use crate::packet::signature::ArbitraryBounded;
270
271#[cfg(test)]
272impl ArbitraryBounded for Signature6 {
273    fn arbitrary_bounded(g: &mut Gen, depth: usize) -> Self {
274        let common = Signature4::arbitrary_bounded(g, depth);
275        let salt_size = common.hash_algo().salt_size().unwrap_or(16);
276        let mut salt = vec![0u8; salt_size];
277        salt.iter_mut().for_each(|p| *p = u8::arbitrary(g));
278        Self::from_common(common, salt)
279            .expect("salt has the right size")
280    }
281}
282
283#[cfg(test)]
284impl_arbitrary_with_bound!(Signature6);