blob: 3ac9f5cc4132461655dfaec088db8fa5a457c0d9 [file] [log] [blame]
Robert Shih5904a722022-01-14 12:43:22 -08001/*
2 * Copyright (C) 2021 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_TAG "drm_hal_test"
18
19#include <gtest/gtest.h>
20#include <log/log.h>
21#include <openssl/aes.h>
22
23#include <memory>
24#include <vector>
25
26#include "drm_hal_common.h"
27
28using ::aidl::android::hardware::drm::EventType;
29using ::aidl::android::hardware::drm::HdcpLevels;
30using ::aidl::android::hardware::drm::KeyRequest;
31using ::aidl::android::hardware::drm::HdcpLevel;
32using ::aidl::android::hardware::drm::IDrmPluginListener;
33using ::aidl::android::hardware::drm::KeyRequestType;
34using ::aidl::android::hardware::drm::KeySetId;
35using ::aidl::android::hardware::drm::KeyStatus;
36using ::aidl::android::hardware::drm::KeyStatusType;
37using ::aidl::android::hardware::drm::KeyType;
38using ::aidl::android::hardware::drm::Mode;
39using ::aidl::android::hardware::drm::OfflineLicenseState;
40using ::aidl::android::hardware::drm::Pattern;
41using ::aidl::android::hardware::drm::SecurityLevel;
42using ::aidl::android::hardware::drm::Status;
43using ::aidl::android::hardware::drm::SubSample;
44using ::aidl::android::hardware::drm::Uuid;
45
46using ::aidl::android::hardware::drm::vts::DrmErr;
47using ::aidl::android::hardware::drm::vts::DrmHalClearkeyTest;
48using ::aidl::android::hardware::drm::vts::DrmHalPluginListener;
49using ::aidl::android::hardware::drm::vts::DrmHalTest;
50using ::aidl::android::hardware::drm::vts::ListenerArgs;
51using ::aidl::android::hardware::drm::vts::kCallbackKeysChange;
52using ::aidl::android::hardware::drm::vts::kCallbackLostState;
53
54using std::string;
55using std::vector;
56
57static const char* const kVideoMp4 = "video/mp4";
58static const char* const kBadMime = "video/unknown";
59static const char* const kDrmErrorTestKey = "drmErrorTest";
60static const char* const kDrmErrorInvalidState = "invalidState";
61static const char* const kDrmErrorResourceContention = "resourceContention";
62static constexpr SecurityLevel kSwSecureCrypto = SecurityLevel::SW_SECURE_CRYPTO;
63static constexpr SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL;
64
65/**
66 * Ensure drm factory supports module UUID Scheme
67 */
68TEST_P(DrmHalTest, VendorUuidSupported) {
69 bool result = false;
70 auto ret =
71 drmFactory->isCryptoSchemeSupported(getAidlUUID(), kVideoMp4, kSwSecureCrypto, &result);
72 ALOGI("kVideoMp4 = %s res %d", kVideoMp4, static_cast<bool>(result));
73 EXPECT_OK(ret);
74 EXPECT_TRUE(result);
75}
76
77/**
78 * Ensure drm factory doesn't support an invalid scheme UUID
79 */
80TEST_P(DrmHalTest, InvalidPluginNotSupported) {
81 const vector<uint8_t> kInvalidUUID = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
82 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
83 bool result = false;
84 auto ret = drmFactory->isCryptoSchemeSupported(toAidlUuid(kInvalidUUID), kVideoMp4,
85 kSwSecureCrypto, &result);
86 EXPECT_OK(ret);
87 EXPECT_FALSE(result);
88}
89
90/**
91 * Ensure drm factory doesn't support an empty UUID
92 */
93TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) {
94 vector<uint8_t> emptyUUID(16);
95 memset(emptyUUID.data(), 0, 16);
96 bool result = false;
97 auto ret = drmFactory->isCryptoSchemeSupported(toAidlUuid(emptyUUID), kVideoMp4,
98 kSwSecureCrypto, &result);
99 EXPECT_OK(ret);
100 EXPECT_FALSE(result);
101}
102
103/**
104 * Ensure drm factory doesn't support an invalid mime type
105 */
106TEST_P(DrmHalTest, BadMimeNotSupported) {
107 bool result = false;
108 auto ret =
109 drmFactory->isCryptoSchemeSupported(getAidlUUID(), kBadMime, kSwSecureCrypto, &result);
110 EXPECT_OK(ret);
111 EXPECT_FALSE(result);
112}
113
114/**
115 * DrmPlugin tests
116 */
117
118/**
119 * Test that a DRM plugin can handle provisioning. While
120 * it is not required that a DRM scheme require provisioning,
121 * it should at least return appropriate status values. If
122 * a provisioning request is returned, it is passed to the
123 * vendor module which should provide a provisioning response
124 * that is delivered back to the HAL.
125 */
126TEST_P(DrmHalTest, DoProvisioning) {
127 for (auto level : {kHwSecureAll, kSwSecureCrypto}) {
128 Status err = Status::OK;
129 auto sid = openSession(level, &err);
130 if (err == Status::OK) {
131 closeSession(sid);
132 } else if (err == Status::ERROR_DRM_CANNOT_HANDLE) {
133 continue;
134 } else {
135 EXPECT_EQ(Status::ERROR_DRM_NOT_PROVISIONED, err);
136 provision();
137 }
138 }
139}
140
141/**
142 * A get key request should fail if no sessionId is provided
143 */
144TEST_P(DrmHalTest, GetKeyRequestNoSession) {
145 SessionId invalidSessionId;
146 vector<uint8_t> initData;
147 KeyedVector optionalParameters;
148 KeyRequest result;
149 auto ret = drmPlugin->getKeyRequest(invalidSessionId, initData, kVideoMp4, KeyType::STREAMING,
150 optionalParameters, &result);
151 EXPECT_TXN(ret);
152 EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
153}
154
155/**
156 * Test that the plugin returns the documented error for the
157 * case of attempting to generate a key request using an
158 * invalid mime type
159 */
160TEST_P(DrmHalTest, GetKeyRequestBadMime) {
161 auto sessionId = openSession();
162 vector<uint8_t> initData;
163 KeyedVector optionalParameters;
164 KeyRequest result;
165 auto ret = drmPlugin->getKeyRequest(sessionId, initData, kBadMime, KeyType::STREAMING,
166 optionalParameters, &result);
167 EXPECT_EQ(EX_SERVICE_SPECIFIC, ret.getExceptionCode());
168 closeSession(sessionId);
169}
170
171/**
172 * Test drm plugin offline key support
173 */
174TEST_P(DrmHalTest, OfflineLicenseTest) {
175 auto sessionId = openSession();
176 vector<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
177 closeSession(sessionId);
178
179 vector<KeySetId> result;
180 auto ret = drmPlugin->getOfflineLicenseKeySetIds(&result);
181 EXPECT_OK(ret);
182 bool found = false;
183 for (KeySetId keySetId2 : result) {
184 if (keySetId == keySetId2.keySetId) {
185 found = true;
186 break;
187 }
188 }
189 EXPECT_TRUE(found) << "keySetId not found";
190
191 ret = drmPlugin->removeOfflineLicense({keySetId});
192 EXPECT_OK(ret);
193
194 ret = drmPlugin->getOfflineLicenseKeySetIds(&result);
195 EXPECT_OK(ret);
196 for (KeySetId keySetId2 : result) {
197 EXPECT_NE(keySetId, keySetId2.keySetId);
198 }
199
200 ret = drmPlugin->removeOfflineLicense({keySetId});
201 EXPECT_TXN(ret);
202 EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
203}
204
205/**
206 * Test drm plugin offline key state
207 */
208TEST_P(DrmHalTest, OfflineLicenseStateTest) {
209 auto sessionId = openSession();
210 DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent(KeyType::OFFLINE);
211 vector<uint8_t> keySetId = loadKeys(sessionId, content, KeyType::OFFLINE);
212 closeSession(sessionId);
213
214 OfflineLicenseState result{};
215 auto ret = drmPlugin->getOfflineLicenseState({keySetId}, &result);
216 EXPECT_OK(ret);
217 EXPECT_EQ(OfflineLicenseState::USABLE, result);
218
219 vector<uint8_t> keyRequest = getKeyRequest(keySetId, content, KeyType::RELEASE);
220 ret = drmPlugin->getOfflineLicenseState({keySetId}, &result);
221 EXPECT_OK(ret);
222 EXPECT_EQ(OfflineLicenseState::INACTIVE, result);
223
224 /**
225 * Get key response from vendor module
226 */
227 vector<uint8_t> keyResponse = vendorModule->handleKeyRequest(keyRequest, content.serverUrl);
228 EXPECT_GT(keyResponse.size(), 0u);
229
230 result = OfflineLicenseState::UNKNOWN;
231 provideKeyResponse(keySetId, keyResponse);
232 ret = drmPlugin->getOfflineLicenseState({keySetId}, &result);
233 EXPECT_TXN(ret);
234 EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
235 EXPECT_EQ(OfflineLicenseState::UNKNOWN, result);
236}
237
238/**
239 * Negative offline license test. Remove empty keySetId
240 */
241TEST_P(DrmHalTest, RemoveEmptyKeySetId) {
242 KeySetId emptyKeySetId;
243 auto ret = drmPlugin->removeOfflineLicense(emptyKeySetId);
244 EXPECT_TXN(ret);
245 EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
246}
247
248/**
249 * Negative offline license test. Get empty keySetId state
250 */
251TEST_P(DrmHalTest, GetEmptyKeySetIdState) {
252 KeySetId emptyKeySetId;
253 OfflineLicenseState result;
254 auto ret = drmPlugin->getOfflineLicenseState(emptyKeySetId, &result);
255 EXPECT_TXN(ret);
256 EXPECT_EQ(Status::BAD_VALUE, DrmErr(ret));
257 EXPECT_EQ(OfflineLicenseState::UNKNOWN, result);
258}
259
260/**
261 * Test that the plugin returns valid connected and max HDCP levels
262 */
263TEST_P(DrmHalTest, GetHdcpLevels) {
264 HdcpLevels result;
265 auto ret = drmPlugin->getHdcpLevels(&result);
266 EXPECT_OK(ret);
267 EXPECT_GE(result.connectedLevel, HdcpLevel::HDCP_NONE);
268 EXPECT_LE(result.maxLevel, HdcpLevel::HDCP_V2_3);
269}
270
271/**
272 * CryptoPlugin Decrypt tests
273 */
274
275/**
276 * Positive decrypt test. "Decrypt" a single clear segment
277 */
278TEST_P(DrmHalTest, ClearSegmentTest) {
279 for (const auto& config : contentConfigurations) {
280 for (const auto& key : config.keys) {
281 const size_t kSegmentSize = 1024;
282 vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
283 const Pattern noPattern = {0, 0};
284 const vector<SubSample> subSamples = {
285 {.numBytesOfClearData = kSegmentSize, .numBytesOfEncryptedData = 0}};
286 auto sessionId = openSession();
287 loadKeys(sessionId, config);
288
289 auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
290 EXPECT_OK(ret);
291
292 uint32_t byteCount =
293 decrypt(Mode::UNENCRYPTED, key.isSecure, toStdArray(key.keyId), &iv[0],
294 subSamples, noPattern, key.clearContentKey, Status::OK);
295 EXPECT_EQ(kSegmentSize, byteCount);
296
297 closeSession(sessionId);
298 }
299 }
300}
301
302/**
303 * Positive decrypt test. Decrypt a single segment using aes_ctr.
304 * Verify data matches.
305 */
306TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) {
307 for (const auto& config : contentConfigurations) {
308 for (const auto& key : config.keys) {
309 const size_t kSegmentSize = 1024;
310 vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
311 const Pattern noPattern = {0, 0};
312 const vector<SubSample> subSamples = {
313 {.numBytesOfClearData = kSegmentSize, .numBytesOfEncryptedData = 0}};
314 auto sessionId = openSession();
315 loadKeys(sessionId, config);
316
317 auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
318 EXPECT_OK(ret);
319
320 uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure, toStdArray(key.keyId), &iv[0],
321 subSamples, noPattern, key.clearContentKey, Status::OK);
322 EXPECT_EQ(kSegmentSize, byteCount);
323
324 closeSession(sessionId);
325 }
326 }
327}
328
329/**
330 * Negative decrypt test. Decrypted frame too large to fit in output buffer
331 */
332TEST_P(DrmHalTest, ErrorFrameTooLarge) {
333 for (const auto& config : contentConfigurations) {
334 for (const auto& key : config.keys) {
335 const size_t kSegmentSize = 1024;
336 vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
337 const Pattern noPattern = {0, 0};
338 const vector<SubSample> subSamples = {
339 {.numBytesOfClearData = kSegmentSize, .numBytesOfEncryptedData = 0}};
340 auto sessionId = openSession();
341 loadKeys(sessionId, config);
342
343 auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
344 EXPECT_OK(ret);
345
346 decrypt(Mode::UNENCRYPTED, key.isSecure, toStdArray(key.keyId), &iv[0], subSamples,
347 noPattern, key.clearContentKey, Status::ERROR_DRM_FRAME_TOO_LARGE);
348
349 closeSession(sessionId);
350 }
351 }
352}
353
354/**
355 * Negative decrypt test. Decrypt without loading keys.
356 */
357TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) {
358 for (const auto& config : contentConfigurations) {
359 for (const auto& key : config.keys) {
360 vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
361 const Pattern noPattern = {0, 0};
362 const vector<SubSample> subSamples = {
363 {.numBytesOfClearData = 256, .numBytesOfEncryptedData = 256}};
364 auto sessionId = openSession();
365
366 auto ret = cryptoPlugin->setMediaDrmSession(sessionId);
367 EXPECT_OK(ret);
368
369 uint32_t byteCount =
370 decrypt(Mode::AES_CTR, key.isSecure, toStdArray(key.keyId), &iv[0], subSamples,
371 noPattern, key.clearContentKey, Status::ERROR_DRM_NO_LICENSE);
372 EXPECT_EQ(0u, byteCount);
373
374 closeSession(sessionId);
375 }
376 }
377}
378
379/**
380 * Ensure clearkey drm factory doesn't support security level higher than supported
381 */
382TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
383 bool result = false;
384 auto ret = drmFactory->isCryptoSchemeSupported(getAidlUUID(), kVideoMp4, kHwSecureAll, &result);
385 EXPECT_OK(ret);
386 EXPECT_FALSE(result);
387}
388
389/**
390 * Test resource contention during attempt to generate key request
391 */
392TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) {
393 auto ret = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention);
394 EXPECT_OK(ret);
395
396 auto sessionId = openSession();
397 vector<uint8_t> initData;
398 KeyedVector optionalParameters;
399 KeyRequest result;
400 ret = drmPlugin->getKeyRequest(sessionId, initData, kVideoMp4, KeyType::STREAMING,
401 optionalParameters, &result);
402 EXPECT_TXN(ret);
403 EXPECT_EQ(Status::ERROR_DRM_RESOURCE_CONTENTION, DrmErr(ret));
404
405 ret = drmPlugin->closeSession(sessionId);
406 EXPECT_TXN(ret);
407 EXPECT_NE(Status::OK, DrmErr(ret));
408}
409
410/**
411 * Test clearkey plugin offline key with mock error
412 */
413TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
414 auto sessionId = openSession();
415 vector<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
416 auto ret = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
417 EXPECT_OK(ret);
418
419 // everything should start failing
420 const Status kInvalidState = Status::ERROR_DRM_INVALID_STATE;
421 vector<KeySetId> result;
422 ret = drmPlugin->getOfflineLicenseKeySetIds(&result);
423 EXPECT_TXN(ret);
424 EXPECT_EQ(kInvalidState, DrmErr(ret));
425 EXPECT_EQ(0u, result.size());
426
427 OfflineLicenseState state = OfflineLicenseState::UNKNOWN;
428 ret = drmPlugin->getOfflineLicenseState({keySetId}, &state);
429 EXPECT_TXN(ret);
430 EXPECT_EQ(kInvalidState, DrmErr(ret));
431 EXPECT_EQ(OfflineLicenseState::UNKNOWN, state);
432
433 ret = drmPlugin->removeOfflineLicense({keySetId});
434 EXPECT_TXN(ret);
435 EXPECT_EQ(kInvalidState, DrmErr(ret));
436 closeSession(sessionId);
437}
438
439/**
440 * Test listener is triggered on key response
441 */
442TEST_P(DrmHalClearkeyTest, ListenerCallbacks) {
443 auto listener = ndk::SharedRefBase::make<DrmHalPluginListener>();
444 auto res = drmPlugin->setListener(listener);
445 EXPECT_OK(res);
446
447 auto sessionId = openSession();
448 loadKeys(sessionId, KeyType::STREAMING);
449 closeSession(sessionId);
450
451 auto args = listener->getEventArgs();
452 EXPECT_EQ(EventType::VENDOR_DEFINED, args.eventType);
453 EXPECT_EQ(sessionId, args.data);
454 EXPECT_EQ(sessionId, args.sessionId);
455
456 args = listener->getExpirationUpdateArgs();
457 EXPECT_EQ(sessionId, args.sessionId);
458 EXPECT_EQ(100, args.expiryTimeInMS);
459
460 args = listener->getKeysChangeArgs();
461 const vector<KeyStatus> keyStatusList = {
462 {{0xa, 0xb, 0xc}, KeyStatusType::USABLE},
463 {{0xd, 0xe, 0xf}, KeyStatusType::EXPIRED},
464 {{0x0, 0x1, 0x2}, KeyStatusType::USABLEINFUTURE},
465 };
466 EXPECT_EQ(sessionId, args.sessionId);
467 EXPECT_EQ(keyStatusList, args.keyStatusList);
468 EXPECT_TRUE(args.hasNewUsableKey);
469}
470
471/**
472 * Test SessionLostState is triggered on error
473 */
474TEST_P(DrmHalClearkeyTest, SessionLostState) {
475 auto listener = ndk::SharedRefBase::make<DrmHalPluginListener>();
476 auto res = drmPlugin->setListener(listener);
477 EXPECT_OK(res);
478
479 res = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
480 EXPECT_OK(res);
481
482 auto sessionId = openSession();
483 auto ret = drmPlugin->closeSession(sessionId);
484
485 auto args = listener->getSessionLostStateArgs();
486 EXPECT_EQ(sessionId, args.sessionId);
487}
488
489/**
490 * Negative decrypt test. Decrypt with invalid key.
491 */
492TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
493 vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
494 const Pattern noPattern = {0, 0};
495 const uint32_t kClearBytes = 512;
496 const uint32_t kEncryptedBytes = 512;
497 const vector<SubSample> subSamples = {
498 {.numBytesOfClearData = kClearBytes, .numBytesOfEncryptedData = kEncryptedBytes}};
499
500 // base 64 encoded JSON response string, must not contain padding character '='
501 const string emptyKeyResponse =
502 "{\"keys\":["
503 "{"
504 "\"kty\":\"oct\""
505 "\"alg\":\"A128KW2\""
506 "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
507 "\"kid\":\"Y2xlYXJrZXlrZXlpZDAyAy\""
508 "}"
509 "{"
510 "\"kty\":\"oct\","
511 "\"alg\":\"A128KW2\""
512 "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," // empty key follows
513 "\"k\":\"R\""
514 "}]"
515 "}";
516 const size_t kEmptyKeyResponseSize = emptyKeyResponse.size();
517
518 vector<uint8_t> invalidResponse;
519 invalidResponse.resize(kEmptyKeyResponseSize);
520 memcpy(invalidResponse.data(), emptyKeyResponse.c_str(), kEmptyKeyResponseSize);
521 decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
522}
523
524/**
525 * Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
526 */
527TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
528 vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
529 const Pattern noPattern = {0, 0};
530 const uint32_t kClearBytes = 512;
531 const uint32_t kEncryptedBytes = 512;
532 const vector<SubSample> subSamples = {
533 {.numBytesOfClearData = kClearBytes, .numBytesOfEncryptedData = kEncryptedBytes}};
534
535 // base 64 encoded JSON response string, must not contain padding character '='
536 const string keyTooLongResponse =
537 "{\"keys\":["
538 "{"
539 "\"kty\":\"oct\","
540 "\"alg\":\"A128KW2\""
541 "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," // key too long
542 "\"k\":\"V2lubmllIHRoZSBwb29oIVdpbm5pZSB0aGUgcG9vaCE=\""
543 "}]"
544 "}";
545 const size_t kKeyTooLongResponseSize = keyTooLongResponse.size();
546
547 vector<uint8_t> invalidResponse;
548 invalidResponse.resize(kKeyTooLongResponseSize);
549 memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize);
550 decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
551}