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}