Skip to main content

pdf_ast/security/
signatures.rs

1pub struct SignatureVerifier;
2
3impl SignatureVerifier {
4    pub fn verify_pkcs7_signature(
5        contents: &[u8],
6        signed_data: &[u8],
7    ) -> SignatureVerificationResult {
8        let handler = crate::crypto::pkcs7::Pkcs7Handler::new();
9        match handler.verify_pkcs7(contents, signed_data) {
10            Ok(result) => SignatureVerificationResult {
11                is_valid: result.is_valid,
12                error: result.error_message.clone(),
13                certificate_chain: result
14                    .certificate_chain
15                    .iter()
16                    .map(|c| CertificateInfo {
17                        subject: c.subject.clone(),
18                        issuer: c.issuer.clone(),
19                        serial_number: c.serial_number.clone(),
20                        not_before: format!("{}", c.not_before),
21                        not_after: format!("{}", c.not_after),
22                        public_key_algorithm: c.public_key_algorithm.clone(),
23                        signature_algorithm: c.signature_algorithm.clone(),
24                        key_usage: c.key_usage.clone(),
25                        extended_key_usage: c.extended_key_usage.clone(),
26                    })
27                    .collect(),
28                signing_time: result.signing_time.map(|t| format!("{}", t)),
29                timestamp_info: result.timestamp_info.as_ref().map(|t| TimestampInfo {
30                    timestamp_authority: t.timestamp_authority.clone(),
31                    timestamp: format!("{}", t.timestamp),
32                    hash_algorithm: t.hash_algorithm.clone(),
33                    is_valid: t.is_valid,
34                }),
35            },
36            Err(e) => SignatureVerificationResult {
37                is_valid: false,
38                error: Some(format!("PKCS#7 verification error: {}", e)),
39                certificate_chain: Vec::new(),
40                signing_time: None,
41                timestamp_info: None,
42            },
43        }
44    }
45
46    pub fn verify_x509_signature(
47        _contents: &[u8],
48        _signed_data: &[u8],
49    ) -> SignatureVerificationResult {
50        SignatureVerificationResult {
51            is_valid: false,
52            error: Some("X.509 verification requires certificate and data context".to_string()),
53            certificate_chain: Vec::new(),
54            signing_time: None,
55            timestamp_info: None,
56        }
57    }
58
59    pub fn extract_signature_info(contents: &[u8]) -> Result<SignatureInfo, String> {
60        // Parse the signature contents to extract basic information
61        // This would normally parse ASN.1/DER encoded PKCS#7 data
62
63        if contents.len() < 10 {
64            return Err("Signature contents too short".to_string());
65        }
66
67        // Check for PKCS#7 magic bytes
68        if contents.starts_with(&[0x30, 0x82]) || contents.starts_with(&[0x30, 0x80]) {
69            Ok(SignatureInfo {
70                format: SignatureFormat::PKCS7,
71                algorithm: "Unknown".to_string(),
72                signer_info: None,
73                certificates: Vec::new(),
74                timestamp: None,
75            })
76        } else {
77            Err("Unknown signature format".to_string())
78        }
79    }
80}
81
82#[derive(Debug, Clone)]
83pub struct SignatureVerificationResult {
84    pub is_valid: bool,
85    pub error: Option<String>,
86    pub certificate_chain: Vec<CertificateInfo>,
87    pub signing_time: Option<String>,
88    pub timestamp_info: Option<TimestampInfo>,
89}
90
91#[derive(Debug, Clone)]
92pub struct SignatureInfo {
93    pub format: SignatureFormat,
94    pub algorithm: String,
95    pub signer_info: Option<SignerInfo>,
96    pub certificates: Vec<CertificateInfo>,
97    pub timestamp: Option<TimestampInfo>,
98}
99
100#[derive(Debug, Clone)]
101pub enum SignatureFormat {
102    PKCS7,
103    X509,
104    CAdES,
105    PAdES,
106}
107
108#[derive(Debug, Clone)]
109pub struct SignerInfo {
110    pub issuer: String,
111    pub serial_number: String,
112    pub digest_algorithm: String,
113    pub signature_algorithm: String,
114}
115
116#[derive(Debug, Clone)]
117pub struct CertificateInfo {
118    pub subject: String,
119    pub issuer: String,
120    pub serial_number: String,
121    pub not_before: String,
122    pub not_after: String,
123    pub public_key_algorithm: String,
124    pub signature_algorithm: String,
125    pub key_usage: Vec<String>,
126    pub extended_key_usage: Vec<String>,
127}
128
129#[derive(Debug, Clone)]
130pub struct TimestampInfo {
131    pub timestamp_authority: String,
132    pub timestamp: String,
133    pub hash_algorithm: String,
134    pub is_valid: bool,
135}
136
137pub fn to_digital_signature(
138    info: &crate::crypto::signature_verification::SignatureInfo,
139) -> crate::security::DigitalSignature {
140    let signature_type = match info.sub_filter.as_str() {
141        "adbe.pkcs7.detached" => crate::security::SignatureType::AdbePkcs7Detached,
142        "adbe.pkcs7.sha1" => crate::security::SignatureType::AdbePkcs7Sha1,
143        "adbe.x509.rsa_sha1" => crate::security::SignatureType::AdbeX509RsaSha1,
144        "ETSI.CAdES.detached" => crate::security::SignatureType::EtsiCadEsDetached,
145        "ETSI.RFC3161" => crate::security::SignatureType::EtsiRfc3161,
146        _ => crate::security::SignatureType::AdbePkcs7Detached,
147    };
148
149    let signer = if info.signer.subject.is_empty() {
150        None
151    } else {
152        Some(info.signer.subject.clone())
153    };
154
155    let certificate_info = if info.signer.subject.is_empty() && info.signer.issuer.is_empty() {
156        None
157    } else {
158        Some(crate::security::CertificateInfo {
159            issuer: info.signer.issuer.clone(),
160            subject: info.signer.subject.clone(),
161            serial_number: bytes_to_hex(&info.signer.serial_number),
162            valid_from: info.signer.not_before.to_string(),
163            valid_to: info.signer.not_after.to_string(),
164            key_usage: info.signer.key_usage.clone(),
165            algorithm: info.filter.clone(),
166        })
167    };
168
169    let validity = match &info.validity {
170        crate::crypto::signature_verification::SignatureValidity::Valid => {
171            crate::security::SignatureValidity::Valid
172        }
173        crate::crypto::signature_verification::SignatureValidity::Unknown(msg) => {
174            crate::security::SignatureValidity::Unknown(msg.clone())
175        }
176        crate::crypto::signature_verification::SignatureValidity::Invalid(msg) => {
177            crate::security::SignatureValidity::Invalid(msg.clone())
178        }
179        other => crate::security::SignatureValidity::Unknown(format!("{:?}", other)),
180    };
181
182    let timestamp = info
183        .timestamp
184        .as_ref()
185        .map(|ts| crate::security::TimestampDetails {
186            time: Some(ts.time.to_string()),
187            policy_oid: ts.policy_oid.clone(),
188            hash_algorithm: ts.hash_algorithm.clone(),
189            signature_valid: ts.signature_valid,
190            tsa_chain_valid: ts.tsa_chain_valid,
191            tsa_pin_valid: ts.tsa_pin_valid,
192            tsa_revocation_events: ts.tsa_revocation_events.clone(),
193        });
194
195    crate::security::DigitalSignature {
196        field_name: info.field_name.clone(),
197        signature_type,
198        signer,
199        signing_time: info.signing_time.map(|t| t.to_string()),
200        certificate_info,
201        validity,
202        location: info.location.clone(),
203        reason: info.reason.clone(),
204        contact_info: info.contact_info.clone(),
205        timestamp,
206    }
207}
208
209fn bytes_to_hex(bytes: &[u8]) -> String {
210    let mut out = String::with_capacity(bytes.len() * 2);
211    for b in bytes {
212        out.push_str(&format!("{:02x}", b));
213    }
214    out
215}
216
217pub fn parse_pdf_date(date_str: &str) -> Result<String, String> {
218    // Parse PDF date format: D:YYYYMMDDHHmmSSOHH'mm
219    if !date_str.starts_with("D:") {
220        return Err("Invalid PDF date format".to_string());
221    }
222
223    let date_part = &date_str[2..];
224    if date_part.len() < 14 {
225        return Err("PDF date too short".to_string());
226    }
227
228    let year = &date_part[0..4];
229    let month = &date_part[4..6];
230    let day = &date_part[6..8];
231    let hour = &date_part[8..10];
232    let minute = &date_part[10..12];
233    let second = &date_part[12..14];
234
235    Ok(format!(
236        "{}-{}-{} {}:{}:{}",
237        year, month, day, hour, minute, second
238    ))
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    #[test]
246    fn test_parse_pdf_date() {
247        let date = "D:20231201120000+01'00";
248        let result = parse_pdf_date(date);
249        assert!(result.is_ok());
250        assert_eq!(result.unwrap(), "2023-12-01 12:00:00");
251    }
252
253    #[test]
254    fn test_invalid_pdf_date() {
255        let date = "InvalidDate";
256        let result = parse_pdf_date(date);
257        assert!(result.is_err());
258    }
259}