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