1pub mod playready;
33pub mod widevine;
34pub mod irdeto;
35pub mod nagra;
36pub mod wiseplay;
37
38use std::fmt;
39use std::io::{Cursor, Read, Write};
40use hex_literal::hex;
41use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
42use zerocopy::FromBytes;
43use serde::{Serialize, Deserialize};
44use prost::Message;
45use base64::prelude::{Engine as _, BASE64_STANDARD};
46use base64::engine;
47use anyhow::{Result, Context, anyhow};
48use tracing::trace;
49use crate::widevine::WidevinePsshData;
50use crate::playready::PlayReadyPsshData;
51use crate::irdeto::IrdetoPsshData;
52use crate::nagra::NagraPsshData;
53use crate::wiseplay::WisePlayPsshData;
54
55
56pub fn version() -> &'static str {
58 env!("CARGO_PKG_VERSION")
59}
60
61pub trait ToBytes {
62 fn to_bytes(&self) -> Vec<u8>;
63}
64
65#[non_exhaustive]
67#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
68pub enum PsshData {
69 Widevine(WidevinePsshData),
70 PlayReady(PlayReadyPsshData),
71 Irdeto(IrdetoPsshData),
72 WisePlay(WisePlayPsshData),
73 Nagra(NagraPsshData),
74 Marlin(Vec<u8>),
75 CommonEnc(Vec<u8>),
76 FairPlay(Vec<u8>),
77}
78
79impl ToBytes for PsshData {
80 fn to_bytes(&self) -> Vec<u8> {
81 match self {
82 PsshData::Widevine(wv) => wv.to_bytes(),
83 PsshData::PlayReady(pr) => pr.to_bytes(),
84 PsshData::Irdeto(ir) => ir.to_bytes(),
85 PsshData::WisePlay(c) => c.to_bytes(),
86 PsshData::Nagra(n) => n.to_bytes(),
87 PsshData::Marlin(m) => m.to_vec(),
88 PsshData::CommonEnc(c) => c.to_vec(),
89 PsshData::FairPlay(c) => c.to_vec(),
90 }
91 }
92}
93
94impl fmt::Display for PsshData {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 match self {
97 PsshData::Widevine(wv) => {
98 let mut items = Vec::new();
99 let mut keys = Vec::new();
100 let json = wv.to_json();
101 if let Some(alg) = json.get("algorithm") {
102 if let Some(a) = alg.as_str() {
103 items.push(String::from(a));
104 }
105 }
106 if let Some(content_id) = json.get("content_id") {
107 if let Some(cid_hex) = content_id.as_str() {
108 if let Ok(cid_octets) = hex::decode(cid_hex) {
109 if let Ok(cid) = String::from_utf8(cid_octets) {
110 items.push(format!("content_id: \"{cid}\""));
111 }
112 }
113 }
114 }
115 if let Some(kav) = json.get("key_id") {
116 if let Some(ka) = kav.as_array() {
117 for kv in ka {
118 if let Some(k) = kv.as_str() {
119 keys.push(String::from(k));
120 }
121 }
122 }
123 }
124 if keys.len() == 1 {
125 if let Some(key) = keys.first() {
126 items.push(format!("key_id: {}", key));
127 }
128 }
129 if keys.len() > 1 {
130 items.push(format!("key_ids: {}", keys.join(", ")));
131 }
132 if let Some(jo) = json.as_object() {
133 for (k, v) in jo.iter() {
134 if k.ne("algorithm") && k.ne("key_id") && k.ne("content_id") {
135 items.push(format!("{k}: {v}"));
136 }
137 }
138 }
139 write!(f, "WidevinePSSHData<{}>", items.join(", "))
140 },
141 PsshData::PlayReady(pr) => write!(f, "PlayReadyPSSHData<{pr:?}>"),
142 PsshData::Irdeto(pd) => write!(f, "IrdetoPSSHData<{}>", pd.xml),
143 PsshData::Marlin(pd) => write!(f, " MarlinPSSHData<len {} octets>", pd.len()),
144 PsshData::Nagra(pd) => write!(f, "NagraPSSHData<{pd:?}>"),
145 PsshData::WisePlay(pd) => write!(f, "WisePlayPSSHData<{}>", pd.json),
146 PsshData::CommonEnc(pd) => write!(f, "CommonPSSHData<len {} octets>", pd.len()),
147 PsshData::FairPlay(pd) => write!(f, "FairPlayPSSHData<len {} octets>", pd.len()),
148 }
149 }
150}
151
152#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromBytes)]
154pub struct DRMSystemId {
155 id: [u8; 16],
156}
157
158impl TryFrom<&[u8]> for DRMSystemId {
159 type Error = ();
160
161 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
162 if let Ok(id) = value.try_into() {
163 Ok(DRMSystemId { id })
164 } else {
165 Err(())
166 }
167 }
168}
169
170impl TryFrom<Vec<u8>> for DRMSystemId {
171 type Error = ();
172
173 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
174 if value.len() == 16 {
175 DRMSystemId::try_from(&value[0..16])
176 } else {
177 Err(())
178 }
179 }
180}
181
182impl TryFrom<&str> for DRMSystemId {
183 type Error = ();
184
185 fn try_from(value: &str) -> Result<Self, Self::Error> {
186 if value.len() == 32 {
187 if let Ok(id) = hex::decode(value) {
188 return DRMSystemId::try_from(id);
189 }
190 }
191 Err(())
192 }
193}
194
195impl ToBytes for DRMSystemId {
196 fn to_bytes(&self) -> Vec<u8> {
197 self.id.into()
198 }
199}
200
201impl fmt::Display for DRMSystemId {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 let family = if self.id == hex!("1077efecc0b24d02ace33c1e52e2fb4b") {
205 "Common"
206 } else if self.id == hex!("69f908af481646ea910ccd5dcccb0a3a") {
207 "CENC"
208 } else if self.id == hex!("edef8ba979d64acea3c827dcd51d21ed") {
209 "Widevine"
210 } else if self.id == hex!("9a04f07998404286ab92e65be0885f95") {
211 "PlayReady"
212 } else if self.id == hex!("6dd8b3c345f44a68bf3a64168d01a4a6") {
213 "ABV"
214 } else if self.id == hex!("f239e769efa348509c16a903c6932efb") {
215 "Adobe Primetime"
216 } else if self.id == hex!("616c7469636173742d50726f74656374") {
217 "Alticast"
218 } else if self.id == hex!("94ce86fb07ff4f43adb893d2fa968ca2") {
219 "Apple FairPlay"
220 } else if self.id == hex!("29701fe43cc74a348c5bae90c7439a47") {
221 "Apple FairPlay-Netflix variant"
224 } else if self.id == hex!("3ea8778f77424bf9b18be834b2acbd47") {
225 "ClearKey AES-128"
226 } else if self.id == hex!("be58615b19c4468488b3c8c57e99e957") {
227 "ClearKey SAMPLE-AES"
228 } else if self.id == hex!("e2719d58a985b3c9781ab030af78d30e") {
229 "ClearKey DASH-IF"
230 } else if self.id == hex!("45d481cb8fe049c0ada9ab2d2455b2f2") {
231 "CoreTrust"
232 } else if self.id == hex!("80a6be7e14484c379e70d5aebe04c8d2") {
233 "Irdeto"
234 } else if self.id == hex!("5e629af538da4063897797ffbd9902d4") {
235 "Marlin"
236 } else if self.id == hex!("adb41c242dbf4a6d958b4457c0d27b95") {
237 "Nagra"
238 } else if self.id == hex!("1f83e1e86ee94f0dba2f5ec4e3ed1a66") {
239 "SecureMedia"
240 } else if self.id == hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") {
241 "WisePlay-ChinaDRM"
244 } else if self.id == hex!("793b79569f944946a94223e7ef7e44b4") {
245 "VisionCrypt"
246 } else {
247 "Unknown"
248 };
249 let hex = hex::encode(self.id);
250 write!(f, "{}/DRMSystemId<{}-{}-{}-{}-{}>",
251 family,
252 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
253 }
254}
255
256impl fmt::Debug for DRMSystemId {
257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258 write!(f, "DRMSystemId<{}>", hex::encode(self.id))
259 }
260}
261
262pub const COMMON_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("1077efecc0b24d02ace33c1e52e2fb4b") };
263pub const CENC_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("69f908af481646ea910ccd5dcccb0a3a") };
264pub const WIDEVINE_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("edef8ba979d64acea3c827dcd51d21ed") };
265pub const PLAYREADY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("9a04f07998404286ab92e65be0885f95") };
266pub const FAIRPLAYNFLX_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("29701fe43cc74a348c5bae90c7439a47") };
267pub const IRDETO_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("80a6be7e14484c379e70d5aebe04c8d2") };
268pub const MARLIN_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("5e629af538da4063897797ffbd9902d4") };
269pub const NAGRA_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("adb41c242dbf4a6d958b4457c0d27b95") };
270pub const WISEPLAY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") };
271
272#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromBytes)]
274pub struct DRMKeyId {
275 id: [u8; 16],
276}
277
278impl TryFrom<&[u8]> for DRMKeyId {
279 type Error = ();
280
281 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
282 if let Ok(id) = value.try_into() {
283 Ok(DRMKeyId { id })
284 } else {
285 Err(())
286 }
287 }
288}
289
290impl TryFrom<Vec<u8>> for DRMKeyId {
291 type Error = ();
292
293 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
294 if value.len() == 16 {
295 DRMKeyId::try_from(&value[0..16])
296 } else {
297 Err(())
298 }
299 }
300}
301
302impl TryFrom<&str> for DRMKeyId {
303 type Error = ();
304
305 fn try_from(value: &str) -> Result<Self, Self::Error> {
306 if value.len() == 32 {
307 if let Ok(id) = hex::decode(value) {
308 return DRMKeyId::try_from(id);
309 }
310 }
311 if value.len() == 36 {
313 let v36 = value.as_bytes();
314 if v36[8] == b'-' &&
315 v36[13] == b'-' &&
316 v36[18] == b'-' &&
317 v36[23] == b'-'
318 {
319 let maybe_hex = value.replace('-', "");
320 if let Ok(id) = hex::decode(maybe_hex) {
321 return DRMKeyId::try_from(id);
322 }
323 }
324 }
325 Err(())
326 }
327}
328
329impl ToBytes for DRMKeyId {
330 fn to_bytes(&self) -> Vec<u8> {
331 self.id.into()
332 }
333}
334
335impl fmt::Display for DRMKeyId {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 let hex = hex::encode(self.id);
339 write!(f, "DRMKeyId<{}-{}-{}-{}-{}>",
340 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
341 }
342}
343
344impl fmt::Debug for DRMKeyId {
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 write!(f, "DRMKeyId<{}>", hex::encode(self.id))
347 }
348}
349
350
351#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
353pub struct PsshBox {
354 pub version: u8,
355 pub flags: u32,
356 pub system_id: DRMSystemId,
357 pub key_ids: Vec<DRMKeyId>,
358 pub pssh_data: PsshData,
359}
360
361impl PsshBox {
362 pub fn new_widevine() -> PsshBox {
364 let empty = WidevinePsshData {
365 provider: None,
366 ..Default::default()
367 };
368 PsshBox {
369 version: 1,
370 flags: 0,
371 system_id: WIDEVINE_SYSTEM_ID,
372 key_ids: vec![],
373 pssh_data: PsshData::Widevine(empty),
374 }
375 }
376
377 pub fn new_playready() -> PsshBox {
379 let empty = PlayReadyPsshData::new();
380 PsshBox {
381 version: 1,
382 flags: 0,
383 system_id: PLAYREADY_SYSTEM_ID,
384 key_ids: vec![],
385 pssh_data: PsshData::PlayReady(empty),
386 }
387 }
388
389 pub fn add_key_id(&mut self, kid: DRMKeyId) {
390 self.key_ids.push(kid);
391 }
392
393 pub fn to_base64(self) -> String {
394 BASE64_STANDARD.encode(self.to_bytes())
395 }
396
397 pub fn to_hex(self) -> String {
398 hex::encode(self.to_bytes())
399 }
400}
401
402impl fmt::Display for PsshBox {
405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406 let mut keys = Vec::new();
407 if self.version == 1 {
408 for key in &self.key_ids {
409 keys.push(hex::encode(key.id));
410 }
411 }
412 let key_str = match keys.len() {
413 0 => String::from(""),
414 1 => format!("key_id: {}, ", keys.first().unwrap()),
415 _ => format!("key_ids: {}, ", keys.join(", ")),
416 };
417 match &self.pssh_data {
418 PsshData::Widevine(wv) => {
419 let mut items = Vec::new();
420 let json = wv.to_json();
421 if let Some(alg) = json.get("algorithm") {
422 if let Some(a) = alg.as_str() {
423 items.push(String::from(a));
424 }
425 }
426 if let Some(kav) = json.get("key_id") {
429 if let Some(ka) = kav.as_array() {
430 for kv in ka {
431 if let Some(k) = kv.as_str() {
432 keys.push(String::from(k));
433 }
434 }
435 }
436 }
437 if keys.len() == 1 {
438 items.push(format!("key_id: {}", keys.first().unwrap()));
439 }
440 if keys.len() > 1 {
441 items.push(format!("key_ids: {}", keys.join(", ")));
442 }
443 if let Some(jo) = json.as_object() {
444 for (k, v) in jo.iter() {
445 if k.ne("algorithm") && k.ne("key_id") {
446 items.push(format!("{k}: {v}"));
447 }
448 }
449 }
450 write!(f, "WidevinePSSH<{}>", items.join(", "))
451 },
452 PsshData::PlayReady(pr) => write!(f, "PlayReadyPSSH<{key_str}{pr:?}>"),
453 PsshData::Irdeto(pd) => write!(f, "IrdetoPSSH<{key_str}{}>", pd.xml),
454 PsshData::Marlin(pd) => write!(f, " MarlinPSSH<{key_str}pssh data len {} octets>", pd.len()),
455 PsshData::Nagra(pd) => write!(f, "NagraPSSH<{key_str}{pd:?}>"),
456 PsshData::WisePlay(pd) => write!(f, "WisePlayPSSH<{key_str}{}>", pd.json),
457 PsshData::CommonEnc(pd) => write!(f, "CommonPSSH<{key_str}pssh data len {} octets>", pd.len()),
458 PsshData::FairPlay(pd) => write!(f, "FairPlayPSSH<{key_str}pssh data len {} octets>", pd.len()),
459 }
460 }
461}
462
463
464impl ToBytes for PsshBox {
465 #[allow(unused_must_use)]
466 fn to_bytes(self: &PsshBox) -> Vec<u8> {
467 let mut out = Vec::new();
468 let pssh_data_bytes = self.pssh_data.to_bytes();
469 let mut total_length: u32 = 4 + 4 + 4 + 16 + 4 + pssh_data_bytes.len() as u32;
475 if self.version == 1 {
476 total_length += 4 + self.key_ids.len() as u32 * 16;
478 }
479 out.write_u32::<BigEndian>(total_length);
480 out.write_all(b"pssh");
481 let version_and_flags: u32 = self.flags ^ ((self.version as u32) << 24);
482 out.write_u32::<BigEndian>(version_and_flags);
483 out.write_all(&self.system_id.id);
484 if self.version == 1 {
485 out.write_u32::<BigEndian>(self.key_ids.len() as u32);
486 for k in &self.key_ids {
487 out.write_all(&k.id);
488 }
489 }
490 out.write_u32::<BigEndian>(pssh_data_bytes.len() as u32);
491 out.write_all(&pssh_data_bytes);
492 out
493 }
494}
495
496
497#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
498pub struct PsshBoxVec(Vec<PsshBox>);
499
500impl PsshBoxVec {
501 pub fn new() -> PsshBoxVec {
502 PsshBoxVec(Vec::new())
503 }
504
505 pub fn contains(&self, bx: &PsshBox) -> bool {
506 self.0.contains(bx)
507 }
508
509 pub fn add(&mut self, bx: PsshBox) {
510 self.0.push(bx);
511 }
512
513 pub fn len(&self) -> usize {
514 self.0.len()
515 }
516
517 pub fn is_empty(&self) -> bool {
518 self.0.is_empty()
519 }
520
521 pub fn iter(&self) -> impl Iterator<Item=&PsshBox>{
522 self.0.iter()
523 }
524
525 pub fn to_base64(self) -> String {
526 let mut buf = Vec::new();
527 for bx in self.0 {
528 buf.append(&mut bx.to_bytes());
529 }
530 BASE64_STANDARD.encode(buf)
531 }
532
533 pub fn to_hex(self) -> String {
534 let mut buf = Vec::new();
535 for bx in self.0 {
536 buf.append(&mut bx.to_bytes());
537 }
538 hex::encode(buf)
539 }
540}
541
542impl Default for PsshBoxVec {
543 fn default() -> Self {
544 Self::new()
545 }
546}
547
548impl IntoIterator for PsshBoxVec {
549 type Item = PsshBox;
550 type IntoIter = std::vec::IntoIter<Self::Item>;
551
552 fn into_iter(self) -> Self::IntoIter {
553 self.0.into_iter()
554 }
555}
556
557impl std::ops::Index<usize> for PsshBoxVec {
558 type Output = PsshBox;
559
560 fn index(&self, index: usize) -> &PsshBox {
561 &self.0[index]
562 }
563}
564
565impl fmt::Display for PsshBoxVec {
566 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
567 let mut items = Vec::new();
568 for pssh in self.iter() {
569 items.push(pssh.to_string());
570 }
571 write!(f, "{}", items.join("\n"))
573 }
574}
575
576pub fn from_base64(init_data: &str) -> Result<PsshBoxVec> {
582 let b64_tolerant_config = engine::GeneralPurposeConfig::new()
583 .with_decode_allow_trailing_bits(true)
584 .with_decode_padding_mode(engine::DecodePaddingMode::Indifferent);
585 let b64_tolerant_engine = engine::GeneralPurpose::new(&base64::alphabet::STANDARD, b64_tolerant_config);
586 if init_data.len() < 8 {
587 return Err(anyhow!("insufficient length for init data"));
588 }
589 if let Ok(buf) = b64_tolerant_engine.decode(init_data) {
591 return from_bytes(&buf);
592 }
593 let total_len = init_data.len();
597 let mut start = 0;
598 let mut boxes = Vec::new();
599 while start < total_len - 1 {
600 let buf = b64_tolerant_engine.decode(&init_data[start..start+7])
601 .context("base64 decoding first 32-bit length word")?;
602 let mut rdr = Cursor::new(buf);
603 let box_size: u32 = rdr.read_u32::<BigEndian>()
604 .context("reading PSSH box size")?;
605 trace!("box size from header = {box_size}");
606 let wanted_octets = (box_size.div_ceil(3) * 4) as usize;
608 let end = start + wanted_octets;
609 trace!("attempting to decode {wanted_octets} octets out of {}", init_data.len());
610 if end > init_data.len() {
611 return Err(anyhow!("insufficient length for init data (wanted {end}, have {})", init_data.len()));
614 }
615 let buf = b64_tolerant_engine.decode(&init_data[start..end])
616 .context("decoding base64")?;
617 let bx = from_bytes(&buf)
618 .context("parsing the PSSH initialization data")?;
619 assert!(bx.len() == 1);
620 trace!("Got one box {}", bx[0].clone());
621 boxes.push(bx[0].clone());
622 start = end;
623 }
624 Ok(PsshBoxVec(boxes))
625}
626
627pub fn from_hex(init_data: &str) -> Result<PsshBoxVec> {
629 let buf = hex::decode(init_data)
630 .context("decoding hex")?;
631 from_bytes(&buf)
632 .context("parsing the PSSH initialization_data")
633}
634
635fn read_pssh_box(rdr: &mut Cursor<&[u8]>) -> Result<PsshBox> {
637 let size: u32 = rdr.read_u32::<BigEndian>()
638 .context("reading PSSH box size")?;
639 trace!("PSSH box of size {size} octets");
640 let mut box_header = [0u8; 4];
641 rdr.read_exact(&mut box_header)
642 .context("reading box header")?;
643 if !box_header.eq(b"pssh") {
645 return Err(anyhow!("expecting BMFF header"));
646 }
647 let version_and_flags: u32 = rdr.read_u32::<BigEndian>()
648 .context("reading PSSH version/flags")?;
649 let version: u8 = (version_and_flags >> 24).try_into().unwrap();
650 trace!("PSSH box version {version}");
651 if version > 1 {
652 return Err(anyhow!("unknown PSSH version {version}"));
653 }
654 let mut system_id_buf = [0u8; 16];
655 rdr.read_exact(&mut system_id_buf)
656 .context("reading system_id")?;
657 let system_id = DRMSystemId { id: system_id_buf };
658 let mut key_ids = Vec::new();
659 if version == 1 {
660 let mut kid_count = rdr.read_u32::<BigEndian>()
661 .context("reading KID count")?;
662 trace!("PSSH box has {kid_count} KIDs in box header");
663 while kid_count > 0 {
664 let mut key = [0u8; 16];
665 rdr.read_exact(&mut key)
666 .context("reading key_id")?;
667 key_ids.push(DRMKeyId { id: key });
668 kid_count -= 1;
669 }
670 }
671 let pssh_data_len = rdr.read_u32::<BigEndian>()
672 .context("reading PSSH data length")?;
673 trace!("PSSH box data length {pssh_data_len} octets");
674 let mut pssh_data = Vec::new();
675 rdr.take(pssh_data_len.into()).read_to_end(&mut pssh_data)
676 .context("extracting PSSH data")?;
677 match system_id {
678 WIDEVINE_SYSTEM_ID => {
679 let wv_pssh_data = WidevinePsshData::decode(Cursor::new(pssh_data))
680 .context("parsing Widevine PSSH data")?;
681 Ok(PsshBox {
682 version,
683 flags: version_and_flags & 0xF,
684 system_id,
685 key_ids,
686 pssh_data: PsshData::Widevine(wv_pssh_data),
687 })
688 },
689 PLAYREADY_SYSTEM_ID => {
690 let pr_pssh_data = playready::parse_pssh_data(&pssh_data)
691 .context("parsing PlayReady PSSH data")?;
692 Ok(PsshBox {
693 version,
694 flags: version_and_flags & 0xF,
695 system_id,
696 key_ids,
697 pssh_data: PsshData::PlayReady(pr_pssh_data),
698 })
699 },
700 IRDETO_SYSTEM_ID => {
701 let ir_pssh_data = irdeto::parse_pssh_data(&pssh_data)
702 .context("parsing Irdeto PSSH data")?;
703 Ok(PsshBox {
704 version,
705 flags: version_and_flags & 0xF,
706 system_id,
707 key_ids,
708 pssh_data: PsshData::Irdeto(ir_pssh_data),
709 })
710 },
711 MARLIN_SYSTEM_ID => {
712 Ok(PsshBox {
713 version,
714 flags: version_and_flags & 0xF,
715 system_id,
716 key_ids,
717 pssh_data: PsshData::Marlin(pssh_data),
718 })
719 },
720 NAGRA_SYSTEM_ID => {
721 let pd = nagra::parse_pssh_data(&pssh_data)
722 .context("parsing Nagra PSSH data")?;
723 Ok(PsshBox {
724 version,
725 flags: version_and_flags & 0xF,
726 system_id,
727 key_ids,
728 pssh_data: PsshData::Nagra(pd),
729 })
730 },
731 WISEPLAY_SYSTEM_ID => {
732 let cdrm_pssh_data = wiseplay::parse_pssh_data(&pssh_data)
733 .context("parsing WisePlay PSSH data")?;
734 Ok(PsshBox {
735 version,
736 flags: version_and_flags & 0xF,
737 system_id,
738 key_ids,
739 pssh_data: PsshData::WisePlay(cdrm_pssh_data),
740 })
741 },
742 COMMON_SYSTEM_ID => {
743 Ok(PsshBox {
744 version,
745 flags: version_and_flags & 0xF,
746 system_id,
747 key_ids,
748 pssh_data: PsshData::CommonEnc(pssh_data),
749 })
750 },
751 FAIRPLAYNFLX_SYSTEM_ID => {
752 Ok(PsshBox {
753 version,
754 flags: version_and_flags & 0xF,
755 system_id,
756 key_ids,
757 pssh_data: PsshData::FairPlay(pssh_data),
758 })
759 },
760 _ => Err(anyhow!("can't parse this system_id type: {:?}", system_id)),
761 }
762}
763
764pub fn from_bytes(init_data: &[u8]) -> Result<PsshBoxVec> {
767 let total_len = init_data.len();
768 let mut rdr = Cursor::new(init_data);
769 let mut boxes = PsshBoxVec::new();
770 while (rdr.position() as usize) < total_len - 1 {
771 let bx = read_pssh_box(&mut rdr)?;
772 boxes.add(bx.clone());
773 trace!("Read one box {bx} from bytes, remaining {} octets", total_len as u64 - rdr.position());
774 let pos = rdr.position() as usize;
775 if let Some(remaining) = &rdr.get_ref().get(pos..total_len) {
776 if remaining.iter().all(|b| *b == 0) {
778 break;
779 }
780 }
781 }
782 Ok(boxes)
783}
784
785pub fn from_buffer(init_data: &[u8]) -> Result<PsshBoxVec> {
788 let total_len = init_data.len();
789 let mut rdr = Cursor::new(init_data);
790 let mut boxes = PsshBoxVec::new();
791 while (rdr.position() as usize) < total_len - 1 {
792 if let Ok(bx) = read_pssh_box(&mut rdr) {
793 boxes.add(bx);
794 } else {
795 break;
796 }
797 }
798 Ok(boxes)
799}
800
801pub fn find_iter(buffer: &[u8]) -> impl Iterator<Item = usize> + '_ {
804 use bstr::ByteSlice;
805
806 buffer.find_iter(b"pssh")
807 .filter(|offset| {
808 if offset+24 > buffer.len() {
809 return false;
810 }
811 let start = offset - 4;
812 let mut rdr = Cursor::new(&buffer[start..]);
813 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
814 let end = start + size as usize;
815 from_bytes(&buffer[start..end]).is_ok()
816 })
817 .map(|offset| offset - 4)
818}
819
820pub fn pprint(pssh: &PsshBox) {
822 println!("PSSH Box v{}", pssh.version);
823 println!(" SystemID: {}", pssh.system_id);
824 if pssh.version == 1 {
825 for key in &pssh.key_ids {
826 println!(" Key ID: {}", key);
827 }
828 }
829 match &pssh.pssh_data {
830 PsshData::Widevine(wv) => println!(" {wv:?}"),
831 PsshData::PlayReady(pr) => println!(" {pr:?}"),
832 PsshData::Irdeto(pd) => {
833 println!("Irdeto XML: {}", pd.xml);
834 },
835 PsshData::Marlin(pd) => {
836 println!(" Marlin PSSH data ({} octets)", pd.len());
837 if !pd.is_empty() {
838 println!("== Hexdump of pssh data ==");
839 let mut hxbuf = Vec::new();
840 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
841 println!("{}", String::from_utf8_lossy(&hxbuf));
842 }
843 },
844 PsshData::Nagra(pd) => println!(" {pd:?}"),
845 PsshData::WisePlay(pd) => {
846 println!(" WisePlay JSON: {}", pd.json);
847 },
848 PsshData::CommonEnc(pd) => {
849 println!(" Common PSSH data ({} octets)", pd.len());
850 if !pd.is_empty() {
851 println!("== Hexdump of pssh data ==");
852 let mut hxbuf = Vec::new();
853 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
854 println!("{}", String::from_utf8_lossy(&hxbuf));
855 }
856 },
857 PsshData::FairPlay(pd) => {
858 println!(" FairPlay PSSH data ({} octets)", pd.len());
859 if !pd.is_empty() {
860 println!("== Hexdump of pssh data ==");
861 let mut hxbuf = Vec::new();
862 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
863 println!("{}", String::from_utf8_lossy(&hxbuf));
864 }
865 },
866 }
867}