1#![doc = include_str!("../README.md")]
2
3mod cert;
4mod error;
5mod voa;
6
7pub mod import;
8pub mod lookup;
9mod signature;
10
11use std::{fs::read, path::Path};
12
13pub use error::Error;
14pub use import::OpenPgpImport;
15use log::{debug, error, info, warn};
16use pgp::types::KeyDetails;
17
18use crate::lookup::VerifierLookup;
19pub use crate::{
20 cert::OpenpgpCert,
21 signature::{OpenpgpSignature, OpenpgpSignatureCheck, SignerInfo},
22 voa::VoaOpenpgp,
23};
24
25pub(crate) const FILE_SUFFIX: &str = ".openpgp";
27
28fn log_sig_data(signature: &OpenpgpSignature) -> String {
34 format!(
35 "{}{}",
36 signature
37 .detached
38 .signature
39 .issuer_fingerprint()
40 .iter()
41 .map(|fingerprint| fingerprint.to_string())
42 .collect::<Vec<String>>()
43 .join(", "),
44 if let Some(path) = signature.source() {
45 format!(" ({})", path.to_string_lossy())
46 } else {
47 "".to_string()
48 }
49 )
50}
51
52fn log_cert_data(cert: &OpenpgpCert) -> String {
58 format!(
59 "{} ({})",
60 cert.fingerprint(),
61 cert.sources
62 .iter()
63 .map(|verifier| verifier.canonicalized().to_string_lossy().to_string())
64 .collect::<Vec<_>>()
65 .join(",")
66 )
67}
68
69pub fn verify_from_file<'a>(
88 signed_file: impl AsRef<Path>,
89 lookup: &'a VerifierLookup,
90 signatures: &'a [OpenpgpSignature],
91) -> Result<Vec<OpenpgpSignatureCheck<'a>>, Error> {
92 let signed_file = signed_file.as_ref();
93 let mut result = Vec::new();
94
95 let data = read(signed_file).map_err(|source| Error::IoPath {
97 path: signed_file.into(),
98 source,
99 context: "reading signed_file",
100 })?;
101
102 'sigs: for signature in signatures {
103 let log_sig_data = log_sig_data(signature);
104 info!("Verifying OpenPGP signature ({log_sig_data}) for file {signed_file:?}");
105
106 let verifiers = lookup.get_matching_verifiers(signature);
107 debug!("Found {} candidate verifiers", verifiers.len());
108
109 for (verifier, cert) in &verifiers {
110 let component_fp = format!("{:02x?}", verifier.as_componentkey().fingerprint());
111 let log_cert_data = log_cert_data(cert);
112
113 if let Err(error) = verifier.verify(&signature.detached.signature, &data) {
115 error!(
121 "Signature verification error for {log_sig_data} for file {signed_file:?} with component key {component_fp} in OpenPGP certificate {log_cert_data}: {error}"
122 );
123 continue; }
125
126 info!(
127 "Successfully verified OpenPGP signature {log_sig_data} for file {signed_file:?} with component key {component_fp} in OpenPGP certificate {log_cert_data}"
128 );
129
130 result.push(OpenpgpSignatureCheck::new(
131 signature,
132 Some(SignerInfo::new(cert, component_fp)),
133 ));
134
135 continue 'sigs; }
137
138 warn!(
139 "Signature {log_sig_data} and file {signed_file:?} could not be verified (tried {} candidate verifiers)",
140 verifiers.len()
141 );
142 result.push(OpenpgpSignatureCheck::new(signature, None));
143 }
144
145 Ok(result)
146}