voa_openpgp/lib.rs
1#![doc = include_str!("../README.md")]
2
3mod cert;
4mod error;
5mod voa;
6
7pub mod import;
8mod signature;
9
10use std::{fs::read, path::Path};
11
12pub use error::Error;
13pub use import::OpenPgpImport;
14use log::debug;
15use pgp::types::KeyDetails;
16
17pub use crate::{cert::OpenpgpCert, signature::OpenpgpSignature, voa::VoaOpenpgp};
18
19/// File name suffix for verifiers in the VOA "openpgp" technology
20pub(crate) const FILE_SUFFIX: &str = ".openpgp";
21
22/// Verifies one or more `signatures` for a `signed_file` with one or more `certs`.
23///
24/// All `signatures` are used to check the data in `signed_file`.
25/// For each signature, all `certs` are considered for finding a matching component key for signing.
26///
27/// The return value is a list of tuples, one entry for each signature (in order).
28/// Signatures that could not be positively verified have a `None` entry.
29/// Successfully verified signatures have a `Some` entry with a tuple of a signature, certificate
30/// and the fingerprint of the component key which verified the data in `signed_file`.
31///
32/// # Note
33///
34/// Does not consider OpenPGP signatures with an unset creation time and instead emits a warning for
35/// them.
36///
37/// # Errors
38///
39/// Returns an error, if the `signed_file` cannot be read.
40#[allow(clippy::type_complexity)]
41pub fn verify_from_file<'a>(
42 signed_file: &Path,
43 certs: &[&'a OpenpgpCert],
44 signatures: &[&'a OpenpgpSignature],
45) -> Result<Vec<(&'a OpenpgpSignature, Option<(&'a OpenpgpCert, String)>)>, Error> {
46 let mut result = Vec::new();
47
48 let data = read(signed_file).map_err(|source| Error::IoPath {
49 path: signed_file.into(),
50 source,
51 context: "reading signed_file",
52 })?;
53
54 'sigs: for sig in signatures {
55 let Some(created) = sig.creation_time() else {
56 // this signature is not ok, we'll skip it
57 debug!("Skipping signature with unset creation time: {sig:?}");
58
59 result.push((*sig, None));
60
61 continue;
62 };
63
64 for cert in certs {
65 // The set of all component keys in this verifier that can be used for data
66 // signature validation, using the signature creation time as the
67 // reference time
68 let valid_keys = cert
69 .certificate
70 .valid_signing_capable_component_keys_at(&created.into());
71
72 for key in valid_keys {
73 if sig.verifiable_with(&key) && key.verify(&sig.detached.signature, &data).is_ok() {
74 let component_fp = format!("{:02x?}", key.as_componentkey().fingerprint());
75
76 result.push((*sig, Some((*cert, component_fp))));
77 continue 'sigs; // we're done with this signature
78 }
79 }
80
81 // We failed to validate this signature as correct
82 }
83
84 result.push((*sig, None));
85 }
86
87 Ok(result)
88}