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