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}