1use crate::error::{Error, Result};
26use dvb_common::{Parse, Serialize};
27
28pub const TABLE_ID: u8 = 0x7B;
30pub const PID: u16 = 0x0000;
34
35pub const AUTH_EXTENSION_FIRST: u16 = 0x0000;
37pub const AUTH_EXTENSION_LAST: u16 = 0x00FF;
39pub const CERTIFICATE_COLLECTION_EXTENSION: u16 = 0x0100;
41
42const HEADER_LEN: usize = 8;
45const SECTION_LENGTH_PREFIX: usize = 3;
47const CRC_LEN: usize = 4;
49
50const AUTH_FIXED_PREFIX: usize = 5;
54
55#[derive(Debug, Clone, PartialEq, Eq)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize))]
62#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
63pub struct SectionHashEntry<'a> {
64 pub reference_type: u8,
66 pub reference: &'a [u8],
68 pub hash: &'a [u8],
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize))]
75#[non_exhaustive]
76pub enum ProtectionMessageBody<'a> {
77 AuthenticationMessage {
79 section_hash_algorithm_identifier: u8,
81 section_hash_length: u8,
83 signature_algorithm_identifier: u8,
85 hashes: Vec<SectionHashEntry<'a>>,
87 extension_bytes: &'a [u8],
89 signature_key_identifier: &'a [u8],
91 signature: &'a [u8],
93 },
94 CertificateCollection {
96 certificates: Vec<&'a [u8]>,
98 },
99 Raw(&'a [u8]),
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize))]
109#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
110pub struct ProtectionMessageSection<'a> {
111 pub table_id_extension: u16,
114 pub version_number: u8,
116 pub current_next_indicator: bool,
118 pub section_number: u8,
120 pub last_section_number: u8,
122 pub body: ProtectionMessageBody<'a>,
124}
125
126impl<'a> Parse<'a> for ProtectionMessageSection<'a> {
127 type Error = crate::error::Error;
128 fn parse(bytes: &'a [u8]) -> Result<Self> {
129 let min_len = HEADER_LEN + CRC_LEN;
130 if bytes.len() < min_len {
131 return Err(Error::BufferTooShort {
132 need: min_len,
133 have: bytes.len(),
134 what: "ProtectionMessageSection",
135 });
136 }
137 if bytes[0] != TABLE_ID {
138 return Err(Error::UnexpectedTableId {
139 table_id: bytes[0],
140 what: "ProtectionMessageSection",
141 expected: &[TABLE_ID],
142 });
143 }
144 let section_length = (((bytes[1] & 0x0F) as usize) << 8) | bytes[2] as usize;
145 let total = super::check_section_length(
146 bytes.len(),
147 SECTION_LENGTH_PREFIX,
148 section_length,
149 HEADER_LEN + CRC_LEN,
150 )?;
151
152 let table_id_extension = u16::from_be_bytes([bytes[3], bytes[4]]);
153 let version_number = (bytes[5] >> 1) & 0x1F;
154 let current_next_indicator = bytes[5] & 0x01 != 0;
155 let section_number = bytes[6];
156 let last_section_number = bytes[7];
157
158 let body_bytes = &bytes[HEADER_LEN..total - CRC_LEN];
159 let body = match table_id_extension {
160 AUTH_EXTENSION_FIRST..=AUTH_EXTENSION_LAST => parse_authentication_message(body_bytes)?,
161 CERTIFICATE_COLLECTION_EXTENSION => parse_certificate_collection(body_bytes)?,
162 _ => ProtectionMessageBody::Raw(body_bytes),
163 };
164
165 Ok(ProtectionMessageSection {
166 table_id_extension,
167 version_number,
168 current_next_indicator,
169 section_number,
170 last_section_number,
171 body,
172 })
173 }
174}
175
176fn parse_authentication_message(body: &[u8]) -> Result<ProtectionMessageBody<'_>> {
178 if body.len() < AUTH_FIXED_PREFIX {
179 return Err(Error::BufferTooShort {
180 need: AUTH_FIXED_PREFIX,
181 have: body.len(),
182 what: "ProtectionMessageSection::AuthenticationMessage",
183 });
184 }
185 let section_hash_algorithm_identifier = body[0];
186 let section_hash_length = body[1];
187 let signature_algorithm_identifier = body[2];
188 let section_hashes_loop_length = (((body[3] & 0x0F) as usize) << 8) | body[4] as usize;
190
191 let loop_start = AUTH_FIXED_PREFIX;
192 let loop_end = loop_start + section_hashes_loop_length;
193 if loop_end > body.len() {
194 return Err(Error::SectionLengthOverflow {
195 declared: section_hashes_loop_length,
196 available: body.len() - loop_start,
197 });
198 }
199
200 let hash_len = section_hash_length as usize;
201 let mut hashes = Vec::new();
202 let mut pos = loop_start;
203 while pos < loop_end {
204 let lead = body[pos];
206 let reference_type = lead >> 4;
207 let reference_length = (lead & 0x0F) as usize;
208 let ref_start = pos + 1;
209 let ref_end = ref_start + reference_length;
210 let hash_end = ref_end + hash_len;
211 if hash_end > loop_end {
212 return Err(Error::SectionLengthOverflow {
213 declared: reference_length + hash_len,
214 available: loop_end - ref_start,
215 });
216 }
217 hashes.push(SectionHashEntry {
218 reference_type,
219 reference: &body[ref_start..ref_end],
220 hash: &body[ref_end..hash_end],
221 });
222 pos = hash_end;
223 }
224
225 if loop_end >= body.len() {
227 return Err(Error::BufferTooShort {
228 need: loop_end + 1,
229 have: body.len(),
230 what: "ProtectionMessageSection::extension_bytes_length",
231 });
232 }
233 let extension_bytes_length = body[loop_end] as usize;
234 let ext_start = loop_end + 1;
235 let ext_end = ext_start + extension_bytes_length;
236 if ext_end > body.len() {
237 return Err(Error::SectionLengthOverflow {
238 declared: extension_bytes_length,
239 available: body.len() - ext_start,
240 });
241 }
242
243 if ext_end >= body.len() {
245 return Err(Error::BufferTooShort {
246 need: ext_end + 1,
247 have: body.len(),
248 what: "ProtectionMessageSection::signature_key_identifier_length",
249 });
250 }
251 let key_id_length = body[ext_end] as usize;
252 let key_start = ext_end + 1;
253 let key_end = key_start + key_id_length;
254 if key_end > body.len() {
255 return Err(Error::SectionLengthOverflow {
256 declared: key_id_length,
257 available: body.len() - key_start,
258 });
259 }
260
261 let signature = &body[key_end..];
263
264 Ok(ProtectionMessageBody::AuthenticationMessage {
265 section_hash_algorithm_identifier,
266 section_hash_length,
267 signature_algorithm_identifier,
268 hashes,
269 extension_bytes: &body[ext_start..ext_end],
270 signature_key_identifier: &body[key_start..key_end],
271 signature,
272 })
273}
274
275fn parse_certificate_collection(body: &[u8]) -> Result<ProtectionMessageBody<'_>> {
277 if body.is_empty() {
278 return Err(Error::BufferTooShort {
279 need: 1,
280 have: 0,
281 what: "ProtectionMessageSection::CertificateCollection",
282 });
283 }
284 let certificate_count = (body[0] & 0x0F) as usize;
286 let mut certificates = Vec::with_capacity(certificate_count);
287 let mut pos = 1;
288 for _ in 0..certificate_count {
289 if pos + 2 > body.len() {
290 return Err(Error::BufferTooShort {
291 need: pos + 2,
292 have: body.len(),
293 what: "ProtectionMessageSection::certificate_length",
294 });
295 }
296 let certificate_length = (((body[pos] & 0x0F) as usize) << 8) | body[pos + 1] as usize;
298 let cert_start = pos + 2;
299 let cert_end = cert_start + certificate_length;
300 if cert_end > body.len() {
301 return Err(Error::SectionLengthOverflow {
302 declared: certificate_length,
303 available: body.len() - cert_start,
304 });
305 }
306 certificates.push(&body[cert_start..cert_end]);
307 pos = cert_end;
308 }
309 Ok(ProtectionMessageBody::CertificateCollection { certificates })
310}
311
312impl ProtectionMessageBody<'_> {
313 fn body_len(&self) -> usize {
315 match self {
316 ProtectionMessageBody::AuthenticationMessage {
317 hashes,
318 extension_bytes,
319 signature_key_identifier,
320 signature,
321 ..
322 } => {
323 let loop_bytes: usize = hashes
324 .iter()
325 .map(|h| 1 + h.reference.len() + h.hash.len())
326 .sum();
327 AUTH_FIXED_PREFIX
328 + loop_bytes
329 + 1
330 + extension_bytes.len()
331 + 1
332 + signature_key_identifier.len()
333 + signature.len()
334 }
335 ProtectionMessageBody::CertificateCollection { certificates } => {
336 1 + certificates.iter().map(|c| 2 + c.len()).sum::<usize>()
337 }
338 ProtectionMessageBody::Raw(raw) => raw.len(),
339 }
340 }
341
342 fn write_into(&self, buf: &mut [u8]) -> Result<usize> {
346 match self {
347 ProtectionMessageBody::AuthenticationMessage {
348 section_hash_algorithm_identifier,
349 section_hash_length,
350 signature_algorithm_identifier,
351 hashes,
352 extension_bytes,
353 signature_key_identifier,
354 signature,
355 } => {
356 buf[0] = *section_hash_algorithm_identifier;
357 buf[1] = *section_hash_length;
358 buf[2] = *signature_algorithm_identifier;
359 let loop_bytes: usize = hashes
360 .iter()
361 .map(|h| 1 + h.reference.len() + h.hash.len())
362 .sum();
363 if loop_bytes > 0x0FFF {
364 return Err(Error::SectionLengthOverflow {
365 declared: loop_bytes,
366 available: 0x0FFF,
367 });
368 }
369 if extension_bytes.len() > u8::MAX as usize {
370 return Err(Error::SectionLengthOverflow {
371 declared: extension_bytes.len(),
372 available: u8::MAX as usize,
373 });
374 }
375 if signature_key_identifier.len() > u8::MAX as usize {
376 return Err(Error::SectionLengthOverflow {
377 declared: signature_key_identifier.len(),
378 available: u8::MAX as usize,
379 });
380 }
381 buf[3] = 0xF0 | ((loop_bytes >> 8) as u8 & 0x0F);
383 buf[4] = (loop_bytes & 0xFF) as u8;
384 let mut pos = AUTH_FIXED_PREFIX;
385 for h in hashes {
386 if h.reference.len() > 0x0F {
388 return Err(Error::SectionLengthOverflow {
389 declared: h.reference.len(),
390 available: 0x0F,
391 });
392 }
393 buf[pos] = (h.reference_type << 4) | (h.reference.len() as u8 & 0x0F);
394 pos += 1;
395 buf[pos..pos + h.reference.len()].copy_from_slice(h.reference);
396 pos += h.reference.len();
397 buf[pos..pos + h.hash.len()].copy_from_slice(h.hash);
398 pos += h.hash.len();
399 }
400 buf[pos] = extension_bytes.len() as u8;
401 pos += 1;
402 buf[pos..pos + extension_bytes.len()].copy_from_slice(extension_bytes);
403 pos += extension_bytes.len();
404 buf[pos] = signature_key_identifier.len() as u8;
405 pos += 1;
406 buf[pos..pos + signature_key_identifier.len()]
407 .copy_from_slice(signature_key_identifier);
408 pos += signature_key_identifier.len();
409 buf[pos..pos + signature.len()].copy_from_slice(signature);
410 pos += signature.len();
411 Ok(pos)
412 }
413 ProtectionMessageBody::CertificateCollection { certificates } => {
414 if certificates.len() > 0x0F {
416 return Err(Error::SectionLengthOverflow {
417 declared: certificates.len(),
418 available: 0x0F,
419 });
420 }
421 buf[0] = 0xF0 | (certificates.len() as u8 & 0x0F);
423 let mut pos = 1;
424 for c in certificates {
425 if c.len() > 0x0FFF {
427 return Err(Error::SectionLengthOverflow {
428 declared: c.len(),
429 available: 0x0FFF,
430 });
431 }
432 buf[pos] = 0xF0 | ((c.len() >> 8) as u8 & 0x0F);
434 buf[pos + 1] = (c.len() & 0xFF) as u8;
435 pos += 2;
436 buf[pos..pos + c.len()].copy_from_slice(c);
437 pos += c.len();
438 }
439 Ok(pos)
440 }
441 ProtectionMessageBody::Raw(raw) => {
442 buf[..raw.len()].copy_from_slice(raw);
443 Ok(raw.len())
444 }
445 }
446 }
447}
448
449impl Serialize for ProtectionMessageSection<'_> {
450 type Error = crate::error::Error;
451 fn serialized_len(&self) -> usize {
452 HEADER_LEN + self.body.body_len() + CRC_LEN
453 }
454 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
455 let len = self.serialized_len();
456 if buf.len() < len {
457 return Err(Error::OutputBufferTooSmall {
458 need: len,
459 have: buf.len(),
460 });
461 }
462 let section_length = (len - SECTION_LENGTH_PREFIX) as u16;
463 buf[0] = TABLE_ID;
464 buf[1] = super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F);
465 buf[2] = (section_length & 0xFF) as u8;
466 buf[3..5].copy_from_slice(&self.table_id_extension.to_be_bytes());
467 buf[5] = 0xC0 | ((self.version_number & 0x1F) << 1) | u8::from(self.current_next_indicator);
469 buf[6] = self.section_number;
470 buf[7] = self.last_section_number;
471 let body_written = self.body.write_into(&mut buf[HEADER_LEN..])?;
472 let body_end = HEADER_LEN + body_written;
473 let crc = dvb_common::crc32_mpeg2::compute(&buf[..body_end]);
474 buf[body_end..len].copy_from_slice(&crc.to_be_bytes());
475 Ok(len)
476 }
477}
478impl<'a> crate::traits::TableDef<'a> for ProtectionMessageSection<'a> {
479 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
480 const NAME: &'static str = "PROTECTION_MESSAGE";
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 fn build_section(extension: u16, version: u8, body: &[u8]) -> Vec<u8> {
489 let section_length = (HEADER_LEN - SECTION_LENGTH_PREFIX + body.len() + CRC_LEN) as u16;
490 let mut v = vec![
491 TABLE_ID,
492 super::super::SECTION_B1_FLAGS_DVB | ((section_length >> 8) as u8 & 0x0F),
493 (section_length & 0xFF) as u8,
494 (extension >> 8) as u8,
495 (extension & 0xFF) as u8,
496 0xC0 | (version << 1) | 0x01,
497 0x00,
498 0x00,
499 ];
500 v.extend_from_slice(body);
501 v.extend_from_slice(&[0, 0, 0, 0]);
502 v
503 }
504
505 fn auth_body() -> Vec<u8> {
507 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);
511 hashes_loop.extend_from_slice(&hash);
512 let loop_len = hashes_loop.len();
513
514 let mut b = vec![
515 0x00, hash.len() as u8, 0x01, 0xF0 | ((loop_len >> 8) as u8 & 0x0F), (loop_len & 0xFF) as u8, ];
521 b.extend_from_slice(&hashes_loop);
522 b.push(2);
524 b.extend_from_slice(&[0xDE, 0xAD]);
525 b.push(3);
527 b.extend_from_slice(&[0x11, 0x22, 0x33]);
528 b.extend_from_slice(&[0x90, 0x91, 0x92, 0x93, 0x94, 0x95]);
530 b
531 }
532
533 fn cert_body() -> Vec<u8> {
535 let c0: &[u8] = &[0x30, 0x82, 0x01, 0x02];
536 let c1: &[u8] = &[0xAB, 0xCD];
537 let mut b = vec![0xF0 | 0x02]; b.push(0xF0 | ((c0.len() >> 8) as u8 & 0x0F));
539 b.push((c0.len() & 0xFF) as u8);
540 b.extend_from_slice(c0);
541 b.push(0xF0 | ((c1.len() >> 8) as u8 & 0x0F));
542 b.push((c1.len() & 0xFF) as u8);
543 b.extend_from_slice(c1);
544 b
545 }
546
547 #[test]
548 fn parse_authentication_message() {
549 let bytes = build_section(0x0042, 5, &auth_body());
550 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
551 assert_eq!(sec.table_id_extension, 0x0042);
552 assert_eq!(sec.version_number, 5);
553 assert!(sec.current_next_indicator);
554 match sec.body {
555 ProtectionMessageBody::AuthenticationMessage {
556 section_hash_algorithm_identifier,
557 section_hash_length,
558 signature_algorithm_identifier,
559 hashes,
560 extension_bytes,
561 signature_key_identifier,
562 signature,
563 } => {
564 assert_eq!(section_hash_algorithm_identifier, 0x00);
565 assert_eq!(section_hash_length, 4);
566 assert_eq!(signature_algorithm_identifier, 0x01);
567 assert_eq!(hashes.len(), 1);
568 assert_eq!(hashes[0].reference_type, 1);
569 assert_eq!(hashes[0].reference, &[0x01]);
570 assert_eq!(hashes[0].hash, &[0xAA, 0xBB, 0xCC, 0xDD]);
571 assert_eq!(extension_bytes, &[0xDE, 0xAD]);
572 assert_eq!(signature_key_identifier, &[0x11, 0x22, 0x33]);
573 assert_eq!(signature, &[0x90, 0x91, 0x92, 0x93, 0x94, 0x95]);
574 }
575 other => panic!("expected AuthenticationMessage, got {other:?}"),
576 }
577 }
578
579 #[test]
580 fn parse_certificate_collection() {
581 let bytes = build_section(CERTIFICATE_COLLECTION_EXTENSION, 0, &cert_body());
582 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
583 assert_eq!(sec.table_id_extension, 0x0100);
584 match sec.body {
585 ProtectionMessageBody::CertificateCollection { certificates } => {
586 assert_eq!(certificates.len(), 2);
587 assert_eq!(certificates[0], &[0x30, 0x82, 0x01, 0x02]);
588 assert_eq!(certificates[1], &[0xAB, 0xCD]);
589 }
590 other => panic!("expected CertificateCollection, got {other:?}"),
591 }
592 }
593
594 #[test]
595 fn reserved_extension_kept_raw() {
596 let raw = [0x01, 0x02, 0x03, 0x04];
597 let bytes = build_section(0x0200, 0, &raw);
598 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
599 assert!(matches!(sec.body, ProtectionMessageBody::Raw(b) if b == raw));
600 }
601
602 #[test]
603 fn parse_rejects_wrong_tag() {
604 let mut bytes = build_section(0x0000, 0, &auth_body());
605 bytes[0] = 0x4D;
606 assert!(matches!(
607 ProtectionMessageSection::parse(&bytes).unwrap_err(),
608 Error::UnexpectedTableId { table_id: 0x4D, .. }
609 ));
610 }
611
612 #[test]
613 fn rejects_short_buffer() {
614 assert!(matches!(
615 ProtectionMessageSection::parse(&[0x7B, 0xB0]).unwrap_err(),
616 Error::BufferTooShort {
617 what: "ProtectionMessageSection",
618 ..
619 }
620 ));
621 }
622
623 #[test]
624 fn auth_loop_overflow_rejected() {
625 let mut body = vec![0x00, 0x04, 0x01, 0xF0, 0xFF]; body.extend_from_slice(&[0x00]); let bytes = build_section(0x0000, 0, &body);
629 assert!(matches!(
630 ProtectionMessageSection::parse(&bytes).unwrap_err(),
631 Error::SectionLengthOverflow { .. }
632 ));
633 }
634
635 #[test]
636 fn cert_length_overflow_rejected() {
637 let body = vec![0xF0 | 0x01, 0x00, 0x10, 0x01]; let bytes = build_section(CERTIFICATE_COLLECTION_EXTENSION, 0, &body);
640 assert!(matches!(
641 ProtectionMessageSection::parse(&bytes).unwrap_err(),
642 Error::SectionLengthOverflow { .. }
643 ));
644 }
645
646 #[test]
647 fn round_trip_authentication_message() {
648 let bytes = build_section(0x0042, 7, &auth_body());
649 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
650 let mut buf = vec![0u8; sec.serialized_len()];
651 sec.serialize_into(&mut buf).unwrap();
652 let re = ProtectionMessageSection::parse(&buf).unwrap();
653 assert_eq!(sec, re);
654 }
655
656 #[test]
657 fn round_trip_certificate_collection() {
658 let bytes = build_section(CERTIFICATE_COLLECTION_EXTENSION, 3, &cert_body());
659 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
660 let mut buf = vec![0u8; sec.serialized_len()];
661 sec.serialize_into(&mut buf).unwrap();
662 let re = ProtectionMessageSection::parse(&buf).unwrap();
663 assert_eq!(sec, re);
664 }
665
666 #[test]
667 fn round_trip_raw_reserved() {
668 let bytes = build_section(0xABCD, 1, &[0xDE, 0xAD, 0xBE, 0xEF]);
669 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
670 let mut buf = vec![0u8; sec.serialized_len()];
671 sec.serialize_into(&mut buf).unwrap();
672 let re = ProtectionMessageSection::parse(&buf).unwrap();
673 assert_eq!(sec, re);
674 }
675
676 #[test]
677 fn table_trait_constants() {
678 assert_eq!(TABLE_ID, 0x7B);
679 assert_eq!(PID, 0x0000);
680 }
681
682 #[test]
683 #[cfg(feature = "serde")]
684 fn serde_json_round_trip() {
685 let bytes = build_section(0x0042, 5, &auth_body());
686 let sec = ProtectionMessageSection::parse(&bytes).unwrap();
687 let j = serde_json::to_string(&sec).unwrap();
688 let reparsed = ProtectionMessageSection::parse(&bytes).unwrap();
695 assert_eq!(serde_json::to_string(&reparsed).unwrap(), j);
696 assert!(j.contains("\"signature_algorithm_identifier\":1"));
697 }
698
699 #[test]
700 fn parse_rejects_zero_section_length() {
701 let mut buf = vec![0u8; 64];
702 buf[0] = TABLE_ID;
703 buf[1] = 0xF0;
704 buf[2] = 0x00;
705 for b in &mut buf[3..] {
706 *b = 0xFF;
707 }
708 assert!(matches!(
709 ProtectionMessageSection::parse(&buf).unwrap_err(),
710 Error::SectionLengthOverflow { .. }
711 ));
712 }
713}