1use crate::error::{Error, Result};
20use crate::objects;
21use crate::tag::ApduTag;
22use alloc::vec::Vec;
23use broadcast_common::{Parse, Serialize};
24
25pub mod tag {
27 use crate::tag::ApduTag;
28 pub const SD_INFO_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x98, 0x00);
30 pub const SD_INFO_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x98, 0x01);
32 pub const SD_START: ApduTag = ApduTag::from_bytes(0x9F, 0x98, 0x02);
34 pub const SD_START_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x98, 0x03);
36 pub const SD_UPDATE: ApduTag = ApduTag::from_bytes(0x9F, 0x98, 0x04);
38 pub const SD_UPDATE_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x98, 0x05);
40}
41
42pub const DRM_UUID_LEN: usize = 16;
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize))]
50pub struct SdInfoReq;
51
52impl<'a> Parse<'a> for SdInfoReq {
53 type Error = Error;
54 fn parse(bytes: &'a [u8]) -> Result<Self> {
55 objects::parse_empty_apdu(bytes, tag::SD_INFO_REQ, "sd_info_req")?;
56 Ok(Self)
57 }
58}
59impl Serialize for SdInfoReq {
60 type Error = Error;
61 fn serialized_len(&self) -> usize {
62 objects::empty_apdu_len()
63 }
64 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
65 objects::serialize_empty_apdu(tag::SD_INFO_REQ, buf)
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize))]
75pub struct SdInfoReply {
76 pub drm_system_ids: Vec<u16>,
79 pub drm_uuids: Vec<[u8; DRM_UUID_LEN]>,
81}
82
83impl<'a> Parse<'a> for SdInfoReply {
84 type Error = Error;
85 fn parse(bytes: &'a [u8]) -> Result<Self> {
86 let body = objects::parse_apdu_header(bytes, tag::SD_INFO_REPLY, "sd_info_reply")?;
87 let mut r = Reader::new(body, "sd_info_reply");
88 let n_sys = r.u8()? as usize;
89 let mut drm_system_ids = Vec::with_capacity(n_sys);
90 for _ in 0..n_sys {
91 drm_system_ids.push(r.u16()?);
92 }
93 let n_uuid = r.u8()? as usize;
94 let mut drm_uuids = Vec::with_capacity(n_uuid);
95 for _ in 0..n_uuid {
96 drm_uuids.push(r.uuid()?);
97 }
98 Ok(Self {
99 drm_system_ids,
100 drm_uuids,
101 })
102 }
103}
104impl Serialize for SdInfoReply {
105 type Error = Error;
106 fn serialized_len(&self) -> usize {
107 objects::apdu_len(
108 1 + self.drm_system_ids.len() * 2 + 1 + self.drm_uuids.len() * DRM_UUID_LEN,
109 )
110 }
111 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
112 let body_len = 1 + self.drm_system_ids.len() * 2 + 1 + self.drm_uuids.len() * DRM_UUID_LEN;
113 let pos = objects::write_apdu_header(tag::SD_INFO_REPLY, body_len, buf)?;
114 let mut w = Writer::new(&mut buf[pos..]);
115 w.u8(self.drm_system_ids.len() as u8);
116 for id in &self.drm_system_ids {
117 w.u16(*id);
118 }
119 w.u8(self.drm_uuids.len() as u8);
120 for uuid in &self.drm_uuids {
121 w.uuid(uuid);
122 }
123 Ok(pos + body_len)
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq)]
132#[cfg_attr(feature = "serde", derive(serde::Serialize))]
133pub struct DrmMetadataRecord<'a> {
134 pub drm_metadata_source: u8,
136 pub drm_system_id: u16,
139 pub drm_uuid: [u8; DRM_UUID_LEN],
141 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
143 pub drm_metadata: &'a [u8],
144}
145
146const METADATA_FIXED: usize = 1 + 2 + DRM_UUID_LEN + 2;
148
149impl<'a> DrmMetadataRecord<'a> {
150 fn parse_from(r: &mut Reader<'a>) -> Result<Self> {
151 let drm_metadata_source = r.u8()?;
152 let drm_system_id = r.u16()?;
153 let drm_uuid = r.uuid()?;
154 let len = r.u16()? as usize;
155 let drm_metadata = r.take(len)?;
156 Ok(Self {
157 drm_metadata_source,
158 drm_system_id,
159 drm_uuid,
160 drm_metadata,
161 })
162 }
163 fn body_len(&self) -> usize {
164 METADATA_FIXED + self.drm_metadata.len()
165 }
166 fn write_into(&self, w: &mut Writer<'_>) {
167 w.u8(self.drm_metadata_source);
168 w.u16(self.drm_system_id);
169 w.uuid(&self.drm_uuid);
170 w.u16(self.drm_metadata.len() as u16);
171 w.bytes(self.drm_metadata);
172 }
173}
174
175#[derive(Debug, Clone, PartialEq, Eq)]
178#[cfg_attr(feature = "serde", derive(serde::Serialize))]
179pub struct SampleTrack<'a> {
180 pub track_pid: u16,
183 #[cfg_attr(feature = "serde", serde(borrow))]
185 pub records: Vec<DrmMetadataRecord<'a>>,
186}
187
188const TRACK_FIXED: usize = 2 + 1;
190const TRACK_PID_MASK: u16 = 0x1FFF;
191
192#[derive(Debug, Clone, PartialEq, Eq)]
195#[cfg_attr(feature = "serde", derive(serde::Serialize))]
196#[non_exhaustive]
197pub enum SamplePayload<'a> {
198 Ts(#[cfg_attr(feature = "serde", serde(borrow))] Vec<DrmMetadataRecord<'a>>),
201 Tracks(#[cfg_attr(feature = "serde", serde(borrow))] Vec<SampleTrack<'a>>),
204}
205
206impl<'a> SamplePayload<'a> {
207 #[must_use]
209 pub fn ts_flag(&self) -> bool {
210 matches!(self, Self::Ts(_))
211 }
212
213 fn parse_from(r: &mut Reader<'a>, ts_flag: bool) -> Result<Self> {
214 if ts_flag {
215 let n = r.u8()? as usize;
216 let mut records = Vec::with_capacity(n);
217 for _ in 0..n {
218 records.push(DrmMetadataRecord::parse_from(r)?);
219 }
220 Ok(Self::Ts(records))
221 } else {
222 let n = r.u8()? as usize;
223 let mut tracks = Vec::with_capacity(n);
224 for _ in 0..n {
225 let pid_word = r.u16()?;
226 let track_pid = pid_word & TRACK_PID_MASK;
227 let m = r.u8()? as usize;
228 let mut records = Vec::with_capacity(m);
229 for _ in 0..m {
230 records.push(DrmMetadataRecord::parse_from(r)?);
231 }
232 tracks.push(SampleTrack { track_pid, records });
233 }
234 Ok(Self::Tracks(tracks))
235 }
236 }
237
238 fn body_len(&self) -> usize {
239 match self {
240 Self::Ts(records) => {
241 1 + records
242 .iter()
243 .map(DrmMetadataRecord::body_len)
244 .sum::<usize>()
245 }
246 Self::Tracks(tracks) => {
247 1 + tracks
248 .iter()
249 .map(|t| {
250 TRACK_FIXED
251 + t.records
252 .iter()
253 .map(DrmMetadataRecord::body_len)
254 .sum::<usize>()
255 })
256 .sum::<usize>()
257 }
258 }
259 }
260
261 fn write_into(&self, w: &mut Writer<'_>) {
262 match self {
263 Self::Ts(records) => {
264 w.u8(records.len() as u8);
265 for rec in records {
266 rec.write_into(w);
267 }
268 }
269 Self::Tracks(tracks) => {
270 w.u8(tracks.len() as u8);
271 for track in tracks {
272 w.u16(track.track_pid & TRACK_PID_MASK);
274 w.u8(track.records.len() as u8);
275 for rec in &track.records {
276 rec.write_into(w);
277 }
278 }
279 }
280 }
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
288#[cfg_attr(feature = "serde", derive(serde::Serialize))]
289pub struct SdStart<'a> {
290 pub lts_id: u8,
292 pub program_number: u16,
294 #[cfg_attr(feature = "serde", serde(borrow))]
296 pub payload: SamplePayload<'a>,
297}
298
299const SD_START_FIXED: usize = 1 + 2 + 1;
301const TS_FLAG_BIT: u8 = 0x01;
303
304impl<'a> Parse<'a> for SdStart<'a> {
305 type Error = Error;
306 fn parse(bytes: &'a [u8]) -> Result<Self> {
307 let body = objects::parse_apdu_header(bytes, tag::SD_START, "sd_start")?;
308 let mut r = Reader::new(body, "sd_start");
309 let lts_id = r.u8()?;
310 let program_number = r.u16()?;
311 let ts_flag = r.u8()? & TS_FLAG_BIT != 0;
312 let payload = SamplePayload::parse_from(&mut r, ts_flag)?;
313 Ok(Self {
314 lts_id,
315 program_number,
316 payload,
317 })
318 }
319}
320impl Serialize for SdStart<'_> {
321 type Error = Error;
322 fn serialized_len(&self) -> usize {
323 objects::apdu_len(SD_START_FIXED + self.payload.body_len())
324 }
325 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
326 let body_len = SD_START_FIXED + self.payload.body_len();
327 let pos = objects::write_apdu_header(tag::SD_START, body_len, buf)?;
328 let mut w = Writer::new(&mut buf[pos..]);
329 w.u8(self.lts_id);
330 w.u16(self.program_number);
331 w.u8(if self.payload.ts_flag() {
332 TS_FLAG_BIT
333 } else {
334 0
335 });
336 self.payload.write_into(&mut w);
337 Ok(pos + body_len)
338 }
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq)]
345#[cfg_attr(feature = "serde", derive(serde::Serialize))]
346#[non_exhaustive]
347pub enum TransmissionStatus {
348 ReadyToReceive,
350 CicamBusy,
352 OtherReason,
354 Reserved(u8),
356}
357impl TransmissionStatus {
358 #[must_use]
360 pub fn from_u8(v: u8) -> Self {
361 match v {
362 0x00 => Self::ReadyToReceive,
363 0x01 => Self::CicamBusy,
364 0x02 => Self::OtherReason,
365 other => Self::Reserved(other),
366 }
367 }
368 #[must_use]
370 pub const fn to_u8(self) -> u8 {
371 match self {
372 Self::ReadyToReceive => 0x00,
373 Self::CicamBusy => 0x01,
374 Self::OtherReason => 0x02,
375 Self::Reserved(v) => v,
376 }
377 }
378 #[must_use]
380 pub fn name(&self) -> &'static str {
381 match self {
382 Self::ReadyToReceive => "ready_to_receive",
383 Self::CicamBusy => "error_cicam_busy",
384 Self::OtherReason => "error_other_reason",
385 Self::Reserved(_) => "reserved",
386 }
387 }
388}
389broadcast_common::impl_spec_display!(TransmissionStatus, Reserved);
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq)]
393#[cfg_attr(feature = "serde", derive(serde::Serialize))]
394#[non_exhaustive]
395pub enum DrmStatus {
396 DecryptionPossible,
398 Undetermined,
400 NoEntitlement,
402 Reserved(u8),
404}
405impl DrmStatus {
406 #[must_use]
408 pub fn from_u8(v: u8) -> Self {
409 match v {
410 0x00 => Self::DecryptionPossible,
411 0x01 => Self::Undetermined,
412 0x02 => Self::NoEntitlement,
413 other => Self::Reserved(other),
414 }
415 }
416 #[must_use]
418 pub const fn to_u8(self) -> u8 {
419 match self {
420 Self::DecryptionPossible => 0x00,
421 Self::Undetermined => 0x01,
422 Self::NoEntitlement => 0x02,
423 Self::Reserved(v) => v,
424 }
425 }
426 #[must_use]
428 pub fn name(&self) -> &'static str {
429 match self {
430 Self::DecryptionPossible => "decryption_possible",
431 Self::Undetermined => "status_undetermined",
432 Self::NoEntitlement => "error_no_entitlement",
433 Self::Reserved(_) => "reserved",
434 }
435 }
436}
437broadcast_common::impl_spec_display!(DrmStatus, Reserved);
438
439#[derive(Debug, Clone, Copy, PartialEq, Eq)]
441#[cfg_attr(feature = "serde", derive(serde::Serialize))]
442pub struct SdStartReply {
443 pub lts_id: u8,
445 pub transmission_status: TransmissionStatus,
447 pub drm_status: DrmStatus,
449 pub drm_system_id: u16,
451 pub drm_uuid: [u8; DRM_UUID_LEN],
453 pub buffer_size: u16,
455 pub data_block_size: u16,
457}
458
459const SD_START_REPLY_BODY: usize = 1 + 1 + 1 + 2 + DRM_UUID_LEN + 2 + 2;
462
463impl<'a> Parse<'a> for SdStartReply {
464 type Error = Error;
465 fn parse(bytes: &'a [u8]) -> Result<Self> {
466 let body = objects::parse_apdu_header(bytes, tag::SD_START_REPLY, "sd_start_reply")?;
467 if body.len() < SD_START_REPLY_BODY {
468 return Err(Error::BufferTooShort {
469 need: SD_START_REPLY_BODY,
470 have: body.len(),
471 what: "sd_start_reply",
472 });
473 }
474 let mut r = Reader::new(body, "sd_start_reply");
475 Ok(Self {
476 lts_id: r.u8()?,
477 transmission_status: TransmissionStatus::from_u8(r.u8()?),
478 drm_status: DrmStatus::from_u8(r.u8()?),
479 drm_system_id: r.u16()?,
480 drm_uuid: r.uuid()?,
481 buffer_size: r.u16()?,
482 data_block_size: r.u16()?,
483 })
484 }
485}
486impl Serialize for SdStartReply {
487 type Error = Error;
488 fn serialized_len(&self) -> usize {
489 objects::apdu_len(SD_START_REPLY_BODY)
490 }
491 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
492 let pos = objects::write_apdu_header(tag::SD_START_REPLY, SD_START_REPLY_BODY, buf)?;
493 let mut w = Writer::new(&mut buf[pos..]);
494 w.u8(self.lts_id);
495 w.u8(self.transmission_status.to_u8());
496 w.u8(self.drm_status.to_u8());
497 w.u16(self.drm_system_id);
498 w.uuid(&self.drm_uuid);
499 w.u16(self.buffer_size);
500 w.u16(self.data_block_size);
501 Ok(pos + SD_START_REPLY_BODY)
502 }
503}
504
505#[derive(Debug, Clone, PartialEq, Eq)]
510#[cfg_attr(feature = "serde", derive(serde::Serialize))]
511pub struct SdUpdate<'a> {
512 pub lts_id: u8,
514 #[cfg_attr(feature = "serde", serde(borrow))]
516 pub payload: SamplePayload<'a>,
517}
518
519const SD_UPDATE_FIXED: usize = 1 + 1;
521
522impl<'a> Parse<'a> for SdUpdate<'a> {
523 type Error = Error;
524 fn parse(bytes: &'a [u8]) -> Result<Self> {
525 let body = objects::parse_apdu_header(bytes, tag::SD_UPDATE, "sd_update")?;
526 let mut r = Reader::new(body, "sd_update");
527 let lts_id = r.u8()?;
528 let ts_flag = r.u8()? & TS_FLAG_BIT != 0;
529 let payload = SamplePayload::parse_from(&mut r, ts_flag)?;
530 Ok(Self { lts_id, payload })
531 }
532}
533impl Serialize for SdUpdate<'_> {
534 type Error = Error;
535 fn serialized_len(&self) -> usize {
536 objects::apdu_len(SD_UPDATE_FIXED + self.payload.body_len())
537 }
538 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
539 let body_len = SD_UPDATE_FIXED + self.payload.body_len();
540 let pos = objects::write_apdu_header(tag::SD_UPDATE, body_len, buf)?;
541 let mut w = Writer::new(&mut buf[pos..]);
542 w.u8(self.lts_id);
543 w.u8(if self.payload.ts_flag() {
544 TS_FLAG_BIT
545 } else {
546 0
547 });
548 self.payload.write_into(&mut w);
549 Ok(pos + body_len)
550 }
551}
552
553#[derive(Debug, Clone, Copy, PartialEq, Eq)]
557#[cfg_attr(feature = "serde", derive(serde::Serialize))]
558pub struct SdUpdateReply {
559 pub lts_id: u8,
561 pub drm_status: DrmStatus,
563}
564
565const SD_UPDATE_REPLY_BODY: usize = 2;
567
568impl<'a> Parse<'a> for SdUpdateReply {
569 type Error = Error;
570 fn parse(bytes: &'a [u8]) -> Result<Self> {
571 let body = objects::parse_apdu_header(bytes, tag::SD_UPDATE_REPLY, "sd_update_reply")?;
572 if body.len() < SD_UPDATE_REPLY_BODY {
573 return Err(Error::BufferTooShort {
574 need: SD_UPDATE_REPLY_BODY,
575 have: body.len(),
576 what: "sd_update_reply",
577 });
578 }
579 Ok(Self {
580 lts_id: body[0],
581 drm_status: DrmStatus::from_u8(body[1]),
582 })
583 }
584}
585impl Serialize for SdUpdateReply {
586 type Error = Error;
587 fn serialized_len(&self) -> usize {
588 objects::apdu_len(SD_UPDATE_REPLY_BODY)
589 }
590 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
591 let pos = objects::write_apdu_header(tag::SD_UPDATE_REPLY, SD_UPDATE_REPLY_BODY, buf)?;
592 buf[pos] = self.lts_id;
593 buf[pos + 1] = self.drm_status.to_u8();
594 Ok(pos + SD_UPDATE_REPLY_BODY)
595 }
596}
597
598#[derive(Debug, Clone, PartialEq, Eq)]
600#[cfg_attr(feature = "serde", derive(serde::Serialize))]
601#[non_exhaustive]
602pub enum SampleDecryptionApdu<'a> {
603 SdInfoReq(SdInfoReq),
605 SdInfoReply(SdInfoReply),
607 SdStart(#[cfg_attr(feature = "serde", serde(borrow))] SdStart<'a>),
609 SdStartReply(SdStartReply),
611 SdUpdate(#[cfg_attr(feature = "serde", serde(borrow))] SdUpdate<'a>),
613 SdUpdateReply(SdUpdateReply),
615}
616
617impl<'a> SampleDecryptionApdu<'a> {
618 pub fn parse(body: &'a [u8]) -> Result<Self> {
620 if body.len() < 3 {
621 return Err(Error::BufferTooShort {
622 need: 3,
623 have: body.len(),
624 what: "sample_decryption apdu_tag",
625 });
626 }
627 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
628 match t {
629 tag::SD_INFO_REQ => Ok(Self::SdInfoReq(SdInfoReq::parse(body)?)),
630 tag::SD_INFO_REPLY => Ok(Self::SdInfoReply(SdInfoReply::parse(body)?)),
631 tag::SD_START => Ok(Self::SdStart(SdStart::parse(body)?)),
632 tag::SD_START_REPLY => Ok(Self::SdStartReply(SdStartReply::parse(body)?)),
633 tag::SD_UPDATE => Ok(Self::SdUpdate(SdUpdate::parse(body)?)),
634 tag::SD_UPDATE_REPLY => Ok(Self::SdUpdateReply(SdUpdateReply::parse(body)?)),
635 _ => Err(Error::UnexpectedApduTag {
636 got: t.as_u24(),
637 expected: tag::SD_INFO_REQ.as_u24(),
638 what: "sample_decryption",
639 }),
640 }
641 }
642}
643
644impl Serialize for SampleDecryptionApdu<'_> {
645 type Error = Error;
646 fn serialized_len(&self) -> usize {
647 match self {
648 Self::SdInfoReq(o) => o.serialized_len(),
649 Self::SdInfoReply(o) => o.serialized_len(),
650 Self::SdStart(o) => o.serialized_len(),
651 Self::SdStartReply(o) => o.serialized_len(),
652 Self::SdUpdate(o) => o.serialized_len(),
653 Self::SdUpdateReply(o) => o.serialized_len(),
654 }
655 }
656 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
657 match self {
658 Self::SdInfoReq(o) => o.serialize_into(buf),
659 Self::SdInfoReply(o) => o.serialize_into(buf),
660 Self::SdStart(o) => o.serialize_into(buf),
661 Self::SdStartReply(o) => o.serialize_into(buf),
662 Self::SdUpdate(o) => o.serialize_into(buf),
663 Self::SdUpdateReply(o) => o.serialize_into(buf),
664 }
665 }
666}
667
668struct Reader<'a> {
671 buf: &'a [u8],
672 pos: usize,
673 what: &'static str,
674}
675impl<'a> Reader<'a> {
676 fn new(buf: &'a [u8], what: &'static str) -> Self {
677 Self { buf, pos: 0, what }
678 }
679 fn take(&mut self, n: usize) -> Result<&'a [u8]> {
680 if self.buf.len() < self.pos + n {
681 return Err(Error::BufferTooShort {
682 need: n,
683 have: self.buf.len().saturating_sub(self.pos),
684 what: self.what,
685 });
686 }
687 let s = &self.buf[self.pos..self.pos + n];
688 self.pos += n;
689 Ok(s)
690 }
691 fn u8(&mut self) -> Result<u8> {
692 Ok(self.take(1)?[0])
693 }
694 fn u16(&mut self) -> Result<u16> {
695 let s = self.take(2)?;
696 Ok(u16::from_be_bytes([s[0], s[1]]))
697 }
698 fn uuid(&mut self) -> Result<[u8; DRM_UUID_LEN]> {
699 let s = self.take(DRM_UUID_LEN)?;
700 let mut u = [0u8; DRM_UUID_LEN];
701 u.copy_from_slice(s);
702 Ok(u)
703 }
704}
705
706struct Writer<'a> {
707 buf: &'a mut [u8],
708 pos: usize,
709}
710impl<'a> Writer<'a> {
711 fn new(buf: &'a mut [u8]) -> Self {
712 Self { buf, pos: 0 }
713 }
714 fn u8(&mut self, v: u8) {
715 self.buf[self.pos] = v;
716 self.pos += 1;
717 }
718 fn u16(&mut self, v: u16) {
719 self.buf[self.pos..self.pos + 2].copy_from_slice(&v.to_be_bytes());
720 self.pos += 2;
721 }
722 fn uuid(&mut self, v: &[u8; DRM_UUID_LEN]) {
723 self.buf[self.pos..self.pos + DRM_UUID_LEN].copy_from_slice(v);
724 self.pos += DRM_UUID_LEN;
725 }
726 fn bytes(&mut self, v: &[u8]) {
727 self.buf[self.pos..self.pos + v.len()].copy_from_slice(v);
728 self.pos += v.len();
729 }
730}
731
732#[cfg(test)]
733mod tests {
734 use super::*;
735
736 const UUID_A: [u8; DRM_UUID_LEN] = [
737 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
738 0xFF,
739 ];
740 const UUID_B: [u8; DRM_UUID_LEN] = [0xFF; DRM_UUID_LEN];
741
742 #[test]
743 fn sd_info_req_round_trips() {
744 let bytes = SdInfoReq.to_bytes();
745 assert_eq!(bytes, [0x9F, 0x98, 0x00, 0x00]);
746 assert_eq!(SdInfoReq::parse(&bytes).unwrap(), SdInfoReq);
747 }
748
749 #[test]
750 fn sd_info_reply_round_trips_and_bites() {
751 let r = SdInfoReply {
752 drm_system_ids: alloc::vec![0x4AD4, 0x1234],
753 drm_uuids: alloc::vec![UUID_A, UUID_B],
754 };
755 let bytes = r.to_bytes();
756 assert_eq!(bytes[0..4], [0x9F, 0x98, 0x01, 0x26]);
759 assert_eq!(bytes[4], 0x02);
760 assert_eq!(&bytes[5..9], &[0x4A, 0xD4, 0x12, 0x34]);
761 assert_eq!(bytes[9], 0x02);
762 assert_eq!(&bytes[10..26], &UUID_A);
763 assert_eq!(&bytes[26..42], &UUID_B);
764 assert_eq!(SdInfoReply::parse(&bytes).unwrap(), r);
765 let mut other = r.clone();
766 other.drm_system_ids[0] = 0x0000;
767 assert_ne!(bytes, other.to_bytes());
768 }
769
770 #[test]
771 fn sd_info_reply_empty_loops() {
772 let r = SdInfoReply {
773 drm_system_ids: Vec::new(),
774 drm_uuids: Vec::new(),
775 };
776 let bytes = r.to_bytes();
777 assert_eq!(bytes, [0x9F, 0x98, 0x01, 0x02, 0x00, 0x00]);
778 assert_eq!(SdInfoReply::parse(&bytes).unwrap(), r);
779 }
780
781 #[test]
782 fn sd_start_ts_flag_round_trips_and_bites() {
783 let s = SdStart {
784 lts_id: 0x07,
785 program_number: 0x0042,
786 payload: SamplePayload::Ts(alloc::vec![DrmMetadataRecord {
787 drm_metadata_source: 0x03, drm_system_id: 0xFFFF,
789 drm_uuid: UUID_A,
790 drm_metadata: &[0xDE, 0xAD, 0xBE, 0xEF],
791 }]),
792 };
793 let bytes = s.to_bytes();
794 assert_eq!(bytes[0..3], [0x9F, 0x98, 0x02]);
798 assert_eq!(bytes[4], 0x07); assert_eq!(&bytes[5..7], &[0x00, 0x42]); assert_eq!(bytes[7], 0x01); assert_eq!(bytes[8], 0x01); assert_eq!(bytes[9], 0x03); assert_eq!(&bytes[10..12], &[0xFF, 0xFF]); assert_eq!(&bytes[12..28], &UUID_A); assert_eq!(&bytes[28..30], &[0x00, 0x04]); assert_eq!(&bytes[30..34], &[0xDE, 0xAD, 0xBE, 0xEF]);
807 assert_eq!(SdStart::parse(&bytes).unwrap(), s);
808 let mut other = s.clone();
810 other.payload = SamplePayload::Ts(alloc::vec![DrmMetadataRecord {
811 drm_metadata_source: 0x03,
812 drm_system_id: 0xFFFF,
813 drm_uuid: UUID_A,
814 drm_metadata: &[0xDE, 0xAD, 0xBE, 0x00],
815 }]);
816 assert_ne!(bytes, other.to_bytes());
817 }
818
819 #[test]
820 fn sd_start_tracks_two_tracks() {
821 let s = SdStart {
822 lts_id: 0x01,
823 program_number: 0x1000,
824 payload: SamplePayload::Tracks(alloc::vec![
825 SampleTrack {
826 track_pid: 0x0100,
827 records: alloc::vec![DrmMetadataRecord {
828 drm_metadata_source: 0x01,
829 drm_system_id: 0x4AD4,
830 drm_uuid: UUID_A,
831 drm_metadata: &[0x01, 0x02],
832 }],
833 },
834 SampleTrack {
835 track_pid: 0x0101,
836 records: Vec::new(),
837 },
838 ]),
839 };
840 let bytes = s.to_bytes();
841 assert_eq!(bytes[7], 0x00); assert_eq!(bytes[8], 0x02); assert_eq!(&bytes[9..11], &[0x01, 0x00]);
845 assert_eq!(bytes[11], 0x01);
846 assert_eq!(SdStart::parse(&bytes).unwrap(), s);
847 let parsed = SdStart::parse(&bytes).unwrap();
849 if let SamplePayload::Tracks(t) = &parsed.payload {
850 assert_eq!(t[0].track_pid, 0x0100);
851 assert_eq!(t[1].track_pid, 0x0101);
852 assert_eq!(t.len(), 2);
853 } else {
854 panic!("expected Tracks");
855 }
856 }
857
858 #[test]
859 fn sd_start_reply_round_trips_and_bites() {
860 let r = SdStartReply {
861 lts_id: 0x05,
862 transmission_status: TransmissionStatus::ReadyToReceive,
863 drm_status: DrmStatus::DecryptionPossible,
864 drm_system_id: 0x4AD4,
865 drm_uuid: UUID_A,
866 buffer_size: 5000,
867 data_block_size: 0,
868 };
869 let bytes = r.to_bytes();
870 assert_eq!(bytes[0..4], [0x9F, 0x98, 0x03, 0x19]);
872 assert_eq!(bytes[4], 0x05); assert_eq!(bytes[5], 0x00); assert_eq!(bytes[6], 0x00); assert_eq!(&bytes[7..9], &[0x4A, 0xD4]);
876 assert_eq!(&bytes[9..25], &UUID_A);
877 assert_eq!(&bytes[25..27], &5000u16.to_be_bytes());
878 assert_eq!(&bytes[27..29], &[0x00, 0x00]);
879 assert_eq!(SdStartReply::parse(&bytes).unwrap(), r);
880 let mut other = r;
881 other.drm_status = DrmStatus::NoEntitlement;
882 assert_eq!(other.to_bytes()[6], 0x02);
883 assert_ne!(bytes, other.to_bytes());
884 }
885
886 #[test]
887 fn sd_update_round_trips() {
888 let u = SdUpdate {
889 lts_id: 0x09,
890 payload: SamplePayload::Tracks(alloc::vec![
891 SampleTrack {
892 track_pid: 0x0200,
893 records: alloc::vec![DrmMetadataRecord {
894 drm_metadata_source: 0x05,
895 drm_system_id: 0xFFFF,
896 drm_uuid: UUID_B,
897 drm_metadata: &[],
898 }],
899 },
900 SampleTrack {
901 track_pid: 0x0201,
902 records: Vec::new(),
903 },
904 ]),
905 };
906 let bytes = u.to_bytes();
907 assert_eq!(bytes[0..3], [0x9F, 0x98, 0x04]);
908 assert_eq!(bytes[4], 0x09); assert_eq!(bytes[5], 0x00); assert_eq!(bytes[6], 0x02); assert_eq!(SdUpdate::parse(&bytes).unwrap(), u);
912 }
913
914 #[test]
915 fn sd_update_reply_round_trips_and_bites() {
916 let r = SdUpdateReply {
917 lts_id: 0x03,
918 drm_status: DrmStatus::Undetermined,
919 };
920 let bytes = r.to_bytes();
921 assert_eq!(bytes, [0x9F, 0x98, 0x05, 0x02, 0x03, 0x01]);
922 assert_eq!(SdUpdateReply::parse(&bytes).unwrap(), r);
923 let other = SdUpdateReply {
924 lts_id: 0x03,
925 drm_status: DrmStatus::DecryptionPossible,
926 };
927 assert_ne!(bytes, other.to_bytes());
928 }
929
930 #[test]
931 fn dispatch_routes_each_tag() {
932 let req = SdInfoReq.to_bytes();
933 assert!(matches!(
934 SampleDecryptionApdu::parse(&req).unwrap(),
935 SampleDecryptionApdu::SdInfoReq(_)
936 ));
937 let reply = SdUpdateReply {
938 lts_id: 0,
939 drm_status: DrmStatus::DecryptionPossible,
940 }
941 .to_bytes();
942 let parsed = SampleDecryptionApdu::parse(&reply).unwrap();
943 assert!(matches!(parsed, SampleDecryptionApdu::SdUpdateReply(_)));
944 assert_eq!(parsed.to_bytes(), reply);
945 assert!(matches!(
946 SampleDecryptionApdu::parse(&[0x9F, 0x98, 0x7E, 0x00]),
947 Err(Error::UnexpectedApduTag { .. })
948 ));
949 }
950}