1use crate::{
4 binary_format::xmr_license::CipherType,
5 crypto::{
6 aes,
7 ecc_p256::{self, ToUntaggedBytes},
8 sha256,
9 },
10 device::Device,
11 license::License,
12 pssh::WrmHeader,
13 xml_key::XmlKey,
14 xml_utils,
15};
16use base64::{prelude::BASE64_STANDARD, Engine};
17use rand::{thread_rng, Rng};
18use std::{
19 fmt,
20 sync::{atomic::AtomicU32, Arc},
21 time::{SystemTime, UNIX_EPOCH},
22};
23use uuid::Uuid;
24
25const PROTOCOL_VERSION: &str = "1";
26const CLIENT_VERSION: &str = "10.0.16384.10011";
27const RGB_MAGIC_CONSTANT_ZERO: [u8; 16] = [
28 0x7e, 0xe9, 0xed, 0x4a, 0xf7, 0x73, 0x22, 0x4f, 0x00, 0xb8, 0xea, 0x7e, 0xfb, 0x02, 0x7c, 0xbb,
29];
30
31#[derive(Clone)]
33pub struct KeyId([u8; 16]);
34
35impl From<[u8; 16]> for KeyId {
36 fn from(value: [u8; 16]) -> Self {
37 KeyId(value)
38 }
39}
40
41impl From<KeyId> for [u8; 16] {
42 fn from(value: KeyId) -> Self {
43 value.0
44 }
45}
46
47impl AsRef<[u8]> for KeyId {
48 fn as_ref(&self) -> &[u8] {
49 &self.0
50 }
51}
52
53impl fmt::Debug for KeyId {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.write_str(hex::encode(self.0).as_str())
56 }
57}
58
59impl fmt::Display for KeyId {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.write_str(hex::encode(self.0).as_str())
62 }
63}
64
65#[derive(Clone)]
67pub struct ContentKey(Box<[u8]>);
68
69impl From<Box<[u8]>> for ContentKey {
70 fn from(value: Box<[u8]>) -> Self {
71 ContentKey(value)
72 }
73}
74
75impl From<ContentKey> for Box<[u8]> {
76 fn from(value: ContentKey) -> Self {
77 value.0
78 }
79}
80
81impl AsRef<[u8]> for ContentKey {
82 fn as_ref(&self) -> &[u8] {
83 &self.0
84 }
85}
86
87impl fmt::Debug for ContentKey {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.write_str(hex::encode(&self.0).as_str())
90 }
91}
92
93impl fmt::Display for ContentKey {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.write_str(hex::encode(&self.0).as_str())
96 }
97}
98
99struct ContentIntegrityKey(Box<[u8]>);
100
101impl From<Box<[u8]>> for ContentIntegrityKey {
102 fn from(value: Box<[u8]>) -> Self {
103 ContentIntegrityKey(value)
104 }
105}
106
107impl From<ContentIntegrityKey> for Box<[u8]> {
108 fn from(value: ContentIntegrityKey) -> Self {
109 value.0
110 }
111}
112
113impl AsRef<[u8]> for ContentIntegrityKey {
114 fn as_ref(&self) -> &[u8] {
115 &self.0
116 }
117}
118
119impl fmt::Debug for ContentIntegrityKey {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 f.write_str(hex::encode(&self.0).as_str())
122 }
123}
124
125impl fmt::Display for ContentIntegrityKey {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 f.write_str(hex::encode(&self.0).as_str())
128 }
129}
130
131type KidCkCi = (KeyId, ContentKey, ContentIntegrityKey);
132type KidCk = (KeyId, ContentKey);
133
134#[derive(Debug, Clone)]
138pub struct Cdm {
139 device: Arc<Device>,
140}
141
142#[derive(Debug, Clone)]
144pub struct Session {
145 id: u32,
146 device: Arc<Device>,
147 xml_key: XmlKey,
148}
149
150impl Cdm {
151 pub fn from_device(device: Device) -> Self {
153 let device = Arc::new(device);
154
155 Self { device }
156 }
157
158 pub fn open_session(&self) -> Session {
160 static SESSION_COUNTER: AtomicU32 = AtomicU32::new(0);
161 let id = SESSION_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
162
163 Session::new(id, Arc::clone(&self.device))
164 }
165}
166
167impl Session {
168 fn new(id: u32, device: Arc<Device>) -> Self {
169 let xml_key = XmlKey::new();
170
171 Self {
172 id,
173 device,
174 xml_key,
175 }
176 }
177
178 pub fn id(&self) -> u32 {
180 self.id
181 }
182
183 pub fn get_license_challenge(&self, wrm_header: WrmHeader) -> Result<String, crate::Error> {
190 let nonce = BASE64_STANDARD.encode(thread_rng().gen::<[u8; 16]>());
191 let wmrm_cipher = BASE64_STANDARD.encode(self.key_data());
192 let cert_cipher = BASE64_STANDARD.encode(self.cipher_data()?);
193
194 let la_content_tag = xml_utils::build_digest_content(
195 String::from(PROTOCOL_VERSION),
196 String::from(CLIENT_VERSION),
197 Self::client_time(),
198 wrm_header.into(),
199 nonce,
200 wmrm_cipher,
201 cert_cipher,
202 )?;
203
204 let la_content = xml_utils::render(&la_content_tag)?;
205 let la_hash = BASE64_STANDARD.encode(sha256::hash(&la_content));
206
207 let signed_info_tag = xml_utils::build_signed_info(la_hash)?;
208 let signed_info = xml_utils::render(&signed_info_tag)?;
209
210 let signature = ecc_p256::sign(self.device.signing_key(), &signed_info);
211 let signature = BASE64_STANDARD.encode(signature);
212
213 let public_key = self
214 .device
215 .signing_key()
216 .verifying_key()
217 .as_affine()
218 .to_untagged_bytes();
219 let public_key = BASE64_STANDARD.encode(public_key);
220
221 let challenge_tag = xml_utils::build_license_challenge(
222 la_content_tag,
223 signed_info_tag,
224 signature,
225 public_key,
226 )?;
227
228 let challenge = xml_utils::render(&challenge_tag)?;
229
230 String::from_utf8(challenge).map_err(|e| e.into())
231 }
232
233 pub fn get_keys_from_challenge_response(
235 &self,
236 response: &str,
237 ) -> Result<Vec<KidCk>, crate::Error> {
238 let licenses = xml_utils::parse_challenge_response(response)?;
239 if licenses.is_empty() {
240 return Err(crate::Error::LicenseMissingError);
241 }
242
243 let device_public_key = self
244 .device
245 .encryption_key()
246 .public()
247 .as_element()
248 .to_untagged_bytes();
249
250 let mut decrypted_keys = Vec::<KidCk>::with_capacity(licenses.len());
251
252 for license in licenses {
253 let license = match License::from_b64(license.as_str()) {
254 Ok(license) => license,
255 Err(e) => {
256 log::error!("Failed to create license: {e:?}");
257 continue;
258 }
259 };
260
261 if *license.public_key()? != *device_public_key {
262 return Err(crate::Error::PublicKeyMismatchError("device"));
263 }
264
265 let aux_key = license.auxiliary_key();
266
267 decrypted_keys.extend(license.encrypted_keys().iter().filter_map(|encrypted_key| {
268 let (kid, ck, ci) = self
269 .decrypt_key(encrypted_key, aux_key)
270 .inspect_err(|e| log::error!("Failed to decrypt key: {e:?}"))
271 .ok()?;
272
273 let (msg, signature) = license
274 .cmac_verification_data()
275 .inspect_err(|e| {
276 log::error!(
277 "Failed to get MAC verification data {e:?}. Skipping KID: {kid:?}"
278 )
279 })
280 .ok()?;
281
282 aes::verify_cmac(ci.as_ref(), msg, signature)
283 .inspect_err(|e| log::error!("Signature mismatch {e:?}. Skipping KID: {kid:?}"))
284 .ok()?;
285
286 Some((kid, ck))
287 }));
288 }
289
290 Ok(decrypted_keys)
291 }
292
293 fn decrypt_key(
294 &self,
295 encrypted_key: &(CipherType, &[u8; 16], &[u8]),
296 aux_key: Option<&[u8; 16]>,
297 ) -> Result<KidCkCi, crate::Error> {
298 match encrypted_key.0 {
299 CipherType::Ecc256 | CipherType::Ecc256WithKZ | CipherType::Ecc256ViaSymmetric => (),
300 _ => {
301 return Err(crate::Error::UnsupportedCipherTypeError(encrypted_key.0));
302 }
303 };
304
305 let decrypted = ecc_p256::decrypt(self.device.encryption_key().secret(), encrypted_key.2)?;
306
307 let mut ci = decrypted
308 .get(..16)
309 .ok_or(crate::Error::SliceOutOfBoundsError(
310 "decrypted",
311 decrypted.len(),
312 ))?
313 .to_vec();
314 let mut ck = decrypted
315 .get(16..32)
316 .ok_or(crate::Error::SliceOutOfBoundsError(
317 "decrypted",
318 decrypted.len(),
319 ))?
320 .to_vec();
321
322 if let Some(aux_key) = aux_key {
323 ci = decrypted.iter().copied().step_by(2).take(16).collect();
324 ck = decrypted
325 .iter()
326 .copied()
327 .skip(1)
328 .step_by(2)
329 .take(16)
330 .collect();
331
332 if encrypted_key.0 == CipherType::Ecc256ViaSymmetric {
333 let embedded_root_license =
334 &encrypted_key
335 .2
336 .get(..144)
337 .ok_or(crate::Error::SliceOutOfBoundsError(
338 "encrypted_key",
339 encrypted_key.2.len(),
340 ))?;
341 let embedded_leaf_license =
342 &encrypted_key
343 .2
344 .get(144..)
345 .ok_or(crate::Error::SliceOutOfBoundsError(
346 "encrypted_key",
347 encrypted_key.2.len(),
348 ))?;
349
350 let rgb_key = ck
351 .iter()
352 .zip(RGB_MAGIC_CONSTANT_ZERO)
353 .map(|v| v.0 ^ v.1)
354 .collect::<Vec<_>>();
355
356 let content_key_prime = aes::encrypt_ecb(&ck, &rgb_key)?;
357 let uplink_x_key = aes::encrypt_ecb(&content_key_prime, aux_key)?;
358
359 let secondary_key = aes::encrypt_ecb(
360 &ck,
361 embedded_root_license
362 .get(128..)
363 .ok_or(crate::Error::SliceOutOfBoundsError(
364 "embedded_root_license",
365 embedded_root_license.len(),
366 ))?,
367 )?;
368
369 let embedded_leaf_license = aes::encrypt_ecb(&uplink_x_key, embedded_leaf_license)?;
370 let embedded_leaf_license =
371 aes::encrypt_ecb(&secondary_key, &embedded_leaf_license)?;
372
373 ci = embedded_leaf_license
374 .get(..16)
375 .ok_or(crate::Error::SliceOutOfBoundsError(
376 "embedded_leaf_license",
377 embedded_leaf_license.len(),
378 ))?
379 .to_vec();
380 ck = embedded_leaf_license
381 .get(16..)
382 .ok_or(crate::Error::SliceOutOfBoundsError(
383 "embedded_leaf_license",
384 embedded_leaf_license.len(),
385 ))?
386 .to_vec();
387 }
388 }
389
390 let uuid = Uuid::from_bytes_le(*encrypted_key.1);
391 let kid = *uuid.as_bytes();
392
393 Ok((
394 KeyId::from(kid),
395 ContentKey::from(ck.into_boxed_slice()),
396 ContentIntegrityKey::from(ci.into_boxed_slice()),
397 ))
398 }
399
400 fn cipher_data(&self) -> Result<Vec<u8>, crate::Error> {
401 let body_tag =
402 xml_utils::build_cipher_data(BASE64_STANDARD.encode(self.device.group_certificate()))?;
403
404 let body = xml_utils::render(&body_tag)?;
405 let ciphertext = aes::encrypt_cbc(self.xml_key.aes_key(), self.xml_key.aes_iv(), &body)?;
406
407 Ok([self.xml_key.aes_iv(), ciphertext.as_slice()].concat())
408 }
409
410 fn key_data(&self) -> Vec<u8> {
411 ecc_p256::encrypt(
412 ecc_p256::wmrm_public_key(),
413 self.xml_key.public_key().as_element(),
414 )
415 }
416
417 fn client_time() -> String {
418 SystemTime::now()
419 .duration_since(UNIX_EPOCH)
420 .unwrap()
421 .as_secs()
422 .to_string()
423 }
424}