1use crate::error::{Error, Result};
26use alloc::vec::Vec;
27use broadcast_common::{Parse, Serialize};
28
29pub const TABLE_ID: u8 = 0x7B;
31pub const AUTH_EXTENSION_FIRST: u16 = 0x0000;
37pub const AUTH_EXTENSION_LAST: u16 = 0x00FF;
39pub const CERTIFICATE_COLLECTION_EXTENSION: u16 = 0x0100;
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize))]
45#[non_exhaustive]
46pub enum ReferenceType {
47 Reserved,
49 SameEs,
51 ComponentTagEs,
53 Unallocated(u8),
55}
56
57impl ReferenceType {
58 #[must_use]
59 pub fn from_u8(v: u8) -> Self {
61 match v & 0x0F {
62 0x0 => Self::Reserved,
63 0x1 => Self::SameEs,
64 0x2 => Self::ComponentTagEs,
65 v => Self::Unallocated(v),
66 }
67 }
68
69 #[must_use]
70 pub const fn to_u8(self) -> u8 {
72 match self {
73 Self::Reserved => 0x0,
74 Self::SameEs => 0x1,
75 Self::ComponentTagEs => 0x2,
76 Self::Unallocated(v) => v,
77 }
78 }
79
80 #[must_use]
81 pub fn name(self) -> &'static str {
83 match self {
84 Self::Reserved => "Reserved",
85 Self::SameEs => "Same ES",
86 Self::ComponentTagEs => "Component Tag ES",
87 Self::Unallocated(_) => "Unallocated",
88 }
89 }
90}
91broadcast_common::impl_spec_display!(ReferenceType, Unallocated);
92
93const HEADER_LEN: usize = 8;
96const SECTION_LENGTH_PREFIX: usize = 3;
98use super::CRC_LEN;
99
100const AUTH_FIXED_PREFIX: usize = 5;
104
105const LOOP_LEN_MAX: usize = 0x0FFF;
108const NIBBLE_MAX: usize = 0x0F;
110const LOOP_LEN_HI_MASK: u8 = 0x0F;
114
115#[derive(Debug, Clone, PartialEq, Eq)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize))]
122#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
123pub struct SectionHashEntry<'a> {
124 pub reference_type: ReferenceType,
126 pub reference: &'a [u8],
128 pub hash: &'a [u8],
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize))]
135#[non_exhaustive]
136pub enum ProtectionMessageBody<'a> {
137 AuthenticationMessage {
139 section_hash_algorithm_identifier: u8,
141 section_hash_length: u8,
143 signature_algorithm_identifier: u8,
145 hashes: Vec<SectionHashEntry<'a>>,
147 extension_bytes: &'a [u8],
149 signature_key_identifier: &'a [u8],
151 signature: &'a [u8],
153 #[cfg_attr(feature = "serde", serde(skip))]
156 reserved_section_hashes_loop_length: u8,
157 },
158 CertificateCollection {
160 certificates: Vec<&'a [u8]>,
162 #[cfg_attr(feature = "serde", serde(skip))]
165 reserved_certificate_count: u8,
166 #[cfg_attr(feature = "serde", serde(skip))]
170 reserved_certificate_lengths: Vec<u8>,
171 },
172 Raw(&'a [u8]),
174}
175
176impl<'a> ProtectionMessageBody<'a> {
177 #[must_use]
179 pub fn certificate_collection(certificates: Vec<&'a [u8]>) -> Self {
180 Self::CertificateCollection {
181 certificates,
182 reserved_certificate_count: super::RESERVED_4,
183 reserved_certificate_lengths: Vec::new(),
184 }
185 }
186}
187
188#[derive(Debug, Clone, PartialEq, Eq)]
193#[cfg_attr(feature = "serde", derive(serde::Serialize))]
194#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
195#[non_exhaustive]
196pub struct ProtectionMessageSection<'a> {
197 pub table_id_extension: u16,
200 pub version_number: u8,
202 pub current_next_indicator: bool,
204 pub section_number: u8,
206 pub last_section_number: u8,
208 pub body: ProtectionMessageBody<'a>,
210 #[cfg_attr(feature = "serde", serde(skip))]
213 pub bits: super::SectionBits,
214}
215
216impl<'a> ProtectionMessageSection<'a> {
217 #[must_use]
219 pub fn new(
220 table_id_extension: u16,
221 version_number: u8,
222 current_next_indicator: bool,
223 section_number: u8,
224 last_section_number: u8,
225 body: ProtectionMessageBody<'a>,
226 ) -> Self {
227 Self {
228 table_id_extension,
229 version_number,
230 current_next_indicator,
231 section_number,
232 last_section_number,
233 body,
234 bits: super::SectionBits::dvb(),
235 }
236 }
237}
238
239impl<'a> Parse<'a> for ProtectionMessageSection<'a> {
240 type Error = crate::error::Error;
241 fn parse(bytes: &'a [u8]) -> Result<Self> {
242 let min_len = HEADER_LEN + CRC_LEN;
243 if bytes.len() < min_len {
244 return Err(Error::BufferTooShort {
245 need: min_len,
246 have: bytes.len(),
247 what: "ProtectionMessageSection",
248 });
249 }
250 if bytes[0] != TABLE_ID {
251 return Err(Error::UnexpectedTableId {
252 table_id: bytes[0],
253 what: "ProtectionMessageSection",
254 expected: &[TABLE_ID],
255 });
256 }
257 let section_length = super::section_length_of(bytes);
258 let total = super::check_section_length(
259 bytes.len(),
260 SECTION_LENGTH_PREFIX,
261 section_length,
262 HEADER_LEN + CRC_LEN,
263 )?;
264
265 let table_id_extension = crate::wire::be_u16(bytes, 3, "protection_message")?;
266 let version_number = super::version_number_of(bytes[5]);
267 let current_next_indicator = super::current_next_of(bytes[5]);
268 let section_number = bytes[6];
269 let last_section_number = bytes[7];
270
271 let body_bytes = &bytes[HEADER_LEN..total - CRC_LEN];
272 let body = match table_id_extension {
273 AUTH_EXTENSION_FIRST..=AUTH_EXTENSION_LAST => parse_authentication_message(body_bytes)?,
274 CERTIFICATE_COLLECTION_EXTENSION => parse_certificate_collection(body_bytes)?,
275 _ => ProtectionMessageBody::Raw(body_bytes),
276 };
277
278 Ok(ProtectionMessageSection {
279 table_id_extension,
280 version_number,
281 current_next_indicator,
282 section_number,
283 last_section_number,
284 body,
285 bits: super::SectionBits::parse(bytes[1], Some(bytes[5])),
286 })
287 }
288}
289
290fn parse_authentication_message(body: &[u8]) -> Result<ProtectionMessageBody<'_>> {
292 if body.len() < AUTH_FIXED_PREFIX {
293 return Err(Error::BufferTooShort {
294 need: AUTH_FIXED_PREFIX,
295 have: body.len(),
296 what: "ProtectionMessageSection::AuthenticationMessage",
297 });
298 }
299 let section_hash_algorithm_identifier = body[0];
300 let section_hash_length = body[1];
301 let signature_algorithm_identifier = body[2];
302 let section_hashes_loop_length = super::desc_loop_len_of(body[3], body[4]);
304
305 let loop_start = AUTH_FIXED_PREFIX;
306 let loop_end = loop_start + section_hashes_loop_length;
307 if loop_end > body.len() {
308 return Err(Error::SectionLengthOverflow {
309 declared: section_hashes_loop_length,
310 available: body.len() - loop_start,
311 });
312 }
313
314 let hash_len = section_hash_length as usize;
315 let mut hashes = Vec::new();
316 let mut pos = loop_start;
317 while pos < loop_end {
318 let lead = body[pos];
320 let reference_type = ReferenceType::from_u8(lead >> 4);
321 let reference_length = (lead & LOOP_LEN_HI_MASK) as usize;
322 let ref_start = pos + 1;
323 let ref_end = ref_start + reference_length;
324 let hash_end = ref_end + hash_len;
325 if hash_end > loop_end {
326 return Err(Error::SectionLengthOverflow {
327 declared: reference_length + hash_len,
328 available: loop_end - ref_start,
329 });
330 }
331 hashes.push(SectionHashEntry {
332 reference_type,
333 reference: &body[ref_start..ref_end],
334 hash: &body[ref_end..hash_end],
335 });
336 pos = hash_end;
337 }
338
339 if loop_end >= body.len() {
341 return Err(Error::BufferTooShort {
342 need: loop_end + 1,
343 have: body.len(),
344 what: "ProtectionMessageSection::extension_bytes_length",
345 });
346 }
347 let extension_bytes_length = body[loop_end] as usize;
348 let ext_start = loop_end + 1;
349 let ext_end = ext_start + extension_bytes_length;
350 if ext_end > body.len() {
351 return Err(Error::SectionLengthOverflow {
352 declared: extension_bytes_length,
353 available: body.len() - ext_start,
354 });
355 }
356
357 if ext_end >= body.len() {
359 return Err(Error::BufferTooShort {
360 need: ext_end + 1,
361 have: body.len(),
362 what: "ProtectionMessageSection::signature_key_identifier_length",
363 });
364 }
365 let key_id_length = body[ext_end] as usize;
366 let key_start = ext_end + 1;
367 let key_end = key_start + key_id_length;
368 if key_end > body.len() {
369 return Err(Error::SectionLengthOverflow {
370 declared: key_id_length,
371 available: body.len() - key_start,
372 });
373 }
374
375 let signature = &body[key_end..];
377
378 Ok(ProtectionMessageBody::AuthenticationMessage {
379 section_hash_algorithm_identifier,
380 section_hash_length,
381 signature_algorithm_identifier,
382 hashes,
383 extension_bytes: &body[ext_start..ext_end],
384 signature_key_identifier: &body[key_start..key_end],
385 signature,
386 reserved_section_hashes_loop_length: super::len_reserved_of(body[3]),
387 })
388}
389
390fn parse_certificate_collection(body: &[u8]) -> Result<ProtectionMessageBody<'_>> {
392 if body.is_empty() {
393 return Err(Error::BufferTooShort {
394 need: 1,
395 have: 0,
396 what: "ProtectionMessageSection::CertificateCollection",
397 });
398 }
399 let certificate_count = (body[0] & LOOP_LEN_HI_MASK) as usize;
401 let mut certificates = Vec::with_capacity(certificate_count);
402 let mut reserved_certificate_lengths = Vec::with_capacity(certificate_count);
403 let mut pos = 1;
404 for _ in 0..certificate_count {
405 if pos + 2 > body.len() {
406 return Err(Error::BufferTooShort {
407 need: pos + 2,
408 have: body.len(),
409 what: "ProtectionMessageSection::certificate_length",
410 });
411 }
412 let certificate_length = super::desc_loop_len_of(body[pos], body[pos + 1]);
414 let cert_start = pos + 2;
415 let cert_end = cert_start + certificate_length;
416 if cert_end > body.len() {
417 return Err(Error::SectionLengthOverflow {
418 declared: certificate_length,
419 available: body.len() - cert_start,
420 });
421 }
422 reserved_certificate_lengths.push(super::len_reserved_of(body[pos]));
423 certificates.push(&body[cert_start..cert_end]);
424 pos = cert_end;
425 }
426 if pos != body.len() {
430 return Err(Error::BufferTooShort {
431 need: body.len() - pos,
432 have: 0,
433 what: "ProtectionMessageSection::CertificateCollection trailing bytes",
434 });
435 }
436 Ok(ProtectionMessageBody::CertificateCollection {
437 certificates,
438 reserved_certificate_count: super::len_reserved_of(body[0]),
439 reserved_certificate_lengths,
440 })
441}
442
443impl ProtectionMessageBody<'_> {
444 fn body_len(&self) -> usize {
446 match self {
447 ProtectionMessageBody::AuthenticationMessage {
448 hashes,
449 extension_bytes,
450 signature_key_identifier,
451 signature,
452 ..
453 } => {
454 let loop_bytes: usize = hashes
455 .iter()
456 .map(|h| 1 + h.reference.len() + h.hash.len())
457 .sum();
458 AUTH_FIXED_PREFIX
459 + loop_bytes
460 + 1
461 + extension_bytes.len()
462 + 1
463 + signature_key_identifier.len()
464 + signature.len()
465 }
466 ProtectionMessageBody::CertificateCollection { certificates, .. } => {
467 1 + certificates.iter().map(|c| 2 + c.len()).sum::<usize>()
468 }
469 ProtectionMessageBody::Raw(raw) => raw.len(),
470 }
471 }
472
473 fn write_into(&self, buf: &mut [u8]) -> Result<usize> {
475 match self {
476 ProtectionMessageBody::AuthenticationMessage {
477 section_hash_algorithm_identifier,
478 section_hash_length,
479 signature_algorithm_identifier,
480 hashes,
481 extension_bytes,
482 signature_key_identifier,
483 signature,
484 reserved_section_hashes_loop_length,
485 } => {
486 buf[0] = *section_hash_algorithm_identifier;
487 buf[1] = *section_hash_length;
488 buf[2] = *signature_algorithm_identifier;
489 let loop_bytes: usize = hashes
490 .iter()
491 .map(|h| 1 + h.reference.len() + h.hash.len())
492 .sum();
493 if loop_bytes > LOOP_LEN_MAX {
494 return Err(Error::SectionLengthOverflow {
495 declared: loop_bytes,
496 available: LOOP_LEN_MAX,
497 });
498 }
499 if extension_bytes.len() > u8::MAX as usize {
500 return Err(Error::SectionLengthOverflow {
501 declared: extension_bytes.len(),
502 available: u8::MAX as usize,
503 });
504 }
505 if signature_key_identifier.len() > u8::MAX as usize {
506 return Err(Error::SectionLengthOverflow {
507 declared: signature_key_identifier.len(),
508 available: u8::MAX as usize,
509 });
510 }
511 super::write_desc_loop_len_r(
513 &mut buf[3..5],
514 loop_bytes,
515 *reserved_section_hashes_loop_length,
516 )?;
517 let mut pos = AUTH_FIXED_PREFIX;
518 for h in hashes {
519 if h.reference.len() > NIBBLE_MAX {
520 return Err(Error::SectionLengthOverflow {
521 declared: h.reference.len(),
522 available: NIBBLE_MAX,
523 });
524 }
525 if h.hash.len() != *section_hash_length as usize {
530 return Err(Error::ValueOutOfRange {
531 field: "Hash.hash",
532 reason: "length must equal section_hash_length",
533 });
534 }
535 buf[pos] = (broadcast_common::len::fit_mask(
536 h.reference_type.to_u8(),
537 0x0F,
538 "reference_type",
539 )? << 4)
540 | broadcast_common::len::fit_bits(
541 h.reference.len() as u64,
542 4,
543 "h.reference",
544 )? as u8;
545 pos += 1;
546 buf[pos..pos + h.reference.len()].copy_from_slice(h.reference);
547 pos += h.reference.len();
548 buf[pos..pos + h.hash.len()].copy_from_slice(h.hash);
549 pos += h.hash.len();
550 }
551 buf[pos] = broadcast_common::len::fit_u8(extension_bytes.len(), "extension_bytes")?;
552 pos += 1;
553 buf[pos..pos + extension_bytes.len()].copy_from_slice(extension_bytes);
554 pos += extension_bytes.len();
555 buf[pos] = broadcast_common::len::fit_u8(
556 signature_key_identifier.len(),
557 "signature_key_identifier",
558 )?;
559 pos += 1;
560 buf[pos..pos + signature_key_identifier.len()]
561 .copy_from_slice(signature_key_identifier);
562 pos += signature_key_identifier.len();
563 buf[pos..pos + signature.len()].copy_from_slice(signature);
564 pos += signature.len();
565 Ok(pos)
566 }
567 ProtectionMessageBody::CertificateCollection {
568 certificates,
569 reserved_certificate_count,
570 reserved_certificate_lengths,
571 } => {
572 if certificates.len() > NIBBLE_MAX {
573 return Err(Error::SectionLengthOverflow {
574 declared: certificates.len(),
575 available: NIBBLE_MAX,
576 });
577 }
578 buf[0] = (broadcast_common::len::fit_mask(
580 *reserved_certificate_count,
581 super::RESERVED_4,
582 "reserved(4)",
583 )? << 4)
584 | broadcast_common::len::fit_bits(certificates.len() as u64, 4, "certificates")?
585 as u8;
586 let mut pos = 1;
587 for (i, c) in certificates.iter().enumerate() {
588 if c.len() > LOOP_LEN_MAX {
589 return Err(Error::SectionLengthOverflow {
590 declared: c.len(),
591 available: LOOP_LEN_MAX,
592 });
593 }
594 super::write_desc_loop_len_r(
596 &mut buf[pos..pos + 2],
597 c.len(),
598 reserved_certificate_lengths
599 .get(i)
600 .copied()
601 .unwrap_or(super::RESERVED_4),
602 )?;
603 pos += 2;
604 buf[pos..pos + c.len()].copy_from_slice(c);
605 pos += c.len();
606 }
607 Ok(pos)
608 }
609 ProtectionMessageBody::Raw(raw) => {
610 buf[..raw.len()].copy_from_slice(raw);
611 Ok(raw.len())
612 }
613 }
614 }
615}
616
617impl Serialize for ProtectionMessageSection<'_> {
618 type Error = crate::error::Error;
619 fn serialized_len(&self) -> usize {
620 HEADER_LEN + self.body.body_len() + CRC_LEN
621 }
622 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
623 let len = self.serialized_len();
624 if buf.len() < len {
625 return Err(Error::OutputBufferTooSmall {
626 need: len,
627 have: buf.len(),
628 });
629 }
630 buf[0] = TABLE_ID;
631 buf[1] = self.bits.b1()?;
632 super::write_section_length(buf, len - SECTION_LENGTH_PREFIX)?;
633 buf[3..5].copy_from_slice(&self.table_id_extension.to_be_bytes());
634 buf[5] = self
636 .bits
637 .b5(self.version_number, self.current_next_indicator)?;
638 buf[6] = self.section_number;
639 buf[7] = self.last_section_number;
640 let body_written = self.body.write_into(&mut buf[HEADER_LEN..])?;
641 let body_end = HEADER_LEN + body_written;
642 super::write_crc(buf, body_end);
643 Ok(len)
644 }
645}
646impl<'a> crate::traits::TableDef<'a> for ProtectionMessageSection<'a> {
647 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
648 const NAME: &'static str = "PROTECTION_MESSAGE";
649}
650
651#[cfg(test)]
652mod tests {
653 use super::*;
654
655 fn build_section(extension: u16, version: u8, body: &[u8]) -> Vec<u8> {
657 let section_length = (HEADER_LEN - SECTION_LENGTH_PREFIX + body.len() + CRC_LEN) as u16;
658 let mut v = vec![
659 TABLE_ID,
660 super::super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F),
661 (section_length & 0xFF) as u8,
662 (extension >> 8) as u8,
663 (extension & 0xFF) as u8,
664 0xC0 | (version << 1) | 0x01,
665 0x00,
666 0x00,
667 ];
668 v.extend_from_slice(body);
669 v.extend_from_slice(&[0, 0, 0, 0]);
670 v
671 }
672
673 fn auth_body() -> Vec<u8> {
675 let reference = [0x01]; let hash = [0xAA, 0xBB, 0xCC, 0xDD]; let mut hashes_loop = vec![(1u8 << 4) | (reference.len() as u8)]; hashes_loop.extend_from_slice(&reference);
679 hashes_loop.extend_from_slice(&hash);
680 let loop_len = hashes_loop.len();
681
682 let mut b = vec![
683 0x00, hash.len() as u8, 0x01, 0xF0 | ((loop_len >> 8) as u8 & 0x0F), (loop_len & 0xFF) as u8, ];
689 b.extend_from_slice(&hashes_loop);
690 b.push(2);
692 b.extend_from_slice(&[0xDE, 0xAD]);
693 b.push(3);
695 b.extend_from_slice(&[0x11, 0x22, 0x33]);
696 b.extend_from_slice(&[0x90, 0x91, 0x92, 0x93, 0x94, 0x95]);
698 b
699 }
700
701 fn cert_body() -> Vec<u8> {
703 let c0: &[u8] = &[0x30, 0x82, 0x01, 0x02];
704 let c1: &[u8] = &[0xAB, 0xCD];
705 let mut b = vec![0xF0 | 0x02]; b.push(0xF0 | ((c0.len() >> 8) as u8 & 0x0F));
707 b.push((c0.len() & 0xFF) as u8);
708 b.extend_from_slice(c0);
709 b.push(0xF0 | ((c1.len() >> 8) as u8 & 0x0F));
710 b.push((c1.len() & 0xFF) as u8);
711 b.extend_from_slice(c1);
712 b
713 }
714
715 #[test]
716 fn parse_authentication_message() {
717 let bytes = build_section(0x0042, 5, &auth_body());
718 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
719 assert_eq!(sec.table_id_extension, 0x0042);
720 assert_eq!(sec.version_number, 5);
721 assert!(sec.current_next_indicator);
722 match sec.body {
723 ProtectionMessageBody::AuthenticationMessage {
724 section_hash_algorithm_identifier,
725 section_hash_length,
726 signature_algorithm_identifier,
727 hashes,
728 extension_bytes,
729 signature_key_identifier,
730 signature,
731 ..
732 } => {
733 assert_eq!(section_hash_algorithm_identifier, 0x00);
734 assert_eq!(section_hash_length, 4);
735 assert_eq!(signature_algorithm_identifier, 0x01);
736 assert_eq!(hashes.len(), 1);
737 assert_eq!(hashes[0].reference_type, ReferenceType::SameEs);
738 assert_eq!(hashes[0].reference, &[0x01]);
739 assert_eq!(hashes[0].hash, &[0xAA, 0xBB, 0xCC, 0xDD]);
740 assert_eq!(extension_bytes, &[0xDE, 0xAD]);
741 assert_eq!(signature_key_identifier, &[0x11, 0x22, 0x33]);
742 assert_eq!(signature, &[0x90, 0x91, 0x92, 0x93, 0x94, 0x95]);
743 }
744 other => panic!("expected AuthenticationMessage, got {other:?}"),
745 }
746 }
747
748 #[test]
749 fn parse_certificate_collection() {
750 let bytes = build_section(CERTIFICATE_COLLECTION_EXTENSION, 0, &cert_body());
751 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
752 assert_eq!(sec.table_id_extension, 0x0100);
753 match sec.body {
754 ProtectionMessageBody::CertificateCollection { certificates, .. } => {
755 assert_eq!(certificates.len(), 2);
756 assert_eq!(certificates[0], &[0x30, 0x82, 0x01, 0x02]);
757 assert_eq!(certificates[1], &[0xAB, 0xCD]);
758 }
759 other => panic!("expected CertificateCollection, got {other:?}"),
760 }
761 }
762
763 #[test]
764 fn reserved_extension_kept_raw() {
765 let raw = [0x01, 0x02, 0x03, 0x04];
766 let bytes = build_section(0x0200, 0, &raw);
767 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
768 assert!(matches!(sec.body, ProtectionMessageBody::Raw(b) if b == raw));
769 }
770
771 #[test]
772 fn parse_rejects_wrong_tag() {
773 let mut bytes = build_section(0x0000, 0, &auth_body());
774 bytes[0] = 0x4D;
775 assert!(matches!(
776 ProtectionMessageSection::parse(&bytes).unwrap_err(),
777 Error::UnexpectedTableId { table_id: 0x4D, .. }
778 ));
779 }
780
781 #[test]
782 fn rejects_short_buffer() {
783 assert!(matches!(
784 ProtectionMessageSection::parse(&[0x7B, 0xB0]).unwrap_err(),
785 Error::BufferTooShort {
786 what: "ProtectionMessageSection",
787 ..
788 }
789 ));
790 }
791
792 #[test]
793 fn auth_loop_overflow_rejected() {
794 let mut body = vec![0x00, 0x04, 0x01, 0xF0, 0xFF];
795 body.extend_from_slice(&[0x00]);
796 let bytes = build_section(0x0000, 0, &body);
797 assert!(matches!(
798 ProtectionMessageSection::parse(&bytes).unwrap_err(),
799 Error::SectionLengthOverflow { .. }
800 ));
801 }
802
803 #[test]
804 fn cert_length_overflow_rejected() {
805 let body = vec![0xF0 | 0x01, 0x00, 0x10, 0x01];
806 let bytes = build_section(CERTIFICATE_COLLECTION_EXTENSION, 0, &body);
807 assert!(matches!(
808 ProtectionMessageSection::parse(&bytes).unwrap_err(),
809 Error::SectionLengthOverflow { .. }
810 ));
811 }
812
813 #[test]
814 fn round_trip_authentication_message() {
815 let bytes = build_section(0x0042, 7, &auth_body());
816 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
817 let mut buf = vec![0u8; sec.serialized_len()];
818 sec.serialize_into(&mut buf).unwrap();
819 let re = ProtectionMessageSection::parse(&buf).unwrap();
820 assert_eq!(sec, re);
821 }
822
823 #[test]
824 fn round_trip_certificate_collection() {
825 let bytes = build_section(CERTIFICATE_COLLECTION_EXTENSION, 3, &cert_body());
826 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
827 let mut buf = vec![0u8; sec.serialized_len()];
828 sec.serialize_into(&mut buf).unwrap();
829 let re = ProtectionMessageSection::parse(&buf).unwrap();
830 assert_eq!(sec, re);
831 }
832
833 #[test]
837 fn serialize_rejects_hash_length_disagreeing_with_section_hash_length() {
838 let bytes = build_section(0x0042, 5, &auth_body());
839 let mut sec = ProtectionMessageSection::parse(&bytes).unwrap();
840 match &mut sec.body {
841 ProtectionMessageBody::AuthenticationMessage { hashes, .. } => {
842 hashes[0].hash = &[0xAA, 0xBB]; }
844 other => panic!("expected AuthenticationMessage, got {other:?}"),
845 }
846 let mut buf = vec![0u8; sec.serialized_len()];
847 assert!(matches!(
848 sec.serialize_into(&mut buf).unwrap_err(),
849 Error::ValueOutOfRange { .. }
850 ));
851 }
852
853 #[test]
857 fn parse_certificate_collection_rejects_trailing_bytes() {
858 let mut body = cert_body();
859 body.push(0xFF); let bytes = build_section(CERTIFICATE_COLLECTION_EXTENSION, 0, &body);
861 assert!(matches!(
862 ProtectionMessageSection::parse(&bytes).unwrap_err(),
863 Error::BufferTooShort { .. }
864 ));
865 }
866
867 #[test]
868 fn round_trip_raw_reserved() {
869 let bytes = build_section(0xABCD, 1, &[0xDE, 0xAD, 0xBE, 0xEF]);
870 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
871 let mut buf = vec![0u8; sec.serialized_len()];
872 sec.serialize_into(&mut buf).unwrap();
873 let re = ProtectionMessageSection::parse(&buf).unwrap();
874 assert_eq!(sec, re);
875 }
876
877 #[test]
878 fn table_trait_constants() {
879 assert_eq!(TABLE_ID, 0x7B);
880 }
881
882 #[test]
883 #[cfg(feature = "serde")]
884 fn serde_json_round_trip() {
885 let bytes = build_section(0x0042, 5, &auth_body());
886 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
887 let j = serde_json::to_string(&sec).unwrap();
888 let reparsed = ProtectionMessageSection::parse(&bytes).unwrap();
889 assert_eq!(serde_json::to_string(&reparsed).unwrap(), j);
890 assert!(j.contains("\"signature_algorithm_identifier\":1"));
891 }
892
893 #[test]
894 fn parse_rejects_zero_section_length() {
895 let mut buf = vec![0u8; 64];
896 buf[0] = TABLE_ID;
897 buf[1] = 0xF0;
898 buf[2] = 0x00;
899 for b in &mut buf[3..] {
900 *b = 0xFF;
901 }
902 assert!(matches!(
903 ProtectionMessageSection::parse(&buf).unwrap_err(),
904 Error::SectionLengthOverflow { .. }
905 ));
906 }
907
908 #[test]
912 fn serialize_rejects_raw_body_exceeding_section_length() {
913 let raw = vec![0xAAu8; 4087];
914 let sec =
915 ProtectionMessageSection::new(0x0200, 0, true, 0, 0, ProtectionMessageBody::Raw(&raw));
916 let mut buf = vec![0u8; sec.serialized_len()];
917 assert!(matches!(
918 sec.serialize_into(&mut buf).unwrap_err(),
919 Error::FieldOverflow(_)
920 ));
921 }
922
923 #[test]
924 fn serialize_accepts_raw_body_at_section_length_boundary() {
925 let raw = vec![0xAAu8; 4086];
926 let sec =
927 ProtectionMessageSection::new(0x0200, 0, true, 0, 0, ProtectionMessageBody::Raw(&raw));
928 let mut buf = vec![0u8; sec.serialized_len()];
929 sec.serialize_into(&mut buf).expect("boundary fits");
930 let reparsed = ProtectionMessageSection::parse(&buf).expect("reparse");
931 assert_eq!(sec, reparsed);
932 }
933
934 #[test]
935 fn reference_type_full_range_round_trip() {
936 for v in 0u8..=0x0F {
937 let rt = ReferenceType::from_u8(v);
938 assert_eq!(
939 rt.to_u8(),
940 v,
941 "ReferenceType round-trip failed for {v:#04x}"
942 );
943 }
944 }
945}