1use crate::error::Result;
6use der::{Decode, Encode};
7use digest::Digest;
8use sha2::{Sha256, Sha384, Sha512};
9use tracing::{debug, trace, warn};
10use x509_cert::{certificate::Certificate, der::asn1::ObjectIdentifier, time::Validity};
11
12mod oids {
14
15 #[allow(dead_code)]
17 pub const SIGNED_DATA: &str = "1.2.840.113549.1.7.2";
18
19 pub const SHA256: &str = "2.16.840.1.101.3.4.2.1";
21 pub const SHA384: &str = "2.16.840.1.101.3.4.2.2";
23 pub const SHA512: &str = "2.16.840.1.101.3.4.2.3";
25
26 pub const RSA_SHA256: &str = "1.2.840.113549.1.1.11";
28 pub const RSA_SHA384: &str = "1.2.840.113549.1.1.12";
30 pub const RSA_SHA512: &str = "1.2.840.113549.1.1.13";
32
33 pub const SIGNING_TIME: &str = "1.2.840.113549.1.9.5";
35 pub const TIMESTAMP_TOKEN: &str = "1.2.840.113549.1.9.16.2.14";
37}
38
39#[derive(Debug, Clone)]
41pub struct EnhancedSignatureInfo {
42 pub format: String,
44 pub size: usize,
46 pub digest_algorithm: String,
48 pub signature_algorithm: String,
50
51 pub is_verified: bool,
53 pub verification_errors: Vec<String>,
55
56 pub certificates: Vec<CertificateInfo>,
58 pub signer_count: usize,
60 pub timestamp_info: Option<TimestampInfo>,
62}
63
64#[derive(Debug, Clone)]
66pub struct CertificateInfo {
67 pub subject: String,
69 pub issuer: String,
71 pub serial_number: String,
73 pub not_before: String,
75 pub not_after: String,
77 pub is_valid: bool,
79}
80
81#[derive(Debug, Clone)]
83pub struct TimestampInfo {
84 pub signing_time: Option<String>,
86 pub is_verified: bool,
88 pub timestamp_authority: Option<String>,
90}
91
92#[allow(clippy::too_many_lines)]
102pub fn parse_and_verify_signature(
103 signature_bytes: &[u8],
104 signed_data: Option<&[u8]>,
105) -> Result<EnhancedSignatureInfo> {
106 trace!("Parsing signature: {} bytes", signature_bytes.len());
107
108 let mut info = EnhancedSignatureInfo {
109 format: "PKCS#7/CMS".to_string(),
110 size: signature_bytes.len(),
111 digest_algorithm: "Unknown".to_string(),
112 signature_algorithm: "Unknown".to_string(),
113 is_verified: false,
114 verification_errors: Vec::new(),
115 certificates: Vec::new(),
116 signer_count: 0,
117 timestamp_info: None,
118 };
119
120 if signature_bytes.is_empty() {
122 info.verification_errors
123 .push("Empty signature data".to_string());
124 return Ok(info);
125 }
126
127 match crate::cms_parser::parse_cms_signature(signature_bytes) {
129 Ok(cms_info) => {
130 debug!("Successfully parsed CMS signature");
131
132 info.signer_count = cms_info.signers.len();
134
135 if let Some(first_signer) = cms_info.signers.first() {
137 info.digest_algorithm
138 .clone_from(&first_signer.digest_algorithm);
139 info.signature_algorithm
140 .clone_from(&first_signer.signature_algorithm);
141
142 if let Some(ref pk) = first_signer.public_key {
144 debug!(
145 "Extracted {} public key: {} bits",
146 pk.algorithm, pk.key_size
147 );
148 }
149 }
150
151 for cert in &cms_info.certificates {
153 let cert_info = CertificateInfo {
154 subject: cert.subject.clone(),
155 issuer: cert.issuer.clone(),
156 serial_number: cert.serial_number.clone(),
157 not_before: "Unknown".to_string(), not_after: "Unknown".to_string(),
159 is_valid: true, };
161 info.certificates.push(cert_info);
162 }
163
164 if let Some(data) = signed_data {
166 if let Some(first_signer) = cms_info.signers.first() {
167 if let Some(ref public_key) = first_signer.public_key {
168 match crate::cms_parser::verify_with_public_key(
169 public_key,
170 data,
171 &first_signer.signature,
172 &first_signer.digest_algorithm,
173 ) {
174 Ok(true) => {
175 info.is_verified = true;
176 debug!("Signature verification successful!");
177 }
178 Ok(false) => {
179 info.verification_errors
180 .push("Signature verification failed".to_string());
181 }
182 Err(e) => {
183 info.verification_errors
184 .push(format!("Verification error: {e}"));
185 }
186 }
187 } else {
188 info.verification_errors
189 .push("No public key found for verification".to_string());
190 }
191 }
192 }
193
194 let pkcs7_info = parse_pkcs7_structure(signature_bytes);
196 if let Some(timestamp) = extract_timestamp_info(&pkcs7_info) {
197 info.timestamp_info = Some(timestamp);
198 }
199 }
200 Err(e) => {
201 warn!("CMS parsing failed, falling back to manual parsing: {e}");
202
203 let pkcs7_info = parse_pkcs7_structure(signature_bytes);
205 info.digest_algorithm
206 .clone_from(&pkcs7_info.digest_algorithm);
207 info.signature_algorithm
208 .clone_from(&pkcs7_info.signature_algorithm);
209 info.signer_count = pkcs7_info.signer_count;
210
211 for cert_der in &pkcs7_info.certificates {
213 match Certificate::from_der(cert_der) {
214 Ok(cert) => {
215 let cert_info = extract_certificate_info(&cert);
216 info.certificates.push(cert_info);
217 }
218 Err(e) => {
219 warn!("Failed to parse certificate: {e}");
220 }
221 }
222 }
223
224 if let Some(timestamp) = extract_timestamp_info(&pkcs7_info) {
226 info.timestamp_info = Some(timestamp);
227 }
228
229 if let Some(data) = signed_data {
231 if verify_signature_data(&pkcs7_info, data) {
232 info.is_verified = true;
233 } else {
234 info.verification_errors
235 .push("Signature verification failed".to_string());
236 }
237 }
238 }
239 }
240
241 Ok(info)
242}
243
244struct Pkcs7Info {
246 digest_algorithm: String,
247 signature_algorithm: String,
248 signer_count: usize,
249 certificates: Vec<Vec<u8>>,
250 #[allow(dead_code)]
251 signature_value: Vec<u8>,
252 raw_data: Vec<u8>,
254}
255
256fn parse_pkcs7_structure(data: &[u8]) -> Pkcs7Info {
258 let mut info = Pkcs7Info {
259 digest_algorithm: "Unknown".to_string(),
260 signature_algorithm: "Unknown".to_string(),
261 signer_count: 0,
262 certificates: Vec::new(),
263 signature_value: Vec::new(),
264 raw_data: data.to_vec(),
265 };
266
267 if let Some(pos) = find_oid_pattern(data, oids::SHA256) {
272 info.digest_algorithm = "SHA-256".to_string();
273 debug!("Found SHA-256 digest algorithm at position {pos}");
274 } else if let Some(pos) = find_oid_pattern(data, oids::SHA384) {
275 info.digest_algorithm = "SHA-384".to_string();
276 debug!("Found SHA-384 digest algorithm at position {pos}");
277 } else if let Some(pos) = find_oid_pattern(data, oids::SHA512) {
278 info.digest_algorithm = "SHA-512".to_string();
279 debug!("Found SHA-512 digest algorithm at position {pos}");
280 }
281
282 if find_oid_pattern(data, oids::RSA_SHA256).is_some() {
284 info.signature_algorithm = "RSA with SHA-256".to_string();
285 } else if find_oid_pattern(data, oids::RSA_SHA384).is_some() {
286 info.signature_algorithm = "RSA with SHA-384".to_string();
287 } else if find_oid_pattern(data, oids::RSA_SHA512).is_some() {
288 info.signature_algorithm = "RSA with SHA-512".to_string();
289 }
290
291 let mut pos = 0;
293 while pos < data.len().saturating_sub(4) {
294 if data[pos] == 0x30 && data[pos + 1] == 0x82 {
296 let len = ((data[pos + 2] as usize) << 8) | (data[pos + 3] as usize);
297
298 if len > 300 && len < 2000 && pos + 4 + len <= data.len() {
300 let cert_data = data[pos..pos + 4 + len].to_vec();
301
302 if cert_data.len() > 100 && cert_data[4] == 0x30 {
304 info.certificates.push(cert_data);
305 debug!("Found potential certificate at position {pos}, length {len}");
306 }
307
308 pos += 4 + len;
309 } else {
310 pos += 1;
311 }
312 } else {
313 pos += 1;
314 }
315 }
316
317 if !info.certificates.is_empty() {
319 info.signer_count = 1;
320 }
321
322 debug!(
323 "Parsed PKCS#7: {} certificates, {} signers, digest: {}, signature: {}",
324 info.certificates.len(),
325 info.signer_count,
326 info.digest_algorithm,
327 info.signature_algorithm
328 );
329
330 info
331}
332
333fn find_oid_pattern(data: &[u8], oid_str: &str) -> Option<usize> {
335 let oid = ObjectIdentifier::new(oid_str).ok()?;
337 let oid_bytes = oid.to_der().ok()?;
338
339 data.windows(oid_bytes.len())
340 .position(|window| window == oid_bytes)
341}
342
343fn extract_certificate_info(cert: &Certificate) -> CertificateInfo {
345 let tbs = &cert.tbs_certificate;
346
347 CertificateInfo {
348 subject: tbs.subject.to_string(),
349 issuer: tbs.issuer.to_string(),
350 serial_number: tbs.serial_number.to_string(),
351 not_before: format!("{}", tbs.validity.not_before),
352 not_after: format!("{}", tbs.validity.not_after),
353 is_valid: is_certificate_valid(&tbs.validity),
354 }
355}
356
357fn is_certificate_valid(_validity: &Validity) -> bool {
359 true
362}
363
364fn extract_timestamp_info(pkcs7: &Pkcs7Info) -> Option<TimestampInfo> {
366 let mut timestamp_info = TimestampInfo {
367 signing_time: None,
368 is_verified: false,
369 timestamp_authority: None,
370 };
371
372 if let Some(pos) = find_oid_pattern(&pkcs7.raw_data, oids::SIGNING_TIME) {
375 timestamp_info.signing_time = Some(format!("Present (position: {pos})"));
377 debug!("Found signing time attribute at position {pos}");
378
379 if pos + 20 < pkcs7.raw_data.len() {
382 let time_data = &pkcs7.raw_data[pos + 11..pos + 30];
384 trace!("Time data near signing time OID: {:02x?}", time_data);
385 }
386 }
387
388 if let Some(pos) = find_oid_pattern(&pkcs7.raw_data, oids::TIMESTAMP_TOKEN) {
390 timestamp_info.timestamp_authority = Some("TSA present".to_string());
391 debug!("Found timestamp token at position {pos}");
392 }
393
394 if timestamp_info.signing_time.is_none() && timestamp_info.timestamp_authority.is_none() {
396 None
397 } else {
398 timestamp_info.is_verified = true; Some(timestamp_info)
401 }
402}
403
404fn verify_signature_data(pkcs7: &Pkcs7Info, signed_data: &[u8]) -> bool {
406 debug!("Signature verification not yet implemented");
412
413 let digest = match pkcs7.digest_algorithm.as_str() {
415 "SHA-256" => Sha256::digest(signed_data).to_vec(),
416 "SHA-384" => Sha384::digest(signed_data).to_vec(),
417 "SHA-512" => Sha512::digest(signed_data).to_vec(),
418 _ => return false,
419 };
420
421 debug!(
422 "Computed {} digest: {} bytes",
423 pkcs7.digest_algorithm,
424 digest.len()
425 );
426
427 false
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434
435 #[test]
436 fn test_oid_pattern_finding() {
437 let oid = ObjectIdentifier::new(oids::SHA256).unwrap();
439 let oid_bytes = oid.to_der().unwrap();
440
441 let mut data = vec![0x00, 0x00];
442 data.extend_from_slice(&oid_bytes);
443 data.extend_from_slice(&[0x00, 0x00]);
444
445 let pos = find_oid_pattern(&data, oids::SHA256);
446 assert_eq!(pos, Some(2));
447 }
448
449 #[test]
450 fn test_empty_signature_parsing() {
451 let result = parse_and_verify_signature(&[], None);
452 assert!(result.is_ok());
453
454 let info = result.unwrap();
455 assert!(!info.is_verified);
456 assert!(!info.verification_errors.is_empty());
457 }
458}