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