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