1#[cfg(feature = "signatures")]
16use cms::content_info::ContentInfo;
17#[cfg(feature = "signatures")]
18use cms::signed_data::SignedData;
19#[cfg(feature = "signatures")]
20use der::{Decode, Encode};
21#[cfg(feature = "signatures")]
22use x509_cert::Certificate;
23
24use super::error::{SignatureError, SignatureResult};
25
26#[cfg(feature = "signatures")]
33const MAX_BER_DEPTH: usize = 100;
34
35#[cfg(feature = "signatures")]
38const MAX_BER_OUTPUT_SIZE: usize = 10 * 1024 * 1024;
39
40#[cfg(feature = "signatures")]
53fn ber_to_der(input: &[u8]) -> SignatureResult<Vec<u8>> {
54 if input.is_empty() {
55 return Ok(Vec::new());
56 }
57
58 if !contains_indefinite_length(input) {
60 return Ok(input.to_vec());
61 }
62
63 let size_budget = std::cmp::min(input.len().saturating_mul(100), MAX_BER_OUTPUT_SIZE);
65
66 convert_element(input, 0, size_budget).map(|(output, _)| output)
68}
69
70#[cfg(feature = "signatures")]
76fn contains_indefinite_length(data: &[u8]) -> bool {
77 if data.len() < 2 {
78 return false;
79 }
80
81 let tag = data[0];
82 let length_byte = data[1];
83
84 let is_constructed = (tag & 0x20) != 0;
87
88 is_constructed && length_byte == 0x80
89}
90
91#[cfg(feature = "signatures")]
103fn convert_element(
104 input: &[u8],
105 depth: usize,
106 size_budget: usize,
107) -> SignatureResult<(Vec<u8>, usize)> {
108 if depth > MAX_BER_DEPTH {
110 return Err(SignatureError::CmsParsingFailed {
111 details: format!(
112 "BER nesting too deep: {} levels (max {})",
113 depth, MAX_BER_DEPTH
114 ),
115 });
116 }
117
118 if input.is_empty() {
119 return Err(SignatureError::CmsParsingFailed {
120 details: "Empty input in BER conversion".to_string(),
121 });
122 }
123
124 let tag = input[0];
125 if input.len() < 2 {
126 return Err(SignatureError::CmsParsingFailed {
127 details: "BER element too short".to_string(),
128 });
129 }
130
131 let length_byte = input[1];
132
133 if length_byte == 0x80 {
135 let is_constructed = (tag & 0x20) != 0;
137 if !is_constructed {
138 return Err(SignatureError::CmsParsingFailed {
139 details: "Indefinite length on primitive type".to_string(),
140 });
141 }
142
143 let content_start = 2;
145 let (content, content_len) =
146 parse_indefinite_content(&input[content_start..], depth + 1, size_budget)?;
147
148 let output_size = 1 + 5 + content.len(); if output_size > size_budget {
151 return Err(SignatureError::CmsParsingFailed {
152 details: format!(
153 "BER output exceeds size limit: {} bytes (max {} MB)",
154 output_size,
155 MAX_BER_OUTPUT_SIZE / 1024 / 1024
156 ),
157 });
158 }
159
160 let mut output = vec![tag];
162 encode_der_length(&mut output, content.len());
163 output.extend(content);
164
165 let consumed = 2 + content_len + 2;
167 Ok((output, consumed))
168 } else if length_byte < 0x80 {
169 let length = length_byte as usize;
171 let total = 2 + length;
172 if input.len() < total {
173 return Err(SignatureError::CmsParsingFailed {
174 details: format!(
175 "BER element truncated (short form): need {} bytes, got {}",
176 total,
177 input.len()
178 ),
179 });
180 }
181
182 let is_constructed = (tag & 0x20) != 0;
184 if is_constructed && length > 0 {
185 let content = &input[2..2 + length];
186 let converted_content = convert_constructed_content(content, depth + 1, size_budget)?;
187
188 let output_size = 1 + 5 + converted_content.len();
190 if output_size > size_budget {
191 return Err(SignatureError::CmsParsingFailed {
192 details: format!(
193 "BER output exceeds size limit: {} bytes (max {} MB)",
194 output_size,
195 MAX_BER_OUTPUT_SIZE / 1024 / 1024
196 ),
197 });
198 }
199
200 let mut output = vec![tag];
201 encode_der_length(&mut output, converted_content.len());
202 output.extend(converted_content);
203 Ok((output, total))
204 } else {
205 Ok((input[..total].to_vec(), total))
207 }
208 } else {
209 let num_octets = (length_byte & 0x7F) as usize;
211
212 if num_octets == 0 {
214 return Err(SignatureError::CmsParsingFailed {
215 details: "Invalid BER length: zero octets in long form".to_string(),
216 });
217 }
218 if num_octets > 4 {
219 return Err(SignatureError::CmsParsingFailed {
220 details: format!(
221 "BER length too large: {} octets (max 4, would exceed 4GB)",
222 num_octets
223 ),
224 });
225 }
226 if input.len() < 2 + num_octets {
227 return Err(SignatureError::CmsParsingFailed {
228 details: format!(
229 "BER truncated reading length: need {} bytes, got {}",
230 2 + num_octets,
231 input.len()
232 ),
233 });
234 }
235
236 let mut length: usize = 0;
238 for i in 0..num_octets {
239 length = (length << 8) | (input[2 + i] as usize);
240 }
241
242 let content_start = 2 + num_octets;
243 let total = content_start + length;
244 if input.len() < total {
245 return Err(SignatureError::CmsParsingFailed {
246 details: format!(
247 "BER element truncated: declared {} bytes content, got {}",
248 length,
249 input.len().saturating_sub(content_start)
250 ),
251 });
252 }
253
254 let is_constructed = (tag & 0x20) != 0;
256 if is_constructed && length > 0 {
257 let content = &input[content_start..total];
258 let converted_content = convert_constructed_content(content, depth + 1, size_budget)?;
259
260 let output_size = 1 + 5 + converted_content.len();
262 if output_size > size_budget {
263 return Err(SignatureError::CmsParsingFailed {
264 details: format!(
265 "BER output exceeds size limit: {} bytes (max {} MB)",
266 output_size,
267 MAX_BER_OUTPUT_SIZE / 1024 / 1024
268 ),
269 });
270 }
271
272 let mut output = vec![tag];
273 encode_der_length(&mut output, converted_content.len());
274 output.extend(converted_content);
275 Ok((output, total))
276 } else {
277 Ok((input[..total].to_vec(), total))
279 }
280 }
281}
282
283#[cfg(feature = "signatures")]
295fn parse_indefinite_content(
296 input: &[u8],
297 depth: usize,
298 size_budget: usize,
299) -> SignatureResult<(Vec<u8>, usize)> {
300 let mut output = Vec::new();
301 let mut pos = 0;
302
303 while pos < input.len() {
304 if input.len() >= pos + 2 && input[pos] == 0x00 && input[pos + 1] == 0x00 {
306 return Ok((output, pos));
307 }
308
309 let remaining_budget = size_budget.saturating_sub(output.len());
311 if remaining_budget == 0 {
312 return Err(SignatureError::CmsParsingFailed {
313 details: format!(
314 "BER output exceeds size limit during indefinite content parsing (max {} MB)",
315 MAX_BER_OUTPUT_SIZE / 1024 / 1024
316 ),
317 });
318 }
319
320 let (element, consumed) = convert_element(&input[pos..], depth, remaining_budget)?;
322 output.extend(element);
323 pos += consumed;
324 }
325
326 Err(SignatureError::CmsParsingFailed {
327 details: "End-of-contents not found in indefinite-length encoding".to_string(),
328 })
329}
330
331#[cfg(feature = "signatures")]
339fn convert_constructed_content(
340 input: &[u8],
341 depth: usize,
342 size_budget: usize,
343) -> SignatureResult<Vec<u8>> {
344 let mut output = Vec::new();
345 let mut pos = 0;
346
347 while pos < input.len() {
348 let remaining_budget = size_budget.saturating_sub(output.len());
350 if remaining_budget == 0 {
351 return Err(SignatureError::CmsParsingFailed {
352 details: format!(
353 "BER output exceeds size limit during constructed content parsing (max {} MB)",
354 MAX_BER_OUTPUT_SIZE / 1024 / 1024
355 ),
356 });
357 }
358
359 let (element, consumed) = convert_element(&input[pos..], depth, remaining_budget)?;
360 output.extend(element);
361 pos += consumed;
362 }
363
364 Ok(output)
365}
366
367#[cfg(feature = "signatures")]
369fn encode_der_length(output: &mut Vec<u8>, length: usize) {
370 if length < 128 {
371 output.push(length as u8);
372 } else if length < 256 {
373 output.push(0x81);
374 output.push(length as u8);
375 } else if length < 65536 {
376 output.push(0x82);
377 output.push((length >> 8) as u8);
378 output.push(length as u8);
379 } else if length < 16777216 {
380 output.push(0x83);
381 output.push((length >> 16) as u8);
382 output.push((length >> 8) as u8);
383 output.push(length as u8);
384 } else {
385 output.push(0x84);
386 output.push((length >> 24) as u8);
387 output.push((length >> 16) as u8);
388 output.push((length >> 8) as u8);
389 output.push(length as u8);
390 }
391}
392
393#[derive(Debug, Clone, Copy, PartialEq, Eq)]
395pub enum DigestAlgorithm {
396 Sha256,
398 Sha384,
400 Sha512,
402}
403
404impl DigestAlgorithm {
405 pub fn oid(&self) -> &'static str {
407 match self {
408 DigestAlgorithm::Sha256 => "2.16.840.1.101.3.4.2.1",
409 DigestAlgorithm::Sha384 => "2.16.840.1.101.3.4.2.2",
410 DigestAlgorithm::Sha512 => "2.16.840.1.101.3.4.2.3",
411 }
412 }
413
414 pub fn name(&self) -> &'static str {
416 match self {
417 DigestAlgorithm::Sha256 => "SHA-256",
418 DigestAlgorithm::Sha384 => "SHA-384",
419 DigestAlgorithm::Sha512 => "SHA-512",
420 }
421 }
422}
423
424#[derive(Debug, Clone, Copy, PartialEq, Eq)]
426pub enum SignatureAlgorithm {
427 RsaSha256,
429 RsaSha384,
431 RsaSha512,
433 EcdsaSha256,
435 EcdsaSha384,
437}
438
439impl SignatureAlgorithm {
440 pub fn name(&self) -> &'static str {
442 match self {
443 SignatureAlgorithm::RsaSha256 => "RSA-SHA256",
444 SignatureAlgorithm::RsaSha384 => "RSA-SHA384",
445 SignatureAlgorithm::RsaSha512 => "RSA-SHA512",
446 SignatureAlgorithm::EcdsaSha256 => "ECDSA-SHA256",
447 SignatureAlgorithm::EcdsaSha384 => "ECDSA-SHA384",
448 }
449 }
450
451 pub fn digest_algorithm(&self) -> DigestAlgorithm {
453 match self {
454 SignatureAlgorithm::RsaSha256 | SignatureAlgorithm::EcdsaSha256 => {
455 DigestAlgorithm::Sha256
456 }
457 SignatureAlgorithm::RsaSha384 | SignatureAlgorithm::EcdsaSha384 => {
458 DigestAlgorithm::Sha384
459 }
460 SignatureAlgorithm::RsaSha512 => DigestAlgorithm::Sha512,
461 }
462 }
463}
464
465#[derive(Debug, Clone)]
467pub struct ParsedSignature {
468 pub digest_algorithm: DigestAlgorithm,
470 pub signature_algorithm: SignatureAlgorithm,
472 pub signature_value: Vec<u8>,
474 pub signer_certificate_der: Vec<u8>,
476 pub signing_time: Option<String>,
478}
479
480impl ParsedSignature {
481 #[cfg(feature = "signatures")]
483 pub fn signer_common_name(&self) -> SignatureResult<String> {
484 use der::asn1::{PrintableStringRef, Utf8StringRef};
485
486 let cert = Certificate::from_der(&self.signer_certificate_der).map_err(|e| {
487 SignatureError::CmsParsingFailed {
488 details: format!("Failed to parse certificate: {}", e),
489 }
490 })?;
491
492 for rdn in cert.tbs_certificate.subject.0.iter() {
494 for atv in rdn.0.iter() {
495 if atv.oid.to_string() == "2.5.4.3" {
497 if let Ok(utf8) = Utf8StringRef::try_from(&atv.value) {
499 return Ok(utf8.as_str().to_string());
500 }
501 if let Ok(printable) = PrintableStringRef::try_from(&atv.value) {
502 return Ok(printable.as_str().to_string());
503 }
504 return Ok(format!("<binary CN: {} bytes>", atv.value.value().len()));
506 }
507 }
508 }
509
510 Err(SignatureError::CmsParsingFailed {
511 details: "Certificate has no common name".to_string(),
512 })
513 }
514
515 #[cfg(not(feature = "signatures"))]
516 pub fn signer_common_name(&self) -> SignatureResult<String> {
517 Err(SignatureError::CmsParsingFailed {
518 details: "signatures feature not enabled".to_string(),
519 })
520 }
521}
522
523#[cfg(feature = "signatures")]
537pub fn parse_pkcs7_signature(contents: &[u8]) -> SignatureResult<ParsedSignature> {
538 use const_oid::ObjectIdentifier;
539
540 let der_contents = ber_to_der(contents)?;
542
543 let content_info =
545 ContentInfo::from_der(&der_contents).map_err(|e| SignatureError::CmsParsingFailed {
546 details: format!("Failed to parse ContentInfo: {}", e),
547 })?;
548
549 const SIGNED_DATA_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.7.2");
551 if content_info.content_type != SIGNED_DATA_OID {
552 return Err(SignatureError::CmsParsingFailed {
553 details: format!(
554 "Expected SignedData, got OID: {}",
555 content_info.content_type
556 ),
557 });
558 }
559
560 let signed_data_bytes =
562 content_info
563 .content
564 .to_der()
565 .map_err(|e| SignatureError::CmsParsingFailed {
566 details: format!("Failed to encode content: {}", e),
567 })?;
568
569 let signed_data =
570 SignedData::from_der(&signed_data_bytes).map_err(|e| SignatureError::CmsParsingFailed {
571 details: format!("Failed to parse SignedData: {}", e),
572 })?;
573
574 let signer_infos: Vec<_> = signed_data.signer_infos.0.iter().collect();
576 if signer_infos.is_empty() {
577 return Err(SignatureError::CmsParsingFailed {
578 details: "No signer info found in SignedData".to_string(),
579 });
580 }
581 let signer_info = &signer_infos[0];
582
583 let digest_algorithm = parse_digest_algorithm(&signer_info.digest_alg.oid.to_string())?;
585
586 let signature_algorithm = parse_signature_algorithm(
588 &signer_info.signature_algorithm.oid.to_string(),
589 digest_algorithm,
590 )?;
591
592 let signature_value = signer_info.signature.as_bytes().to_vec();
594
595 let certificates =
597 signed_data
598 .certificates
599 .as_ref()
600 .ok_or_else(|| SignatureError::CmsParsingFailed {
601 details: "No certificates in SignedData".to_string(),
602 })?;
603
604 let cert_choices: Vec<_> = certificates.0.iter().collect();
606 if cert_choices.is_empty() {
607 return Err(SignatureError::CmsParsingFailed {
608 details: "No certificates found".to_string(),
609 });
610 }
611
612 let signer_certificate_der = match &cert_choices[0] {
614 cms::cert::CertificateChoices::Certificate(cert) => {
615 cert.to_der()
616 .map_err(|e| SignatureError::CmsParsingFailed {
617 details: format!("Failed to encode certificate: {}", e),
618 })?
619 }
620 _ => {
621 return Err(SignatureError::CmsParsingFailed {
622 details: "Unsupported certificate type".to_string(),
623 })
624 }
625 };
626
627 let signing_time = extract_signing_time(signer_info);
629
630 Ok(ParsedSignature {
631 digest_algorithm,
632 signature_algorithm,
633 signature_value,
634 signer_certificate_der,
635 signing_time,
636 })
637}
638
639#[cfg(not(feature = "signatures"))]
640pub fn parse_pkcs7_signature(_contents: &[u8]) -> SignatureResult<ParsedSignature> {
641 Err(SignatureError::CmsParsingFailed {
642 details: "signatures feature not enabled".to_string(),
643 })
644}
645
646#[cfg(feature = "signatures")]
648fn parse_digest_algorithm(oid: &str) -> SignatureResult<DigestAlgorithm> {
649 match oid {
650 "2.16.840.1.101.3.4.2.1" => Ok(DigestAlgorithm::Sha256),
651 "2.16.840.1.101.3.4.2.2" => Ok(DigestAlgorithm::Sha384),
652 "2.16.840.1.101.3.4.2.3" => Ok(DigestAlgorithm::Sha512),
653 _ => Err(SignatureError::UnsupportedAlgorithm {
654 algorithm: format!("digest OID: {}", oid),
655 }),
656 }
657}
658
659#[cfg(feature = "signatures")]
661fn parse_signature_algorithm(
662 oid: &str,
663 digest: DigestAlgorithm,
664) -> SignatureResult<SignatureAlgorithm> {
665 match oid {
666 "1.2.840.113549.1.1.1" => match digest {
668 DigestAlgorithm::Sha256 => Ok(SignatureAlgorithm::RsaSha256),
669 DigestAlgorithm::Sha384 => Ok(SignatureAlgorithm::RsaSha384),
670 DigestAlgorithm::Sha512 => Ok(SignatureAlgorithm::RsaSha512),
671 },
672 "1.2.840.113549.1.1.11" => Ok(SignatureAlgorithm::RsaSha256),
674 "1.2.840.113549.1.1.12" => Ok(SignatureAlgorithm::RsaSha384),
676 "1.2.840.113549.1.1.13" => Ok(SignatureAlgorithm::RsaSha512),
678 "1.2.840.10045.4.3.2" => Ok(SignatureAlgorithm::EcdsaSha256),
680 "1.2.840.10045.4.3.3" => Ok(SignatureAlgorithm::EcdsaSha384),
682 _ => Err(SignatureError::UnsupportedAlgorithm {
683 algorithm: format!("signature OID: {}", oid),
684 }),
685 }
686}
687
688#[cfg(feature = "signatures")]
690fn extract_signing_time(signer_info: &cms::signed_data::SignerInfo) -> Option<String> {
691 const SIGNING_TIME_OID: &str = "1.2.840.113549.1.9.5";
693
694 signer_info.signed_attrs.as_ref().and_then(|attrs| {
695 for attr in attrs.iter() {
696 if attr.oid.to_string() == SIGNING_TIME_OID {
697 return Some("(signing time present)".to_string());
700 }
701 }
702 None
703 })
704}
705
706#[cfg(test)]
707mod tests {
708 use super::*;
709
710 #[test]
713 fn test_digest_algorithm_oid() {
714 assert_eq!(DigestAlgorithm::Sha256.oid(), "2.16.840.1.101.3.4.2.1");
715 assert_eq!(DigestAlgorithm::Sha384.oid(), "2.16.840.1.101.3.4.2.2");
716 assert_eq!(DigestAlgorithm::Sha512.oid(), "2.16.840.1.101.3.4.2.3");
717 }
718
719 #[test]
720 fn test_digest_algorithm_name() {
721 assert_eq!(DigestAlgorithm::Sha256.name(), "SHA-256");
722 assert_eq!(DigestAlgorithm::Sha384.name(), "SHA-384");
723 assert_eq!(DigestAlgorithm::Sha512.name(), "SHA-512");
724 }
725
726 #[test]
727 fn test_signature_algorithm_name() {
728 assert_eq!(SignatureAlgorithm::RsaSha256.name(), "RSA-SHA256");
729 assert_eq!(SignatureAlgorithm::EcdsaSha256.name(), "ECDSA-SHA256");
730 }
731
732 #[test]
733 fn test_signature_algorithm_digest() {
734 assert_eq!(
735 SignatureAlgorithm::RsaSha256.digest_algorithm(),
736 DigestAlgorithm::Sha256
737 );
738 assert_eq!(
739 SignatureAlgorithm::RsaSha384.digest_algorithm(),
740 DigestAlgorithm::Sha384
741 );
742 assert_eq!(
743 SignatureAlgorithm::EcdsaSha384.digest_algorithm(),
744 DigestAlgorithm::Sha384
745 );
746 }
747
748 #[test]
749 fn test_digest_algorithm_clone_copy() {
750 let alg = DigestAlgorithm::Sha256;
751 let cloned = alg.clone();
752 let copied = alg;
753 assert_eq!(alg, cloned);
754 assert_eq!(alg, copied);
755 }
756
757 #[test]
758 fn test_signature_algorithm_clone_copy() {
759 let alg = SignatureAlgorithm::RsaSha256;
760 let cloned = alg.clone();
761 let copied = alg;
762 assert_eq!(alg, cloned);
763 assert_eq!(alg, copied);
764 }
765
766 #[test]
767 fn test_digest_algorithm_debug() {
768 let debug = format!("{:?}", DigestAlgorithm::Sha256);
769 assert!(debug.contains("Sha256"));
770 }
771
772 #[test]
773 fn test_signature_algorithm_debug() {
774 let debug = format!("{:?}", SignatureAlgorithm::EcdsaSha256);
775 assert!(debug.contains("EcdsaSha256"));
776 }
777
778 #[cfg(feature = "signatures")]
781 #[test]
782 fn test_parse_digest_algorithm_sha256() {
783 let result = parse_digest_algorithm("2.16.840.1.101.3.4.2.1");
784 assert!(result.is_ok());
785 assert_eq!(result.unwrap(), DigestAlgorithm::Sha256);
786 }
787
788 #[cfg(feature = "signatures")]
789 #[test]
790 fn test_parse_digest_algorithm_sha384() {
791 let result = parse_digest_algorithm("2.16.840.1.101.3.4.2.2");
792 assert!(result.is_ok());
793 assert_eq!(result.unwrap(), DigestAlgorithm::Sha384);
794 }
795
796 #[cfg(feature = "signatures")]
797 #[test]
798 fn test_parse_digest_algorithm_sha512() {
799 let result = parse_digest_algorithm("2.16.840.1.101.3.4.2.3");
800 assert!(result.is_ok());
801 assert_eq!(result.unwrap(), DigestAlgorithm::Sha512);
802 }
803
804 #[cfg(feature = "signatures")]
805 #[test]
806 fn test_parse_digest_algorithm_unsupported() {
807 let result = parse_digest_algorithm("1.2.3.4.5");
808 assert!(result.is_err());
809 let err = result.unwrap_err();
810 assert!(matches!(err, SignatureError::UnsupportedAlgorithm { .. }));
811 }
812
813 #[cfg(feature = "signatures")]
814 #[test]
815 fn test_parse_signature_algorithm_rsa_sha256() {
816 let result = parse_signature_algorithm("1.2.840.113549.1.1.11", DigestAlgorithm::Sha256);
817 assert!(result.is_ok());
818 assert_eq!(result.unwrap(), SignatureAlgorithm::RsaSha256);
819 }
820
821 #[cfg(feature = "signatures")]
822 #[test]
823 fn test_parse_signature_algorithm_ecdsa_sha256() {
824 let result = parse_signature_algorithm("1.2.840.10045.4.3.2", DigestAlgorithm::Sha256);
825 assert!(result.is_ok());
826 assert_eq!(result.unwrap(), SignatureAlgorithm::EcdsaSha256);
827 }
828
829 #[cfg(feature = "signatures")]
830 #[test]
831 fn test_parse_signature_algorithm_unsupported() {
832 let result = parse_signature_algorithm("1.2.3.4.5", DigestAlgorithm::Sha256);
833 assert!(result.is_err());
834 let err = result.unwrap_err();
835 assert!(matches!(err, SignatureError::UnsupportedAlgorithm { .. }));
836 }
837
838 #[cfg(feature = "signatures")]
839 #[test]
840 fn test_parse_pkcs7_invalid_der() {
841 let invalid = vec![0x00, 0x01, 0x02, 0x03];
842 let result = parse_pkcs7_signature(&invalid);
843 assert!(result.is_err());
844 let err = result.unwrap_err();
845 assert!(matches!(err, SignatureError::CmsParsingFailed { .. }));
846 }
847
848 #[cfg(feature = "signatures")]
849 #[test]
850 fn test_parse_pkcs7_empty_input() {
851 let result = parse_pkcs7_signature(&[]);
852 assert!(result.is_err());
853 }
854
855 #[test]
856 fn test_parsed_signature_debug() {
857 let sig = ParsedSignature {
858 digest_algorithm: DigestAlgorithm::Sha256,
859 signature_algorithm: SignatureAlgorithm::RsaSha256,
860 signature_value: vec![1, 2, 3],
861 signer_certificate_der: vec![4, 5, 6],
862 signing_time: Some("2024-01-01".to_string()),
863 };
864 let debug = format!("{:?}", sig);
865 assert!(debug.contains("Sha256"));
866 assert!(debug.contains("RsaSha256"));
867 }
868
869 #[test]
870 fn test_parsed_signature_clone() {
871 let sig = ParsedSignature {
872 digest_algorithm: DigestAlgorithm::Sha256,
873 signature_algorithm: SignatureAlgorithm::RsaSha256,
874 signature_value: vec![1, 2, 3],
875 signer_certificate_der: vec![4, 5, 6],
876 signing_time: None,
877 };
878 let cloned = sig.clone();
879 assert_eq!(sig.digest_algorithm, cloned.digest_algorithm);
880 assert_eq!(sig.signature_value, cloned.signature_value);
881 }
882
883 #[cfg(feature = "signatures")]
888 #[test]
889 fn test_ber_to_der_depth_limit_protection() {
890 let depth = MAX_BER_DEPTH + 50; let mut ber = Vec::new();
895 for _ in 0..depth {
897 ber.push(0x30); ber.push(0x80); }
900 ber.extend_from_slice(&[0x02, 0x01, 0x00]); for _ in 0..depth {
904 ber.push(0x00);
905 ber.push(0x00);
906 }
907
908 let result = ber_to_der(&ber);
909 assert!(result.is_err(), "Should reject deeply nested BER");
910
911 let err_msg = result.unwrap_err().to_string();
912 assert!(
913 err_msg.contains("nesting too deep") || err_msg.contains("depth"),
914 "Error should mention depth limit, got: {}",
915 err_msg
916 );
917 }
918
919 #[cfg(feature = "signatures")]
920 #[test]
921 fn test_ber_to_der_size_limit_protection() {
922 let ber = vec![
932 0x30, 0x80, 0x04, 0x82, 0x00, 0x10, ];
935 let mut full_ber = ber;
937 full_ber.extend(vec![0x41; 16]);
938 full_ber.extend_from_slice(&[0x00, 0x00]); let result = convert_element(&full_ber, 0, 5);
942 assert!(
943 result.is_err(),
944 "Should reject BER when output exceeds size budget"
945 );
946
947 let err_msg = result.unwrap_err().to_string();
948 assert!(
949 err_msg.contains("exceeds") || err_msg.contains("limit"),
950 "Error should mention size limit, got: {}",
951 err_msg
952 );
953 }
954
955 #[cfg(feature = "signatures")]
956 #[test]
957 fn test_ber_to_der_buffer_overread_protection() {
958 let truncated_ber = vec![
963 0x30, 0x80, 0x30, 0x85, 0x01,
966 0x02, ];
969
970 let result = ber_to_der(&truncated_ber);
971 assert!(
972 result.is_err(),
973 "Should reject BER with invalid/truncated length"
974 );
975
976 let err_msg = result.unwrap_err().to_string();
977 assert!(
979 err_msg.contains("too large")
980 || err_msg.contains("truncated")
981 || err_msg.contains("octets"),
982 "Error should mention length issue, got: {}",
983 err_msg
984 );
985 }
986
987 #[cfg(feature = "signatures")]
988 #[test]
989 fn test_ber_to_der_length_zero_octets() {
990 let invalid = vec![0x02, 0x80]; let result = ber_to_der(&invalid);
996 let _ = result; }
1001
1002 #[cfg(feature = "signatures")]
1003 #[test]
1004 fn test_contains_indefinite_length_primitive_false_positive() {
1005 let primitive = vec![0x04, 0x80]; assert!(
1009 !contains_indefinite_length(&primitive),
1010 "Should not detect indefinite length on primitive type"
1011 );
1012
1013 let integer = vec![0x02, 0x80];
1015 assert!(
1016 !contains_indefinite_length(&integer),
1017 "Should not detect indefinite length on INTEGER"
1018 );
1019 }
1020
1021 #[cfg(feature = "signatures")]
1022 #[test]
1023 fn test_contains_indefinite_length_constructed_true() {
1024 let sequence = vec![0x30, 0x80]; assert!(
1027 contains_indefinite_length(&sequence),
1028 "Should detect indefinite length on SEQUENCE"
1029 );
1030
1031 let set = vec![0x31, 0x80]; assert!(
1033 contains_indefinite_length(&set),
1034 "Should detect indefinite length on SET"
1035 );
1036
1037 let context = vec![0xA0, 0x80]; assert!(
1040 contains_indefinite_length(&context),
1041 "Should detect indefinite length on context-specific constructed"
1042 );
1043 }
1044
1045 #[cfg(feature = "signatures")]
1046 #[test]
1047 fn test_ber_to_der_valid_conversion() {
1048 let ber = vec![
1051 0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00, ];
1055
1056 let result = ber_to_der(&ber);
1057 assert!(result.is_ok(), "Valid BER should convert successfully");
1058
1059 let der = result.unwrap();
1060 assert_eq!(der[0], 0x30, "Should be SEQUENCE");
1062 assert_eq!(der[1], 0x03, "Length should be 3 (definite)");
1063 assert_eq!(&der[2..5], &[0x02, 0x01, 0x42], "Content preserved");
1064 }
1065
1066 #[cfg(feature = "signatures")]
1067 #[test]
1068 fn test_ber_to_der_already_der() {
1069 let der = vec![
1071 0x30, 0x03, 0x02, 0x01, 0x42, ];
1074
1075 let result = ber_to_der(&der);
1076 assert!(result.is_ok());
1077 assert_eq!(result.unwrap(), der, "DER should pass through unchanged");
1078 }
1079
1080 #[cfg(feature = "signatures")]
1081 #[test]
1082 fn test_ber_to_der_empty_input() {
1083 let result = ber_to_der(&[]);
1084 assert!(result.is_ok());
1085 assert!(result.unwrap().is_empty());
1086 }
1087
1088 #[cfg(feature = "signatures")]
1089 #[test]
1090 fn test_ber_to_der_moderate_nesting_ok() {
1091 let depth = 10;
1093
1094 let mut ber = Vec::new();
1095 for _ in 0..depth {
1096 ber.push(0x30);
1097 ber.push(0x80);
1098 }
1099 ber.extend_from_slice(&[0x02, 0x01, 0x00]); for _ in 0..depth {
1101 ber.push(0x00);
1102 ber.push(0x00);
1103 }
1104
1105 let result = ber_to_der(&ber);
1106 assert!(
1107 result.is_ok(),
1108 "Moderate nesting ({} levels) should work",
1109 depth
1110 );
1111 }
1112}