1 /*
2  * Copyright (C) 2022 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 #include <media/NdkMediaCrypto.h>
17 #include <media/NdkMediaDrm.h>
18 #include "fuzzer/FuzzedDataProvider.h"
19 
20 constexpr int32_t kMinBytes = 1;
21 constexpr int32_t kMaxBytes = 256;
22 constexpr int32_t kMinParamVal = 0;
23 constexpr int32_t kMaxParamVal = 3;
24 constexpr int32_t kMediaUUIdSize = sizeof(AMediaUUID);
25 constexpr int32_t kMinProvisionResponseSize = 0;
26 constexpr int32_t kMaxProvisionResponseSize = 16;
27 constexpr int32_t kMessageSize = 16;
28 constexpr int32_t kMinAPIcase = 0;
29 constexpr int32_t kMaxdecryptEncryptAPIs = 10;
30 constexpr int32_t kMaxpropertyAPIs = 3;
31 constexpr int32_t kMaxsetListenerAPIs = 2;
32 constexpr int32_t kMaxndkDrmAPIs = 3;
33 uint8_t signature[kMessageSize];
34 
35 enum MediaUUID { INVALID_UUID = 0, PSSH_BOX_UUID, CLEARKEY_UUID, kMaxValue = CLEARKEY_UUID };
36 
37 constexpr uint8_t kCommonPsshBoxUUID[] = {0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
38                                           0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B};
39 
40 constexpr uint8_t kClearKeyUUID[] = {0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
41                                      0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E};
42 
43 constexpr uint8_t kInvalidUUID[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
44                                     0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
45 
46 uint8_t kClearkeyPssh[] = {
47         // BMFF box header (4 bytes size + 'pssh')
48         0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
49         // full box header (version = 1 flags = 0)
50         0x01, 0x00, 0x00, 0x00,
51         // system id
52         0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
53         0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
54         // number of key ids
55         0x00, 0x00, 0x00, 0x01,
56         // key id
57         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
58         0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
59         // size of data, must be zero
60         0x00, 0x00, 0x00, 0x00};
61 
62 std::string kPropertyName = "clientId";
63 std::string kMimeType[] = {"video/mp4", "audio/mp4"};
64 std::string kCipherAlgorithm[] = {"AES/CBC/NoPadding", ""};
65 std::string kMacAlgorithm[] = {"HmacSHA256", ""};
66 
67 class NdkMediaDrmFuzzer {
68   public:
NdkMediaDrmFuzzer(const uint8_t * data,size_t size)69     NdkMediaDrmFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
70     void invokeNdkDrm();
KeysChangeListener(AMediaDrm * drm,const AMediaDrmSessionId * sessionId,const AMediaDrmKeyStatus * keysStatus,size_t numKeys,bool hasNewUsableKey)71     static void KeysChangeListener(AMediaDrm* drm, const AMediaDrmSessionId* sessionId,
72                                    const AMediaDrmKeyStatus* keysStatus, size_t numKeys,
73                                    bool hasNewUsableKey) {
74         (void)drm;
75         (void)sessionId;
76         (void)keysStatus;
77         (void)numKeys;
78         (void)hasNewUsableKey;
79     };
80 
ExpirationUpdateListener(AMediaDrm * drm,const AMediaDrmSessionId * sessionId,int64_t expiryTimeInMS)81     static void ExpirationUpdateListener(AMediaDrm* drm, const AMediaDrmSessionId* sessionId,
82                                          int64_t expiryTimeInMS) {
83         (void)drm;
84         (void)sessionId;
85         (void)expiryTimeInMS;
86     };
87 
listener(AMediaDrm * drm,const AMediaDrmSessionId * sessionId,AMediaDrmEventType eventType,int extra,const uint8_t * data,size_t dataSize)88     static void listener(AMediaDrm* drm, const AMediaDrmSessionId* sessionId,
89                          AMediaDrmEventType eventType, int extra, const uint8_t* data,
90                          size_t dataSize) {
91         (void)drm;
92         (void)sessionId;
93         (void)eventType;
94         (void)extra;
95         (void)data;
96         (void)dataSize;
97     }
98 
99   private:
100     FuzzedDataProvider mFdp;
101     void invokeDrmCreatePlugin();
102     void invokeDrmSetListener();
103     void invokeDrmPropertyAPI();
104     void invokeDrmDecryptEncryptAPI();
105     void invokeDrmSecureStopAPI();
106     AMediaDrmSessionId mSessionId = {};
107     AMediaDrm* mDrm = nullptr;
108 };
109 
invokeDrmCreatePlugin()110 void NdkMediaDrmFuzzer::invokeDrmCreatePlugin() {
111     const uint8_t* mediaUUID = nullptr;
112     uint32_t uuidEnum = mFdp.ConsumeEnum<MediaUUID>();
113     switch (uuidEnum) {
114         case INVALID_UUID: {
115             mediaUUID = kInvalidUUID;
116             break;
117         }
118         case PSSH_BOX_UUID: {
119             mediaUUID = kCommonPsshBoxUUID;
120             break;
121         }
122         case CLEARKEY_UUID:
123         default: {
124             mediaUUID = kClearKeyUUID;
125             break;
126         }
127     }
128     mDrm = AMediaDrm_createByUUID(mediaUUID);
129 }
130 
invokeDrmSecureStopAPI()131 void NdkMediaDrmFuzzer::invokeDrmSecureStopAPI() {
132     // get maximum number of secure stops
133     AMediaDrmSecureStop secureStops;
134     size_t numSecureStops = kMaxParamVal;
135     // The API behavior could change based on the drm object (clearkey or
136     // psshbox) This API detects secure stops msg and release them.
137     AMediaDrm_getSecureStops(mDrm, &secureStops, &numSecureStops);
138     AMediaDrm_releaseSecureStops(mDrm, &secureStops);
139 }
140 
invokeDrmSetListener()141 void NdkMediaDrmFuzzer::invokeDrmSetListener() {
142     int32_t setListenerAPI = mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxsetListenerAPIs);
143     switch (setListenerAPI) {
144         case 0: {  // set on key change listener
145             AMediaDrm_setOnKeysChangeListener(mDrm, KeysChangeListener);
146             break;
147         }
148         case 1: {  // set on expiration on update listener
149             AMediaDrm_setOnExpirationUpdateListener(mDrm, ExpirationUpdateListener);
150             break;
151         }
152         case 2:
153         default: {  // set on event listener
154             AMediaDrm_setOnEventListener(mDrm, listener);
155             break;
156         }
157     }
158 }
159 
invokeDrmPropertyAPI()160 void NdkMediaDrmFuzzer::invokeDrmPropertyAPI() {
161     int32_t propertyAPI = mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxpropertyAPIs);
162     switch (propertyAPI) {
163         case 0: {  // set property byte array
164             uint8_t value[kMediaUUIdSize];
165             std::string name =
166                     mFdp.ConsumeBool() ? kPropertyName : mFdp.ConsumeRandomLengthString(kMaxBytes);
167             const char* propertyName = name.c_str();
168             AMediaDrm_setPropertyByteArray(mDrm, propertyName, value, sizeof(value));
169             break;
170         }
171         case 1: {  // get property in byte array
172             AMediaDrmByteArray array;
173             std::string name =
174                     mFdp.ConsumeBool() ? kPropertyName : mFdp.ConsumeRandomLengthString(kMaxBytes);
175             const char* propertyName = name.c_str();
176             AMediaDrm_getPropertyByteArray(mDrm, propertyName, &array);
177             break;
178         }
179         case 2: {  // set string type property
180             std::string propertyName = mFdp.ConsumeRandomLengthString(kMaxBytes);
181             std::string value = mFdp.ConsumeRandomLengthString(kMaxBytes);
182             AMediaDrm_setPropertyString(mDrm, propertyName.c_str(), value.c_str());
183             break;
184         }
185         case 3:
186         default: {  //  get property in string
187             const char* stringValue = nullptr;
188             std::string propertyName = mFdp.ConsumeRandomLengthString(kMaxBytes);
189             AMediaDrm_getPropertyString(mDrm, propertyName.c_str(), &stringValue);
190             break;
191         }
192     }
193 }
194 
invokeDrmDecryptEncryptAPI()195 void NdkMediaDrmFuzzer::invokeDrmDecryptEncryptAPI() {
196     int32_t decryptEncryptAPI =
197             mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxdecryptEncryptAPIs);
198     switch (decryptEncryptAPI) {
199         case 0: {  // Check if crypto scheme is supported
200             std::string mimeType = mFdp.ConsumeBool() ? mFdp.PickValueInArray(kMimeType)
201                                                       : mFdp.ConsumeRandomLengthString(kMaxBytes);
202             AMediaDrm_isCryptoSchemeSupported(kClearKeyUUID, mimeType.c_str());
203             break;
204         }
205         case 1: {  // get a provision request byte array
206             const uint8_t* legacyRequest;
207             size_t legacyRequestSize = 1;
208             const char* legacyDefaultUrl;
209             AMediaDrm_getProvisionRequest(mDrm, &legacyRequest, &legacyRequestSize,
210                                           &legacyDefaultUrl);
211             break;
212         }
213         case 2: {  // provide a response to the DRM engine plugin
214             const int32_t provisionresponseSize = mFdp.ConsumeIntegralInRange<size_t>(
215                     kMinProvisionResponseSize, kMaxProvisionResponseSize);
216             uint8_t provisionResponse[provisionresponseSize];
217             AMediaDrm_provideProvisionResponse(mDrm, provisionResponse, sizeof(provisionResponse));
218             break;
219         }
220         case 3: {  // get key request
221             const uint8_t* keyRequest = nullptr;
222             size_t keyRequestSize = 0;
223             std::string mimeType = mFdp.ConsumeBool() ? mFdp.PickValueInArray(kMimeType)
224                                                       : mFdp.ConsumeRandomLengthString(kMaxBytes);
225             size_t numOptionalParameters =
226                     mFdp.ConsumeIntegralInRange<size_t>(kMinParamVal, kMaxParamVal);
227             AMediaDrmKeyValue optionalParameters[numOptionalParameters];
228             std::string keys[numOptionalParameters];
229             std::string values[numOptionalParameters];
230             for (int i = 0; i < numOptionalParameters; ++i) {
231                 keys[i] = mFdp.ConsumeRandomLengthString(kMaxBytes);
232                 values[i] = mFdp.ConsumeRandomLengthString(kMaxBytes);
233                 optionalParameters[i].mKey = keys[i].c_str();
234                 optionalParameters[i].mValue = values[i].c_str();
235             }
236             AMediaDrmKeyType keyType = (AMediaDrmKeyType)mFdp.ConsumeIntegralInRange<int>(
237                     KEY_TYPE_STREAMING, KEY_TYPE_RELEASE);
238             AMediaDrm_getKeyRequest(mDrm, &mSessionId, kClearkeyPssh, sizeof(kClearkeyPssh),
239                                     mimeType.c_str(), keyType, optionalParameters,
240                                     numOptionalParameters, &keyRequest, &keyRequestSize);
241             break;
242         }
243         case 4: {  // query key status
244             size_t numPairs = mFdp.ConsumeIntegralInRange<size_t>(kMinParamVal, kMaxParamVal);
245             AMediaDrmKeyValue keyStatus[numPairs];
246             AMediaDrm_queryKeyStatus(mDrm, &mSessionId, keyStatus, &numPairs);
247             break;
248         }
249         case 5: {  // provide key response
250             std::string key = mFdp.ConsumeRandomLengthString(kMaxBytes);
251             const char* keyResponse = key.c_str();
252             AMediaDrmKeySetId keySetId;
253             AMediaDrm_provideKeyResponse(mDrm, &mSessionId,
254                                          reinterpret_cast<const uint8_t*>(keyResponse),
255                                          sizeof(keyResponse), &keySetId);
256             break;
257         }
258         case 6: {  // restore key
259             AMediaDrmKeySetId keySetId;
260             AMediaDrm_restoreKeys(mDrm, &mSessionId, &keySetId);
261             break;
262         }
263 
264         case 7: {  // Check signature verification using the specified Algorithm
265             std::string algorithm = kMacAlgorithm[mFdp.ConsumeBool()];
266             std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
267                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
268             std::vector<uint8_t> message = mFdp.ConsumeBytes<uint8_t>(
269                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
270             AMediaDrm_verify(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), message.data(),
271                              message.size(), signature, sizeof(signature));
272             break;
273         }
274         case 8: {  // Generate a signature using the specified Algorithm
275             std::string algorithm = kMacAlgorithm[mFdp.ConsumeBool()];
276             std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
277                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
278             std::vector<uint8_t> message = mFdp.ConsumeBytes<uint8_t>(
279                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
280             size_t signatureSize = sizeof(signature);
281             AMediaDrm_sign(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), message.data(),
282                            message.size(), signature, &signatureSize);
283             break;
284         }
285         case 9: {  // Decrypt the data using algorithm
286             std::string algorithm = kCipherAlgorithm[mFdp.ConsumeBool()];
287             std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
288                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
289             std::vector<uint8_t> iv = mFdp.ConsumeBytes<uint8_t>(
290                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
291             std::vector<uint8_t> input = mFdp.ConsumeBytes<uint8_t>(
292                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
293             uint8_t output[kMessageSize];
294             AMediaDrm_decrypt(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), iv.data(),
295                               input.data(), output, input.size());
296             break;
297         }
298         case 10:
299         default: {  // Encrypt the data using algorithm
300             std::string algorithm = kCipherAlgorithm[mFdp.ConsumeBool()];
301             std::vector<uint8_t> keyId = mFdp.ConsumeBytes<uint8_t>(
302                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
303             std::vector<uint8_t> iv = mFdp.ConsumeBytes<uint8_t>(
304                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
305             std::vector<uint8_t> input = mFdp.ConsumeBytes<uint8_t>(
306                     mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
307             uint8_t output[kMessageSize];
308             AMediaDrm_encrypt(mDrm, &mSessionId, algorithm.c_str(), keyId.data(), iv.data(),
309                               input.data(), output, input.size());
310             break;
311         }
312     }
313     AMediaDrm_removeKeys(mDrm, &mSessionId);
314 }
315 
invokeNdkDrm()316 void NdkMediaDrmFuzzer::invokeNdkDrm() {
317     while (mFdp.remaining_bytes() > 0) {
318         // The API is called at start as it creates a AMediaDrm Object.
319         // mDrm AMediaDrm object is used in the below APIs.
320         invokeDrmCreatePlugin();
321         if (mDrm) {
322             // The API opens session and returns "mSessionId" session Id.
323             // "mSessionId" is required in the below APIs.
324             AMediaDrm_openSession(mDrm, &mSessionId);
325             int32_t ndkDrmAPI = mFdp.ConsumeIntegralInRange<size_t>(kMinAPIcase, kMaxndkDrmAPIs);
326             switch (ndkDrmAPI) {
327                 case 0: {
328                     invokeDrmDecryptEncryptAPI();
329                     break;
330                 }
331                 case 1: {
332                     invokeDrmPropertyAPI();
333                     break;
334                 }
335                 case 2: {
336                     invokeDrmSetListener();
337                     break;
338                 }
339                 case 3:
340                 default: {
341                     invokeDrmSecureStopAPI();
342                     break;
343                 }
344             }
345             AMediaDrm_closeSession(mDrm, &mSessionId);
346             AMediaDrm_release(mDrm);
347         }
348     }
349 }
350 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)351 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
352     NdkMediaDrmFuzzer ndkMediaDrmFuzzer(data, size);
353     ndkMediaDrmFuzzer.invokeNdkDrm();
354     return 0;
355 }
356