1use super::{CryptoError, CryptoResult, SignatureVerificationResult, TimestampInfo};
2use crate::types::PdfValue;
3
4pub struct PdfSignatureHandler;
6
7impl Default for PdfSignatureHandler {
8 fn default() -> Self {
9 Self::new()
10 }
11}
12
13impl PdfSignatureHandler {
14 pub fn new() -> Self {
15 Self
16 }
17
18 pub fn verify_pdf_signature(
20 &self,
21 signature_dict: &std::collections::HashMap<String, PdfValue>,
22 ) -> CryptoResult<SignatureVerificationResult> {
23 let filter = self.extract_filter(signature_dict)?;
25 let contents = self.extract_contents(signature_dict)?;
26 let byte_range = self.extract_byte_range(signature_dict)?;
27
28 match filter.as_str() {
29 "Adobe.PPKLite" | "Adobe.PPKMS" => self.verify_pkcs7_signature(&contents, &byte_range),
30 "Adobe.PPK" => self.verify_x509_rsa_signature(&contents, &byte_range),
31 "ETSI.CAdES.detached" => self.verify_cades_signature(&contents, &byte_range),
32 "ETSI.RFC3161" => self.verify_timestamp_signature(&contents, &byte_range),
33 _ => Err(CryptoError::UnsupportedAlgorithm(format!(
34 "Unsupported signature filter: {}",
35 filter
36 ))),
37 }
38 }
39
40 fn extract_filter(
42 &self,
43 sig_dict: &std::collections::HashMap<String, PdfValue>,
44 ) -> CryptoResult<String> {
45 match sig_dict.get("Filter") {
46 Some(PdfValue::Name(name)) => Ok(name.without_slash().to_string()),
47 _ => Err(CryptoError::InvalidSignatureFormat(
48 "Missing or invalid Filter".to_string(),
49 )),
50 }
51 }
52
53 fn extract_contents(
55 &self,
56 sig_dict: &std::collections::HashMap<String, PdfValue>,
57 ) -> CryptoResult<Vec<u8>> {
58 match sig_dict.get("Contents") {
59 Some(PdfValue::String(s)) => {
60 self.decode_hex_string(s.as_bytes())
62 }
63 _ => Err(CryptoError::InvalidSignatureFormat(
64 "Missing or invalid Contents".to_string(),
65 )),
66 }
67 }
68
69 fn extract_byte_range(
71 &self,
72 sig_dict: &std::collections::HashMap<String, PdfValue>,
73 ) -> CryptoResult<Vec<u64>> {
74 match sig_dict.get("ByteRange") {
75 Some(PdfValue::Array(arr)) => {
76 let mut byte_range = Vec::new();
77 for item in arr.iter() {
78 match item {
79 PdfValue::Integer(i) => byte_range.push(*i as u64),
80 _ => {
81 return Err(CryptoError::InvalidSignatureFormat(
82 "Invalid ByteRange format".to_string(),
83 ))
84 }
85 }
86 }
87 if byte_range.len() != 4 {
88 return Err(CryptoError::InvalidSignatureFormat(
89 "ByteRange must have 4 elements".to_string(),
90 ));
91 }
92 Ok(byte_range)
93 }
94 _ => Err(CryptoError::InvalidSignatureFormat(
95 "Missing or invalid ByteRange".to_string(),
96 )),
97 }
98 }
99
100 fn decode_hex_string(&self, hex_str: &[u8]) -> CryptoResult<Vec<u8>> {
102 let hex_str = std::str::from_utf8(hex_str).map_err(|_| {
103 CryptoError::InvalidSignatureFormat("Invalid UTF-8 in hex string".to_string())
104 })?;
105
106 let hex_str = hex_str.trim_start_matches('<').trim_end_matches('>');
107
108 if hex_str.len() % 2 != 0 {
109 return Err(CryptoError::InvalidSignatureFormat(
110 "Hex string length must be even".to_string(),
111 ));
112 }
113
114 let mut result = Vec::with_capacity(hex_str.len() / 2);
115 for chunk in hex_str.as_bytes().chunks_exact(2) {
116 let hex_byte = std::str::from_utf8(chunk).map_err(|_| {
117 CryptoError::InvalidSignatureFormat("Invalid hex character".to_string())
118 })?;
119 let byte = u8::from_str_radix(hex_byte, 16).map_err(|_| {
120 CryptoError::InvalidSignatureFormat("Invalid hex digit".to_string())
121 })?;
122 result.push(byte);
123 }
124
125 Ok(result)
126 }
127
128 fn verify_pkcs7_signature(
130 &self,
131 contents: &[u8],
132 byte_range: &[u64],
133 ) -> CryptoResult<SignatureVerificationResult> {
134 #[cfg(feature = "crypto")]
135 {
136 self.verify_pkcs7_with_openssl(contents, byte_range)
137 }
138 #[cfg(not(feature = "crypto"))]
139 {
140 Ok(SignatureVerificationResult {
141 is_valid: false,
142 signer_certificate: None,
143 signing_time: None,
144 algorithm: "PKCS#7".to_string(),
145 error_message: Some("PKCS#7 verification requires crypto feature".to_string()),
146 certificate_chain: Vec::new(),
147 timestamp_info: None,
148 })
149 }
150 }
151
152 fn verify_x509_rsa_signature(
154 &self,
155 contents: &[u8],
156 byte_range: &[u64],
157 ) -> CryptoResult<SignatureVerificationResult> {
158 #[cfg(feature = "crypto")]
159 {
160 self.verify_x509_with_openssl(contents, byte_range)
161 }
162 #[cfg(not(feature = "crypto"))]
163 {
164 Ok(SignatureVerificationResult {
165 is_valid: false,
166 signer_certificate: None,
167 signing_time: None,
168 algorithm: "X.509 RSA".to_string(),
169 error_message: Some("X.509 verification requires crypto feature".to_string()),
170 certificate_chain: Vec::new(),
171 timestamp_info: None,
172 })
173 }
174 }
175
176 fn verify_cades_signature(
178 &self,
179 contents: &[u8],
180 byte_range: &[u64],
181 ) -> CryptoResult<SignatureVerificationResult> {
182 #[cfg(feature = "crypto")]
183 {
184 self.verify_cades_with_openssl(contents, byte_range)
185 }
186 #[cfg(not(feature = "crypto"))]
187 {
188 Ok(SignatureVerificationResult {
189 is_valid: false,
190 signer_certificate: None,
191 signing_time: None,
192 algorithm: "CAdES".to_string(),
193 error_message: Some("CAdES verification requires crypto feature".to_string()),
194 certificate_chain: Vec::new(),
195 timestamp_info: None,
196 })
197 }
198 }
199
200 fn verify_timestamp_signature(
202 &self,
203 contents: &[u8],
204 byte_range: &[u64],
205 ) -> CryptoResult<SignatureVerificationResult> {
206 #[cfg(feature = "crypto")]
207 {
208 self.verify_timestamp_with_openssl(contents, byte_range)
209 }
210 #[cfg(not(feature = "crypto"))]
211 {
212 Ok(SignatureVerificationResult {
213 is_valid: false,
214 signer_certificate: None,
215 signing_time: None,
216 algorithm: "RFC3161 Timestamp".to_string(),
217 error_message: Some("Timestamp verification requires crypto feature".to_string()),
218 certificate_chain: Vec::new(),
219 timestamp_info: None,
220 })
221 }
222 }
223
224 #[cfg(feature = "crypto")]
226 fn verify_pkcs7_with_openssl(
227 &self,
228 _contents: &[u8],
229 _byte_range: &[u64],
230 ) -> CryptoResult<SignatureVerificationResult> {
231 Ok(SignatureVerificationResult {
234 is_valid: false,
235 signer_certificate: None,
236 signing_time: None,
237 algorithm: "PKCS#7".to_string(),
238 error_message: Some(
239 "OpenSSL PKCS#7 verification not yet fully implemented".to_string(),
240 ),
241 certificate_chain: Vec::new(),
242 timestamp_info: None,
243 })
244 }
245
246 #[cfg(feature = "crypto")]
247 fn verify_x509_with_openssl(
248 &self,
249 _contents: &[u8],
250 _byte_range: &[u64],
251 ) -> CryptoResult<SignatureVerificationResult> {
252 Ok(SignatureVerificationResult {
254 is_valid: false,
255 signer_certificate: None,
256 signing_time: None,
257 algorithm: "X.509 RSA".to_string(),
258 error_message: Some("OpenSSL X.509 verification not yet fully implemented".to_string()),
259 certificate_chain: Vec::new(),
260 timestamp_info: None,
261 })
262 }
263
264 #[cfg(feature = "crypto")]
265 fn verify_cades_with_openssl(
266 &self,
267 _contents: &[u8],
268 _byte_range: &[u64],
269 ) -> CryptoResult<SignatureVerificationResult> {
270 Ok(SignatureVerificationResult {
272 is_valid: false,
273 signer_certificate: None,
274 signing_time: None,
275 algorithm: "CAdES".to_string(),
276 error_message: Some("CAdES verification not yet fully implemented".to_string()),
277 certificate_chain: Vec::new(),
278 timestamp_info: None,
279 })
280 }
281
282 #[cfg(feature = "crypto")]
283 fn verify_timestamp_with_openssl(
284 &self,
285 _contents: &[u8],
286 _byte_range: &[u64],
287 ) -> CryptoResult<SignatureVerificationResult> {
288 Ok(SignatureVerificationResult {
290 is_valid: false,
291 signer_certificate: None,
292 signing_time: None,
293 algorithm: "RFC3161 Timestamp".to_string(),
294 error_message: Some("RFC3161 verification not yet fully implemented".to_string()),
295 certificate_chain: Vec::new(),
296 timestamp_info: Some(TimestampInfo {
297 timestamp: super::chrono::Utc::now(),
298 timestamp_authority: "Unknown TSA".to_string(),
299 hash_algorithm: "SHA-256".to_string(),
300 is_valid: false,
301 error_message: Some("Not implemented".to_string()),
302 }),
303 })
304 }
305}
306
307pub struct SignatureValidator;
309
310impl SignatureValidator {
311 pub fn validate_signature_dict(
313 &self,
314 sig_dict: &std::collections::HashMap<String, PdfValue>,
315 ) -> Result<Vec<String>, Vec<String>> {
316 let mut errors = Vec::new();
317 let mut warnings = Vec::new();
318
319 if !sig_dict.contains_key("Filter") {
321 errors.push("Missing required Filter field".to_string());
322 }
323 if !sig_dict.contains_key("Contents") {
324 errors.push("Missing required Contents field".to_string());
325 }
326 if !sig_dict.contains_key("ByteRange") {
327 errors.push("Missing required ByteRange field".to_string());
328 }
329
330 if !sig_dict.contains_key("M") {
332 warnings.push("Missing signing time (M field)".to_string());
333 }
334 if !sig_dict.contains_key("Name") {
335 warnings.push("Missing signer name (Name field)".to_string());
336 }
337 if !sig_dict.contains_key("Reason") {
338 warnings.push("Missing signing reason (Reason field)".to_string());
339 }
340
341 if let Some(PdfValue::Array(arr)) = sig_dict.get("ByteRange") {
343 if arr.len() != 4 {
344 errors.push("ByteRange must contain exactly 4 integers".to_string());
345 } else {
346 for (i, item) in arr.iter().enumerate() {
347 if !matches!(item, PdfValue::Integer(_)) {
348 errors.push(format!("ByteRange[{}] must be an integer", i));
349 }
350 }
351 }
352 }
353
354 if errors.is_empty() {
355 Ok(warnings)
356 } else {
357 Err(errors)
358 }
359 }
360
361 pub fn extract_signature_metadata(
363 &self,
364 sig_dict: &std::collections::HashMap<String, PdfValue>,
365 ) -> SignatureMetadata {
366 SignatureMetadata {
367 filter: sig_dict
368 .get("Filter")
369 .and_then(|v| v.as_name())
370 .map(|n| n.without_slash().to_string()),
371 sub_filter: sig_dict
372 .get("SubFilter")
373 .and_then(|v| v.as_name())
374 .map(|n| n.without_slash().to_string()),
375 name: sig_dict
376 .get("Name")
377 .and_then(|v| v.as_string())
378 .map(|s| s.to_string_lossy()),
379 location: sig_dict
380 .get("Location")
381 .and_then(|v| v.as_string())
382 .map(|s| s.to_string_lossy()),
383 reason: sig_dict
384 .get("Reason")
385 .and_then(|v| v.as_string())
386 .map(|s| s.to_string_lossy()),
387 contact_info: sig_dict
388 .get("ContactInfo")
389 .and_then(|v| v.as_string())
390 .map(|s| s.to_string_lossy()),
391 signing_time: sig_dict
392 .get("M")
393 .and_then(|v| v.as_string())
394 .map(|s| s.to_string_lossy()),
395 }
396 }
397}
398
399#[derive(Debug, Clone)]
400pub struct SignatureMetadata {
401 pub filter: Option<String>,
402 pub sub_filter: Option<String>,
403 pub name: Option<String>,
404 pub location: Option<String>,
405 pub reason: Option<String>,
406 pub contact_info: Option<String>,
407 pub signing_time: Option<String>,
408}
409
410pub struct SignatureFormatDetector;
412
413impl SignatureFormatDetector {
414 pub fn detect_format(&self, signature_data: &[u8]) -> SignatureFormat {
416 if signature_data.len() < 10 {
417 return SignatureFormat::Unknown;
418 }
419
420 if signature_data[0] == 0x30 {
422 if self.looks_like_pkcs7(signature_data) {
423 return SignatureFormat::PKCS7;
424 } else if self.looks_like_x509_cert(signature_data) {
425 return SignatureFormat::X509Certificate;
426 }
427 }
428
429 if signature_data.starts_with(b"-----BEGIN") {
431 if signature_data.starts_with(b"-----BEGIN PKCS7") {
432 return SignatureFormat::Pkcs7Pem;
433 } else if signature_data.starts_with(b"-----BEGIN CERTIFICATE") {
434 return SignatureFormat::X509CertificatePem;
435 }
436 }
437
438 if self.looks_like_timestamp_token(signature_data) {
440 return SignatureFormat::Rfc3161Timestamp;
441 }
442
443 SignatureFormat::Unknown
444 }
445
446 fn looks_like_pkcs7(&self, data: &[u8]) -> bool {
447 data.len() > 50 && data[0] == 0x30
450 }
451
452 fn looks_like_x509_cert(&self, data: &[u8]) -> bool {
453 data.len() > 100 && data[0] == 0x30
456 }
457
458 fn looks_like_timestamp_token(&self, data: &[u8]) -> bool {
459 data.len() > 20 && data[0] == 0x30
462 }
463}
464
465#[derive(Debug, Clone, PartialEq)]
466pub enum SignatureFormat {
467 PKCS7,
468 Pkcs7Pem,
469 X509Certificate,
470 X509CertificatePem,
471 Rfc3161Timestamp,
472 CAdES,
473 PAdES,
474 Unknown,
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480 use crate::types::{PdfArray, PdfName, PdfString};
481 use std::collections::HashMap;
482
483 #[test]
484 fn test_signature_validator() {
485 let validator = SignatureValidator;
486 let mut sig_dict = HashMap::new();
487
488 let result = validator.validate_signature_dict(&sig_dict);
490 assert!(result.is_err());
491
492 sig_dict.insert(
494 "Filter".to_string(),
495 PdfValue::Name(PdfName::new("Adobe.PPKLite")),
496 );
497 sig_dict.insert(
498 "Contents".to_string(),
499 PdfValue::String(PdfString::new_literal(b"<3082...")),
500 );
501
502 let mut byte_range = PdfArray::new();
503 byte_range.push(PdfValue::Integer(0));
504 byte_range.push(PdfValue::Integer(1000));
505 byte_range.push(PdfValue::Integer(2000));
506 byte_range.push(PdfValue::Integer(3000));
507 sig_dict.insert("ByteRange".to_string(), PdfValue::Array(byte_range));
508
509 let result = validator.validate_signature_dict(&sig_dict);
510 assert!(result.is_ok());
511 }
512
513 #[test]
514 fn test_hex_string_decoding() {
515 let handler = PdfSignatureHandler::new();
516
517 let hex_str = b"<48656C6C6F>";
518 let result = handler.decode_hex_string(hex_str).unwrap();
519 assert_eq!(result, b"Hello");
520
521 let invalid_hex = b"<GG>";
522 assert!(handler.decode_hex_string(invalid_hex).is_err());
523 }
524
525 #[test]
526 fn test_signature_format_detection() {
527 let detector = SignatureFormatDetector;
528
529 let mut der_data = vec![0x30, 0x82, 0x01, 0x00]; der_data.resize(60, 0x00); let format = detector.detect_format(&der_data);
534 assert!(matches!(
536 format,
537 SignatureFormat::PKCS7 | SignatureFormat::X509Certificate
538 ));
539
540 let small_der_data = [0x30, 0x82, 0x01, 0x00]; let format = detector.detect_format(&small_der_data);
543 assert_eq!(format, SignatureFormat::Unknown);
544
545 let pem_data = b"-----BEGIN CERTIFICATE-----";
547 let format = detector.detect_format(pem_data);
548 assert_eq!(format, SignatureFormat::X509CertificatePem);
549
550 let unknown_data = b"unknown";
552 let format = detector.detect_format(unknown_data);
553 assert_eq!(format, SignatureFormat::Unknown);
554 }
555}