1use crate::error::PdfError;
51
52use super::cms::{IssuerAndSerial, OID_SIGNED_DATA};
53use super::der::{
54 maybe_read_context, read_context, read_expected, read_integer_bytes, read_integer_u64,
55 read_octet_string, read_oid, read_sequence, read_set, read_tlv, tag, Class,
56};
57
58#[derive(Debug, Clone)]
64pub enum SignerIdentifier {
65 IssuerAndSerial(IssuerAndSerial),
67 SubjectKeyIdentifier(Vec<u8>),
71}
72
73#[derive(Debug, Clone)]
78pub struct Attribute {
79 pub oid: Vec<u64>,
81 pub values: Vec<Vec<u8>>,
85}
86
87#[derive(Debug, Clone)]
94pub struct SignerInfo {
95 pub version: u64,
97 pub sid: SignerIdentifier,
99 pub digest_algorithm_oid: Vec<u64>,
101 pub digest_algorithm_params: Vec<u8>,
105 pub signed_attrs: Vec<Attribute>,
111 pub signed_attrs_der: Option<Vec<u8>>,
119 pub signature_algorithm_oid: Vec<u64>,
122 pub signature_algorithm_params: Vec<u8>,
124 pub signature: Vec<u8>,
127 pub unsigned_attrs: Vec<Attribute>,
129}
130
131#[derive(Debug, Clone)]
134pub struct SignedData {
135 pub version: u64,
137 pub digest_algorithms: Vec<(Vec<u64>, Vec<u8>)>,
139 pub encap_content_type: Vec<u64>,
145 pub encap_content_octets: Option<Vec<u8>>,
150 pub certs: Vec<Vec<u8>>,
156 pub crls: Vec<Vec<u8>>,
161 pub signer_infos: Vec<SignerInfo>,
164}
165
166pub fn parse_signed_data(data: &[u8]) -> Result<SignedData, PdfError> {
172 let (body, rest) = read_sequence(data)?;
173 if !rest.is_empty() {
174 return Err(PdfError::other(
175 "CMS SignedData: trailing bytes after ContentInfo SEQUENCE",
176 ));
177 }
178 let (oid, rest) = read_oid(body)?;
179 if oid != OID_SIGNED_DATA {
180 return Err(PdfError::other(format!(
181 "CMS SignedData: ContentInfo contentType must be id-signedData (got {oid:?})"
182 )));
183 }
184 let (content, rest) = read_context(rest, 0)?;
185 if !rest.is_empty() {
186 return Err(PdfError::other(
187 "CMS SignedData: trailing bytes after [0] EXPLICIT content",
188 ));
189 }
190 parse_signed_data_inner(content)
191}
192
193pub fn parse_signed_data_inner(data: &[u8]) -> Result<SignedData, PdfError> {
195 let (body, rest) = read_sequence(data)?;
196 if !rest.is_empty() {
197 return Err(PdfError::other(
198 "CMS SignedData: trailing bytes after SignedData SEQUENCE",
199 ));
200 }
201 let (version, body) = read_integer_u64(body)?;
202 if version > 5 {
203 return Err(PdfError::other(format!(
204 "CMS SignedData: unsupported version {version}"
205 )));
206 }
207 let (da_set, body) = read_set(body)?;
209 let mut digest_algorithms: Vec<(Vec<u64>, Vec<u8>)> = Vec::new();
210 let mut cursor = da_set;
211 while !cursor.is_empty() {
212 let (alg_seq, after) = read_sequence(cursor)?;
213 let (alg_oid, alg_params) = read_oid(alg_seq)?;
214 digest_algorithms.push((alg_oid, alg_params.to_vec()));
215 cursor = after;
216 }
217
218 let (eci, body) = read_sequence(body)?;
223 let (eci_oid, eci_rest) = read_oid(eci)?;
224 let (eci_econtent_opt, eci_rest) = maybe_read_context(eci_rest, 0)?;
225 if !eci_rest.is_empty() {
226 return Err(PdfError::other(
227 "CMS SignedData: trailing bytes after EncapsulatedContentInfo",
228 ));
229 }
230 let encap_content_octets = match eci_econtent_opt {
231 Some(b) => {
232 if let Ok((tlv, rest_inner)) = read_tlv(b) {
237 if rest_inner.is_empty()
238 && tlv.class == Class::Universal
239 && tlv.tag_number == tag::OCTET_STRING
240 {
241 Some(tlv.body.to_vec())
242 } else {
243 Some(b.to_vec())
244 }
245 } else {
246 Some(b.to_vec())
247 }
248 }
249 None => None,
250 };
251
252 let mut cursor = body;
254 let mut certs: Vec<Vec<u8>> = Vec::new();
255 if !cursor.is_empty() {
256 let (peek, _) = read_tlv(cursor)?;
257 if peek.class == Class::ContextSpecific && peek.tag_number == 0 {
258 let (set_body, after) = read_tlv(cursor)?;
259 certs = split_set_into_raw_entries(set_body.body)?;
260 cursor = after;
261 }
262 }
263 let mut crls: Vec<Vec<u8>> = Vec::new();
265 if !cursor.is_empty() {
266 let (peek, _) = read_tlv(cursor)?;
267 if peek.class == Class::ContextSpecific && peek.tag_number == 1 {
268 let (set_body, after) = read_tlv(cursor)?;
269 crls = split_set_into_raw_entries(set_body.body)?;
270 cursor = after;
271 }
272 }
273
274 let (si_set, after_si) = read_set(cursor)?;
276 if !after_si.is_empty() {
277 return Err(PdfError::other(
278 "CMS SignedData: trailing bytes after signerInfos SET",
279 ));
280 }
281 let mut signer_infos: Vec<SignerInfo> = Vec::new();
282 let mut si_cursor = si_set;
283 while !si_cursor.is_empty() {
284 let (info, tail) = parse_signer_info(si_cursor)?;
285 signer_infos.push(info);
286 si_cursor = tail;
287 }
288 if signer_infos.is_empty() {
289 return Err(PdfError::other(
290 "CMS SignedData: signerInfos SET must contain at least one SignerInfo",
291 ));
292 }
293
294 Ok(SignedData {
295 version,
296 digest_algorithms,
297 encap_content_type: eci_oid,
298 encap_content_octets,
299 certs,
300 crls,
301 signer_infos,
302 })
303}
304
305fn parse_signer_info(data: &[u8]) -> Result<(SignerInfo, &[u8]), PdfError> {
307 let (body, tail) = read_sequence(data)?;
308 let (version, body) = read_integer_u64(body)?;
309 if version != 1 && version != 3 {
310 return Err(PdfError::other(format!(
311 "CMS SignedData: unsupported SignerInfo version {version} (expected 1 or 3)"
312 )));
313 }
314 let (sid, body) = if version == 1 {
316 let (ias_body, rest) = read_sequence(body)?;
317 let (issuer_tlv, ias_after_issuer) = read_tlv(ias_body)?;
318 if issuer_tlv.class != Class::Universal || issuer_tlv.tag_number != tag::SEQUENCE {
319 return Err(PdfError::other(
320 "CMS SignedData: SignerInfo IAS issuer must be a SEQUENCE",
321 ));
322 }
323 let issuer_total = ias_body.len() - ias_after_issuer.len();
324 let issuer_der = ias_body[..issuer_total].to_vec();
325 let (serial_body, _) = read_integer_bytes(ias_after_issuer)?;
326 (
327 SignerIdentifier::IssuerAndSerial(IssuerAndSerial {
328 issuer_der,
329 serial: serial_body.to_vec(),
330 }),
331 rest,
332 )
333 } else {
334 let (tlv, rest) = read_tlv(body)?;
336 if tlv.class != Class::ContextSpecific || tlv.tag_number != 0 {
337 return Err(PdfError::other(format!(
338 "CMS SignedData: SignerInfo[v=3] expects [0] SubjectKeyIdentifier (got class={:?} tag={})",
339 tlv.class, tlv.tag_number
340 )));
341 }
342 if tlv.constructed {
343 return Err(PdfError::other(
344 "CMS SignedData: SignerInfo SKI must be primitive [0] IMPLICIT OCTET STRING",
345 ));
346 }
347 (
348 SignerIdentifier::SubjectKeyIdentifier(tlv.body.to_vec()),
349 rest,
350 )
351 };
352
353 let (da_seq, body) = read_sequence(body)?;
355 let (da_oid, da_params) = read_oid(da_seq)?;
356 let digest_algorithm_oid = da_oid;
357 let digest_algorithm_params = da_params.to_vec();
358
359 let mut cursor = body;
361 let mut signed_attrs: Vec<Attribute> = Vec::new();
362 let mut signed_attrs_der: Option<Vec<u8>> = None;
363 if !cursor.is_empty() {
364 let (peek, _) = read_tlv(cursor)?;
365 if peek.class == Class::ContextSpecific && peek.tag_number == 0 {
366 let (sa_tlv, after) = read_tlv(cursor)?;
367 signed_attrs_der = Some(sa_tlv.body.to_vec());
368 signed_attrs = split_attributes(sa_tlv.body)?;
369 cursor = after;
370 }
371 }
372
373 let (sa_seq, body) = read_sequence(cursor)?;
375 let (sa_oid, sa_params) = read_oid(sa_seq)?;
376 let signature_algorithm_oid = sa_oid;
377 let signature_algorithm_params = sa_params.to_vec();
378
379 let (sig_bytes, body) = read_octet_string(body)?;
381 let signature = sig_bytes.to_vec();
382
383 let mut cursor = body;
385 let mut unsigned_attrs: Vec<Attribute> = Vec::new();
386 if !cursor.is_empty() {
387 let (peek, _) = read_tlv(cursor)?;
388 if peek.class == Class::ContextSpecific && peek.tag_number == 1 {
389 let (ua_tlv, after) = read_tlv(cursor)?;
390 unsigned_attrs = split_attributes(ua_tlv.body)?;
391 cursor = after;
392 }
393 }
394 if !cursor.is_empty() {
395 return Err(PdfError::other(
396 "CMS SignedData: trailing bytes after SignerInfo body",
397 ));
398 }
399
400 Ok((
401 SignerInfo {
402 version,
403 sid,
404 digest_algorithm_oid,
405 digest_algorithm_params,
406 signed_attrs,
407 signed_attrs_der,
408 signature_algorithm_oid,
409 signature_algorithm_params,
410 signature,
411 unsigned_attrs,
412 },
413 tail,
414 ))
415}
416
417fn split_attributes(body: &[u8]) -> Result<Vec<Attribute>, PdfError> {
419 let mut out = Vec::new();
420 let mut cursor = body;
421 while !cursor.is_empty() {
422 let (attr_seq, after) = read_sequence(cursor)?;
423 let (oid, attr_rest) = read_oid(attr_seq)?;
424 let (set_tlv, after_set) = read_expected(attr_rest, Class::Universal, tag::SET)?;
426 if !after_set.is_empty() {
427 return Err(PdfError::other(
428 "CMS SignedData: Attribute has trailing bytes after attrValues SET",
429 ));
430 }
431 let mut values: Vec<Vec<u8>> = Vec::new();
433 let mut vcursor = set_tlv.body;
434 while !vcursor.is_empty() {
435 let before = vcursor.len();
436 let (_tlv, after_v) = read_tlv(vcursor)?;
437 let consumed = before - after_v.len();
438 values.push(vcursor[..consumed].to_vec());
439 vcursor = after_v;
440 }
441 out.push(Attribute { oid, values });
442 cursor = after;
443 }
444 Ok(out)
445}
446
447fn split_set_into_raw_entries(set_body: &[u8]) -> Result<Vec<Vec<u8>>, PdfError> {
452 let mut out = Vec::new();
453 let mut cursor = set_body;
454 while !cursor.is_empty() {
455 let before_len = cursor.len();
456 let (_tlv, after) = read_tlv(cursor)?;
457 let consumed = before_len - after.len();
458 out.push(cursor[..consumed].to_vec());
459 cursor = after;
460 }
461 Ok(out)
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467 use crate::pubsec::der;
468
469 #[test]
474 fn parse_minimal_signed_data_v1_ias() {
475 let issuer_der = der::write_sequence(b"O=Round-19 Signer");
476 let serial = vec![0x01, 0x42];
477 let digest_oid = vec![2u64, 16, 840, 1, 101, 3, 4, 2, 1]; let signature_oid = vec![1u64, 2, 840, 113549, 1, 1, 1]; let signature_bytes = vec![0xAAu8; 256];
480
481 let mut si_body = der::write_integer_u64(1); let ias_body = {
484 let mut b = issuer_der.clone();
485 b.extend_from_slice(&der::write_integer_bytes(&serial));
486 b
487 };
488 si_body.extend_from_slice(&der::write_sequence(&ias_body));
489 let da_alg = {
491 let mut b = der::write_oid(&digest_oid);
492 b.extend_from_slice(&der::write_null());
493 der::write_sequence(&b)
494 };
495 si_body.extend_from_slice(&da_alg);
496 let sig_alg = {
498 let mut b = der::write_oid(&signature_oid);
499 b.extend_from_slice(&der::write_null());
500 der::write_sequence(&b)
501 };
502 si_body.extend_from_slice(&sig_alg);
503 si_body.extend_from_slice(&der::write_octet_string(&signature_bytes));
505 let signer_info = der::write_sequence(&si_body);
506
507 let da_set = der::write_set(&da_alg);
509
510 let payload = b"OXIDEAV-attached-econtent-bytes";
512 let eci_body = {
513 let mut b = der::write_oid(&[1u64, 2, 840, 113549, 1, 7, 1]); let octet = der::write_octet_string(payload);
516 b.extend_from_slice(&der::write_context_constructed(0, &octet));
517 b
518 };
519 let eci = der::write_sequence(&eci_body);
520
521 let si_set = der::write_set(&signer_info);
523
524 let mut sd_body = der::write_integer_u64(1);
526 sd_body.extend_from_slice(&da_set);
527 sd_body.extend_from_slice(&eci);
528 sd_body.extend_from_slice(&si_set);
529 let sd = der::write_sequence(&sd_body);
530
531 let outer_body = {
533 let mut b = der::write_oid(&OID_SIGNED_DATA);
534 b.extend_from_slice(&der::write_context_constructed(0, &sd));
535 b
536 };
537 let envelope = der::write_sequence(&outer_body);
538
539 let parsed = parse_signed_data(&envelope).expect("parse SignedData");
540 assert_eq!(parsed.version, 1);
541 assert_eq!(parsed.digest_algorithms.len(), 1);
542 assert_eq!(parsed.digest_algorithms[0].0, digest_oid);
543 assert_eq!(parsed.encap_content_type, vec![1, 2, 840, 113549, 1, 7, 1]);
544 assert_eq!(parsed.encap_content_octets.as_deref(), Some(&payload[..]));
545 assert!(parsed.certs.is_empty());
546 assert!(parsed.crls.is_empty());
547 assert_eq!(parsed.signer_infos.len(), 1);
548 let si = &parsed.signer_infos[0];
549 assert_eq!(si.version, 1);
550 match &si.sid {
551 SignerIdentifier::IssuerAndSerial(ias) => {
552 assert_eq!(ias.issuer_der, issuer_der);
553 assert_eq!(ias.serial, serial);
554 }
555 other => panic!("expected IAS got {other:?}"),
556 }
557 assert_eq!(si.digest_algorithm_oid, digest_oid);
558 assert_eq!(si.signature_algorithm_oid, signature_oid);
559 assert_eq!(si.signature, signature_bytes);
560 assert!(si.signed_attrs.is_empty());
561 assert!(si.signed_attrs_der.is_none());
562 assert!(si.unsigned_attrs.is_empty());
563 }
564
565 #[test]
569 fn parse_signed_data_v3_ski_with_signed_attrs() {
570 let signer_ski = vec![0xCDu8; 20];
571 let digest_oid = vec![2u64, 16, 840, 1, 101, 3, 4, 2, 1]; let signature_oid = vec![1u64, 2, 840, 10045, 4, 3, 2]; let signature_bytes = vec![0xBBu8; 72];
574
575 let attr_oid = vec![1u64, 2, 840, 113549, 1, 9, 3]; let attr_value = der::write_oid(&[1u64, 2, 840, 113549, 1, 7, 1]); let attr_seq_body = {
579 let mut b = der::write_oid(&attr_oid);
580 b.extend_from_slice(&der::write_set(&attr_value));
581 b
582 };
583 let attr_seq = der::write_sequence(&attr_seq_body);
584 let signed_attrs_implicit_body = attr_seq.clone();
588 let signed_attrs_tlv = der::write_tlv(
589 der::Class::ContextSpecific,
590 true,
591 0,
592 &signed_attrs_implicit_body,
593 );
594
595 let mut si_body = der::write_integer_u64(3); si_body.extend_from_slice(&der::write_context_primitive(0, &signer_ski));
599 let da_alg = {
600 let mut b = der::write_oid(&digest_oid);
601 b.extend_from_slice(&der::write_null());
602 der::write_sequence(&b)
603 };
604 si_body.extend_from_slice(&da_alg);
605 si_body.extend_from_slice(&signed_attrs_tlv);
606 let sig_alg = {
607 let mut b = der::write_oid(&signature_oid);
608 b.extend_from_slice(&der::write_null());
609 der::write_sequence(&b)
610 };
611 si_body.extend_from_slice(&sig_alg);
612 si_body.extend_from_slice(&der::write_octet_string(&signature_bytes));
613 let signer_info = der::write_sequence(&si_body);
614
615 let da_set = der::write_set(&da_alg);
616 let eci_body = der::write_oid(&[1u64, 2, 840, 113549, 1, 7, 1]);
618 let eci = der::write_sequence(&eci_body);
619 let si_set = der::write_set(&signer_info);
620
621 let mut sd_body = der::write_integer_u64(3); sd_body.extend_from_slice(&da_set);
623 sd_body.extend_from_slice(&eci);
624 sd_body.extend_from_slice(&si_set);
625 let sd = der::write_sequence(&sd_body);
626
627 let outer_body = {
628 let mut b = der::write_oid(&OID_SIGNED_DATA);
629 b.extend_from_slice(&der::write_context_constructed(0, &sd));
630 b
631 };
632 let envelope = der::write_sequence(&outer_body);
633
634 let parsed = parse_signed_data(&envelope).expect("parse v3 SKI SignedData");
635 assert_eq!(parsed.version, 3);
636 assert!(parsed.encap_content_octets.is_none(), "detached signature");
637 let si = &parsed.signer_infos[0];
638 match &si.sid {
639 SignerIdentifier::SubjectKeyIdentifier(b) => assert_eq!(b, &signer_ski),
640 other => panic!("expected SKI got {other:?}"),
641 }
642 assert_eq!(si.signed_attrs.len(), 1);
643 assert_eq!(si.signed_attrs[0].oid, attr_oid);
644 assert_eq!(si.signed_attrs[0].values.len(), 1);
645 assert!(si.signed_attrs_der.is_some());
646 assert_eq!(si.signature, signature_bytes);
647 }
648
649 #[test]
650 fn rejects_envelope_with_wrong_oid() {
651 let inner = der::write_sequence(&der::write_integer_u64(0));
654 let outer_body = {
655 let mut b = der::write_oid(&[1u64, 2, 840, 113549, 1, 7, 3]);
656 b.extend_from_slice(&der::write_context_constructed(0, &inner));
657 b
658 };
659 let envelope = der::write_sequence(&outer_body);
660 let err = parse_signed_data(&envelope).expect_err("must reject");
661 let msg = format!("{err}");
662 assert!(msg.contains("id-signedData"), "{msg}");
663 }
664}