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