playready/
device.rs

1//! Creating and parsing devices.
2
3use crate::{
4    binary_format::{self, device::PRD_SCALAR_SIZE},
5    certificate::CertificateChain,
6    crypto::ecc_p256::{FromBytes, Keypair, ToUntaggedBytes, SCALAR_SIZE},
7};
8use binrw::{BinRead, BinWrite};
9use p256::ecdsa::SigningKey;
10use p256::elliptic_curve::PrimeField;
11use rand::{thread_rng, Rng};
12use std::{
13    fs::File,
14    io::{Cursor, Read},
15    path::Path,
16};
17
18/// Represents PlayReady device. Usually created from .prd file.
19#[derive(Debug, Clone)]
20pub struct Device {
21    group_key: Option<SigningKey>,
22    encryption_key: Keypair,
23    signing_key: SigningKey,
24    cert_chain: CertificateChain,
25}
26
27impl Device {
28    /// Creates new [`Device`].
29    pub fn new(
30        group_key: Option<SigningKey>,
31        encryption_key: Keypair,
32        signing_key: SigningKey,
33        cert_chain: CertificateChain,
34    ) -> Self {
35        Self {
36            group_key,
37            encryption_key,
38            signing_key,
39            cert_chain,
40        }
41    }
42
43    /// Creates new [`Device`] from slices containing raw bytes with keys and certificate chain.
44    pub fn from_slices(
45        group_key: Option<&[u8]>,
46        encryption_key: &[u8],
47        signing_key: &[u8],
48        cert_chain: &[u8],
49    ) -> Result<Self, crate::Error> {
50        let group_key = match group_key {
51            Some(group_key) => Some(SigningKey::from_slice(group_key)?),
52            None => None,
53        };
54
55        let encryption_key = Keypair::from_bytes(encryption_key)?;
56        let signing_key = SigningKey::from_slice(signing_key)?;
57        let cert_chain = CertificateChain::from_bytes(cert_chain)?;
58
59        Ok(Self {
60            group_key,
61            encryption_key,
62            signing_key,
63            cert_chain,
64        })
65    }
66
67    /// Creates new [`Device`] from bytes.
68    pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::Error> {
69        let device = binary_format::device::Device::read(&mut Cursor::new(bytes))?;
70
71        let group_key = match &device.inner {
72            binary_format::device::DeviceInner::V2(_) => None,
73            binary_format::device::DeviceInner::V3(v3) => Some(&v3.group_key),
74        };
75
76        let group_key = match group_key {
77            Some(group_key) => Some(SigningKey::from_slice(&group_key[..SCALAR_SIZE])?),
78            None => None,
79        };
80
81        let encryption_key = match &device.inner {
82            binary_format::device::DeviceInner::V2(v2) => &v2.encryption_key,
83            binary_format::device::DeviceInner::V3(v3) => &v3.encryption_key,
84        };
85
86        let signing_key = match &device.inner {
87            binary_format::device::DeviceInner::V2(v2) => &v2.signing_key,
88            binary_format::device::DeviceInner::V3(v3) => &v3.signing_key,
89        };
90
91        let encryption_key = Keypair::from_bytes(&encryption_key[..SCALAR_SIZE])?;
92        let signing_key = SigningKey::from_slice(&signing_key[..SCALAR_SIZE])?;
93
94        let group_certificate = match device.inner {
95            binary_format::device::DeviceInner::V2(v2) => v2.group_certificate,
96            binary_format::device::DeviceInner::V3(v3) => v3.group_certificate,
97        };
98
99        let cert_chain = CertificateChain::from_vec(group_certificate)?;
100
101        Ok(Self {
102            group_key,
103            encryption_key,
104            signing_key,
105            cert_chain,
106        })
107    }
108
109    /// Creates new [`Device`] from .prd file.
110    ///
111    /// # Arguments
112    ///
113    /// `path` - path to .prd file
114    pub fn from_prd(path: impl AsRef<Path>) -> Result<Self, crate::Error> {
115        let mut file = File::open(path)?;
116        let mut bytes = Vec::<u8>::new();
117        file.read_to_end(&mut bytes)?;
118
119        Self::from_bytes(&bytes)
120    }
121
122    /// Returns device signing key.
123    pub fn signing_key(&self) -> &SigningKey {
124        &self.signing_key
125    }
126
127    /// Returns device encryption key.
128    pub fn encryption_key(&self) -> &Keypair {
129        &self.encryption_key
130    }
131
132    /// Returns device group certificate.
133    pub fn group_certificate(&self) -> &[u8] {
134        self.cert_chain.raw()
135    }
136
137    /// Returns device group key.
138    pub fn group_key(&self) -> Option<&SigningKey> {
139        self.group_key.as_ref()
140    }
141
142    /// Returns name of the device parsed from certificate chain.
143    pub fn name(&self) -> Result<String, crate::Error> {
144        self.cert_chain.name()
145    }
146
147    /// Returns security level (SL????) of the device parsed from certificate chain.
148    pub fn security_level(&self) -> Result<u32, crate::Error> {
149        self.cert_chain.security_level()
150    }
151
152    /// Performs signature verification of certificates bundled in [`CertificateChain`].
153    pub fn verify(&self) -> Result<(), crate::Error> {
154        let public_signing_key = self
155            .signing_key
156            .verifying_key()
157            .as_affine()
158            .to_untagged_bytes();
159
160        let public_encryption_key = self
161            .encryption_key
162            .public()
163            .as_element()
164            .to_untagged_bytes();
165
166        if *public_signing_key != *self.cert_chain.public_signing_key()? {
167            return Err(crate::Error::PublicKeyMismatchError("signing key"));
168        }
169
170        if *public_encryption_key != *self.cert_chain.public_encryption_key()? {
171            return Err(crate::Error::PublicKeyMismatchError("encryption key"));
172        }
173
174        if let Some(group_key) = self.group_key() {
175            let group_key = group_key.verifying_key().as_affine().to_untagged_bytes();
176
177            if *group_key != *self.cert_chain.public_group_key()? {
178                return Err(crate::Error::PublicKeyMismatchError("group key"));
179            }
180        }
181
182        self.cert_chain.verify_signatures()
183    }
184
185    /// Creates and provisions device from certificate chain and group key.
186    pub fn provision(
187        cert_chain: CertificateChain,
188        group_key: SigningKey,
189    ) -> Result<Self, crate::Error> {
190        let mut rng = thread_rng();
191
192        let client_id = rng.gen::<[u8; 16]>();
193        let cert_id = rng.gen::<[u8; 16]>();
194
195        let encryption_key = Keypair::generate(&mut rng);
196        let signing_key = SigningKey::random(&mut rng);
197
198        let public_encryption_key = encryption_key
199            .public()
200            .as_element()
201            .to_untagged_bytes()
202            .to_vec();
203
204        let public_signing_key = signing_key
205            .verifying_key()
206            .as_affine()
207            .to_untagged_bytes()
208            .to_vec();
209
210        let cert_chain = cert_chain.provision(
211            cert_id,
212            client_id,
213            public_signing_key,
214            public_encryption_key,
215            &group_key,
216        )?;
217
218        cert_chain.verify_signatures()?;
219
220        Ok(Self {
221            group_key: Some(group_key),
222            encryption_key,
223            signing_key,
224            cert_chain,
225        })
226    }
227
228    /// Generates reprovisioned [`Device`].
229    pub fn reprovision(self) -> Result<Self, crate::Error> {
230        let Device {
231            cert_chain,
232            group_key,
233            ..
234        } = self;
235
236        let group_key = group_key.ok_or(crate::Error::GroupKeyMissingError)?;
237
238        Self::provision(cert_chain, group_key)
239    }
240
241    /// Creates and provisions device from file containing certificate chain
242    /// (usually named bgroupcert.dat) and file with group key (usually zgpriv.dat).
243    pub fn provision_from_files(
244        group_cert_path: impl AsRef<Path>,
245        group_key_path: impl AsRef<Path>,
246    ) -> Result<Self, crate::Error> {
247        let mut file = File::open(group_cert_path)?;
248        let mut bytes = Vec::<u8>::new();
249        file.read_to_end(&mut bytes)?;
250
251        let cert_chain = CertificateChain::from_bytes(&bytes)?;
252
253        file = File::open(group_key_path)?;
254        bytes.clear();
255        file.read_to_end(&mut bytes)?;
256
257        let group_key = SigningKey::from_slice(bytes.get(..SCALAR_SIZE).ok_or(
258            crate::Error::SliceOutOfBoundsError("group_key", bytes.len()),
259        )?)?;
260
261        Self::provision(cert_chain, group_key)
262    }
263
264    /// Serializes and writes device to file specified by path.
265    pub fn write_to_file(&self, path: impl AsRef<Path>) -> Result<(), crate::Error> {
266        let mut encryption_key = [0u8; PRD_SCALAR_SIZE];
267        let mut signing_key = [0u8; PRD_SCALAR_SIZE];
268
269        encryption_key[..SCALAR_SIZE]
270            .copy_from_slice(&self.encryption_key.secret().expose_scalar().to_repr());
271        signing_key[..SCALAR_SIZE].copy_from_slice(&self.signing_key.to_bytes());
272
273        let group_certificate = self.group_certificate().to_vec();
274        let group_certificate_length = u32::try_from(group_certificate.len()).unwrap();
275
276        let device = if let Some(ref key) = self.group_key {
277            let mut group_key = [0u8; PRD_SCALAR_SIZE];
278            group_key[..SCALAR_SIZE].copy_from_slice(&key.to_bytes());
279
280            binary_format::device::Device {
281                version: 3,
282                inner: binary_format::device::DeviceInner::V3(binary_format::device::DeviceV3 {
283                    group_key,
284                    encryption_key,
285                    signing_key,
286                    group_certificate_length,
287                    group_certificate,
288                }),
289            }
290        } else {
291            binary_format::device::Device {
292                version: 2,
293                inner: binary_format::device::DeviceInner::V2(binary_format::device::DeviceV2 {
294                    encryption_key,
295                    signing_key,
296                    group_certificate_length,
297                    group_certificate,
298                }),
299            }
300        };
301
302        let mut file = File::create(path)?;
303        device.write(&mut file)?;
304
305        Ok(())
306    }
307}
308
309impl TryFrom<&[u8]> for Device {
310    type Error = crate::Error;
311
312    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
313        Self::from_bytes(value)
314    }
315}