1pub mod playready;
33pub mod widevine;
34pub mod irdeto;
35pub mod nagra;
36pub mod wiseplay;
37
38use std::fmt;
39use std::io::{self, 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!("6770616363656e6364726d746f6f6c31") {
209 "GPAC"
211 } else if self.id == hex!("edef8ba979d64acea3c827dcd51d21ed") {
212 "Widevine"
213 } else if self.id == hex!("9a04f07998404286ab92e65be0885f95") {
214 "PlayReady"
215 } else if self.id == hex!("6dd8b3c345f44a68bf3a64168d01a4a6") {
216 "ABV"
217 } else if self.id == hex!("f239e769efa348509c16a903c6932efb") {
218 "Adobe Primetime"
219 } else if self.id == hex!("616c7469636173742d50726f74656374") {
220 "Alticast"
221 } else if self.id == hex!("94ce86fb07ff4f43adb893d2fa968ca2") {
222 "Apple FairPlay"
223 } else if self.id == hex!("29701fe43cc74a348c5bae90c7439a47") {
224 "Apple FairPlay-Netflix variant"
227 } else if self.id == hex!("3ea8778f77424bf9b18be834b2acbd47") {
228 "ClearKey AES-128"
229 } else if self.id == hex!("be58615b19c4468488b3c8c57e99e957") {
230 "ClearKey SAMPLE-AES"
231 } else if self.id == hex!("e2719d58a985b3c9781ab030af78d30e") {
232 "ClearKey DASH-IF"
233 } else if self.id == hex!("45d481cb8fe049c0ada9ab2d2455b2f2") {
234 "CoreTrust"
235 } else if self.id == hex!("80a6be7e14484c379e70d5aebe04c8d2") {
236 "Irdeto"
237 } else if self.id == hex!("5e629af538da4063897797ffbd9902d4") {
238 "Marlin"
239 } else if self.id == hex!("adb41c242dbf4a6d958b4457c0d27b95") {
240 "Nagra"
241 } else if self.id == hex!("1f83e1e86ee94f0dba2f5ec4e3ed1a66") {
242 "SecureMedia"
243 } else if self.id == hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") {
244 "WisePlay-ChinaDRM"
247 } else if self.id == hex!("793b79569f944946a94223e7ef7e44b4") {
248 "VisionCrypt"
249 } else {
250 "Unknown"
251 };
252 let hex = hex::encode(self.id);
253 write!(f, "{}/DRMSystemId<{}-{}-{}-{}-{}>",
254 family,
255 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
256 }
257}
258
259impl fmt::Debug for DRMSystemId {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 write!(f, "DRMSystemId<{}>", hex::encode(self.id))
262 }
263}
264
265pub const COMMON_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("1077efecc0b24d02ace33c1e52e2fb4b") };
266pub const CENC_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("69f908af481646ea910ccd5dcccb0a3a") };
267pub const WIDEVINE_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("edef8ba979d64acea3c827dcd51d21ed") };
268pub const PLAYREADY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("9a04f07998404286ab92e65be0885f95") };
269pub const FAIRPLAYNFLX_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("29701fe43cc74a348c5bae90c7439a47") };
270pub const IRDETO_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("80a6be7e14484c379e70d5aebe04c8d2") };
271pub const MARLIN_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("5e629af538da4063897797ffbd9902d4") };
272pub const NAGRA_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("adb41c242dbf4a6d958b4457c0d27b95") };
273pub const WISEPLAY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") };
274
275#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromBytes)]
277pub struct DRMKeyId {
278 id: [u8; 16],
279}
280
281impl TryFrom<&[u8]> for DRMKeyId {
282 type Error = ();
283
284 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
285 if let Ok(id) = value.try_into() {
286 Ok(DRMKeyId { id })
287 } else {
288 Err(())
289 }
290 }
291}
292
293impl TryFrom<Vec<u8>> for DRMKeyId {
294 type Error = ();
295
296 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
297 if value.len() == 16 {
298 DRMKeyId::try_from(&value[0..16])
299 } else {
300 Err(())
301 }
302 }
303}
304
305impl TryFrom<&str> for DRMKeyId {
306 type Error = ();
307
308 fn try_from(value: &str) -> Result<Self, Self::Error> {
309 if value.len() == 32 {
310 if let Ok(id) = hex::decode(value) {
311 return DRMKeyId::try_from(id);
312 }
313 }
314 if value.len() == 36 {
316 let v36 = value.as_bytes();
317 if v36[8] == b'-' &&
318 v36[13] == b'-' &&
319 v36[18] == b'-' &&
320 v36[23] == b'-'
321 {
322 let maybe_hex = value.replace('-', "");
323 if let Ok(id) = hex::decode(maybe_hex) {
324 return DRMKeyId::try_from(id);
325 }
326 }
327 }
328 Err(())
329 }
330}
331
332impl ToBytes for DRMKeyId {
333 fn to_bytes(&self) -> Vec<u8> {
334 self.id.into()
335 }
336}
337
338impl fmt::Display for DRMKeyId {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 let hex = hex::encode(self.id);
342 write!(f, "DRMKeyId<{}-{}-{}-{}-{}>",
343 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
344 }
345}
346
347impl fmt::Debug for DRMKeyId {
348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 write!(f, "DRMKeyId<{}>", hex::encode(self.id))
350 }
351}
352
353
354#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
356pub struct PsshBox {
357 pub version: u8,
358 pub flags: u32,
359 pub system_id: DRMSystemId,
360 pub key_ids: Vec<DRMKeyId>,
361 pub pssh_data: PsshData,
362}
363
364impl PsshBox {
365 pub fn new_widevine() -> PsshBox {
367 let empty = WidevinePsshData {
368 provider: None,
369 ..Default::default()
370 };
371 PsshBox {
372 version: 1,
373 flags: 0,
374 system_id: WIDEVINE_SYSTEM_ID,
375 key_ids: vec![],
376 pssh_data: PsshData::Widevine(empty),
377 }
378 }
379
380 pub fn new_playready() -> PsshBox {
382 let empty = PlayReadyPsshData::new();
383 PsshBox {
384 version: 1,
385 flags: 0,
386 system_id: PLAYREADY_SYSTEM_ID,
387 key_ids: vec![],
388 pssh_data: PsshData::PlayReady(empty),
389 }
390 }
391
392 pub fn add_key_id(&mut self, kid: DRMKeyId) {
393 self.key_ids.push(kid);
394 }
395
396 pub fn to_base64(self) -> String {
397 BASE64_STANDARD.encode(self.to_bytes())
398 }
399
400 pub fn to_hex(self) -> String {
401 hex::encode(self.to_bytes())
402 }
403}
404
405impl fmt::Display for PsshBox {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 let mut keys = Vec::new();
410 if self.version == 1 {
411 for key in &self.key_ids {
412 keys.push(hex::encode(key.id));
413 }
414 }
415 let key_str = match keys.len() {
416 0 => String::from(""),
417 1 => format!("key_id: {}, ", keys.first().unwrap()),
418 _ => format!("key_ids: {}, ", keys.join(", ")),
419 };
420 match &self.pssh_data {
421 PsshData::Widevine(wv) => {
422 let mut items = Vec::new();
423 let json = wv.to_json();
424 if let Some(alg) = json.get("algorithm") {
425 if let Some(a) = alg.as_str() {
426 items.push(String::from(a));
427 }
428 }
429 if let Some(kav) = json.get("key_id") {
432 if let Some(ka) = kav.as_array() {
433 for kv in ka {
434 if let Some(k) = kv.as_str() {
435 keys.push(String::from(k));
436 }
437 }
438 }
439 }
440 if keys.len() == 1 {
441 items.push(format!("key_id: {}", keys.first().unwrap()));
442 }
443 if keys.len() > 1 {
444 items.push(format!("key_ids: {}", keys.join(", ")));
445 }
446 if let Some(jo) = json.as_object() {
447 for (k, v) in jo.iter() {
448 if k.ne("algorithm") && k.ne("key_id") {
449 items.push(format!("{k}: {v}"));
450 }
451 }
452 }
453 write!(f, "WidevinePSSH<{}>", items.join(", "))
454 },
455 PsshData::PlayReady(pr) => write!(f, "PlayReadyPSSH<{key_str}{pr:?}>"),
456 PsshData::Irdeto(pd) => write!(f, "IrdetoPSSH<{key_str}{}>", pd.xml),
457 PsshData::Marlin(pd) => write!(f, " MarlinPSSH<{key_str}pssh data len {} octets>", pd.len()),
458 PsshData::Nagra(pd) => write!(f, "NagraPSSH<{key_str}{pd:?}>"),
459 PsshData::WisePlay(pd) => write!(f, "WisePlayPSSH<{key_str}{}>", pd.json),
460 PsshData::CommonEnc(pd) => write!(f, "CommonPSSH<{key_str}pssh data len {} octets>", pd.len()),
461 PsshData::FairPlay(pd) => write!(f, "FairPlayPSSH<{key_str}pssh data len {} octets>", pd.len()),
462 }
463 }
464}
465
466
467impl ToBytes for PsshBox {
468 #[allow(unused_must_use)]
469 fn to_bytes(self: &PsshBox) -> Vec<u8> {
470 let mut out = Vec::new();
471 let pssh_data_bytes = self.pssh_data.to_bytes();
472 let mut total_length: u32 = 4 + 4 + 4 + 16 + 4 + pssh_data_bytes.len() as u32;
478 if self.version == 1 {
479 total_length += 4 + self.key_ids.len() as u32 * 16;
481 }
482 out.write_u32::<BigEndian>(total_length);
483 out.write_all(b"pssh");
484 let version_and_flags: u32 = self.flags ^ ((self.version as u32) << 24);
485 out.write_u32::<BigEndian>(version_and_flags);
486 out.write_all(&self.system_id.id);
487 if self.version == 1 {
488 out.write_u32::<BigEndian>(self.key_ids.len() as u32);
489 for k in &self.key_ids {
490 out.write_all(&k.id);
491 }
492 }
493 out.write_u32::<BigEndian>(pssh_data_bytes.len() as u32);
494 out.write_all(&pssh_data_bytes);
495 out
496 }
497}
498
499
500#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
501pub struct PsshBoxVec(Vec<PsshBox>);
502
503impl PsshBoxVec {
504 pub fn new() -> PsshBoxVec {
505 PsshBoxVec(Vec::new())
506 }
507
508 pub fn contains(&self, bx: &PsshBox) -> bool {
509 self.0.contains(bx)
510 }
511
512 pub fn add(&mut self, bx: PsshBox) {
513 self.0.push(bx);
514 }
515
516 pub fn len(&self) -> usize {
517 self.0.len()
518 }
519
520 pub fn is_empty(&self) -> bool {
521 self.0.is_empty()
522 }
523
524 pub fn iter(&self) -> impl Iterator<Item=&PsshBox>{
525 self.0.iter()
526 }
527
528 pub fn to_base64(self) -> String {
529 let mut buf = Vec::new();
530 for bx in self.0 {
531 buf.append(&mut bx.to_bytes());
532 }
533 BASE64_STANDARD.encode(buf)
534 }
535
536 pub fn to_hex(self) -> String {
537 let mut buf = Vec::new();
538 for bx in self.0 {
539 buf.append(&mut bx.to_bytes());
540 }
541 hex::encode(buf)
542 }
543}
544
545impl Default for PsshBoxVec {
546 fn default() -> Self {
547 Self::new()
548 }
549}
550
551impl IntoIterator for PsshBoxVec {
552 type Item = PsshBox;
553 type IntoIter = std::vec::IntoIter<Self::Item>;
554
555 fn into_iter(self) -> Self::IntoIter {
556 self.0.into_iter()
557 }
558}
559
560impl std::ops::Index<usize> for PsshBoxVec {
561 type Output = PsshBox;
562
563 fn index(&self, index: usize) -> &PsshBox {
564 &self.0[index]
565 }
566}
567
568impl fmt::Display for PsshBoxVec {
569 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
570 let mut items = Vec::new();
571 for pssh in self.iter() {
572 items.push(pssh.to_string());
573 }
574 write!(f, "{}", items.join("\n"))
576 }
577}
578
579pub fn from_base64(init_data: &str) -> Result<PsshBoxVec> {
585 let b64_tolerant_config = engine::GeneralPurposeConfig::new()
586 .with_decode_allow_trailing_bits(true)
587 .with_decode_padding_mode(engine::DecodePaddingMode::Indifferent);
588 let b64_tolerant_engine = engine::GeneralPurpose::new(&base64::alphabet::STANDARD, b64_tolerant_config);
589 if init_data.len() < 8 {
590 return Err(anyhow!("insufficient length for init data"));
591 }
592 if let Ok(buf) = b64_tolerant_engine.decode(init_data) {
594 return from_bytes(&buf);
595 }
596 let total_len = init_data.len();
600 let mut start = 0;
601 let mut boxes = Vec::new();
602 while start < total_len - 1 {
603 let buf = b64_tolerant_engine.decode(&init_data[start..start+7])
604 .context("base64 decoding first 32-bit length word")?;
605 let mut rdr = Cursor::new(buf);
606 let box_size: u32 = rdr.read_u32::<BigEndian>()
607 .context("reading PSSH box size")?;
608 trace!("box size from header = {box_size}");
609 let wanted_octets = (box_size.div_ceil(3) * 4) as usize;
611 let end = start + wanted_octets;
612 trace!("attempting to decode {wanted_octets} octets out of {}", init_data.len());
613 if end > init_data.len() {
614 return Err(anyhow!("insufficient length for init data (wanted {end}, have {})", init_data.len()));
617 }
618 let buf = b64_tolerant_engine.decode(&init_data[start..end])
619 .context("decoding base64")?;
620 let bx = from_bytes(&buf)
621 .context("parsing the PSSH initialization data")?;
622 assert!(bx.len() == 1);
623 trace!("Got one box {}", bx[0].clone());
624 boxes.push(bx[0].clone());
625 start = end;
626 }
627 Ok(PsshBoxVec(boxes))
628}
629
630pub fn from_hex(init_data: &str) -> Result<PsshBoxVec> {
632 let buf = hex::decode(init_data)
633 .context("decoding hex")?;
634 from_bytes(&buf)
635 .context("parsing the PSSH initialization_data")
636}
637
638fn read_pssh_box(rdr: &mut Cursor<&[u8]>) -> Result<PsshBox> {
640 let size: u32 = rdr.read_u32::<BigEndian>()
641 .context("reading PSSH box size")?;
642 trace!("PSSH box of size {size} octets");
643 let mut box_header = [0u8; 4];
644 rdr.read_exact(&mut box_header)
645 .context("reading box header")?;
646 if !box_header.eq(b"pssh") {
648 return Err(anyhow!("expecting BMFF header"));
649 }
650 let version_and_flags: u32 = rdr.read_u32::<BigEndian>()
651 .context("reading PSSH version/flags")?;
652 let version: u8 = (version_and_flags >> 24).try_into().unwrap();
653 trace!("PSSH box version {version}");
654 if version > 1 {
655 return Err(anyhow!("unknown PSSH version {version}"));
656 }
657 let mut system_id_buf = [0u8; 16];
658 rdr.read_exact(&mut system_id_buf)
659 .context("reading system_id")?;
660 let system_id = DRMSystemId { id: system_id_buf };
661 let mut key_ids = Vec::new();
662 if version == 1 {
663 let mut kid_count = rdr.read_u32::<BigEndian>()
664 .context("reading KID count")?;
665 trace!("PSSH box has {kid_count} KIDs in box header");
666 while kid_count > 0 {
667 let mut key = [0u8; 16];
668 rdr.read_exact(&mut key)
669 .context("reading key_id")?;
670 key_ids.push(DRMKeyId { id: key });
671 kid_count -= 1;
672 }
673 }
674 let pssh_data_len = rdr.read_u32::<BigEndian>()
675 .context("reading PSSH data length")?;
676 trace!("PSSH box data length {pssh_data_len} octets");
677 let mut pssh_data = Vec::new();
678 rdr.take(pssh_data_len.into()).read_to_end(&mut pssh_data)
679 .context("extracting PSSH data")?;
680 match system_id {
681 WIDEVINE_SYSTEM_ID => {
682 let wv_pssh_data = WidevinePsshData::decode(Cursor::new(pssh_data))
683 .context("parsing Widevine PSSH data")?;
684 Ok(PsshBox {
685 version,
686 flags: version_and_flags & 0xF,
687 system_id,
688 key_ids,
689 pssh_data: PsshData::Widevine(wv_pssh_data),
690 })
691 },
692 PLAYREADY_SYSTEM_ID => {
693 let pr_pssh_data = playready::parse_pssh_data(&pssh_data)
694 .context("parsing PlayReady PSSH data")?;
695 Ok(PsshBox {
696 version,
697 flags: version_and_flags & 0xF,
698 system_id,
699 key_ids,
700 pssh_data: PsshData::PlayReady(pr_pssh_data),
701 })
702 },
703 IRDETO_SYSTEM_ID => {
704 let ir_pssh_data = irdeto::parse_pssh_data(&pssh_data)
705 .context("parsing Irdeto PSSH data")?;
706 Ok(PsshBox {
707 version,
708 flags: version_and_flags & 0xF,
709 system_id,
710 key_ids,
711 pssh_data: PsshData::Irdeto(ir_pssh_data),
712 })
713 },
714 MARLIN_SYSTEM_ID => {
715 Ok(PsshBox {
716 version,
717 flags: version_and_flags & 0xF,
718 system_id,
719 key_ids,
720 pssh_data: PsshData::Marlin(pssh_data),
721 })
722 },
723 NAGRA_SYSTEM_ID => {
724 let pd = nagra::parse_pssh_data(&pssh_data)
725 .context("parsing Nagra PSSH data")?;
726 Ok(PsshBox {
727 version,
728 flags: version_and_flags & 0xF,
729 system_id,
730 key_ids,
731 pssh_data: PsshData::Nagra(pd),
732 })
733 },
734 WISEPLAY_SYSTEM_ID => {
735 let cdrm_pssh_data = wiseplay::parse_pssh_data(&pssh_data)
736 .context("parsing WisePlay PSSH data")?;
737 Ok(PsshBox {
738 version,
739 flags: version_and_flags & 0xF,
740 system_id,
741 key_ids,
742 pssh_data: PsshData::WisePlay(cdrm_pssh_data),
743 })
744 },
745 COMMON_SYSTEM_ID => {
746 Ok(PsshBox {
747 version,
748 flags: version_and_flags & 0xF,
749 system_id,
750 key_ids,
751 pssh_data: PsshData::CommonEnc(pssh_data),
752 })
753 },
754 FAIRPLAYNFLX_SYSTEM_ID => {
755 Ok(PsshBox {
756 version,
757 flags: version_and_flags & 0xF,
758 system_id,
759 key_ids,
760 pssh_data: PsshData::FairPlay(pssh_data),
761 })
762 },
763 _ => Err(anyhow!("can't parse this system_id type: {:?}", system_id)),
764 }
765}
766
767pub fn from_bytes(init_data: &[u8]) -> Result<PsshBoxVec> {
770 let total_len = init_data.len();
771 let mut rdr = Cursor::new(init_data);
772 let mut boxes = PsshBoxVec::new();
773 while (rdr.position() as usize) < total_len - 1 {
774 let bx = read_pssh_box(&mut rdr)?;
775 boxes.add(bx.clone());
776 trace!("Read one box {bx} from bytes, remaining {} octets", total_len as u64 - rdr.position());
777 let pos = rdr.position() as usize;
778 if let Some(remaining) = &rdr.get_ref().get(pos..total_len) {
779 if remaining.iter().all(|b| *b == 0) {
781 break;
782 }
783 }
784 }
785 Ok(boxes)
786}
787
788pub fn from_buffer(init_data: &[u8]) -> Result<PsshBoxVec> {
791 let total_len = init_data.len();
792 let mut rdr = Cursor::new(init_data);
793 let mut boxes = PsshBoxVec::new();
794 while (rdr.position() as usize) < total_len - 1 {
795 if let Ok(bx) = read_pssh_box(&mut rdr) {
796 boxes.add(bx);
797 } else {
798 break;
799 }
800 }
801 Ok(boxes)
802}
803
804pub fn find_iter(buffer: &[u8]) -> impl Iterator<Item = usize> + '_ {
807 use bstr::ByteSlice;
808
809 buffer.find_iter(b"pssh")
810 .filter(|offset| {
811 if offset+24 > buffer.len() {
812 return false;
813 }
814 if offset+4 < 8 {
815 return false;
816 }
817 let start = offset - 4;
818 let mut rdr = Cursor::new(&buffer[start..]);
819 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
820 let end = start + size as usize;
821 if end > buffer.len() {
822 return false;
823 }
824 from_bytes(&buffer[start..end]).is_ok()
825 })
826 .map(|offset| offset - 4)
827}
828
829
830pub fn find_boxes_buffer(buffer: &[u8]) -> impl Iterator<Item = PsshBox> + '_ {
833 use bstr::ByteSlice;
834
835 let mut boxes = Vec::new();
836 for offset in buffer.find_iter(b"pssh") {
837 if offset+24 > buffer.len() || offset+4 < 8 {
838 break;
839 }
840 let start = offset - 4;
841 let mut rdr = Cursor::new(&buffer[start..]);
842 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
843 let end = start + size as usize;
844 if end > buffer.len() {
845 break;
846 }
847 if let Ok(pbv) = from_bytes(&buffer[start..end]) {
848 for pb in pbv {
849 boxes.push(pb);
850 }
851 }
852 }
853 boxes.into_iter()
854}
855
856
857pub fn find_boxes_stream<R>(reader: R) -> impl Iterator<Item = Result<PsshBox, io::Error>>
863where
864 R: Read,
865{
866 PsshBoxIterator::new(reader)
867}
868
869struct PsshBoxIterator<R> {
870 reader: R,
871 buffer: Vec<u8>,
872 buffer_pos: usize,
873 pending_boxes : Vec<PsshBox>,
874}
875
876impl<R> PsshBoxIterator<R> {
877 fn new(reader: R) -> Self {
878 PsshBoxIterator {
879 reader,
880 buffer: Vec::new(),
881 buffer_pos: 0,
882 pending_boxes: Vec::new(),
883 }
884 }
885}
886
887impl<R> Iterator for PsshBoxIterator<R>
888where
889 R: Read,
890{
891 type Item = Result<PsshBox, io::Error>;
892
893 fn next(&mut self) -> Option<Self::Item> {
894 use bstr::ByteSlice;
895
896 if let Some(bx) = self.pending_boxes.pop() {
897 return Some(Ok(bx));
898 }
899 let mut buf = [0; 1024 * 1024];
900 loop {
901 if self.buffer_pos > 0 {
902 self.buffer = self.buffer.split_off(self.buffer_pos);
903 }
904 let bytes_read = match self.reader.read(&mut buf) {
905 Ok(n) => n,
906 Err(e) => return Some(Err(e)),
907 };
908 if self.buffer_pos == 0 && bytes_read == 0 {
909 return None;
910 }
911 self.buffer.extend_from_slice(&buf[..bytes_read]);
912 self.buffer_pos = 0;
913 if let Some(offset) = self.buffer.find(b"pssh") {
914 trace!("Found pssh cookie at offset {offset}");
915 if offset + 24 > self.buffer.len() {
916 self.buffer_pos = offset + 4;
917 continue;
918 }
919 if offset < 4 {
920 self.buffer_pos = 4;
921 continue;
922 }
923 let start = offset - 4;
924 let buffer_len = self.buffer.len();
925 let mut rdr = Cursor::new(&self.buffer[start..buffer_len]);
926 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
927 let end = start + size as usize;
928 if end > self.buffer.len() {
929 self.buffer_pos = offset + 1;
930 continue;
931 }
932 if let Ok(pbv) = from_bytes(&self.buffer[start..end]) {
933 self.buffer_pos = end;
934 for pb in pbv {
935 self.pending_boxes.push(pb);
936 }
937 if let Some(bx) = self.pending_boxes.pop() {
938 return Some(Ok(bx));
939 }
940 } else {
941 self.buffer_pos = offset + 4;
942 }
943 } else {
944 if self.buffer.len() >= 3 {
947 self.buffer_pos = self.buffer.len() - 3;
948 }
949 }
950 }
951 }
952}
953
954
955pub fn pprint(pssh: &PsshBox) {
957 println!("PSSH Box v{}", pssh.version);
958 println!(" SystemID: {}", pssh.system_id);
959 if pssh.version == 1 {
960 for key in &pssh.key_ids {
961 println!(" Key ID: {key}");
962 }
963 }
964 match &pssh.pssh_data {
965 PsshData::Widevine(wv) => println!(" {wv:?}"),
966 PsshData::PlayReady(pr) => println!(" {pr:?}"),
967 PsshData::Irdeto(pd) => {
968 println!("Irdeto XML: {}", pd.xml);
969 },
970 PsshData::Marlin(pd) => {
971 println!(" Marlin PSSH data ({} octets)", pd.len());
972 if !pd.is_empty() {
973 println!("== Hexdump of pssh data ==");
974 let mut hxbuf = Vec::new();
975 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
976 println!("{}", String::from_utf8_lossy(&hxbuf));
977 }
978 },
979 PsshData::Nagra(pd) => println!(" {pd:?}"),
980 PsshData::WisePlay(pd) => {
981 println!(" WisePlay JSON: {}", pd.json);
982 },
983 PsshData::CommonEnc(pd) => {
984 println!(" Common PSSH data ({} octets)", pd.len());
985 if !pd.is_empty() {
986 println!("== Hexdump of pssh data ==");
987 let mut hxbuf = Vec::new();
988 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
989 println!("{}", String::from_utf8_lossy(&hxbuf));
990 }
991 },
992 PsshData::FairPlay(pd) => {
993 println!(" FairPlay PSSH data ({} octets)", pd.len());
994 if !pd.is_empty() {
995 println!("== Hexdump of pssh data ==");
996 let mut hxbuf = Vec::new();
997 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
998 println!("{}", String::from_utf8_lossy(&hxbuf));
999 }
1000 },
1001 }
1002}