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