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}