1use crate::error::{Error, Result};
20use crate::objects;
21use crate::tag::ApduTag;
22use alloc::vec::Vec;
23use dvb_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))]
196pub enum SamplePayload<'a> {
197 Ts(#[cfg_attr(feature = "serde", serde(borrow))] Vec<DrmMetadataRecord<'a>>),
200 Tracks(#[cfg_attr(feature = "serde", serde(borrow))] Vec<SampleTrack<'a>>),
203}
204
205impl<'a> SamplePayload<'a> {
206 #[must_use]
208 pub fn ts_flag(&self) -> bool {
209 matches!(self, Self::Ts(_))
210 }
211
212 fn parse_from(r: &mut Reader<'a>, ts_flag: bool) -> Result<Self> {
213 if ts_flag {
214 let n = r.u8()? as usize;
215 let mut records = Vec::with_capacity(n);
216 for _ in 0..n {
217 records.push(DrmMetadataRecord::parse_from(r)?);
218 }
219 Ok(Self::Ts(records))
220 } else {
221 let n = r.u8()? as usize;
222 let mut tracks = Vec::with_capacity(n);
223 for _ in 0..n {
224 let pid_word = r.u16()?;
225 let track_pid = pid_word & TRACK_PID_MASK;
226 let m = r.u8()? as usize;
227 let mut records = Vec::with_capacity(m);
228 for _ in 0..m {
229 records.push(DrmMetadataRecord::parse_from(r)?);
230 }
231 tracks.push(SampleTrack { track_pid, records });
232 }
233 Ok(Self::Tracks(tracks))
234 }
235 }
236
237 fn body_len(&self) -> usize {
238 match self {
239 Self::Ts(records) => {
240 1 + records
241 .iter()
242 .map(DrmMetadataRecord::body_len)
243 .sum::<usize>()
244 }
245 Self::Tracks(tracks) => {
246 1 + tracks
247 .iter()
248 .map(|t| {
249 TRACK_FIXED
250 + t.records
251 .iter()
252 .map(DrmMetadataRecord::body_len)
253 .sum::<usize>()
254 })
255 .sum::<usize>()
256 }
257 }
258 }
259
260 fn write_into(&self, w: &mut Writer<'_>) {
261 match self {
262 Self::Ts(records) => {
263 w.u8(records.len() as u8);
264 for rec in records {
265 rec.write_into(w);
266 }
267 }
268 Self::Tracks(tracks) => {
269 w.u8(tracks.len() as u8);
270 for track in tracks {
271 w.u16(track.track_pid & TRACK_PID_MASK);
273 w.u8(track.records.len() as u8);
274 for rec in &track.records {
275 rec.write_into(w);
276 }
277 }
278 }
279 }
280 }
281}
282
283#[derive(Debug, Clone, PartialEq, Eq)]
287#[cfg_attr(feature = "serde", derive(serde::Serialize))]
288pub struct SdStart<'a> {
289 pub lts_id: u8,
291 pub program_number: u16,
293 #[cfg_attr(feature = "serde", serde(borrow))]
295 pub payload: SamplePayload<'a>,
296}
297
298const SD_START_FIXED: usize = 1 + 2 + 1;
300const TS_FLAG_BIT: u8 = 0x01;
302
303impl<'a> Parse<'a> for SdStart<'a> {
304 type Error = Error;
305 fn parse(bytes: &'a [u8]) -> Result<Self> {
306 let body = objects::parse_apdu_header(bytes, tag::SD_START, "sd_start")?;
307 let mut r = Reader::new(body, "sd_start");
308 let lts_id = r.u8()?;
309 let program_number = r.u16()?;
310 let ts_flag = r.u8()? & TS_FLAG_BIT != 0;
311 let payload = SamplePayload::parse_from(&mut r, ts_flag)?;
312 Ok(Self {
313 lts_id,
314 program_number,
315 payload,
316 })
317 }
318}
319impl Serialize for SdStart<'_> {
320 type Error = Error;
321 fn serialized_len(&self) -> usize {
322 objects::apdu_len(SD_START_FIXED + self.payload.body_len())
323 }
324 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
325 let body_len = SD_START_FIXED + self.payload.body_len();
326 let pos = objects::write_apdu_header(tag::SD_START, body_len, buf)?;
327 let mut w = Writer::new(&mut buf[pos..]);
328 w.u8(self.lts_id);
329 w.u16(self.program_number);
330 w.u8(if self.payload.ts_flag() {
331 TS_FLAG_BIT
332 } else {
333 0
334 });
335 self.payload.write_into(&mut w);
336 Ok(pos + body_len)
337 }
338}
339
340#[derive(Debug, Clone, Copy, PartialEq, Eq)]
344#[cfg_attr(feature = "serde", derive(serde::Serialize))]
345#[non_exhaustive]
346pub enum TransmissionStatus {
347 ReadyToReceive,
349 CicamBusy,
351 OtherReason,
353 Reserved(u8),
355}
356impl TransmissionStatus {
357 #[must_use]
359 pub fn from_u8(v: u8) -> Self {
360 match v {
361 0x00 => Self::ReadyToReceive,
362 0x01 => Self::CicamBusy,
363 0x02 => Self::OtherReason,
364 other => Self::Reserved(other),
365 }
366 }
367 #[must_use]
369 pub const fn to_u8(self) -> u8 {
370 match self {
371 Self::ReadyToReceive => 0x00,
372 Self::CicamBusy => 0x01,
373 Self::OtherReason => 0x02,
374 Self::Reserved(v) => v,
375 }
376 }
377 #[must_use]
379 pub fn name(&self) -> &'static str {
380 match self {
381 Self::ReadyToReceive => "ready_to_receive",
382 Self::CicamBusy => "error_cicam_busy",
383 Self::OtherReason => "error_other_reason",
384 Self::Reserved(_) => "reserved",
385 }
386 }
387}
388dvb_common::impl_spec_display!(TransmissionStatus, Reserved);
389
390#[derive(Debug, Clone, Copy, PartialEq, Eq)]
392#[cfg_attr(feature = "serde", derive(serde::Serialize))]
393#[non_exhaustive]
394pub enum DrmStatus {
395 DecryptionPossible,
397 Undetermined,
399 NoEntitlement,
401 Reserved(u8),
403}
404impl DrmStatus {
405 #[must_use]
407 pub fn from_u8(v: u8) -> Self {
408 match v {
409 0x00 => Self::DecryptionPossible,
410 0x01 => Self::Undetermined,
411 0x02 => Self::NoEntitlement,
412 other => Self::Reserved(other),
413 }
414 }
415 #[must_use]
417 pub const fn to_u8(self) -> u8 {
418 match self {
419 Self::DecryptionPossible => 0x00,
420 Self::Undetermined => 0x01,
421 Self::NoEntitlement => 0x02,
422 Self::Reserved(v) => v,
423 }
424 }
425 #[must_use]
427 pub fn name(&self) -> &'static str {
428 match self {
429 Self::DecryptionPossible => "decryption_possible",
430 Self::Undetermined => "status_undetermined",
431 Self::NoEntitlement => "error_no_entitlement",
432 Self::Reserved(_) => "reserved",
433 }
434 }
435}
436dvb_common::impl_spec_display!(DrmStatus, Reserved);
437
438#[derive(Debug, Clone, Copy, PartialEq, Eq)]
440#[cfg_attr(feature = "serde", derive(serde::Serialize))]
441pub struct SdStartReply {
442 pub lts_id: u8,
444 pub transmission_status: TransmissionStatus,
446 pub drm_status: DrmStatus,
448 pub drm_system_id: u16,
450 pub drm_uuid: [u8; DRM_UUID_LEN],
452 pub buffer_size: u16,
454 pub data_block_size: u16,
456}
457
458const SD_START_REPLY_BODY: usize = 1 + 1 + 1 + 2 + DRM_UUID_LEN + 2 + 2;
461
462impl<'a> Parse<'a> for SdStartReply {
463 type Error = Error;
464 fn parse(bytes: &'a [u8]) -> Result<Self> {
465 let body = objects::parse_apdu_header(bytes, tag::SD_START_REPLY, "sd_start_reply")?;
466 if body.len() < SD_START_REPLY_BODY {
467 return Err(Error::BufferTooShort {
468 need: SD_START_REPLY_BODY,
469 have: body.len(),
470 what: "sd_start_reply",
471 });
472 }
473 let mut r = Reader::new(body, "sd_start_reply");
474 Ok(Self {
475 lts_id: r.u8()?,
476 transmission_status: TransmissionStatus::from_u8(r.u8()?),
477 drm_status: DrmStatus::from_u8(r.u8()?),
478 drm_system_id: r.u16()?,
479 drm_uuid: r.uuid()?,
480 buffer_size: r.u16()?,
481 data_block_size: r.u16()?,
482 })
483 }
484}
485impl Serialize for SdStartReply {
486 type Error = Error;
487 fn serialized_len(&self) -> usize {
488 objects::apdu_len(SD_START_REPLY_BODY)
489 }
490 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
491 let pos = objects::write_apdu_header(tag::SD_START_REPLY, SD_START_REPLY_BODY, buf)?;
492 let mut w = Writer::new(&mut buf[pos..]);
493 w.u8(self.lts_id);
494 w.u8(self.transmission_status.to_u8());
495 w.u8(self.drm_status.to_u8());
496 w.u16(self.drm_system_id);
497 w.uuid(&self.drm_uuid);
498 w.u16(self.buffer_size);
499 w.u16(self.data_block_size);
500 Ok(pos + SD_START_REPLY_BODY)
501 }
502}
503
504#[derive(Debug, Clone, PartialEq, Eq)]
509#[cfg_attr(feature = "serde", derive(serde::Serialize))]
510pub struct SdUpdate<'a> {
511 pub lts_id: u8,
513 #[cfg_attr(feature = "serde", serde(borrow))]
515 pub payload: SamplePayload<'a>,
516}
517
518const SD_UPDATE_FIXED: usize = 1 + 1;
520
521impl<'a> Parse<'a> for SdUpdate<'a> {
522 type Error = Error;
523 fn parse(bytes: &'a [u8]) -> Result<Self> {
524 let body = objects::parse_apdu_header(bytes, tag::SD_UPDATE, "sd_update")?;
525 let mut r = Reader::new(body, "sd_update");
526 let lts_id = r.u8()?;
527 let ts_flag = r.u8()? & TS_FLAG_BIT != 0;
528 let payload = SamplePayload::parse_from(&mut r, ts_flag)?;
529 Ok(Self { lts_id, payload })
530 }
531}
532impl Serialize for SdUpdate<'_> {
533 type Error = Error;
534 fn serialized_len(&self) -> usize {
535 objects::apdu_len(SD_UPDATE_FIXED + self.payload.body_len())
536 }
537 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
538 let body_len = SD_UPDATE_FIXED + self.payload.body_len();
539 let pos = objects::write_apdu_header(tag::SD_UPDATE, body_len, buf)?;
540 let mut w = Writer::new(&mut buf[pos..]);
541 w.u8(self.lts_id);
542 w.u8(if self.payload.ts_flag() {
543 TS_FLAG_BIT
544 } else {
545 0
546 });
547 self.payload.write_into(&mut w);
548 Ok(pos + body_len)
549 }
550}
551
552#[derive(Debug, Clone, Copy, PartialEq, Eq)]
556#[cfg_attr(feature = "serde", derive(serde::Serialize))]
557pub struct SdUpdateReply {
558 pub lts_id: u8,
560 pub drm_status: DrmStatus,
562}
563
564const SD_UPDATE_REPLY_BODY: usize = 2;
566
567impl<'a> Parse<'a> for SdUpdateReply {
568 type Error = Error;
569 fn parse(bytes: &'a [u8]) -> Result<Self> {
570 let body = objects::parse_apdu_header(bytes, tag::SD_UPDATE_REPLY, "sd_update_reply")?;
571 if body.len() < SD_UPDATE_REPLY_BODY {
572 return Err(Error::BufferTooShort {
573 need: SD_UPDATE_REPLY_BODY,
574 have: body.len(),
575 what: "sd_update_reply",
576 });
577 }
578 Ok(Self {
579 lts_id: body[0],
580 drm_status: DrmStatus::from_u8(body[1]),
581 })
582 }
583}
584impl Serialize for SdUpdateReply {
585 type Error = Error;
586 fn serialized_len(&self) -> usize {
587 objects::apdu_len(SD_UPDATE_REPLY_BODY)
588 }
589 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
590 let pos = objects::write_apdu_header(tag::SD_UPDATE_REPLY, SD_UPDATE_REPLY_BODY, buf)?;
591 buf[pos] = self.lts_id;
592 buf[pos + 1] = self.drm_status.to_u8();
593 Ok(pos + SD_UPDATE_REPLY_BODY)
594 }
595}
596
597#[derive(Debug, Clone, PartialEq, Eq)]
599#[cfg_attr(feature = "serde", derive(serde::Serialize))]
600#[non_exhaustive]
601pub enum SampleDecryptionApdu<'a> {
602 SdInfoReq(SdInfoReq),
604 SdInfoReply(SdInfoReply),
606 SdStart(#[cfg_attr(feature = "serde", serde(borrow))] SdStart<'a>),
608 SdStartReply(SdStartReply),
610 SdUpdate(#[cfg_attr(feature = "serde", serde(borrow))] SdUpdate<'a>),
612 SdUpdateReply(SdUpdateReply),
614}
615
616impl<'a> SampleDecryptionApdu<'a> {
617 pub fn parse(body: &'a [u8]) -> Result<Self> {
619 if body.len() < 3 {
620 return Err(Error::BufferTooShort {
621 need: 3,
622 have: body.len(),
623 what: "sample_decryption apdu_tag",
624 });
625 }
626 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
627 match t {
628 tag::SD_INFO_REQ => Ok(Self::SdInfoReq(SdInfoReq::parse(body)?)),
629 tag::SD_INFO_REPLY => Ok(Self::SdInfoReply(SdInfoReply::parse(body)?)),
630 tag::SD_START => Ok(Self::SdStart(SdStart::parse(body)?)),
631 tag::SD_START_REPLY => Ok(Self::SdStartReply(SdStartReply::parse(body)?)),
632 tag::SD_UPDATE => Ok(Self::SdUpdate(SdUpdate::parse(body)?)),
633 tag::SD_UPDATE_REPLY => Ok(Self::SdUpdateReply(SdUpdateReply::parse(body)?)),
634 _ => Err(Error::UnexpectedApduTag {
635 got: t.as_u24(),
636 expected: tag::SD_INFO_REQ.as_u24(),
637 what: "sample_decryption",
638 }),
639 }
640 }
641}
642
643impl Serialize for SampleDecryptionApdu<'_> {
644 type Error = Error;
645 fn serialized_len(&self) -> usize {
646 match self {
647 Self::SdInfoReq(o) => o.serialized_len(),
648 Self::SdInfoReply(o) => o.serialized_len(),
649 Self::SdStart(o) => o.serialized_len(),
650 Self::SdStartReply(o) => o.serialized_len(),
651 Self::SdUpdate(o) => o.serialized_len(),
652 Self::SdUpdateReply(o) => o.serialized_len(),
653 }
654 }
655 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
656 match self {
657 Self::SdInfoReq(o) => o.serialize_into(buf),
658 Self::SdInfoReply(o) => o.serialize_into(buf),
659 Self::SdStart(o) => o.serialize_into(buf),
660 Self::SdStartReply(o) => o.serialize_into(buf),
661 Self::SdUpdate(o) => o.serialize_into(buf),
662 Self::SdUpdateReply(o) => o.serialize_into(buf),
663 }
664 }
665}
666
667struct Reader<'a> {
670 buf: &'a [u8],
671 pos: usize,
672 what: &'static str,
673}
674impl<'a> Reader<'a> {
675 fn new(buf: &'a [u8], what: &'static str) -> Self {
676 Self { buf, pos: 0, what }
677 }
678 fn take(&mut self, n: usize) -> Result<&'a [u8]> {
679 if self.buf.len() < self.pos + n {
680 return Err(Error::BufferTooShort {
681 need: n,
682 have: self.buf.len().saturating_sub(self.pos),
683 what: self.what,
684 });
685 }
686 let s = &self.buf[self.pos..self.pos + n];
687 self.pos += n;
688 Ok(s)
689 }
690 fn u8(&mut self) -> Result<u8> {
691 Ok(self.take(1)?[0])
692 }
693 fn u16(&mut self) -> Result<u16> {
694 let s = self.take(2)?;
695 Ok(u16::from_be_bytes([s[0], s[1]]))
696 }
697 fn uuid(&mut self) -> Result<[u8; DRM_UUID_LEN]> {
698 let s = self.take(DRM_UUID_LEN)?;
699 let mut u = [0u8; DRM_UUID_LEN];
700 u.copy_from_slice(s);
701 Ok(u)
702 }
703}
704
705struct Writer<'a> {
706 buf: &'a mut [u8],
707 pos: usize,
708}
709impl<'a> Writer<'a> {
710 fn new(buf: &'a mut [u8]) -> Self {
711 Self { buf, pos: 0 }
712 }
713 fn u8(&mut self, v: u8) {
714 self.buf[self.pos] = v;
715 self.pos += 1;
716 }
717 fn u16(&mut self, v: u16) {
718 self.buf[self.pos..self.pos + 2].copy_from_slice(&v.to_be_bytes());
719 self.pos += 2;
720 }
721 fn uuid(&mut self, v: &[u8; DRM_UUID_LEN]) {
722 self.buf[self.pos..self.pos + DRM_UUID_LEN].copy_from_slice(v);
723 self.pos += DRM_UUID_LEN;
724 }
725 fn bytes(&mut self, v: &[u8]) {
726 self.buf[self.pos..self.pos + v.len()].copy_from_slice(v);
727 self.pos += v.len();
728 }
729}
730
731#[cfg(test)]
732mod tests {
733 use super::*;
734
735 const UUID_A: [u8; DRM_UUID_LEN] = [
736 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
737 0xFF,
738 ];
739 const UUID_B: [u8; DRM_UUID_LEN] = [0xFF; DRM_UUID_LEN];
740
741 #[test]
742 fn sd_info_req_round_trips() {
743 let bytes = SdInfoReq.to_bytes();
744 assert_eq!(bytes, [0x9F, 0x98, 0x00, 0x00]);
745 assert_eq!(SdInfoReq::parse(&bytes).unwrap(), SdInfoReq);
746 }
747
748 #[test]
749 fn sd_info_reply_round_trips_and_bites() {
750 let r = SdInfoReply {
751 drm_system_ids: alloc::vec![0x4AD4, 0x1234],
752 drm_uuids: alloc::vec![UUID_A, UUID_B],
753 };
754 let bytes = r.to_bytes();
755 assert_eq!(bytes[0..4], [0x9F, 0x98, 0x01, 0x26]);
758 assert_eq!(bytes[4], 0x02);
759 assert_eq!(&bytes[5..9], &[0x4A, 0xD4, 0x12, 0x34]);
760 assert_eq!(bytes[9], 0x02);
761 assert_eq!(&bytes[10..26], &UUID_A);
762 assert_eq!(&bytes[26..42], &UUID_B);
763 assert_eq!(SdInfoReply::parse(&bytes).unwrap(), r);
764 let mut other = r.clone();
765 other.drm_system_ids[0] = 0x0000;
766 assert_ne!(bytes, other.to_bytes());
767 }
768
769 #[test]
770 fn sd_info_reply_empty_loops() {
771 let r = SdInfoReply {
772 drm_system_ids: Vec::new(),
773 drm_uuids: Vec::new(),
774 };
775 let bytes = r.to_bytes();
776 assert_eq!(bytes, [0x9F, 0x98, 0x01, 0x02, 0x00, 0x00]);
777 assert_eq!(SdInfoReply::parse(&bytes).unwrap(), r);
778 }
779
780 #[test]
781 fn sd_start_ts_flag_round_trips_and_bites() {
782 let s = SdStart {
783 lts_id: 0x07,
784 program_number: 0x0042,
785 payload: SamplePayload::Ts(alloc::vec![DrmMetadataRecord {
786 drm_metadata_source: 0x03, drm_system_id: 0xFFFF,
788 drm_uuid: UUID_A,
789 drm_metadata: &[0xDE, 0xAD, 0xBE, 0xEF],
790 }]),
791 };
792 let bytes = s.to_bytes();
793 assert_eq!(bytes[0..3], [0x9F, 0x98, 0x02]);
797 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]);
806 assert_eq!(SdStart::parse(&bytes).unwrap(), s);
807 let mut other = s.clone();
809 other.payload = SamplePayload::Ts(alloc::vec![DrmMetadataRecord {
810 drm_metadata_source: 0x03,
811 drm_system_id: 0xFFFF,
812 drm_uuid: UUID_A,
813 drm_metadata: &[0xDE, 0xAD, 0xBE, 0x00],
814 }]);
815 assert_ne!(bytes, other.to_bytes());
816 }
817
818 #[test]
819 fn sd_start_tracks_two_tracks() {
820 let s = SdStart {
821 lts_id: 0x01,
822 program_number: 0x1000,
823 payload: SamplePayload::Tracks(alloc::vec![
824 SampleTrack {
825 track_pid: 0x0100,
826 records: alloc::vec![DrmMetadataRecord {
827 drm_metadata_source: 0x01,
828 drm_system_id: 0x4AD4,
829 drm_uuid: UUID_A,
830 drm_metadata: &[0x01, 0x02],
831 }],
832 },
833 SampleTrack {
834 track_pid: 0x0101,
835 records: Vec::new(),
836 },
837 ]),
838 };
839 let bytes = s.to_bytes();
840 assert_eq!(bytes[7], 0x00); assert_eq!(bytes[8], 0x02); assert_eq!(&bytes[9..11], &[0x01, 0x00]);
844 assert_eq!(bytes[11], 0x01);
845 assert_eq!(SdStart::parse(&bytes).unwrap(), s);
846 let parsed = SdStart::parse(&bytes).unwrap();
848 if let SamplePayload::Tracks(t) = &parsed.payload {
849 assert_eq!(t[0].track_pid, 0x0100);
850 assert_eq!(t[1].track_pid, 0x0101);
851 assert_eq!(t.len(), 2);
852 } else {
853 panic!("expected Tracks");
854 }
855 }
856
857 #[test]
858 fn sd_start_reply_round_trips_and_bites() {
859 let r = SdStartReply {
860 lts_id: 0x05,
861 transmission_status: TransmissionStatus::ReadyToReceive,
862 drm_status: DrmStatus::DecryptionPossible,
863 drm_system_id: 0x4AD4,
864 drm_uuid: UUID_A,
865 buffer_size: 5000,
866 data_block_size: 0,
867 };
868 let bytes = r.to_bytes();
869 assert_eq!(bytes[0..4], [0x9F, 0x98, 0x03, 0x19]);
871 assert_eq!(bytes[4], 0x05); assert_eq!(bytes[5], 0x00); assert_eq!(bytes[6], 0x00); assert_eq!(&bytes[7..9], &[0x4A, 0xD4]);
875 assert_eq!(&bytes[9..25], &UUID_A);
876 assert_eq!(&bytes[25..27], &5000u16.to_be_bytes());
877 assert_eq!(&bytes[27..29], &[0x00, 0x00]);
878 assert_eq!(SdStartReply::parse(&bytes).unwrap(), r);
879 let mut other = r;
880 other.drm_status = DrmStatus::NoEntitlement;
881 assert_eq!(other.to_bytes()[6], 0x02);
882 assert_ne!(bytes, other.to_bytes());
883 }
884
885 #[test]
886 fn sd_update_round_trips() {
887 let u = SdUpdate {
888 lts_id: 0x09,
889 payload: SamplePayload::Tracks(alloc::vec![
890 SampleTrack {
891 track_pid: 0x0200,
892 records: alloc::vec![DrmMetadataRecord {
893 drm_metadata_source: 0x05,
894 drm_system_id: 0xFFFF,
895 drm_uuid: UUID_B,
896 drm_metadata: &[],
897 }],
898 },
899 SampleTrack {
900 track_pid: 0x0201,
901 records: Vec::new(),
902 },
903 ]),
904 };
905 let bytes = u.to_bytes();
906 assert_eq!(bytes[0..3], [0x9F, 0x98, 0x04]);
907 assert_eq!(bytes[4], 0x09); assert_eq!(bytes[5], 0x00); assert_eq!(bytes[6], 0x02); assert_eq!(SdUpdate::parse(&bytes).unwrap(), u);
911 }
912
913 #[test]
914 fn sd_update_reply_round_trips_and_bites() {
915 let r = SdUpdateReply {
916 lts_id: 0x03,
917 drm_status: DrmStatus::Undetermined,
918 };
919 let bytes = r.to_bytes();
920 assert_eq!(bytes, [0x9F, 0x98, 0x05, 0x02, 0x03, 0x01]);
921 assert_eq!(SdUpdateReply::parse(&bytes).unwrap(), r);
922 let other = SdUpdateReply {
923 lts_id: 0x03,
924 drm_status: DrmStatus::DecryptionPossible,
925 };
926 assert_ne!(bytes, other.to_bytes());
927 }
928
929 #[test]
930 fn dispatch_routes_each_tag() {
931 let req = SdInfoReq.to_bytes();
932 assert!(matches!(
933 SampleDecryptionApdu::parse(&req).unwrap(),
934 SampleDecryptionApdu::SdInfoReq(_)
935 ));
936 let reply = SdUpdateReply {
937 lts_id: 0,
938 drm_status: DrmStatus::DecryptionPossible,
939 }
940 .to_bytes();
941 let parsed = SampleDecryptionApdu::parse(&reply).unwrap();
942 assert!(matches!(parsed, SampleDecryptionApdu::SdUpdateReply(_)));
943 assert_eq!(parsed.to_bytes(), reply);
944 assert!(matches!(
945 SampleDecryptionApdu::parse(&[0x9F, 0x98, 0x7E, 0x00]),
946 Err(Error::UnexpectedApduTag { .. })
947 ));
948 }
949}