voa_openpgp/
signature.rs

1//! Detached OpenPGP signatures for use with VOA verifiers.
2
3use std::{
4    path::{Path, PathBuf},
5    time::SystemTime,
6};
7
8use pgp::{
9    composed::{Deserializable, DetachedSignature},
10    types::KeyDetails,
11};
12use rpgpie::certificate::SignatureVerifier;
13
14use crate::{Error, OpenpgpCert};
15
16/// A detached OpenPGP signature for use in VOA verification.
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct OpenpgpSignature {
19    pub(crate) detached: DetachedSignature,
20
21    /// The file that this signature was loaded from
22    pub(crate) source: Option<PathBuf>,
23}
24
25impl OpenpgpSignature {
26    /// Creates an [`OpenpgpSignature`] from the file in `path`.
27    ///
28    /// # Errors
29    ///
30    /// Returns an error:
31    /// - If `path` can't be read
32    /// - If an [`OpenpgpSignature`] cannot be parsed from the content of the file
33    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
34        let path = path.as_ref();
35
36        let data = std::fs::read(path).map_err(|source| Error::IoPath {
37            path: path.into(),
38            source,
39            context: "foo",
40        })?;
41
42        let mut sig = Self::from_slice(data.as_slice())?;
43        sig.source = Some(path.to_path_buf());
44
45        Ok(sig)
46    }
47
48    /// Creates an [`OpenpgpSignature`] from `data`.
49    ///
50    /// # Errors
51    ///
52    /// Returns an error, if an [`OpenpgpSignature`] cannot be parsed from `data`.
53    pub(crate) fn from_slice(data: &[u8]) -> Result<Self, Error> {
54        let detached = DetachedSignature::from_bytes(data).map_err(|source| Error::OpenPgp {
55            context: "while parsing detached signature",
56            source,
57        })?;
58
59        Ok(Self {
60            detached,
61            source: None,
62        })
63    }
64
65    /// Returns the creation time.
66    ///
67    /// Considering the creation time of a detached OpenPGP signature prevents attacks where a
68    /// signature claims to be very old, and thereby is allowed to use a weak cryptographic
69    /// mechanism.
70    ///
71    /// # Note
72    ///
73    /// Users of [`OpenpgpSignature`] are advised to discard signatures, if their creation time is
74    /// implausible, e.g. if a signature claims to be older than the data, that it claims to be
75    /// over.
76    ///
77    /// # Errors
78    ///
79    /// Returns an error if no creation time is found.
80    pub fn creation_time(&self) -> Option<SystemTime> {
81        self.detached.signature.created().map(|c| (*c).into())
82    }
83
84    /// The source file that this signature was loaded from, if known
85    pub fn source(&self) -> Option<&Path> {
86        self.source.as_deref()
87    }
88
89    /// Returns `true` if `verifier`  matches any
90    /// [Issuer](https://www.rfc-editor.org/rfc/rfc9580.html#name-issuer-key-id) or
91    /// [IssuerFingerprint](https://www.rfc-editor.org/rfc/rfc9580.html#issuer-fingerprint-subpacket)
92    /// subpacket in this [`OpenpgpSignature`].
93    pub(crate) fn verifiable_with(&self, verifier: &SignatureVerifier) -> bool {
94        let issuer = self.detached.signature.issuer();
95        let issuer_fp = self.detached.signature.issuer_fingerprint();
96
97        issuer.contains(&&verifier.as_componentkey().key_id())
98            || issuer_fp.contains(&&verifier.as_componentkey().fingerprint())
99    }
100}
101
102/// The OpenPGP certificate that issued an OpenPGP signature, and the fingerprint of the
103/// component key (within the `certificate`) that created the signature.
104///
105/// Holds a reference to an [`OpenpgpCert`] and an OpenPGP fingerprint string.
106#[derive(Debug)]
107pub struct SignerInfo<'a> {
108    certificate: &'a OpenpgpCert,
109    component_fingerprint: String,
110}
111
112impl<'a> SignerInfo<'a> {
113    /// Creates a new [`SignerInfo`] from an [`OpenpgpCert`] and a fingerprint.
114    ///
115    /// This struct must only be constructed once the cryptographic validity of the target
116    /// signature has been successfully verified with this signer.
117    pub(crate) fn new(
118        certificate: &'a OpenpgpCert,
119        component_fingerprint: String,
120    ) -> SignerInfo<'a> {
121        Self {
122            certificate,
123            component_fingerprint,
124        }
125    }
126
127    /// Returns a reference to the OpenPGP certificate.
128    pub fn certificate(&self) -> &OpenpgpCert {
129        self.certificate
130    }
131
132    /// Returns a reference to the OpenPGP fingerprint of the component key that issued the
133    /// signature.
134    pub fn component_fingerprint(&self) -> &str {
135        &self.component_fingerprint
136    }
137}
138
139/// The representation of an OpenPGP signature check.
140///
141/// Holds a reference to an [`OpenpgpSignature`] and optionally a [`SignerInfo`].
142#[derive(Debug)]
143pub struct OpenpgpSignatureCheck<'a> {
144    signature: &'a OpenpgpSignature,
145    signer_info: Option<SignerInfo<'a>>,
146}
147
148impl<'a> OpenpgpSignatureCheck<'a> {
149    /// Creates a new [`OpenpgpSignatureCheck`] from an [`OpenpgpSignature`] and an optional
150    /// [`SignerInfo`].
151    ///
152    /// `signer_info` is `Some` if the signature has been cryptographically validated with that
153    /// signer, and `None` if verification could not determine a valid signer.
154    pub(crate) fn new(
155        signature: &'a OpenpgpSignature,
156        signer_info: Option<SignerInfo<'a>>,
157    ) -> OpenpgpSignatureCheck<'a> {
158        Self {
159            signature,
160            signer_info,
161        }
162    }
163
164    /// Returns a reference to the signature.
165    pub fn signature(&self) -> &OpenpgpSignature {
166        self.signature
167    }
168
169    /// Returns a reference to the signer information.
170    pub fn signer_info(&self) -> Option<&SignerInfo<'a>> {
171        self.signer_info.as_ref()
172    }
173}