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