android13/hardware/interfaces/drm/1.2/vts/functional/drm_hal_common.cpp

558 lines
19 KiB
C++

/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vendor_modules.h"
#define LOG_TAG "drm_hal_common@1.2"
#include <android/hidl/allocator/1.0/IAllocator.h>
#include <gtest/gtest.h>
#include <hidl/HidlSupport.h>
#include <hidlmemory/mapping.h>
#include <log/log.h>
#include <openssl/aes.h>
#include <random>
#include "drm_hal_clearkey_module.h"
#include "android/hardware/drm/1.2/vts/drm_hal_common.h"
using ::android::hardware::drm::V1_0::BufferType;
using ::android::hardware::drm::V1_0::DestinationBuffer;
using ICryptoPluginV1_0 = ::android::hardware::drm::V1_0::ICryptoPlugin;
using IDrmPluginV1_0 = ::android::hardware::drm::V1_0::IDrmPlugin;
using ::android::hardware::drm::V1_0::KeyValue;
using ::android::hardware::drm::V1_0::SharedBuffer;
using StatusV1_0 = ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::KeyRequestType;
using ::android::hardware::drm::V1_2::KeySetId;
using ::android::hardware::drm::V1_2::OfflineLicenseState;
using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_memory;
using ::android::hidl::allocator::V1_0::IAllocator;
using std::random_device;
using std::mt19937;
namespace android {
namespace hardware {
namespace drm {
namespace V1_2 {
namespace vts {
const char *kCallbackLostState = "LostState";
const char *kCallbackKeysChange = "KeysChange";
drm_vts::VendorModules *DrmHalTest::gVendorModules = nullptr;
/**
* DrmHalPluginListener
*/
Return<void> DrmHalPluginListener::sendSessionLostState(const hidl_vec<uint8_t>& sessionId) {
ListenerEventArgs args;
args.sessionId = sessionId;
NotifyFromCallback(kCallbackLostState, args);
return Void();
}
Return<void> DrmHalPluginListener::sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
ListenerEventArgs args;
args.sessionId = sessionId;
args.keyStatusList = keyStatusList;
args.hasNewUsableKey = hasNewUsableKey;
NotifyFromCallback(kCallbackKeysChange, args);
return Void();
}
static DrmHalVTSVendorModule_V1* getModuleForInstance(const std::string& instance) {
if (instance == "clearkey" || instance == "default") {
return new DrmHalVTSClearkeyModule();
}
return static_cast<DrmHalVTSVendorModule_V1*>(DrmHalTest::gVendorModules->getModuleByName(instance));
}
/**
* DrmHalTest
*/
DrmHalTest::DrmHalTest() : vendorModule(getModuleForInstance(GetParamService())) {}
void DrmHalTest::SetUp() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("Running test %s.%s from (vendor) module %s",
test_info->test_case_name(), test_info->name(),
GetParamService().c_str());
const string instance = GetParamService();
drmFactory = IDrmFactory::getService(instance);
ASSERT_NE(drmFactory, nullptr);
drmPlugin = createDrmPlugin();
cryptoFactory = ICryptoFactory::getService(instance);
ASSERT_NE(cryptoFactory, nullptr);
cryptoPlugin = createCryptoPlugin();
if (!vendorModule) {
ASSERT_NE(instance, "widevine") << "Widevine requires vendor module.";
ASSERT_NE(instance, "clearkey") << "Clearkey requires vendor module.";
GTEST_SKIP() << "No vendor module installed";
}
ASSERT_EQ(instance, vendorModule->getServiceName());
contentConfigurations = vendorModule->getContentConfigurations();
// If drm scheme not installed skip subsequent tests
if (!drmFactory->isCryptoSchemeSupported(getUUID())) {
if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
GTEST_SKIP() << "vendor module drm scheme not supported";
} else {
FAIL() << "param scheme must be supported: " << android::hardware::toString(GetParamUUID());
}
}
ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() << " drm@1.2 plugin";
ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't find " << vendorModule->getServiceName() << " crypto@1.2 plugin";
}
sp<IDrmPlugin> DrmHalTest::createDrmPlugin() {
if (drmFactory == nullptr) {
return nullptr;
}
sp<IDrmPlugin> plugin = nullptr;
hidl_string packageName("android.hardware.drm.test");
auto res =
drmFactory->createPlugin(getUUID(), packageName,
[&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
plugin = IDrmPlugin::castFrom(pluginV1_0);
});
if (!res.isOk()) {
ALOGE("createDrmPlugin remote call failed");
}
return plugin;
}
sp<ICryptoPlugin> DrmHalTest::createCryptoPlugin() {
if (cryptoFactory == nullptr) {
return nullptr;
}
sp<ICryptoPlugin> plugin = nullptr;
hidl_vec<uint8_t> initVec;
auto res = cryptoFactory->createPlugin(
getUUID(), initVec,
[&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) {
EXPECT_EQ(StatusV1_0::OK == status, pluginV1_0 != nullptr);
plugin = ICryptoPlugin::castFrom(pluginV1_0);
});
if (!res.isOk()) {
ALOGE("createCryptoPlugin remote call failed");
}
return plugin;
}
hidl_array<uint8_t, 16> DrmHalTest::getUUID() {
if (GetParamUUID() == hidl_array<uint8_t, 16>()) {
return getVendorUUID();
}
return GetParamUUID();
}
hidl_array<uint8_t, 16> DrmHalTest::getVendorUUID() {
if (vendorModule == nullptr) return {};
vector<uint8_t> uuid = vendorModule->getUUID();
return hidl_array<uint8_t, 16>(&uuid[0]);
}
void DrmHalTest::provision() {
hidl_string certificateType;
hidl_string certificateAuthority;
hidl_vec<uint8_t> provisionRequest;
hidl_string defaultUrl;
auto res = drmPlugin->getProvisionRequest_1_2(
certificateType, certificateAuthority,
[&](StatusV1_2 status, const hidl_vec<uint8_t>& request,
const hidl_string& url) {
if (status == StatusV1_2::OK) {
EXPECT_NE(request.size(), 0u);
provisionRequest = request;
defaultUrl = url;
} else if (status == StatusV1_2::ERROR_DRM_CANNOT_HANDLE) {
EXPECT_EQ(0u, request.size());
}
});
EXPECT_OK(res);
if (provisionRequest.size() > 0) {
vector<uint8_t> response = vendorModule->handleProvisioningRequest(
provisionRequest, defaultUrl);
ASSERT_NE(0u, response.size());
auto res = drmPlugin->provideProvisionResponse(
response, [&](StatusV1_0 status, const hidl_vec<uint8_t>&,
const hidl_vec<uint8_t>&) {
EXPECT_EQ(StatusV1_0::OK, status);
});
EXPECT_OK(res);
}
}
SessionId DrmHalTest::openSession(SecurityLevel level, StatusV1_0 *err) {
SessionId sessionId;
auto res = drmPlugin->openSession_1_1(level,
[&](StatusV1_0 status, const hidl_vec<unsigned char> &id) {
*err = status;
sessionId = id;
});
EXPECT_OK(res);
return sessionId;
}
/**
* Helper method to open a session and verify that a non-empty
* session ID is returned
*/
SessionId DrmHalTest::openSession() {
SessionId sessionId;
auto res = drmPlugin->openSession([&](StatusV1_0 status, const hidl_vec<unsigned char> &id) {
EXPECT_EQ(StatusV1_0::OK, status);
EXPECT_NE(id.size(), 0u);
sessionId = id;
});
EXPECT_OK(res);
return sessionId;
}
/**
* Helper method to close a session
*/
void DrmHalTest::closeSession(const SessionId& sessionId) {
StatusV1_0 status = drmPlugin->closeSession(sessionId);
EXPECT_EQ(StatusV1_0::OK, status);
}
hidl_vec<uint8_t> DrmHalTest::getKeyRequest(
const SessionId& sessionId,
const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
const KeyType& type = KeyType::STREAMING) {
hidl_vec<uint8_t> keyRequest;
auto res = drmPlugin->getKeyRequest_1_2(
sessionId, configuration.initData, configuration.mimeType, type,
toHidlKeyedVector(configuration.optionalParameters),
[&](Status status, const hidl_vec<uint8_t>& request,
KeyRequestType requestType, const hidl_string&) {
EXPECT_EQ(Status::OK, status) << "Failed to get "
"key request for configuration "
<< configuration.name;
if (type == KeyType::RELEASE) {
EXPECT_EQ(KeyRequestType::RELEASE, requestType);
} else {
EXPECT_EQ(KeyRequestType::INITIAL, requestType);
}
EXPECT_NE(request.size(), 0u) << "Expected key request size"
" to have length > 0 bytes";
keyRequest = request;
});
EXPECT_OK(res);
return keyRequest;
}
DrmHalVTSVendorModule_V1::ContentConfiguration DrmHalTest::getContent(const KeyType& type) const {
for (const auto& config : contentConfigurations) {
if (type != KeyType::OFFLINE || config.policy.allowOffline) {
return config;
}
}
EXPECT_TRUE(false) << "no content configurations found";
return {};
}
hidl_vec<uint8_t> DrmHalTest::provideKeyResponse(
const SessionId& sessionId,
const hidl_vec<uint8_t>& keyResponse) {
hidl_vec<uint8_t> keySetId;
auto res = drmPlugin->provideKeyResponse(
sessionId, keyResponse,
[&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
EXPECT_EQ(StatusV1_0::OK, status) << "Failure providing "
"key response for configuration ";
keySetId = myKeySetId;
});
EXPECT_OK(res);
return keySetId;
}
/**
* Helper method to load keys for subsequent decrypt tests.
* These tests use predetermined key request/response to
* avoid requiring a round trip to a license server.
*/
hidl_vec<uint8_t> DrmHalTest::loadKeys(
const SessionId& sessionId,
const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
const KeyType& type) {
hidl_vec<uint8_t> keyRequest = getKeyRequest(sessionId, configuration, type);
/**
* Get key response from vendor module
*/
hidl_vec<uint8_t> keyResponse =
vendorModule->handleKeyRequest(keyRequest, configuration.serverUrl);
EXPECT_NE(keyResponse.size(), 0u) << "Expected key response size "
"to have length > 0 bytes";
return provideKeyResponse(sessionId, keyResponse);
}
hidl_vec<uint8_t> DrmHalTest::loadKeys(
const SessionId& sessionId,
const KeyType& type) {
return loadKeys(sessionId, getContent(type), type);
}
KeyedVector DrmHalTest::toHidlKeyedVector(
const map<string, string>& params) {
std::vector<KeyValue> stdKeyedVector;
for (auto it = params.begin(); it != params.end(); ++it) {
KeyValue keyValue;
keyValue.key = it->first;
keyValue.value = it->second;
stdKeyedVector.push_back(keyValue);
}
return KeyedVector(stdKeyedVector);
}
hidl_array<uint8_t, 16> DrmHalTest::toHidlArray(const vector<uint8_t>& vec) {
EXPECT_EQ(16u, vec.size());
return hidl_array<uint8_t, 16>(&vec[0]);
}
/**
* getDecryptMemory allocates memory for decryption, then sets it
* as a shared buffer base in the crypto hal. The allocated and
* mapped IMemory is returned.
*
* @param size the size of the memory segment to allocate
* @param the index of the memory segment which will be used
* to refer to it for decryption.
*/
sp<IMemory> DrmHalTest::getDecryptMemory(size_t size, size_t index) {
sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
EXPECT_NE(nullptr, ashmemAllocator.get());
hidl_memory hidlMemory;
auto res = ashmemAllocator->allocate(
size, [&](bool success, const hidl_memory& memory) {
EXPECT_EQ(success, true);
EXPECT_EQ(memory.size(), size);
hidlMemory = memory;
});
EXPECT_OK(res);
sp<IMemory> mappedMemory = mapMemory(hidlMemory);
EXPECT_NE(nullptr, mappedMemory.get());
res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
EXPECT_OK(res);
return mappedMemory;
}
void DrmHalTest::fillRandom(const sp<IMemory>& memory) {
random_device rd;
mt19937 rand(rd());
for (size_t i = 0; i < memory->getSize() / sizeof(uint32_t); i++) {
auto p = static_cast<uint32_t*>(
static_cast<void*>(memory->getPointer()));
p[i] = rand();
}
}
uint32_t DrmHalTest::decrypt(Mode mode, bool isSecure,
const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
const vector<uint8_t>& key, StatusV1_2 expectedStatus) {
const size_t kSegmentIndex = 0;
uint8_t localIv[AES_BLOCK_SIZE];
memcpy(localIv, iv, AES_BLOCK_SIZE);
size_t totalSize = 0;
for (size_t i = 0; i < subSamples.size(); i++) {
totalSize += subSamples[i].numBytesOfClearData;
totalSize += subSamples[i].numBytesOfEncryptedData;
}
// The first totalSize bytes of shared memory is the encrypted
// input, the second totalSize bytes (if exists) is the decrypted output.
size_t factor = expectedStatus == StatusV1_2::ERROR_DRM_FRAME_TOO_LARGE ? 1 : 2;
sp<IMemory> sharedMemory =
getDecryptMemory(totalSize * factor, kSegmentIndex);
const SharedBuffer sourceBuffer = {
.bufferId = kSegmentIndex, .offset = 0, .size = totalSize};
fillRandom(sharedMemory);
const DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
{.bufferId = kSegmentIndex,
.offset = totalSize,
.size = totalSize},
.secureMemory = nullptr};
const uint64_t offset = 0;
uint32_t bytesWritten = 0;
auto res = cryptoPlugin->decrypt_1_2(isSecure, keyId, localIv, mode, pattern,
subSamples, sourceBuffer, offset, destBuffer,
[&](StatusV1_2 status, uint32_t count, string detailedError) {
EXPECT_EQ(expectedStatus, status) << "Unexpected decrypt status " <<
detailedError;
bytesWritten = count;
});
EXPECT_OK(res);
if (bytesWritten != totalSize) {
return bytesWritten;
}
uint8_t* base = static_cast<uint8_t*>(
static_cast<void*>(sharedMemory->getPointer()));
// generate reference vector
vector<uint8_t> reference(totalSize);
memcpy(localIv, iv, AES_BLOCK_SIZE);
switch (mode) {
case Mode::UNENCRYPTED:
memcpy(&reference[0], base, totalSize);
break;
case Mode::AES_CTR:
aes_ctr_decrypt(&reference[0], base, localIv, subSamples, key);
break;
case Mode::AES_CBC:
aes_cbc_decrypt(&reference[0], base, localIv, subSamples, key);
break;
case Mode::AES_CBC_CTS:
EXPECT_TRUE(false) << "AES_CBC_CTS mode not supported";
break;
}
// compare reference to decrypted data which is at base + total size
EXPECT_EQ(0, memcmp(static_cast<void *>(&reference[0]),
static_cast<void*>(base + totalSize), totalSize))
<< "decrypt data mismatch";
return totalSize;
}
/**
* Decrypt a list of clear+encrypted subsamples using the specified key
* in AES-CTR mode
*/
void DrmHalTest::aes_ctr_decrypt(uint8_t* dest, uint8_t* src,
uint8_t* iv, const hidl_vec<SubSample>& subSamples,
const vector<uint8_t>& key) {
AES_KEY decryptionKey;
AES_set_encrypt_key(&key[0], 128, &decryptionKey);
size_t offset = 0;
unsigned int blockOffset = 0;
uint8_t previousEncryptedCounter[AES_BLOCK_SIZE];
memset(previousEncryptedCounter, 0, AES_BLOCK_SIZE);
for (size_t i = 0; i < subSamples.size(); i++) {
const SubSample& subSample = subSamples[i];
if (subSample.numBytesOfClearData > 0) {
memcpy(dest + offset, src + offset, subSample.numBytesOfClearData);
offset += subSample.numBytesOfClearData;
}
if (subSample.numBytesOfEncryptedData > 0) {
AES_ctr128_encrypt(src + offset, dest + offset,
subSample.numBytesOfEncryptedData, &decryptionKey,
iv, previousEncryptedCounter, &blockOffset);
offset += subSample.numBytesOfEncryptedData;
}
}
}
/**
* Decrypt a list of clear+encrypted subsamples using the specified key
* in AES-CBC mode
*/
void DrmHalTest::aes_cbc_decrypt(uint8_t* dest, uint8_t* src,
uint8_t* iv, const hidl_vec<SubSample>& subSamples,
const vector<uint8_t>& key) {
AES_KEY decryptionKey;
AES_set_encrypt_key(&key[0], 128, &decryptionKey);
size_t offset = 0;
for (size_t i = 0; i < subSamples.size(); i++) {
memcpy(dest + offset, src + offset, subSamples[i].numBytesOfClearData);
offset += subSamples[i].numBytesOfClearData;
AES_cbc_encrypt(src + offset, dest + offset, subSamples[i].numBytesOfEncryptedData,
&decryptionKey, iv, 0 /* decrypt */);
offset += subSamples[i].numBytesOfEncryptedData;
}
}
/**
* Helper method to test decryption with invalid keys is returned
*/
void DrmHalClearkeyTestV1_2::decryptWithInvalidKeys(
hidl_vec<uint8_t>& invalidResponse,
vector<uint8_t>& iv,
const Pattern& noPattern,
const vector<SubSample>& subSamples) {
DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent();
if (content.keys.empty()) {
FAIL() << "no keys";
}
const auto& key = content.keys[0];
auto sessionId = openSession();
auto res = drmPlugin->provideKeyResponse(
sessionId, invalidResponse,
[&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
EXPECT_EQ(StatusV1_0::OK, status);
EXPECT_EQ(0u, myKeySetId.size());
});
EXPECT_OK(res);
EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk());
uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure,
toHidlArray(key.keyId), &iv[0], subSamples, noPattern,
key.clearContentKey, Status::ERROR_DRM_NO_LICENSE);
EXPECT_EQ(0u, byteCount);
closeSession(sessionId);
}
} // namespace vts
} // namespace V1_2
} // namespace drm
} // namespace hardware
} // namespace android