• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,uint32_t * clearbytes,uint32_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         uint32_t *clearbytes,
195         uint32_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(uint32_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             (uint32_t*) cleardata,
289             (uint32_t*) crypteddata);
290 }
291 
292 }   // namespace android
293 
294