1use std::fmt;
12use std::io::{Read, Cursor};
13use std::fmt::{Error, Write};
14use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
15use serde::{Serialize, Deserialize};
16use serde_with::{serde_as, skip_serializing_none};
17use serde_with::base64::Base64;
18use quick_xml::encoding::DecodingReader;
19use num_enum::TryFromPrimitive;
20use tracing::trace;
21use anyhow::{Result, Context, anyhow};
22use crate::ToBytes;
23
24
25struct Utf16Writer(Vec<u16>);
26
27impl Write for Utf16Writer {
28 fn write_str(&mut self, s: &str) -> Result<(), Error> {
29 self.0.extend(s.encode_utf16());
30 Ok(())
31 }
32
33 fn write_char(&mut self, c: char) -> Result<(), Error> {
34 self.0.extend(c.encode_utf16(&mut [0; 2]).iter());
35 Ok(())
36 }
37}
38
39pub fn to_utf16(xml: &str) -> Vec<u16> {
40 let mut writer = Utf16Writer(Vec::new());
41 write!(writer, "{xml}")
42 .expect("writing XML as UTF-16");
43 writer.0
44}
45
46fn serialize_xmlns<S>(os: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
47where S: serde::Serializer {
48 if let Some(s) = os {
49 serializer.serialize_str(s)
50 } else {
51 serializer.serialize_str("http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader")
52 }
53}
54
55#[serde_as]
56#[skip_serializing_none]
57#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(default)]
59pub struct PlayReadyKid {
60 #[serde(rename = "@value")]
61 pub value: Option<String>,
62 #[serde(rename = "@ALGID")]
63 pub algid: Option<String>,
64 #[serde_as(as = "Option<Base64>")]
65 #[serde(rename = "@CHECKSUM")]
66 pub checksum: Option<Vec<u8>>,
67 #[serde_as(as = "Base64")]
68 #[serde(rename = "$text")]
69 pub content: Vec<u8>,
70}
71
72#[skip_serializing_none]
75#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(default)]
77pub struct ProtectInfo {
78 #[serde(rename = "KEYLEN")]
79 pub keylen: Option<u32>,
80 #[serde(rename = "ALGID")]
81 pub algid: Option<String>,
82 #[serde(rename = "KIDS")]
83 pub kids: Vec<PlayReadyKid>,
84}
85
86
87#[serde_as]
88#[skip_serializing_none]
89#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90#[serde(rename = "WRMDATA")]
91#[serde(default)]
92pub struct WRMData {
93 #[serde(rename = "KID")]
94 pub kids: Vec<PlayReadyKid>,
95 #[serde(rename = "PROTECTINFO")]
96 pub protect_info: Option<ProtectInfo>,
97 #[serde_as(as = "Option<Base64>")]
98 #[serde(rename = "CHECKSUM")]
99 pub checksum: Option<Vec<u8>>,
100 #[serde(rename = "LA_URL")]
102 pub la_url: Option<String>,
103 #[serde(rename = "LUI_URL")]
105 pub lui_url: Option<String>,
106 #[serde(rename = "DS_ID")]
108 pub ds_id: Option<String>,
109 #[serde(rename(serialize = "CUSTOMATTRIBUTES"))]
111 pub custom_attributes: Option<String>,
112 #[serde(rename = "DECRYPTORSETUP")]
113 pub decryptor_setup: Option<String>,
114}
115
116#[skip_serializing_none]
117#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(rename = "WRMHEADER")]
119#[serde(default)]
120pub struct WRMHeader {
121 #[serde(rename = "@xmlns", serialize_with="serialize_xmlns")]
122 pub xmlns: Option<String>,
123 #[serde(rename = "@version")]
124 pub version: String,
125 #[serde(rename = "DATA")]
126 pub data: WRMData,
127}
128
129impl ToBytes for WRMHeader {
130 fn to_bytes(&self) -> Vec<u8> {
131 let xml = quick_xml::se::to_string(self)
132 .expect("parsing WRMHeader XML");
133 let mut out = Vec::<u8>::new();
134 for u in to_utf16(&xml) {
135 let _ = out.write_u16::<LittleEndian>(u);
136 }
137 out
138 }
139}
140
141#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TryFromPrimitive)]
142#[repr(u16)]
143pub enum PlayReadyRecordType {
144 #[default]
145 RightsManagement = 1,
146 Reserved = 2,
147 EmbeddedLicenseStore = 3,
148}
149
150impl ToBytes for PlayReadyRecordType {
151 fn to_bytes(&self) -> Vec<u8> {
152 let mut buf = Vec::new();
153 let _ = buf.write_u16::<LittleEndian>(*self as u16);
154 buf
155 }
156}
157
158#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
159pub struct PlayReadyRecord {
160 pub record_type: PlayReadyRecordType,
161 pub record_value: WRMHeader,
162}
163
164impl PlayReadyRecord {
165 pub fn new() -> PlayReadyRecord {
166 let xml = "<WRMHEADER><DATA></DATA></WRMHEADER>";
167 let mut rv: WRMHeader = quick_xml::de::from_str(xml).unwrap();
168 rv.xmlns = Some(String::from("http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader"));
169 rv.version = String::from("4.0.0.0");
170 PlayReadyRecord {
171 record_type: PlayReadyRecordType::RightsManagement,
172 record_value: rv,
173 }
174 }
175}
176
177impl ToBytes for PlayReadyRecord {
178 fn to_bytes(&self) -> Vec<u8> {
179 let mut buf = Vec::new();
180 buf.append(&mut self.record_type.to_bytes());
181 let mut val_bytes = self.record_value.to_bytes();
182 let _ = buf.write_u16::<LittleEndian>(val_bytes.len().try_into().unwrap());
183 buf.append(&mut val_bytes);
184 buf
185 }
186}
187
188fn parse_playready_record(rdr: &mut Cursor<&[u8]>) -> Result<PlayReadyRecord> {
189 let record_type = rdr.read_u16::<LittleEndian>()
190 .context("reading record_type field")?;
191 if record_type != 1 {
192 return Err(anyhow!("can't parse PlayReady record of type {record_type}"));
193 }
194 let record_length = rdr.read_u16::<LittleEndian>()
195 .context("reading record_length field")?;
196 let mut wrmh_u8 = Vec::new();
197 rdr.take(record_length.into()).read_to_end(&mut wrmh_u8)?;
198 let wrmh_u16 = wrmh_u8
199 .chunks(2)
200 .map(|e| u16::from_le_bytes(e.try_into().unwrap()))
201 .collect::<Vec<_>>();
202 let mut xml = String::from_utf16(&wrmh_u16)
203 .context("decoding UTF-16")?;
204 let mut custom_attributes: Option<String> = None;
208 if let Some(start) = xml.find("<CUSTOMATTRIBUTES") {
209 if let Some(end) = xml.find("</CUSTOMATTRIBUTES>") {
210 if end < start {
211 return Err(anyhow!("invalid CUSTOMATTRIBUTES element"));
212 }
213 if let Some(subseq) = xml.get(start..end) {
214 let ca_tag_end = subseq.find('>')
215 .context("finding end of CUSTOMATTRIBUTES element")?;
216 let inner_start = ca_tag_end + 1;
217 trace!("start = {}, inner_start = {}", start, inner_start);
218 if let Some(inner) = subseq.get(inner_start..) {
219 custom_attributes = Some(String::from(inner));
220 }
221 xml.replace_range(start..end + 19, "");
222 }
223 }
224 }
225 let decoder = DecodingReader::new(xml.as_ref());
226 let mut xd = quick_xml::de::Deserializer::from_reader(decoder);
227 let mut wrm_header: WRMHeader = serde_path_to_error::deserialize(&mut xd)
228 .context("parsing PlayReady XML")?;
229 wrm_header.data.custom_attributes = custom_attributes;
230 Ok(PlayReadyRecord {
231 record_type: PlayReadyRecordType::try_from(record_type)?,
232 record_value: wrm_header,
233 })
234}
235
236#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
237pub struct PlayReadyPsshData {
238 pub record: Vec<PlayReadyRecord>,
239}
240
241impl PlayReadyPsshData {
242 pub fn new() -> PlayReadyPsshData {
243 let empty_record = PlayReadyRecord::new();
244 let mut empty = PlayReadyPsshData::default();
245 empty.record.push(empty_record);
246 empty
247 }
248}
249
250impl fmt::Debug for PlayReadyPsshData {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 let mut items = Vec::new();
253 for r in &self.record {
254 if r.record_type == PlayReadyRecordType::RightsManagement {
255 let xml = quick_xml::se::to_string(&r.record_value)
256 .map_err(|_| fmt::Error)?;
257 items.push(format!("RightsManagementRecord: {xml}"));
258 } else {
259 items.push(format!("{r:?}"));
260 }
261 }
262 write!(f, "PlayReadyPsshData<{}>", items.join(", "))
263 }
264}
265
266
267impl ToBytes for PlayReadyPsshData {
268 #[allow(unused_must_use)]
269 fn to_bytes(&self) -> Vec<u8> {
270 let mut buf = Vec::<u8>::new();
271 let mut records_buf = Vec::<u8>::new();
272 for r in &self.record {
273 trace!("Serializing playready, record of length {}", r.to_bytes().len());
274 records_buf.append(&mut r.to_bytes());
275 }
276 let total_length: u32 = 4 + 2 + records_buf.len() as u32;
277 buf.write_u32::<LittleEndian>(total_length).unwrap();
278 buf.write_u16::<LittleEndian>(self.record.len().try_into().unwrap()).unwrap();
279 buf.append(&mut records_buf);
280 buf
281 }
282}
283
284pub fn parse_pssh_data(buf: &[u8]) -> Result<PlayReadyPsshData> {
285 let mut rdr = Cursor::new(buf);
286 let blen = buf.len() as u32;
287 let length = rdr.read_u32::<LittleEndian>()
288 .context("reading pssh data length")?;
289 if length != blen {
290 return Err(anyhow!("header length {length} different from buffer length {blen}"));
291 }
292 let record_count = rdr.read_u16::<LittleEndian>()
293 .context("reading pssh data record count")?;
294 let mut records = Vec::new();
295 for _ in 1..=record_count {
296 records.push(parse_playready_record(&mut rdr)?);
297 }
298 Ok(PlayReadyPsshData {
299 record: records,
300 })
301}