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