1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "NuPlayerDrm"
19
20 #include "NuPlayerDrm.h"
21
22 #include <mediadrm/DrmUtils.h>
23 #include <utils/Log.h>
24
25
26 namespace android {
27
28 // static helpers - internal
29
CreateDrm(status_t * pstatus)30 sp<IDrm> NuPlayerDrm::CreateDrm(status_t *pstatus)
31 {
32 return DrmUtils::MakeDrm(pstatus);
33 }
34
createCrypto(status_t * pstatus)35 sp<ICrypto> NuPlayerDrm::createCrypto(status_t *pstatus)
36 {
37
38 return DrmUtils::MakeCrypto(pstatus);
39 }
40
parsePSSH(const void * pssh,size_t psshsize)41 Vector<DrmUUID> NuPlayerDrm::parsePSSH(const void *pssh, size_t psshsize)
42 {
43 Vector<DrmUUID> drmSchemes, empty;
44 const int DATALEN_SIZE = 4;
45
46 // the format of the buffer is 1 or more of:
47 // {
48 // 16 byte uuid
49 // 4 byte data length N
50 // N bytes of data
51 // }
52 // Determine the number of entries in the source data.
53 // Since we got the data from stagefright, we trust it is valid and properly formatted.
54
55 const uint8_t *data = (const uint8_t*)pssh;
56 size_t len = psshsize;
57 size_t numentries = 0;
58 while (len > 0) {
59 if (len < DrmUUID::UUID_SIZE) {
60 ALOGE("ParsePSSH: invalid PSSH data");
61 return empty;
62 }
63
64 const uint8_t *uuidPtr = data;
65
66 // skip uuid
67 data += DrmUUID::UUID_SIZE;
68 len -= DrmUUID::UUID_SIZE;
69
70 // get data length
71 if (len < DATALEN_SIZE) {
72 ALOGE("ParsePSSH: invalid PSSH data");
73 return empty;
74 }
75
76 uint32_t datalen = *((uint32_t*)data);
77 data += DATALEN_SIZE;
78 len -= DATALEN_SIZE;
79
80 if (len < datalen) {
81 ALOGE("ParsePSSH: invalid PSSH data");
82 return empty;
83 }
84
85 // skip the data
86 data += datalen;
87 len -= datalen;
88
89 DrmUUID _uuid(uuidPtr);
90 drmSchemes.add(_uuid);
91
92 ALOGV("ParsePSSH[%zu]: %s: %s", numentries,
93 _uuid.toHexString().string(),
94 DrmUUID::arrayToHex(data, datalen).string()
95 );
96
97 numentries++;
98 }
99
100 return drmSchemes;
101 }
102
getSupportedDrmSchemes(const void * pssh,size_t psshsize)103 Vector<DrmUUID> NuPlayerDrm::getSupportedDrmSchemes(const void *pssh, size_t psshsize)
104 {
105 Vector<DrmUUID> psshDRMs = parsePSSH(pssh, psshsize);
106
107 Vector<DrmUUID> supportedDRMs;
108 // temporary DRM object for crypto Scheme enquiry (without creating a plugin)
109 status_t status = OK;
110 sp<IDrm> drm = CreateDrm(&status);
111 if (drm != NULL) {
112 for (size_t i = 0; i < psshDRMs.size(); i++) {
113 DrmUUID uuid = psshDRMs[i];
114 bool isSupported = false;
115 status = drm->isCryptoSchemeSupported(uuid.ptr(), String8(),
116 DrmPlugin::kSecurityLevelUnknown, &isSupported);
117 if (status == OK && isSupported) {
118 supportedDRMs.add(uuid);
119 }
120 }
121
122 drm.clear();
123 } else {
124 ALOGE("getSupportedDrmSchemes: Can't create Drm obj: %d", status);
125 }
126
127 ALOGV("getSupportedDrmSchemes: psshDRMs: %zu supportedDRMs: %zu",
128 psshDRMs.size(), supportedDRMs.size());
129
130 return supportedDRMs;
131 }
132
133 // static helpers - public
134
createCryptoAndPlugin(const uint8_t uuid[16],const Vector<uint8_t> & drmSessionId,status_t & status)135 sp<ICrypto> NuPlayerDrm::createCryptoAndPlugin(const uint8_t uuid[16],
136 const Vector<uint8_t> &drmSessionId, status_t &status)
137 {
138 // Extra check
139 if (drmSessionId.isEmpty()) {
140 status = INVALID_OPERATION;
141 ALOGE("createCryptoAndPlugin: Failed. Empty drmSessionId. status: %d", status);
142 return NULL;
143 }
144
145 status = OK;
146 sp<ICrypto> crypto = createCrypto(&status);
147 if (crypto == NULL) {
148 ALOGE("createCryptoAndPlugin: createCrypto failed. status: %d", status);
149 return NULL;
150 }
151 ALOGV("createCryptoAndPlugin: createCrypto succeeded");
152
153 status = crypto->createPlugin(uuid, drmSessionId.array(), drmSessionId.size());
154 if (status != OK) {
155 ALOGE("createCryptoAndPlugin: createCryptoPlugin failed. status: %d", status);
156 // crypto will clean itself when leaving the current scope
157 return NULL;
158 }
159
160 return crypto;
161 }
162
163 // Parcel has only private copy constructor so passing it in rather than returning
retrieveDrmInfo(const void * pssh,size_t psshsize,Parcel * parcel)164 void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel)
165 {
166 // 1) PSSH bytes
167 parcel->writeUint32(psshsize);
168 parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
169
170 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO PSSH: size: %zu %s", psshsize,
171 DrmUUID::arrayToHex((uint8_t*)pssh, psshsize).string());
172
173 // 2) supportedDRMs
174 Vector<DrmUUID> supportedDRMs = getSupportedDrmSchemes(pssh, psshsize);
175 parcel->writeUint32(supportedDRMs.size());
176 for (size_t i = 0; i < supportedDRMs.size(); i++) {
177 DrmUUID uuid = supportedDRMs[i];
178 parcel->writeByteArray(DrmUUID::UUID_SIZE, uuid.ptr());
179
180 ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO supportedScheme[%zu] %s", i,
181 uuid.toHexString().string());
182 }
183 }
184
185 ////////////////////////////////////////////////////////////////////////////////////////////
186 /// Helpers for NuPlayerDecoder
187 ////////////////////////////////////////////////////////////////////////////////////////////
188
makeCryptoInfo(int numSubSamples,uint8_t key[kBlockSize],uint8_t iv[kBlockSize],CryptoPlugin::Mode mode,size_t * clearbytes,size_t * encryptedbytes)189 NuPlayerDrm::CryptoInfo *NuPlayerDrm::makeCryptoInfo(
190 int numSubSamples,
191 uint8_t key[kBlockSize],
192 uint8_t iv[kBlockSize],
193 CryptoPlugin::Mode mode,
194 size_t *clearbytes,
195 size_t *encryptedbytes)
196 {
197 // size needed to store all the crypto data
198 size_t cryptosize;
199 // sizeof(CryptoInfo) + sizeof(CryptoPlugin::SubSample) * numSubSamples;
200 if (__builtin_mul_overflow(sizeof(CryptoPlugin::SubSample), numSubSamples, &cryptosize) ||
201 __builtin_add_overflow(cryptosize, sizeof(CryptoInfo), &cryptosize)) {
202 ALOGE("crypto size overflow");
203 return NULL;
204 }
205
206 CryptoInfo *ret = (CryptoInfo*) malloc(cryptosize);
207 if (ret == NULL) {
208 ALOGE("couldn't allocate %zu bytes", cryptosize);
209 return NULL;
210 }
211 ret->numSubSamples = numSubSamples;
212 memcpy(ret->key, key, kBlockSize);
213 memcpy(ret->iv, iv, kBlockSize);
214 ret->mode = mode;
215 ret->pattern.mEncryptBlocks = 0;
216 ret->pattern.mSkipBlocks = 0;
217 ret->subSamples = (CryptoPlugin::SubSample*)(ret + 1);
218 CryptoPlugin::SubSample *subSamples = ret->subSamples;
219
220 for (int i = 0; i < numSubSamples; i++) {
221 subSamples[i].mNumBytesOfClearData = (clearbytes == NULL) ? 0 : clearbytes[i];
222 subSamples[i].mNumBytesOfEncryptedData = (encryptedbytes == NULL) ?
223 0 :
224 encryptedbytes[i];
225 }
226
227 return ret;
228 }
229
getSampleCryptoInfo(MetaDataBase & meta)230 NuPlayerDrm::CryptoInfo *NuPlayerDrm::getSampleCryptoInfo(MetaDataBase &meta)
231 {
232 uint32_t type;
233 const void *crypteddata;
234 size_t cryptedsize;
235
236 if (!meta.findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
237 return NULL;
238 }
239 size_t numSubSamples = cryptedsize / sizeof(size_t);
240
241 if (numSubSamples <= 0) {
242 ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
243 return NULL;
244 }
245
246 const void *cleardata;
247 size_t clearsize;
248 if (meta.findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
249 if (clearsize != cryptedsize) {
250 // The two must be of the same length.
251 ALOGE("getSampleCryptoInfo mismatch cryptedsize: %zu != clearsize: %zu",
252 cryptedsize, clearsize);
253 return NULL;
254 }
255 }
256
257 const void *key;
258 size_t keysize;
259 if (meta.findData(kKeyCryptoKey, &type, &key, &keysize)) {
260 if (keysize != kBlockSize) {
261 ALOGE("getSampleCryptoInfo Keys must be %d bytes in length: %zu",
262 kBlockSize, keysize);
263 // Keys must be 16 bytes in length.
264 return NULL;
265 }
266 }
267
268 const void *iv;
269 size_t ivsize;
270 if (meta.findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
271 if (ivsize != kBlockSize) {
272 ALOGE("getSampleCryptoInfo IV must be %d bytes in length: %zu",
273 kBlockSize, ivsize);
274 // IVs must be 16 bytes in length.
275 return NULL;
276 }
277 }
278
279 int32_t mode;
280 if (!meta.findInt32(kKeyCryptoMode, &mode)) {
281 mode = CryptoPlugin::kMode_AES_CTR;
282 }
283
284 return makeCryptoInfo(numSubSamples,
285 (uint8_t*) key,
286 (uint8_t*) iv,
287 (CryptoPlugin::Mode)mode,
288 (size_t*) cleardata,
289 (size_t*) crypteddata);
290 }
291
292 } // namespace android
293
294