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