cmail_rpgp/packet/signature/
config.rs

1use std::io::Read;
2
3use byteorder::{BigEndian, ByteOrder};
4use chrono::{DateTime, Utc};
5use log::debug;
6use rand::{CryptoRng, Rng};
7
8use crate::crypto::hash::{HashAlgorithm, Hasher};
9use crate::crypto::public_key::PublicKeyAlgorithm;
10use crate::errors::Result;
11use crate::packet::{
12    Signature, SignatureType, SignatureVersion, Subpacket, SubpacketData, SubpacketType,
13};
14use crate::ser::Serialize;
15use crate::types::{Fingerprint, KeyId, KeyVersion, PublicKeyTrait, SecretKeyTrait, Tag};
16
17#[derive(Clone, PartialEq, Eq, Debug)]
18pub struct SignatureConfig {
19    pub typ: SignatureType,
20    pub pub_alg: PublicKeyAlgorithm,
21    pub hash_alg: HashAlgorithm,
22
23    pub unhashed_subpackets: Vec<Subpacket>,
24    pub hashed_subpackets: Vec<Subpacket>,
25
26    pub version_specific: SignatureVersionSpecific,
27}
28
29#[derive(Clone, PartialEq, Eq, Debug)]
30pub enum SignatureVersionSpecific {
31    V2 {
32        created: DateTime<Utc>,
33        issuer: KeyId,
34    },
35    V3 {
36        created: DateTime<Utc>,
37        issuer: KeyId,
38    },
39    V4,
40    V6 {
41        salt: Vec<u8>,
42    },
43}
44
45impl From<&SignatureVersionSpecific> for SignatureVersion {
46    fn from(value: &SignatureVersionSpecific) -> Self {
47        match value {
48            SignatureVersionSpecific::V2 { .. } => SignatureVersion::V2,
49            SignatureVersionSpecific::V3 { .. } => SignatureVersion::V3,
50            SignatureVersionSpecific::V4 => SignatureVersion::V4,
51            SignatureVersionSpecific::V6 { .. } => SignatureVersion::V6,
52        }
53    }
54}
55
56impl SignatureConfig {
57    /// Constructor for a v2 SignatureConfig (which represents the data of a v2 OpenPGP signature packet)
58    ///
59    /// OpenPGP v2 Signatures are historical and not used anymore.
60    pub fn v2(
61        typ: SignatureType,
62        pub_alg: PublicKeyAlgorithm,
63        hash_alg: HashAlgorithm,
64        created: DateTime<Utc>,
65        issuer: KeyId,
66    ) -> Self {
67        Self {
68            typ,
69            pub_alg,
70            hash_alg,
71            hashed_subpackets: Vec::new(),
72            unhashed_subpackets: Vec::new(),
73            version_specific: SignatureVersionSpecific::V2 { created, issuer },
74        }
75    }
76
77    /// Constructor for a v3 SignatureConfig (which represents the data of a v3 OpenPGP signature packet)
78    ///
79    /// OpenPGP v3 Signatures are historical and not used anymore.
80    pub fn v3(
81        typ: SignatureType,
82        pub_alg: PublicKeyAlgorithm,
83        hash_alg: HashAlgorithm,
84        created: DateTime<Utc>,
85        issuer: KeyId,
86    ) -> Self {
87        Self {
88            typ,
89            pub_alg,
90            hash_alg,
91            hashed_subpackets: Vec::new(),
92            unhashed_subpackets: Vec::new(),
93            version_specific: SignatureVersionSpecific::V3 { created, issuer },
94        }
95    }
96
97    /// Constructor for a v4 SignatureConfig (which represents the data of a v4 OpenPGP signature packet)
98    ///
99    /// OpenPGP v4 signatures were first specified in RFC 2440, and are commonly produced by
100    /// OpenPGP v4 keys.
101    pub fn v4(typ: SignatureType, pub_alg: PublicKeyAlgorithm, hash_alg: HashAlgorithm) -> Self {
102        Self {
103            typ,
104            pub_alg,
105            hash_alg,
106            unhashed_subpackets: vec![],
107            hashed_subpackets: vec![],
108            version_specific: SignatureVersionSpecific::V4,
109        }
110    }
111
112    /// Generate v6 signature salt with the appropriate length for `hash_alg`
113    /// https://www.rfc-editor.org/rfc/rfc9580.html#name-hash-algorithms
114    fn v6_salt_for<R: CryptoRng + Rng>(mut rng: R, hash_alg: HashAlgorithm) -> Result<Vec<u8>> {
115        let Some(salt_len) = hash_alg.salt_len() else {
116            bail!("Unknown v6 signature salt length for hash algorithm {hash_alg:?}");
117        };
118
119        let mut salt = vec![0; salt_len];
120        rng.fill_bytes(&mut salt);
121
122        Ok(salt)
123    }
124
125    /// Constructor for a v6 SignatureConfig (which represents the data of a v6 OpenPGP signature packet).
126    /// Generates a new salt via `rng`.
127    ///
128    /// OpenPGP v6 signatures are specified in RFC 9580, they are produced by OpenPGP v6 keys.
129    pub fn v6<R: CryptoRng + Rng>(
130        rng: R,
131        typ: SignatureType,
132        pub_alg: PublicKeyAlgorithm,
133        hash_alg: HashAlgorithm,
134    ) -> Result<Self> {
135        Ok(Self::v6_with_salt(
136            typ,
137            pub_alg,
138            hash_alg,
139            Self::v6_salt_for(rng, hash_alg)?,
140        ))
141    }
142
143    /// Constructor for a v6 SignatureConfig (which represents the data of a v6 OpenPGP signature packet).
144    ///
145    /// OpenPGP v6 signatures are specified in RFC 9580, they are produced by OpenPGP v6 keys.
146    pub fn v6_with_salt(
147        typ: SignatureType,
148        pub_alg: PublicKeyAlgorithm,
149        hash_alg: HashAlgorithm,
150        salt: Vec<u8>,
151    ) -> Self {
152        Self {
153            typ,
154            pub_alg,
155            hash_alg,
156            unhashed_subpackets: Vec::new(),
157            hashed_subpackets: Vec::new(),
158            version_specific: SignatureVersionSpecific::V6 { salt },
159        }
160    }
161
162    pub fn version(&self) -> SignatureVersion {
163        (&self.version_specific).into()
164    }
165
166    /// Sign the given data.
167    pub fn sign<F, R>(self, key: &impl SecretKeyTrait, key_pw: F, data: R) -> Result<Signature>
168    where
169        F: FnOnce() -> String,
170        R: Read,
171    {
172        ensure!(
173            (self.version() == SignatureVersion::V4 && key.version() == KeyVersion::V4)
174                || (self.version() == SignatureVersion::V6 && key.version() == KeyVersion::V6),
175            "signature version {:?} not allowed for signer key version {:?}",
176            self.version(),
177            key.version()
178        );
179
180        let mut hasher = self.hash_alg.new_hasher()?;
181
182        if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
183            hasher.update(salt.as_ref())
184        }
185
186        self.hash_data_to_sign(&mut *hasher, data)?;
187        let len = self.hash_signature_data(&mut hasher)?;
188        hasher.update(&self.trailer(len)?);
189
190        let hash = &hasher.finish()[..];
191
192        let signed_hash_value = [hash[0], hash[1]];
193        let signature = key.create_signature(key_pw, self.hash_alg, hash)?;
194
195        Ok(Signature::from_config(self, signed_hash_value, signature))
196    }
197
198    /// Create a certification self-signature.
199    pub fn sign_certification<F>(
200        self,
201        key: &impl SecretKeyTrait,
202        key_pw: F,
203        tag: Tag,
204        id: &impl Serialize,
205    ) -> Result<Signature>
206    where
207        F: FnOnce() -> String,
208    {
209        self.sign_certification_third_party(key, key_pw, key, tag, id)
210    }
211
212    /// Create a certification third-party signature.
213    pub fn sign_certification_third_party<F>(
214        self,
215        signer: &impl SecretKeyTrait,
216        signer_pw: F,
217        signee: &impl PublicKeyTrait,
218        tag: Tag,
219        id: &impl Serialize,
220    ) -> Result<Signature>
221    where
222        F: FnOnce() -> String,
223    {
224        ensure!(
225            (self.version() == SignatureVersion::V4 && signer.version() == KeyVersion::V4)
226                || (self.version() == SignatureVersion::V6 && signer.version() == KeyVersion::V6),
227            "signature version {:?} not allowed for signer key version {:?}",
228            self.version(),
229            signer.version()
230        );
231        ensure!(
232            self.is_certification(),
233            "can not sign non certification as certification"
234        );
235
236        debug!("signing certification {:#?}", self.typ);
237
238        let mut hasher = self.hash_alg.new_hasher()?;
239
240        if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
241            hasher.update(salt.as_ref())
242        }
243
244        signee.serialize_for_hashing(&mut hasher)?;
245
246        let mut packet_buf = Vec::new();
247        id.to_writer(&mut packet_buf)?;
248
249        match self.version() {
250            SignatureVersion::V2 | SignatureVersion::V3 => {
251                // Nothing to do
252            }
253            SignatureVersion::V4 | SignatureVersion::V6 => {
254                let prefix = match tag {
255                    Tag::UserId => 0xB4,
256                    Tag::UserAttribute => 0xD1,
257                    _ => bail!("invalid tag for certification signature: {:?}", tag),
258                };
259
260                let mut prefix_buf = [prefix, 0u8, 0u8, 0u8, 0u8];
261                BigEndian::write_u32(&mut prefix_buf[1..], packet_buf.len().try_into()?);
262
263                // prefixes
264                hasher.update(&prefix_buf);
265            }
266            SignatureVersion::V5 => {
267                bail!("v5 signature unsupported sign tps")
268            }
269            SignatureVersion::Other(version) => {
270                bail!("unsupported signature version {}", version)
271            }
272        }
273
274        // the packet content
275        hasher.update(&packet_buf);
276
277        let len = self.hash_signature_data(&mut hasher)?;
278        hasher.update(&self.trailer(len)?);
279
280        let hash = &hasher.finish()[..];
281
282        let signed_hash_value = [hash[0], hash[1]];
283        let signature = signer.create_signature(signer_pw, self.hash_alg, hash)?;
284
285        Ok(Signature::from_config(self, signed_hash_value, signature))
286    }
287
288    /// Sign a key binding.
289    pub fn sign_key_binding<F>(
290        self,
291        signing_key: &impl SecretKeyTrait,
292        key_pw: F,
293        key: &impl PublicKeyTrait,
294    ) -> Result<Signature>
295    where
296        F: FnOnce() -> String,
297    {
298        ensure!(
299            (self.version() == SignatureVersion::V4 && signing_key.version() == KeyVersion::V4)
300                || (self.version() == SignatureVersion::V6
301                    && signing_key.version() == KeyVersion::V6),
302            "signature version {:?} not allowed for signer key version {:?}",
303            self.version(),
304            signing_key.version()
305        );
306        debug!(
307            "signing key binding: {:#?} - {:#?} - {:#?}",
308            self, signing_key, key
309        );
310
311        let mut hasher = self.hash_alg.new_hasher()?;
312
313        if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
314            hasher.update(salt.as_ref())
315        }
316
317        // Signing Key
318        signing_key.serialize_for_hashing(&mut hasher)?;
319
320        // Key being bound
321        key.serialize_for_hashing(&mut hasher)?;
322
323        let len = self.hash_signature_data(&mut hasher)?;
324        hasher.update(&self.trailer(len)?);
325
326        let hash = &hasher.finish()[..];
327        let signed_hash_value = [hash[0], hash[1]];
328        let signature = signing_key.create_signature(key_pw, self.hash_alg, hash)?;
329
330        Ok(Signature::from_config(self, signed_hash_value, signature))
331    }
332
333    /// Signs a direct key signature or a revocation.
334    pub fn sign_key<F>(
335        self,
336        signing_key: &impl SecretKeyTrait,
337        key_pw: F,
338        key: &impl PublicKeyTrait,
339    ) -> Result<Signature>
340    where
341        F: FnOnce() -> String,
342    {
343        ensure!(
344            (self.version() == SignatureVersion::V4 && signing_key.version() == KeyVersion::V4)
345                || (self.version() == SignatureVersion::V6
346                    && signing_key.version() == KeyVersion::V6),
347            "signature version {:?} not allowed for signer key version {:?}",
348            self.version(),
349            signing_key.version()
350        );
351        debug!("signing key (revocation): {:#?} - {:#?}", self, key);
352
353        let mut hasher = self.hash_alg.new_hasher()?;
354
355        if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
356            hasher.update(salt.as_ref())
357        }
358
359        key.serialize_for_hashing(&mut hasher)?;
360
361        let len = self.hash_signature_data(&mut hasher)?;
362        hasher.update(&self.trailer(len)?);
363
364        let hash = &hasher.finish()[..];
365        let signed_hash_value = [hash[0], hash[1]];
366        let signature = signing_key.create_signature(key_pw, self.hash_alg, hash)?;
367
368        Ok(Signature::from_config(self, signed_hash_value, signature))
369    }
370
371    /// Returns what kind of signature this is.
372    pub fn typ(&self) -> SignatureType {
373        self.typ
374    }
375
376    /// Calculate the serialized version of this packet, but only the part relevant for hashing.
377    pub fn hash_signature_data(&self, hasher: &mut dyn std::io::Write) -> Result<usize> {
378        match self.version() {
379            SignatureVersion::V2 | SignatureVersion::V3 => {
380                let created = {
381                    if let SignatureVersionSpecific::V2 { created, .. }
382                    | SignatureVersionSpecific::V3 { created, .. } = self.version_specific
383                    {
384                        created
385                    } else {
386                        bail!("must exist for a v2/3 signature")
387                    }
388                };
389
390                let mut buf = [0u8; 5];
391                buf[0] = self.typ.into();
392                BigEndian::write_u32(&mut buf[1..], created.timestamp().try_into()?);
393
394                hasher.write_all(&buf)?;
395
396                // no trailer
397                Ok(0)
398            }
399            SignatureVersion::V4 | SignatureVersion::V6 => {
400                // TODO: reduce duplication with serialization code
401
402                let mut res = vec![
403                    // the signature version
404                    self.version().into(),
405                    // the signature type
406                    self.typ.into(),
407                    // the public-key algorithm
408                    self.pub_alg.into(),
409                    // the hash algorithm
410                    self.hash_alg.into(),
411                ];
412
413                // hashed subpackets
414                let mut hashed_subpackets = Vec::new();
415                for packet in &self.hashed_subpackets {
416                    debug!("hashing {:#?}", packet);
417
418                    // If a subpacket is encountered that is marked critical but is unknown to the
419                    // evaluating implementation, the evaluator SHOULD consider the signature to be
420                    // in error.
421                    //
422                    // (See https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.7-6)
423                    if packet.is_critical && matches!(packet.typ(), SubpacketType::Other(_)) {
424                        // "[..] The purpose of the critical bit is to allow the signer to tell an
425                        // evaluator that it would prefer a new, unknown feature to generate an
426                        // error rather than being ignored."
427                        //
428                        // (See https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.7-8:)
429
430                        bail!("Unknown critical subpacket {:?}", packet);
431                    }
432
433                    // If the version octet does not match the signature version, the receiving
434                    // implementation MUST treat it as a malformed signature
435                    //
436                    // (See https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.35-3)
437                    if let SubpacketData::IssuerFingerprint(fp) = &packet.data {
438                        match (self.version(), fp.version()) {
439                            (SignatureVersion::V6, Some(KeyVersion::V6)) => {},
440                            (SignatureVersion::V4, Some(KeyVersion::V4)) => {},
441                            _ => bail!("IntendedRecipientFingerprint {:?} doesn't match signature version {:?}", fp, self.version())
442                        }
443                    }
444
445                    packet.to_writer(&mut hashed_subpackets)?;
446                }
447
448                // append hashed area length, as u16 for v4, and u32 for v6
449                if self.version() == SignatureVersion::V4 {
450                    res.extend(u16::try_from(hashed_subpackets.len())?.to_be_bytes());
451                } else if self.version() == SignatureVersion::V6 {
452                    res.extend(u32::try_from(hashed_subpackets.len())?.to_be_bytes());
453                }
454
455                res.extend(hashed_subpackets);
456
457                hasher.write_all(&res)?;
458
459                Ok(res.len())
460            }
461            SignatureVersion::V5 => {
462                bail!("v5 signature unsupported hash data")
463            }
464            SignatureVersion::Other(version) => {
465                bail!("unsupported signature version {}", version)
466            }
467        }
468    }
469
470    pub fn hash_data_to_sign<R>(&self, hasher: &mut dyn Hasher, mut data: R) -> Result<usize>
471    where
472        R: Read,
473    {
474        match self.typ {
475            SignatureType::Text |
476                // assumes that the passed in text was already valid utf8 and normalized
477            SignatureType::Binary => {
478                Ok(std::io::copy(&mut data, hasher)? as usize)
479            }
480            SignatureType::Timestamp |
481            SignatureType::Standalone => {
482                let mut val = [0u8;1];
483                data.read_exact(&mut val[..])?;
484                hasher.update(&val[..]);
485                Ok(1)
486            }
487            SignatureType::CertGeneric
488            | SignatureType::CertPersona
489            | SignatureType::CertCasual
490            | SignatureType::CertPositive
491            | SignatureType::CertRevocation => {
492                unimplemented_err!("{:?}", self.typ);
493            }
494            SignatureType::SubkeyBinding
495            | SignatureType::SubkeyRevocation
496            | SignatureType::KeyBinding
497            | SignatureType::Key => {
498                unimplemented_err!("{:?}", self.typ);
499            }
500            SignatureType::KeyRevocation => unimplemented_err!("KeyRevocation"),
501            SignatureType::ThirdParty => unimplemented_err!("signing ThirdParty"),
502
503            SignatureType::Other(id) => unimplemented_err!("Other ({})", id),
504        }
505    }
506
507    pub fn trailer(&self, len: usize) -> Result<Vec<u8>> {
508        match self.version() {
509            SignatureVersion::V2 | SignatureVersion::V3 => {
510                // Nothing to do
511                Ok(Vec::new())
512            }
513            SignatureVersion::V4 | SignatureVersion::V6 => {
514                let mut trailer = vec![self.version().into(), 0xFF, 0, 0, 0, 0];
515                BigEndian::write_u32(&mut trailer[2..], len.try_into()?);
516                Ok(trailer)
517            }
518            SignatureVersion::V5 => {
519                bail!("v5 signature unsupported")
520            }
521            SignatureVersion::Other(version) => {
522                bail!("unsupported signature version {}", version)
523            }
524        }
525    }
526
527    /// Returns an iterator of all subpackets in the signature: all subpackets in the hashed area
528    /// followed by all subpackets in the unhashed area.
529    #[deprecated(
530        note = "Usually only hashed_subpackets should be used. unhashed_subpackets are only safe and useful to access in rare circumstances. When they are needed, unhashed_subpackets should be explicitly called."
531    )]
532    pub fn subpackets(&self) -> impl Iterator<Item = &Subpacket> {
533        self.hashed_subpackets().chain(self.unhashed_subpackets())
534    }
535
536    /// Returns an iterator over the hashed subpackets of this signature.
537    pub fn hashed_subpackets(&self) -> impl Iterator<Item = &Subpacket> {
538        self.hashed_subpackets.iter()
539    }
540
541    /// Returns an iterator over the unhashed subpackets of this signature.
542    pub fn unhashed_subpackets(&self) -> impl Iterator<Item = &Subpacket> {
543        self.unhashed_subpackets.iter()
544    }
545
546    /// Returns if the signature is a certification or not.
547    pub fn is_certification(&self) -> bool {
548        matches!(
549            self.typ,
550            SignatureType::CertGeneric
551                | SignatureType::CertPersona
552                | SignatureType::CertCasual
553                | SignatureType::CertPositive
554                | SignatureType::CertRevocation
555        )
556    }
557
558    /// Signature Creation Time.
559    ///
560    /// The time the signature was made.
561    /// MUST be present in the hashed area.
562    ///
563    /// <https://www.rfc-editor.org/rfc/rfc9580.html#name-signature-creation-time>
564    ///
565    /// Returns the first Signature Creation Time subpacket, only from the hashed area.
566    pub fn created(&self) -> Option<&DateTime<Utc>> {
567        if let SignatureVersionSpecific::V2 { created, .. }
568        | SignatureVersionSpecific::V3 { created, .. } = &self.version_specific
569        {
570            return Some(created);
571        }
572
573        self.hashed_subpackets().find_map(|p| match p.data {
574            SubpacketData::SignatureCreationTime(ref d) => Some(d),
575            _ => None,
576        })
577    }
578
579    /// Issuer Key ID.
580    ///
581    /// The OpenPGP Key ID of the key issuing the signature.
582    ///
583    /// <https://www.rfc-editor.org/rfc/rfc9580.html#name-issuer-key-id>
584    ///
585    /// Returns Issuer subpacket data from both the hashed and unhashed area.
586    pub fn issuer(&self) -> Vec<&KeyId> {
587        // legacy v2/v3 signatures have an explicit "issuer" field
588        if let SignatureVersionSpecific::V2 { issuer, .. }
589        | SignatureVersionSpecific::V3 { issuer, .. } = &self.version_specific
590        {
591            return vec![issuer];
592        }
593
594        // v4+ signatures use subpackets
595        //
596        // We consider data from both the hashed and unhashed area here, because the issuer Key ID
597        // only acts as a hint. The signature will be cryptographically checked using the purported
598        // issuer's key material. An attacker cannot successfully claim an issuer Key ID that they
599        // can't produce a cryptographically valid signature for.
600        self.hashed_subpackets()
601            .chain(self.unhashed_subpackets())
602            .filter_map(|sp| match sp.data {
603                SubpacketData::Issuer(ref id) => Some(id),
604                _ => None,
605            })
606            .collect()
607    }
608
609    /// Issuer Fingerprint.
610    ///
611    /// The OpenPGP Key fingerprint of the key issuing the signature.
612    ///
613    /// <https://www.rfc-editor.org/rfc/rfc9580.html#name-issuer-fingerprint>
614    ///
615    /// Returns Issuer Fingerprint subpacket data from both the hashed and unhashed area.
616    pub fn issuer_fingerprint(&self) -> Vec<&Fingerprint> {
617        self.hashed_subpackets()
618            .chain(self.unhashed_subpackets())
619            .filter_map(|sp| match &sp.data {
620                SubpacketData::IssuerFingerprint(fp) => Some(fp),
621                _ => None,
622            })
623            .collect()
624    }
625}