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;
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}