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 Mobi(Vec<u8>),
78}
79
80impl ToBytes for PsshData {
81 fn to_bytes(&self) -> Vec<u8> {
82 match self {
83 PsshData::Widevine(wv) => wv.to_bytes(),
84 PsshData::PlayReady(pr) => pr.to_bytes(),
85 PsshData::Irdeto(ir) => ir.to_bytes(),
86 PsshData::WisePlay(c) => c.to_bytes(),
87 PsshData::Nagra(n) => n.to_bytes(),
88 PsshData::Marlin(m) => m.to_vec(),
89 PsshData::CommonEnc(c) => c.to_vec(),
90 PsshData::FairPlay(c) => c.to_vec(),
91 PsshData::Mobi(c) => c.to_vec(),
92 }
93 }
94}
95
96impl fmt::Display for PsshData {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match self {
99 PsshData::Widevine(wv) => {
100 let mut items = Vec::new();
101 let mut keys = Vec::new();
102 let json = wv.to_json();
103 if let Some(alg) = json.get("algorithm") {
104 if let Some(a) = alg.as_str() {
105 items.push(String::from(a));
106 }
107 }
108 if let Some(content_id) = json.get("content_id") {
109 if let Some(cid_hex) = content_id.as_str() {
110 if let Ok(cid_octets) = hex::decode(cid_hex) {
111 if let Ok(cid) = String::from_utf8(cid_octets) {
112 items.push(format!("content_id: \"{cid}\""));
113 }
114 }
115 }
116 }
117 if let Some(kav) = json.get("key_id") {
118 if let Some(ka) = kav.as_array() {
119 for kv in ka {
120 if let Some(k) = kv.as_str() {
121 keys.push(String::from(k));
122 }
123 }
124 }
125 }
126 if keys.len() == 1 {
127 if let Some(key) = keys.first() {
128 items.push(format!("key_id: {key}"));
129 }
130 }
131 if keys.len() > 1 {
132 items.push(format!("key_ids: {}", keys.join(", ")));
133 }
134 if let Some(jo) = json.as_object() {
135 for (k, v) in jo.iter() {
136 if k.ne("algorithm") && k.ne("key_id") && k.ne("content_id") {
137 items.push(format!("{k}: {v}"));
138 }
139 }
140 }
141 write!(f, "WidevinePSSHData<{}>", items.join(", "))
142 },
143 PsshData::PlayReady(pr) => write!(f, "PlayReadyPSSHData<{pr:?}>"),
144 PsshData::Irdeto(pd) => write!(f, "IrdetoPSSHData<{}>", pd.xml),
145 PsshData::Marlin(pd) => write!(f, " MarlinPSSHData<len {} octets>", pd.len()),
146 PsshData::Nagra(pd) => write!(f, "NagraPSSHData<{pd:?}>"),
147 PsshData::WisePlay(pd) => write!(f, "WisePlayPSSHData<{}>", pd.json),
148 PsshData::CommonEnc(pd) => write!(f, "CommonPSSHData<len {} octets>", pd.len()),
149 PsshData::FairPlay(pd) => write!(f, "FairPlayPSSHData<len {} octets>", pd.len()),
150 PsshData::Mobi(pd) => write!(f, "MobiPSSHData<len {} octets>", pd.len()),
151 }
152 }
153}
154
155#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromBytes)]
157pub struct DRMSystemId {
158 id: [u8; 16],
159}
160
161impl TryFrom<&[u8]> for DRMSystemId {
162 type Error = ();
163
164 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
165 if let Ok(id) = value.try_into() {
166 Ok(DRMSystemId { id })
167 } else {
168 Err(())
169 }
170 }
171}
172
173impl TryFrom<Vec<u8>> for DRMSystemId {
174 type Error = ();
175
176 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
177 if value.len() == 16 {
178 DRMSystemId::try_from(&value[0..16])
179 } else {
180 Err(())
181 }
182 }
183}
184
185impl TryFrom<&str> for DRMSystemId {
186 type Error = ();
187
188 fn try_from(value: &str) -> Result<Self, Self::Error> {
189 if value.len() == 32 {
190 if let Ok(id) = hex::decode(value) {
191 return DRMSystemId::try_from(id);
192 }
193 }
194 Err(())
195 }
196}
197
198impl ToBytes for DRMSystemId {
199 fn to_bytes(&self) -> Vec<u8> {
200 self.id.into()
201 }
202}
203
204impl fmt::Display for DRMSystemId {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 let family = if self.id == hex!("1077efecc0b24d02ace33c1e52e2fb4b") {
208 "Common"
209 } else if self.id == hex!("6770616363656e6364726d746f6f6c31") {
210 "GPAC"
212 } else if self.id == hex!("edef8ba979d64acea3c827dcd51d21ed") {
213 "Widevine"
214 } else if self.id == hex!("9a04f07998404286ab92e65be0885f95") {
215 "PlayReady"
216 } else if self.id == hex!("6dd8b3c345f44a68bf3a64168d01a4a6") {
217 "ABV"
218 } else if self.id == hex!("f239e769efa348509c16a903c6932efb") {
219 "Adobe Primetime"
220 } else if self.id == hex!("616c7469636173742d50726f74656374") {
221 "Alticast"
222 } else if self.id == hex!("94ce86fb07ff4f43adb893d2fa968ca2") {
223 "Apple FairPlay"
224 } else if self.id == hex!("29701fe43cc74a348c5bae90c7439a47") {
225 "Apple FairPlay-Netflix variant"
228 } else if self.id == hex!("3ea8778f77424bf9b18be834b2acbd47") {
229 "ClearKey AES-128"
230 } else if self.id == hex!("be58615b19c4468488b3c8c57e99e957") {
231 "ClearKey SAMPLE-AES"
232 } else if self.id == hex!("e2719d58a985b3c9781ab030af78d30e") {
233 "ClearKey DASH-IF"
234 } else if self.id == hex!("45d481cb8fe049c0ada9ab2d2455b2f2") {
235 "CoreTrust"
236 } else if self.id == hex!("80a6be7e14484c379e70d5aebe04c8d2") {
237 "Irdeto"
238 } else if self.id == hex!("5e629af538da4063897797ffbd9902d4") {
239 "Marlin"
244 } else if self.id == hex!("69f908af481646ea910ccd5dcccb0a3a") {
245 "Marlin"
246 } else if self.id == hex!("adb41c242dbf4a6d958b4457c0d27b95") {
247 "Nagra"
248 } else if self.id == hex!("1f83e1e86ee94f0dba2f5ec4e3ed1a66") {
249 "SecureMedia"
250 } else if self.id == hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") {
251 "WisePlay-ChinaDRM"
254 } else if self.id == hex!("793b79569f944946a94223e7ef7e44b4") {
255 "VisionCrypt"
256 } else if self.id == hex!("6a99532d869f59229a91113ab7b1e2f3") {
257 "MobiDRM"
258 } else {
259 "Unknown"
260 };
261 let hex = hex::encode(self.id);
262 write!(f, "{}/DRMSystemId<{}-{}-{}-{}-{}>",
263 family,
264 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
265 }
266}
267
268impl fmt::Debug for DRMSystemId {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 write!(f, "DRMSystemId<{}>", hex::encode(self.id))
271 }
272}
273
274pub const COMMON_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("1077efecc0b24d02ace33c1e52e2fb4b") };
275pub const WIDEVINE_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("edef8ba979d64acea3c827dcd51d21ed") };
276pub const PLAYREADY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("9a04f07998404286ab92e65be0885f95") };
277pub const FAIRPLAYNFLX_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("29701fe43cc74a348c5bae90c7439a47") };
278pub const IRDETO_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("80a6be7e14484c379e70d5aebe04c8d2") };
279pub const MARLIN_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("69f908af481646ea910ccd5dcccb0a3a") };
280pub const NAGRA_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("adb41c242dbf4a6d958b4457c0d27b95") };
281pub const WISEPLAY_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("3d5e6d359b9a41e8b843dd3c6e72c42c") };
282pub const MOBI_SYSTEM_ID: DRMSystemId = DRMSystemId { id: hex!("6a99532d869f59229a91113ab7b1e2f3") };
283
284#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, FromBytes)]
286pub struct DRMKeyId {
287 id: [u8; 16],
288}
289
290impl TryFrom<&[u8]> for DRMKeyId {
291 type Error = ();
292
293 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
294 if let Ok(id) = value.try_into() {
295 Ok(DRMKeyId { id })
296 } else {
297 Err(())
298 }
299 }
300}
301
302impl TryFrom<Vec<u8>> for DRMKeyId {
303 type Error = ();
304
305 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
306 if value.len() == 16 {
307 DRMKeyId::try_from(&value[0..16])
308 } else {
309 Err(())
310 }
311 }
312}
313
314impl TryFrom<&str> for DRMKeyId {
315 type Error = ();
316
317 fn try_from(value: &str) -> Result<Self, Self::Error> {
318 if value.len() == 32 {
319 if let Ok(id) = hex::decode(value) {
320 return DRMKeyId::try_from(id);
321 }
322 }
323 if value.len() == 36 {
325 let v36 = value.as_bytes();
326 if v36[8] == b'-' &&
327 v36[13] == b'-' &&
328 v36[18] == b'-' &&
329 v36[23] == b'-'
330 {
331 let maybe_hex = value.replace('-', "");
332 if let Ok(id) = hex::decode(maybe_hex) {
333 return DRMKeyId::try_from(id);
334 }
335 }
336 }
337 Err(())
338 }
339}
340
341impl ToBytes for DRMKeyId {
342 fn to_bytes(&self) -> Vec<u8> {
343 self.id.into()
344 }
345}
346
347impl fmt::Display for DRMKeyId {
348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 let hex = hex::encode(self.id);
351 write!(f, "DRMKeyId<{}-{}-{}-{}-{}>",
352 &hex[0..8], &hex[8..12], &hex[12..16], &hex[16..20], &hex[20..32])
353 }
354}
355
356impl fmt::Debug for DRMKeyId {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 write!(f, "DRMKeyId<{}>", hex::encode(self.id))
359 }
360}
361
362
363#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
365pub struct PsshBox {
366 pub version: u8,
367 pub flags: u32,
368 pub system_id: DRMSystemId,
369 pub key_ids: Vec<DRMKeyId>,
370 pub pssh_data: PsshData,
371}
372
373impl PsshBox {
374 pub fn new_widevine() -> PsshBox {
376 let empty = WidevinePsshData {
377 provider: None,
378 ..Default::default()
379 };
380 PsshBox {
381 version: 1,
382 flags: 0,
383 system_id: WIDEVINE_SYSTEM_ID,
384 key_ids: vec![],
385 pssh_data: PsshData::Widevine(empty),
386 }
387 }
388
389 pub fn new_playready() -> PsshBox {
391 let empty = PlayReadyPsshData::new();
392 PsshBox {
393 version: 1,
394 flags: 0,
395 system_id: PLAYREADY_SYSTEM_ID,
396 key_ids: vec![],
397 pssh_data: PsshData::PlayReady(empty),
398 }
399 }
400
401 pub fn add_key_id(&mut self, kid: DRMKeyId) {
402 self.key_ids.push(kid);
403 }
404
405 pub fn to_base64(self) -> String {
406 BASE64_STANDARD.encode(self.to_bytes())
407 }
408
409 pub fn to_hex(self) -> String {
410 hex::encode(self.to_bytes())
411 }
412}
413
414impl fmt::Display for PsshBox {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 let mut keys = Vec::new();
419 if self.version == 1 {
420 for key in &self.key_ids {
421 keys.push(hex::encode(key.id));
422 }
423 }
424 let key_str = match keys.len() {
425 0 => String::from(""),
426 1 => format!("key_id: {}, ", keys.first().unwrap()),
427 _ => format!("key_ids: {}, ", keys.join(", ")),
428 };
429 match &self.pssh_data {
430 PsshData::Widevine(wv) => {
431 let mut items = Vec::new();
432 let json = wv.to_json();
433 if let Some(alg) = json.get("algorithm") {
434 if let Some(a) = alg.as_str() {
435 items.push(String::from(a));
436 }
437 }
438 if let Some(kav) = json.get("key_id") {
441 if let Some(ka) = kav.as_array() {
442 for kv in ka {
443 if let Some(k) = kv.as_str() {
444 keys.push(String::from(k));
445 }
446 }
447 }
448 }
449 if keys.len() == 1 {
450 items.push(format!("key_id: {}", keys.first().unwrap()));
451 }
452 if keys.len() > 1 {
453 items.push(format!("key_ids: {}", keys.join(", ")));
454 }
455 if let Some(jo) = json.as_object() {
456 for (k, v) in jo.iter() {
457 if k.ne("algorithm") && k.ne("key_id") {
458 items.push(format!("{k}: {v}"));
459 }
460 }
461 }
462 write!(f, "WidevinePSSH<{}>", items.join(", "))
463 },
464 PsshData::PlayReady(pr) => write!(f, "PlayReadyPSSH<{key_str}{pr:?}>"),
465 PsshData::Irdeto(pd) => write!(f, "IrdetoPSSH<{key_str}{}>", pd.xml),
466 PsshData::Marlin(pd) => write!(f, " MarlinPSSH<{key_str}pssh data len {} octets>", pd.len()),
467 PsshData::Nagra(pd) => write!(f, "NagraPSSH<{key_str}{pd:?}>"),
468 PsshData::WisePlay(pd) => write!(f, "WisePlayPSSH<{key_str}{}>", pd.json),
469 PsshData::CommonEnc(pd) => write!(f, "CommonPSSH<{key_str}pssh data len {} octets>", pd.len()),
470 PsshData::FairPlay(pd) => write!(f, "FairPlayPSSH<{key_str}pssh data len {} octets>", pd.len()),
471 PsshData::Mobi(pd) => write!(f, "MobiPSSH<{key_str}pssh data len {} octets>", pd.len()),
472 }
473 }
474}
475
476
477impl ToBytes for PsshBox {
478 #[allow(unused_must_use)]
479 fn to_bytes(self: &PsshBox) -> Vec<u8> {
480 let mut out = Vec::new();
481 let pssh_data_bytes = self.pssh_data.to_bytes();
482 let mut total_length: u32 = 4 + 4 + 4 + 16 + 4 + pssh_data_bytes.len() as u32;
488 if self.version == 1 {
489 total_length += 4 + self.key_ids.len() as u32 * 16;
491 }
492 out.write_u32::<BigEndian>(total_length);
493 out.write_all(b"pssh");
494 let version_and_flags: u32 = self.flags ^ ((self.version as u32) << 24);
495 out.write_u32::<BigEndian>(version_and_flags);
496 out.write_all(&self.system_id.id);
497 if self.version == 1 {
498 out.write_u32::<BigEndian>(self.key_ids.len() as u32);
499 for k in &self.key_ids {
500 out.write_all(&k.id);
501 }
502 }
503 out.write_u32::<BigEndian>(pssh_data_bytes.len() as u32);
504 out.write_all(&pssh_data_bytes);
505 out
506 }
507}
508
509
510#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
511pub struct PsshBoxVec(Vec<PsshBox>);
512
513impl PsshBoxVec {
514 pub fn new() -> PsshBoxVec {
515 PsshBoxVec(Vec::new())
516 }
517
518 pub fn contains(&self, bx: &PsshBox) -> bool {
519 self.0.contains(bx)
520 }
521
522 pub fn add(&mut self, bx: PsshBox) {
523 self.0.push(bx);
524 }
525
526 pub fn len(&self) -> usize {
527 self.0.len()
528 }
529
530 pub fn is_empty(&self) -> bool {
531 self.0.is_empty()
532 }
533
534 pub fn iter(&self) -> impl Iterator<Item=&PsshBox>{
535 self.0.iter()
536 }
537
538 pub fn to_base64(self) -> String {
539 let mut buf = Vec::new();
540 for bx in self.0 {
541 buf.append(&mut bx.to_bytes());
542 }
543 BASE64_STANDARD.encode(buf)
544 }
545
546 pub fn to_hex(self) -> String {
547 let mut buf = Vec::new();
548 for bx in self.0 {
549 buf.append(&mut bx.to_bytes());
550 }
551 hex::encode(buf)
552 }
553}
554
555impl Default for PsshBoxVec {
556 fn default() -> Self {
557 Self::new()
558 }
559}
560
561impl IntoIterator for PsshBoxVec {
562 type Item = PsshBox;
563 type IntoIter = std::vec::IntoIter<Self::Item>;
564
565 fn into_iter(self) -> Self::IntoIter {
566 self.0.into_iter()
567 }
568}
569
570impl std::ops::Index<usize> for PsshBoxVec {
571 type Output = PsshBox;
572
573 fn index(&self, index: usize) -> &PsshBox {
574 &self.0[index]
575 }
576}
577
578impl fmt::Display for PsshBoxVec {
579 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580 let mut items = Vec::new();
581 for pssh in self.iter() {
582 items.push(pssh.to_string());
583 }
584 write!(f, "{}", items.join("\n"))
586 }
587}
588
589pub fn from_base64(init_data: &str) -> Result<PsshBoxVec> {
595 let b64_tolerant_config = engine::GeneralPurposeConfig::new()
596 .with_decode_allow_trailing_bits(true)
597 .with_decode_padding_mode(engine::DecodePaddingMode::Indifferent);
598 let b64_tolerant_engine = engine::GeneralPurpose::new(&base64::alphabet::STANDARD, b64_tolerant_config);
599 if init_data.len() < 8 {
600 return Err(anyhow!("insufficient length for init data"));
601 }
602 if let Ok(buf) = b64_tolerant_engine.decode(init_data) {
604 return from_bytes(&buf);
605 }
606 let total_len = init_data.len();
610 let mut start = 0;
611 let mut boxes = Vec::new();
612 while start < total_len - 1 {
613 let buf = b64_tolerant_engine.decode(&init_data[start..start+7])
614 .context("base64 decoding first 32-bit length word")?;
615 let mut rdr = Cursor::new(buf);
616 let box_size: u32 = rdr.read_u32::<BigEndian>()
617 .context("reading PSSH box size")?;
618 trace!("box size from header = {box_size}");
619 let wanted_octets = (box_size.div_ceil(3) * 4) as usize;
621 let end = start + wanted_octets;
622 trace!("attempting to decode {wanted_octets} octets out of {}", init_data.len());
623 if end > init_data.len() {
624 return Err(anyhow!("insufficient length for init data (wanted {end}, have {})", init_data.len()));
627 }
628 let buf = b64_tolerant_engine.decode(&init_data[start..end])
629 .context("decoding base64")?;
630 let bx = from_bytes(&buf)
631 .context("parsing the PSSH initialization data")?;
632 assert!(bx.len() == 1);
633 trace!("Got one box {}", bx[0].clone());
634 boxes.push(bx[0].clone());
635 start = end;
636 }
637 Ok(PsshBoxVec(boxes))
638}
639
640pub fn from_hex(init_data: &str) -> Result<PsshBoxVec> {
642 let buf = hex::decode(init_data)
643 .context("decoding hex")?;
644 from_bytes(&buf)
645 .context("parsing the PSSH initialization_data")
646}
647
648fn read_pssh_box(rdr: &mut Cursor<&[u8]>) -> Result<PsshBox> {
650 let size: u32 = rdr.read_u32::<BigEndian>()
651 .context("reading PSSH box size")?;
652 trace!("PSSH box of size {size} octets");
653 let mut box_header = [0u8; 4];
654 rdr.read_exact(&mut box_header)
655 .context("reading box header")?;
656 if !box_header.eq(b"pssh") {
658 return Err(anyhow!("expecting BMFF header"));
659 }
660 let version_and_flags: u32 = rdr.read_u32::<BigEndian>()
661 .context("reading PSSH version/flags")?;
662 let version: u8 = (version_and_flags >> 24).try_into().unwrap();
663 trace!("PSSH box version {version}");
664 if version > 1 {
665 return Err(anyhow!("unknown PSSH version {version}"));
666 }
667 let mut system_id_buf = [0u8; 16];
668 rdr.read_exact(&mut system_id_buf)
669 .context("reading system_id")?;
670 let system_id = DRMSystemId { id: system_id_buf };
671 let mut key_ids = Vec::new();
672 if version == 1 {
673 let mut kid_count = rdr.read_u32::<BigEndian>()
674 .context("reading KID count")?;
675 trace!("PSSH box has {kid_count} KIDs in box header");
676 while kid_count > 0 {
677 let mut key = [0u8; 16];
678 rdr.read_exact(&mut key)
679 .context("reading key_id")?;
680 key_ids.push(DRMKeyId { id: key });
681 kid_count -= 1;
682 }
683 }
684 let pssh_data_len = rdr.read_u32::<BigEndian>()
685 .context("reading PSSH data length")?;
686 trace!("PSSH box data length {pssh_data_len} octets");
687 let mut pssh_data = Vec::new();
688 rdr.take(pssh_data_len.into()).read_to_end(&mut pssh_data)
689 .context("extracting PSSH data")?;
690 match system_id {
691 WIDEVINE_SYSTEM_ID => {
692 let wv_pssh_data = WidevinePsshData::decode(Cursor::new(pssh_data))
693 .context("parsing Widevine PSSH data")?;
694 Ok(PsshBox {
695 version,
696 flags: version_and_flags & 0xF,
697 system_id,
698 key_ids,
699 pssh_data: PsshData::Widevine(wv_pssh_data),
700 })
701 },
702 PLAYREADY_SYSTEM_ID => {
703 let pr_pssh_data = playready::parse_pssh_data(&pssh_data)
704 .context("parsing PlayReady PSSH data")?;
705 Ok(PsshBox {
706 version,
707 flags: version_and_flags & 0xF,
708 system_id,
709 key_ids,
710 pssh_data: PsshData::PlayReady(pr_pssh_data),
711 })
712 },
713 IRDETO_SYSTEM_ID => {
714 let ir_pssh_data = irdeto::parse_pssh_data(&pssh_data)
715 .context("parsing Irdeto PSSH data")?;
716 Ok(PsshBox {
717 version,
718 flags: version_and_flags & 0xF,
719 system_id,
720 key_ids,
721 pssh_data: PsshData::Irdeto(ir_pssh_data),
722 })
723 },
724 MARLIN_SYSTEM_ID => {
725 Ok(PsshBox {
726 version,
727 flags: version_and_flags & 0xF,
728 system_id,
729 key_ids,
730 pssh_data: PsshData::Marlin(pssh_data),
731 })
732 },
733 NAGRA_SYSTEM_ID => {
734 let pd = nagra::parse_pssh_data(&pssh_data)
735 .context("parsing Nagra PSSH data")?;
736 Ok(PsshBox {
737 version,
738 flags: version_and_flags & 0xF,
739 system_id,
740 key_ids,
741 pssh_data: PsshData::Nagra(pd),
742 })
743 },
744 WISEPLAY_SYSTEM_ID => {
745 let cdrm_pssh_data = wiseplay::parse_pssh_data(&pssh_data)
746 .context("parsing WisePlay PSSH data")?;
747 Ok(PsshBox {
748 version,
749 flags: version_and_flags & 0xF,
750 system_id,
751 key_ids,
752 pssh_data: PsshData::WisePlay(cdrm_pssh_data),
753 })
754 },
755 COMMON_SYSTEM_ID => {
756 Ok(PsshBox {
757 version,
758 flags: version_and_flags & 0xF,
759 system_id,
760 key_ids,
761 pssh_data: PsshData::CommonEnc(pssh_data),
762 })
763 },
764 FAIRPLAYNFLX_SYSTEM_ID => {
765 Ok(PsshBox {
766 version,
767 flags: version_and_flags & 0xF,
768 system_id,
769 key_ids,
770 pssh_data: PsshData::FairPlay(pssh_data),
771 })
772 },
773 MOBI_SYSTEM_ID => {
774 Ok(PsshBox {
775 version,
776 flags: version_and_flags & 0xF,
777 system_id,
778 key_ids,
779 pssh_data: PsshData::Mobi(pssh_data),
780 })
781 },
782 _ => Err(anyhow!("can't parse this system_id type: {:?}", system_id)),
783 }
784}
785
786pub fn from_bytes(init_data: &[u8]) -> Result<PsshBoxVec> {
789 let total_len = init_data.len();
790 let mut rdr = Cursor::new(init_data);
791 let mut boxes = PsshBoxVec::new();
792 while (rdr.position() as usize) < total_len - 1 {
793 let bx = read_pssh_box(&mut rdr)?;
794 boxes.add(bx.clone());
795 trace!("Read one box {bx} from bytes, remaining {} octets", total_len as u64 - rdr.position());
796 let pos = rdr.position() as usize;
797 if let Some(remaining) = &rdr.get_ref().get(pos..total_len) {
798 if remaining.iter().all(|b| *b == 0) {
800 break;
801 }
802 }
803 }
804 Ok(boxes)
805}
806
807pub fn from_buffer(init_data: &[u8]) -> Result<PsshBoxVec> {
810 let total_len = init_data.len();
811 let mut rdr = Cursor::new(init_data);
812 let mut boxes = PsshBoxVec::new();
813 while (rdr.position() as usize) < total_len - 1 {
814 if let Ok(bx) = read_pssh_box(&mut rdr) {
815 boxes.add(bx);
816 } else {
817 break;
818 }
819 }
820 Ok(boxes)
821}
822
823pub fn find_iter(buffer: &[u8]) -> impl Iterator<Item = usize> + '_ {
826 use bstr::ByteSlice;
827
828 buffer.find_iter(b"pssh")
829 .filter(|offset| {
830 if offset+24 > buffer.len() {
831 return false;
832 }
833 if offset+4 < 8 {
834 return false;
835 }
836 let start = offset - 4;
837 let mut rdr = Cursor::new(&buffer[start..]);
838 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
839 let end = start + size as usize;
840 if end > buffer.len() {
841 return false;
842 }
843 from_bytes(&buffer[start..end]).is_ok()
844 })
845 .map(|offset| offset - 4)
846}
847
848
849pub fn find_boxes_buffer(buffer: &[u8]) -> impl Iterator<Item = PsshBox> + '_ {
852 use bstr::ByteSlice;
853
854 let mut boxes = Vec::new();
855 for offset in buffer.find_iter(b"pssh") {
856 if offset+24 > buffer.len() || offset+4 < 8 {
857 break;
858 }
859 let start = offset - 4;
860 let mut rdr = Cursor::new(&buffer[start..]);
861 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
862 let end = start + size as usize;
863 if end > buffer.len() {
864 break;
865 }
866 if let Ok(pbv) = from_bytes(&buffer[start..end]) {
867 for pb in pbv {
868 boxes.push(pb);
869 }
870 }
871 }
872 boxes.into_iter()
873}
874
875
876pub fn find_boxes_stream<R>(reader: R) -> impl Iterator<Item = Result<PsshBox, io::Error>>
882where
883 R: Read,
884{
885 PsshBoxIterator::new(reader)
886}
887
888struct PsshBoxIterator<R> {
889 reader: R,
890 buffer: Vec<u8>,
891 buffer_pos: usize,
892 pending_boxes : Vec<PsshBox>,
893 read_buffer: Vec<u8>,
894}
895
896impl<R> PsshBoxIterator<R> {
897 fn new(reader: R) -> Self {
898 PsshBoxIterator {
899 reader,
900 buffer: Vec::new(),
901 buffer_pos: 0,
902 pending_boxes: Vec::new(),
903 read_buffer: vec![0; 8 * 1024],
904 }
905 }
906}
907
908impl<R> Iterator for PsshBoxIterator<R>
909where
910 R: Read,
911{
912 type Item = Result<PsshBox, io::Error>;
913
914 fn next(&mut self) -> Option<Self::Item> {
915 use bstr::ByteSlice;
916
917 if let Some(bx) = self.pending_boxes.pop() {
918 return Some(Ok(bx));
919 }
920 loop {
921 if self.buffer_pos > 0 {
922 self.buffer = self.buffer.split_off(self.buffer_pos);
923 }
924 let bytes_read = match self.reader.read(&mut self.read_buffer) {
925 Ok(n) => n,
926 Err(e) => return Some(Err(e)),
927 };
928 if self.buffer_pos == 0 && bytes_read == 0 {
929 return None;
930 }
931 self.buffer.extend_from_slice(&self.read_buffer[..bytes_read]);
932 self.buffer_pos = 0;
933 if let Some(offset) = self.buffer.find(b"pssh") {
934 trace!("Found pssh cookie at offset {offset}");
935 if offset + 24 > self.buffer.len() {
936 self.buffer_pos = offset + 4;
937 continue;
938 }
939 if offset < 4 {
940 self.buffer_pos = 4;
941 continue;
942 }
943 let start = offset - 4;
944 let buffer_len = self.buffer.len();
945 let mut rdr = Cursor::new(&self.buffer[start..buffer_len]);
946 let size: u32 = rdr.read_u32::<BigEndian>().unwrap();
947 let end = start + size as usize;
948 if end > self.buffer.len() {
949 self.buffer_pos = offset + 1;
950 continue;
951 }
952 if let Ok(pbv) = from_bytes(&self.buffer[start..end]) {
953 self.buffer_pos = end;
954 for pb in pbv {
955 self.pending_boxes.push(pb);
956 }
957 if let Some(bx) = self.pending_boxes.pop() {
958 return Some(Ok(bx));
959 }
960 } else {
961 self.buffer_pos = offset + 4;
962 }
963 } else {
964 if self.buffer.len() >= 3 {
967 self.buffer_pos = self.buffer.len() - 3;
968 }
969 }
970 }
971 }
972}
973
974
975pub fn pprint(pssh: &PsshBox) {
977 println!("PSSH Box v{}", pssh.version);
978 println!(" SystemID: {}", pssh.system_id);
979 if pssh.version == 1 {
980 for key in &pssh.key_ids {
981 println!(" Key ID: {key}");
982 }
983 }
984 match &pssh.pssh_data {
985 PsshData::Widevine(wv) => println!(" {wv:?}"),
986 PsshData::PlayReady(pr) => println!(" {pr:?}"),
987 PsshData::Irdeto(pd) => {
988 println!("Irdeto XML: {}", pd.xml);
989 },
990 PsshData::Marlin(pd) => {
991 println!(" Marlin PSSH data ({} octets)", pd.len());
992 if !pd.is_empty() {
993 println!("== Hexdump of pssh data ==");
994 let mut hxbuf = Vec::new();
995 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
996 println!("{}", String::from_utf8_lossy(&hxbuf));
997 }
998 },
999 PsshData::Nagra(pd) => println!(" {pd:?}"),
1000 PsshData::WisePlay(pd) => {
1001 println!(" WisePlay JSON: {}", pd.json);
1002 },
1003 PsshData::CommonEnc(pd) => {
1004 println!(" Common PSSH data ({} octets)", pd.len());
1005 if !pd.is_empty() {
1006 println!("== Hexdump of pssh data ==");
1007 let mut hxbuf = Vec::new();
1008 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
1009 println!("{}", String::from_utf8_lossy(&hxbuf));
1010 }
1011 },
1012 PsshData::FairPlay(pd) => {
1013 println!(" FairPlay PSSH data ({} octets)", pd.len());
1014 if !pd.is_empty() {
1015 println!("== Hexdump of pssh data ==");
1016 let mut hxbuf = Vec::new();
1017 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
1018 println!("{}", String::from_utf8_lossy(&hxbuf));
1019 }
1020 },
1021 PsshData::Mobi(pd) => {
1022 println!(" MobiDRM PSSH data ({} octets)", pd.len());
1023 if !pd.is_empty() {
1024 println!("== Hexdump of pssh data ==");
1025 let mut hxbuf = Vec::new();
1026 hxdmp::hexdump(pd, &mut hxbuf).unwrap();
1027 println!("{}", String::from_utf8_lossy(&hxbuf));
1028 }
1029 },
1030 }
1031}