OEMCrypto v16.1
Merge of http://go/wvgerrit/93404 This CL updates the Widevine CDM to support OEMCrypto v16.1 Test: Tested in 16.2 CL Bug: 141247171 Change-Id: I69bd993500f6fb63bf6010c8b0250dc7acc3d71b
This commit is contained in:
@@ -7,18 +7,24 @@ endif
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
oec_device_features.cpp \
|
||||
oec_decrypt_fallback_chain.cpp \
|
||||
oec_key_deriver.cpp \
|
||||
oec_session_util.cpp \
|
||||
oemcrypto_session_tests_helper.cpp \
|
||||
oemcrypto_test.cpp \
|
||||
oemcrypto_test_android.cpp \
|
||||
oemcrypto_test_main.cpp \
|
||||
wvcrc.cpp \
|
||||
../../cdm/util/test/test_sleep.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
$(LOCAL_PATH)/../include \
|
||||
$(LOCAL_PATH)/../odk/include \
|
||||
$(LOCAL_PATH)/../odk/kdo/include \
|
||||
$(LOCAL_PATH)/../ref/src \
|
||||
vendor/widevine/libwvdrmengine/cdm/core/include \
|
||||
vendor/widevine/libwvdrmengine/cdm/util/include \
|
||||
vendor/widevine/libwvdrmengine/cdm/util/test \
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libcdm \
|
||||
@@ -27,7 +33,10 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libgtest \
|
||||
libgtest_main \
|
||||
libwvlevel3 \
|
||||
libcdm_protos \
|
||||
libcdm_utils \
|
||||
libwv_kdo \
|
||||
libwv_odk \
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libbase \
|
||||
@@ -37,4 +46,3 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libstagefright_foundation \
|
||||
libutils \
|
||||
libz \
|
||||
|
||||
|
||||
178
libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.cpp
Normal file
178
libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#include "oec_decrypt_fallback_chain.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "oemcrypto_types.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void advance_dest_buffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
|
||||
switch (dest_buffer->type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
dest_buffer->buffer.clear.address += bytes;
|
||||
dest_buffer->buffer.clear.address_length -= bytes;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
dest_buffer->buffer.secure.offset += bytes;
|
||||
break;
|
||||
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
// Nothing to do for this buffer type.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void advance_iv_ctr(uint8_t (*subsample_iv)[wvoec::KEY_IV_SIZE], size_t bytes) {
|
||||
uint64_t counter;
|
||||
constexpr size_t half_iv_size = wvoec::KEY_IV_SIZE / 2;
|
||||
memcpy(&counter, &(*subsample_iv)[half_iv_size], half_iv_size);
|
||||
|
||||
const size_t increment =
|
||||
bytes / wvoec::AES_128_BLOCK_SIZE; // The truncation here is intentional
|
||||
counter = wvcdm::htonll64(wvcdm::ntohll64(counter) + increment);
|
||||
|
||||
memcpy(&(*subsample_iv)[half_iv_size], &counter, half_iv_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
// Decrypts the given array of samples. Handles fallback behavior correctly if
|
||||
// the OEMCrypto implementation does not accept multiple samples.
|
||||
OEMCryptoResult DecryptFallbackChain::Decrypt(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription* samples,
|
||||
size_t samples_length, OEMCryptoCipherMode cipher_mode,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_DecryptCENC(session_id, samples, samples_length, pattern);
|
||||
|
||||
// No need for a fallback. Abort early.
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||
|
||||
// Fall back to decrypting individual samples.
|
||||
for (size_t i = 0; i < samples_length; ++i) {
|
||||
sts = DecryptSample(session_id, samples[i], cipher_mode, pattern);
|
||||
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||
}
|
||||
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Decrypts the given sample. Handles fallback behavior correctly if the
|
||||
// OEMCrypto implementation does not accept full samples.
|
||||
OEMCryptoResult DecryptFallbackChain::DecryptSample(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
OEMCryptoCipherMode cipher_mode,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||
|
||||
// No need for a fallback. Abort early.
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||
|
||||
// Fall back to decrypting individual subsamples.
|
||||
OEMCrypto_SampleDescription fake_sample = sample;
|
||||
for (size_t i = 0; i < sample.subsamples_length; ++i) {
|
||||
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[i];
|
||||
|
||||
const size_t length =
|
||||
subsample.num_bytes_clear + subsample.num_bytes_encrypted;
|
||||
fake_sample.buffers.input_data_length = length;
|
||||
fake_sample.subsamples = &subsample;
|
||||
fake_sample.subsamples_length = 1;
|
||||
|
||||
sts = DecryptSubsample(session_id, fake_sample, pattern);
|
||||
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||
|
||||
fake_sample.buffers.input_data += length;
|
||||
advance_dest_buffer(&fake_sample.buffers.output_descriptor, length);
|
||||
if (cipher_mode == OEMCrypto_CipherMode_CTR) {
|
||||
advance_iv_ctr(&fake_sample.iv,
|
||||
subsample.block_offset + subsample.num_bytes_encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Decrypts the given subsample. Handles fallback behavior correctly if the
|
||||
// OEMCrypto implementation does not accept full subsamples.
|
||||
OEMCryptoResult DecryptFallbackChain::DecryptSubsample(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||
|
||||
// No need for a fallback. Abort early.
|
||||
if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts;
|
||||
|
||||
// Fall back to decrypting individual subsample halves.
|
||||
const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0];
|
||||
OEMCrypto_SampleDescription fake_sample = sample;
|
||||
OEMCrypto_SubSampleDescription fake_subsample;
|
||||
fake_sample.subsamples = &fake_subsample;
|
||||
fake_sample.subsamples_length = 1;
|
||||
|
||||
if (subsample.num_bytes_clear > 0) {
|
||||
fake_sample.buffers.input_data_length = subsample.num_bytes_clear;
|
||||
fake_subsample.num_bytes_clear = subsample.num_bytes_clear;
|
||||
fake_subsample.num_bytes_encrypted = 0;
|
||||
fake_subsample.block_offset = 0;
|
||||
|
||||
fake_subsample.subsample_flags = 0;
|
||||
if (subsample.subsample_flags & OEMCrypto_FirstSubsample)
|
||||
fake_subsample.subsample_flags |= OEMCrypto_FirstSubsample;
|
||||
if (subsample.subsample_flags & OEMCrypto_LastSubsample &&
|
||||
subsample.num_bytes_encrypted == 0)
|
||||
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
||||
|
||||
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
|
||||
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||
|
||||
// Advance the buffers for the other half, in case they're needed.
|
||||
fake_sample.buffers.input_data += subsample.num_bytes_clear;
|
||||
advance_dest_buffer(&fake_sample.buffers.output_descriptor,
|
||||
subsample.num_bytes_clear);
|
||||
}
|
||||
|
||||
if (subsample.num_bytes_encrypted > 0) {
|
||||
fake_sample.buffers.input_data_length = subsample.num_bytes_encrypted;
|
||||
fake_subsample.num_bytes_clear = 0;
|
||||
fake_subsample.num_bytes_encrypted = subsample.num_bytes_encrypted;
|
||||
fake_subsample.block_offset = subsample.block_offset;
|
||||
|
||||
fake_subsample.subsample_flags = 0;
|
||||
if (subsample.subsample_flags & OEMCrypto_FirstSubsample &&
|
||||
subsample.num_bytes_clear == 0)
|
||||
fake_subsample.subsample_flags |= OEMCrypto_FirstSubsample;
|
||||
if (subsample.subsample_flags & OEMCrypto_LastSubsample)
|
||||
fake_subsample.subsample_flags |= OEMCrypto_LastSubsample;
|
||||
|
||||
sts = DecryptSubsampleHalf(session_id, fake_sample, pattern);
|
||||
if (sts != OEMCrypto_SUCCESS) return sts;
|
||||
}
|
||||
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Decrypts the given subsample half. There is no fallback behavior after this;
|
||||
// an OEMCrypto_ERROR_BUFFER_TOO_LARGE produced here will be returned to the
|
||||
// caller.
|
||||
OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
return OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern);
|
||||
// In a real CDM, you would want some fallback here to handle the case where
|
||||
// the buffer is too big for the OEMCrypto implementation. But in the case of
|
||||
// the tests, we won't be passing a buffer that's too big unless we are trying
|
||||
// to test that failure condition, so there's no need to handle that case
|
||||
// here.
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
59
libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.h
Normal file
59
libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_
|
||||
#define CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "disallow_copy_and_assign.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
// This class groups static methods relating to providing proper fallback
|
||||
// behavior when calling DecryptCENC in OEMCrypto v16. Outside code can leverage
|
||||
// this behavior by passing the samples to be decrypted to Decrypt(), which will
|
||||
// set off the chain of fallback functions as needed.
|
||||
//
|
||||
// The behavior of this class is pathological. For each block of data, it will
|
||||
// greedily try every possible way of passing data to OEMCrypto until one works.
|
||||
// In the order tried, the ways to send data are:
|
||||
// 1) Multiple Samples at once
|
||||
// 2) Individual Samples one at a time
|
||||
// 3) Individual Subsamples one at a time
|
||||
// 4) Individual Half-Subsamples one at a time
|
||||
// On a device that only accepts half-subsamples, the way OEMCrypto v15 did,
|
||||
// this results in many needless roundtrips to OEMCrypto. This would be
|
||||
// inefficient behavior for a real CDM, but for the sake of testing, we want to
|
||||
// use the maximal way the OEMCrypto implementation will accept the data. And,
|
||||
// for implementations that do not accept multiple samples or subsamples per
|
||||
// call, we want to test that they correctly reject larger calls.
|
||||
class DecryptFallbackChain {
|
||||
public:
|
||||
static OEMCryptoResult Decrypt(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription* samples,
|
||||
size_t samples_length, OEMCryptoCipherMode cipher_mode,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
|
||||
private:
|
||||
static OEMCryptoResult DecryptSample(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
OEMCryptoCipherMode cipher_mode,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
|
||||
static OEMCryptoResult DecryptSubsample(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
|
||||
static OEMCryptoResult DecryptSubsampleHalf(
|
||||
OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern);
|
||||
|
||||
// There is no reason to have an instance of this class.
|
||||
DecryptFallbackChain() = delete;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(DecryptFallbackChain);
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_
|
||||
@@ -60,9 +60,8 @@ bool CanChangeTime() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
bool force_load_test_keybox) {
|
||||
cast_receiver = is_cast_receiver;
|
||||
void DeviceFeatures::Initialize() {
|
||||
if (initialized_) return;
|
||||
uses_keybox = false;
|
||||
uses_certificate = false;
|
||||
loads_certificate = false;
|
||||
@@ -76,7 +75,6 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
printf("OEMCrypto_Initialize failed. All tests will fail.\n");
|
||||
return;
|
||||
}
|
||||
uint32_t nonce = 0;
|
||||
uint8_t buffer[1];
|
||||
size_t size = 0;
|
||||
provisioning_method = OEMCrypto_GetProvisioningMethod();
|
||||
@@ -92,14 +90,10 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
}
|
||||
// If the device uses a keybox, check to see if loading a certificate is
|
||||
// installed.
|
||||
if (provisioning_method == OEMCrypto_Keybox) {
|
||||
loads_certificate =
|
||||
(OEMCrypto_ERROR_NOT_IMPLEMENTED !=
|
||||
OEMCrypto_RewrapDeviceRSAKey(session, buffer, 0, buffer, 0, &nonce,
|
||||
buffer, 0, buffer, buffer, &size));
|
||||
} else if (provisioning_method == OEMCrypto_OEMCertificate) {
|
||||
// If the device says it uses Provisioning 3.0, then it should be able to
|
||||
// load a DRM certificate. These devices must support RewrapDeviceRSAKey30.
|
||||
if (provisioning_method == OEMCrypto_Keybox ||
|
||||
provisioning_method == OEMCrypto_OEMCertificate) {
|
||||
// Devices with a keybox or OEM Certificate are required to support loading
|
||||
// a DRM certificate.
|
||||
loads_certificate = true;
|
||||
} else {
|
||||
// Other devices are either broken, or they have a baked in certificate.
|
||||
@@ -122,11 +116,7 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
// usage tables.
|
||||
if (api_version > 12) usage_table = OEMCrypto_SupportsUsageTable();
|
||||
printf("usage_table = %s.\n", usage_table ? "true" : "false");
|
||||
if (force_load_test_keybox) {
|
||||
derive_key_method = FORCE_TEST_KEYBOX;
|
||||
} else {
|
||||
PickDerivedKey();
|
||||
}
|
||||
PickDerivedKey();
|
||||
if (api_version >= 13) {
|
||||
uint32_t supported_cert = OEMCrypto_SupportedCertificates();
|
||||
if (supported_cert & OEMCrypto_Supports_RSA_CAST) {
|
||||
@@ -166,9 +156,6 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
case LOAD_TEST_RSA_KEY:
|
||||
printf("LOAD_TEST_RSA_KEY: Call LoadTestRSAKey before deriving keys.\n");
|
||||
break;
|
||||
case FORCE_TEST_KEYBOX:
|
||||
printf("FORCE_TEST_KEYBOX: User requested calling InstallKeybox.\n");
|
||||
break;
|
||||
case TEST_PROVISION_30:
|
||||
printf("TEST_PROVISION_30: Device provisioed with OEM Cert.\n");
|
||||
break;
|
||||
@@ -178,14 +165,15 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
printf("SecurityLevel is %s (%s)\n",
|
||||
supports_level_1 ? "Level 1" : "Not Level 1",
|
||||
security_level.c_str());
|
||||
CheckSecureBuffers();
|
||||
OEMCrypto_Terminate();
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
std::string filter = initial_filter;
|
||||
// clang-format off
|
||||
if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*");
|
||||
if (derive_key_method
|
||||
!= FORCE_TEST_KEYBOX) FilterOut(&filter, "*ForceKeybox*");
|
||||
if (!uses_certificate) FilterOut(&filter, "OEMCrypto*Cert*");
|
||||
if (!loads_certificate) FilterOut(&filter, "OEMCryptoLoadsCert*");
|
||||
if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*");
|
||||
@@ -202,10 +190,12 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
if (api_version < 13) FilterOut(&filter, "*API13*");
|
||||
if (api_version < 14) FilterOut(&filter, "*API14*");
|
||||
if (api_version < 15) FilterOut(&filter, "*API15*");
|
||||
if (api_version < 16) FilterOut(&filter, "*API16*");
|
||||
// clang-format on
|
||||
// Some tests may require root access. If user is not root, filter these tests
|
||||
// out.
|
||||
if (!CanChangeTime()) {
|
||||
FilterOut(&filter, "UsageTableTest.TimeRollbackPrevention");
|
||||
FilterOut(&filter, "*TimeRollbackPrevention*");
|
||||
}
|
||||
// Performance tests take a long time. Filter them out if they are not
|
||||
// specifically requested.
|
||||
@@ -239,7 +229,8 @@ void DeviceFeatures::PickDerivedKey() {
|
||||
}
|
||||
if (uses_keybox) {
|
||||
// If device uses a keybox, try to load the test keybox.
|
||||
if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox(NULL, 0)) {
|
||||
if (OEMCrypto_ERROR_NOT_IMPLEMENTED !=
|
||||
OEMCrypto_LoadTestKeybox(nullptr, 0)) {
|
||||
derive_key_method = LOAD_TEST_KEYBOX;
|
||||
}
|
||||
} else if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) {
|
||||
@@ -247,6 +238,39 @@ void DeviceFeatures::PickDerivedKey() {
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceFeatures::CheckSecureBuffers() {
|
||||
output_types_.push_back({false, OEMCrypto_BufferType_Clear});
|
||||
output_types_.push_back({true, OEMCrypto_BufferType_Clear});
|
||||
test_secure_buffers = false;
|
||||
OEMCrypto_SESSION session;
|
||||
OEMCryptoResult result = OEMCrypto_OpenSession(&session);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
printf("--- ERROR: Could not open session: %d ----\n", result);
|
||||
return;
|
||||
}
|
||||
OEMCrypto_DestBufferDesc output_descriptor;
|
||||
output_descriptor.type = OEMCrypto_BufferType_Secure;
|
||||
int secure_fd;
|
||||
result = OEMCrypto_AllocateSecureBuffer(session, 42, &output_descriptor,
|
||||
&secure_fd);
|
||||
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
printf("Secure buffers will not be tested\n");
|
||||
return;
|
||||
}
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
printf("--- ERROR: Could not create secure buffer: %d ----\n", result);
|
||||
return;
|
||||
}
|
||||
result = OEMCrypto_FreeSecureBuffer(session, &output_descriptor, secure_fd);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
printf("--- ERROR: Could not free secure buffer: %d ----\n", result);
|
||||
return;
|
||||
}
|
||||
printf("Secure buffers will be tested\n");
|
||||
output_types_.push_back({false, OEMCrypto_BufferType_Secure});
|
||||
test_secure_buffers = true;
|
||||
}
|
||||
|
||||
void DeviceFeatures::FilterOut(std::string* current_filter,
|
||||
const std::string& new_filter) {
|
||||
if (current_filter->find('-') == std::string::npos) {
|
||||
@@ -256,6 +280,14 @@ void DeviceFeatures::FilterOut(std::string* current_filter,
|
||||
}
|
||||
}
|
||||
|
||||
// Return the list of output types for the decrypt tests.
|
||||
const std::vector<OutputType>& DeviceFeatures::GetOutputTypes() {
|
||||
if (!initialized_) {
|
||||
Initialize();
|
||||
}
|
||||
return output_types_;
|
||||
}
|
||||
|
||||
const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method) {
|
||||
switch (method) {
|
||||
case OEMCrypto_ProvisioningError:
|
||||
|
||||
@@ -2,12 +2,26 @@
|
||||
#define CDM_OEC_DEVICE_FEATURES_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_types.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
// These tests are designed to work for this version:
|
||||
constexpr unsigned int kCurrentAPI = 16;
|
||||
// The API version when Core Messages were introduced.
|
||||
constexpr unsigned int kCoreMessagesAPI = 16;
|
||||
|
||||
// An output type for testing. The type field is secure, clear, or direct. If
|
||||
// the type is clear, then decrypt_inplace could be true. Otherwise,
|
||||
// decrypt_inplace is false.
|
||||
struct OutputType {
|
||||
bool decrypt_inplace;
|
||||
OEMCryptoBufferType type;
|
||||
};
|
||||
|
||||
// Keeps track of which features are supported by the version of OEMCrypto being
|
||||
// tested. See the integration guide for a list of optional features.
|
||||
class DeviceFeatures {
|
||||
@@ -19,7 +33,6 @@ class DeviceFeatures {
|
||||
NO_METHOD, // Cannot derive known session keys.
|
||||
LOAD_TEST_KEYBOX, // Call LoadTestKeybox before deriving keys.
|
||||
LOAD_TEST_RSA_KEY, // Call LoadTestRSAKey before deriving keys.
|
||||
FORCE_TEST_KEYBOX, // User requested calling InstallKeybox.
|
||||
TEST_PROVISION_30, // Device has OEM Certificate installed.
|
||||
};
|
||||
|
||||
@@ -34,23 +47,36 @@ class DeviceFeatures {
|
||||
bool supports_level_1; // Device supports Level 1 security.
|
||||
uint32_t resource_rating; // Device's resource rating tier.
|
||||
bool supports_crc; // Supported decrypt hash type CRC.
|
||||
bool test_secure_buffers; // If we can create a secure buffer for testing.
|
||||
uint32_t api_version;
|
||||
OEMCrypto_ProvisioningMethod provisioning_method;
|
||||
|
||||
// This should be called from the test program's main procedure.
|
||||
void Initialize(bool is_cast_receiver, bool force_load_test_keybox);
|
||||
void Initialize();
|
||||
void set_cast_receiver(bool is_cast_receiver) {
|
||||
cast_receiver = is_cast_receiver;
|
||||
}
|
||||
// Generate a GTest filter of tests that should not be run. This should be
|
||||
// called after Initialize. Tests are filtered out based on which features
|
||||
// are not supported. For example, a device that uses Provisioning 3.0 will
|
||||
// have all keybox tests filtered out.
|
||||
std::string RestrictFilter(const std::string& initial_filter);
|
||||
|
||||
// Get a list of output types that should be tested.
|
||||
const std::vector<OutputType>& GetOutputTypes();
|
||||
|
||||
private:
|
||||
// Decide which method should be used to derive session keys, based on
|
||||
// supported featuers.
|
||||
void PickDerivedKey();
|
||||
// Decide if secure buffers can be created, and initialize output_types_.
|
||||
void CheckSecureBuffers();
|
||||
// Add a GTest filter restriction to the current filter.
|
||||
void FilterOut(std::string* current_filter, const std::string& new_filter);
|
||||
|
||||
// A list of possible output types.
|
||||
std::vector<OutputType> output_types_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
// There is one global set of features for the version of OEMCrypto being
|
||||
|
||||
168
libwvdrmengine/oemcrypto/test/oec_key_deriver.cpp
Normal file
168
libwvdrmengine/oemcrypto/test/oec_key_deriver.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
//
|
||||
// OEMCrypto unit tests
|
||||
//
|
||||
|
||||
#include "oec_session_util.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/cmac.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "disallow_copy_and_assign.h"
|
||||
#include "log.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_test_data.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "platform.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
void Encryptor::set_enc_key(const std::vector<uint8_t>& enc_key) {
|
||||
enc_key_ = enc_key;
|
||||
}
|
||||
|
||||
void Encryptor::CBCEncrypt(const uint8_t* data, uint8_t* encrypted_data,
|
||||
size_t data_length,
|
||||
const uint8_t (&iv)[KEY_IV_SIZE]) const {
|
||||
ASSERT_EQ(enc_key_.size(), KEY_SIZE);
|
||||
ASSERT_NE(data, nullptr);
|
||||
ASSERT_NE(encrypted_data, nullptr);
|
||||
AES_KEY aes_key;
|
||||
static const int key_size = KEY_SIZE * 8; // in bits.
|
||||
AES_set_encrypt_key(enc_key_.data(), key_size, &aes_key);
|
||||
uint8_t iv_buffer[KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, iv, KEY_IV_SIZE);
|
||||
AES_cbc_encrypt(data, encrypted_data, data_length, &aes_key, iv_buffer,
|
||||
AES_ENCRYPT);
|
||||
}
|
||||
|
||||
void Encryptor::PadAndEncryptProvisioningMessage(
|
||||
RSAPrivateKeyMessage* data, RSAPrivateKeyMessage* encrypted) const {
|
||||
EXPECT_EQ(1, GetRandBytes(data->rsa_key_iv, KEY_IV_SIZE));
|
||||
ASSERT_EQ(enc_key_.size(), KEY_SIZE);
|
||||
*encrypted = *data;
|
||||
size_t padding = AES_BLOCK_SIZE - (data->rsa_key_length % AES_BLOCK_SIZE);
|
||||
memset(data->rsa_key + data->rsa_key_length, static_cast<uint8_t>(padding),
|
||||
padding);
|
||||
encrypted->rsa_key_length = data->rsa_key_length + padding;
|
||||
AES_KEY aes_key;
|
||||
static const int key_size = KEY_SIZE * 8; // in bits.
|
||||
AES_set_encrypt_key(enc_key_.data(), key_size, &aes_key);
|
||||
uint8_t iv_buffer[KEY_IV_SIZE];
|
||||
memcpy(iv_buffer, &data->rsa_key_iv[0], KEY_IV_SIZE);
|
||||
AES_cbc_encrypt(&data->rsa_key[0], &encrypted->rsa_key[0],
|
||||
encrypted->rsa_key_length, &aes_key, iv_buffer, AES_ENCRYPT);
|
||||
}
|
||||
|
||||
// This generates the data for deriving one key. If there are failures in
|
||||
// this function, then there is something wrong with the test program and its
|
||||
// dependency on BoringSSL.
|
||||
void KeyDeriver::DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
|
||||
int counter, vector<uint8_t>* out) {
|
||||
ASSERT_NE(key, nullptr);
|
||||
ASSERT_FALSE(context.empty());
|
||||
ASSERT_GE(4, counter);
|
||||
ASSERT_LE(1, counter);
|
||||
ASSERT_NE(out, nullptr);
|
||||
|
||||
const EVP_CIPHER* cipher = EVP_aes_128_cbc();
|
||||
CMAC_CTX* cmac_ctx = CMAC_CTX_new();
|
||||
ASSERT_NE(nullptr, cmac_ctx);
|
||||
|
||||
ASSERT_TRUE(CMAC_Init(cmac_ctx, key, KEY_SIZE, cipher, 0));
|
||||
|
||||
std::vector<uint8_t> message;
|
||||
message.push_back(static_cast<uint8_t>(counter));
|
||||
message.insert(message.end(), context.begin(), context.end());
|
||||
|
||||
ASSERT_TRUE(CMAC_Update(cmac_ctx, message.data(), message.size()));
|
||||
|
||||
size_t reslen;
|
||||
uint8_t res[128];
|
||||
ASSERT_TRUE(CMAC_Final(cmac_ctx, res, &reslen));
|
||||
|
||||
out->assign(res, res + reslen);
|
||||
CMAC_CTX_free(cmac_ctx);
|
||||
}
|
||||
|
||||
// This generates the data for deriving a set of keys. If there are failures in
|
||||
// this function, then there is something wrong with the test program and its
|
||||
// dependency on BoringSSL.
|
||||
void KeyDeriver::DeriveKeys(const uint8_t* master_key,
|
||||
const vector<uint8_t>& mac_key_context,
|
||||
const vector<uint8_t>& enc_key_context) {
|
||||
// Generate derived key for mac key
|
||||
std::vector<uint8_t> mac_key_part2;
|
||||
DeriveKey(master_key, mac_key_context, 1, &mac_key_server_);
|
||||
DeriveKey(master_key, mac_key_context, 2, &mac_key_part2);
|
||||
mac_key_server_.insert(mac_key_server_.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
DeriveKey(master_key, mac_key_context, 3, &mac_key_client_);
|
||||
DeriveKey(master_key, mac_key_context, 4, &mac_key_part2);
|
||||
mac_key_client_.insert(mac_key_client_.end(), mac_key_part2.begin(),
|
||||
mac_key_part2.end());
|
||||
|
||||
// Generate derived key for encryption key
|
||||
std::vector<uint8_t> enc_key;
|
||||
DeriveKey(master_key, enc_key_context, 1, &enc_key);
|
||||
set_enc_key(enc_key);
|
||||
}
|
||||
|
||||
void KeyDeriver::set_mac_keys(const uint8_t* mac_keys) {
|
||||
ASSERT_EQ(mac_key_server_.size(), MAC_KEY_SIZE);
|
||||
ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE);
|
||||
memcpy(mac_key_server_.data(), mac_keys, MAC_KEY_SIZE);
|
||||
memcpy(mac_key_client_.data(), mac_keys + MAC_KEY_SIZE, MAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
void KeyDeriver::ServerSignBuffer(const uint8_t* data, size_t data_length,
|
||||
std::vector<uint8_t>* signature) const {
|
||||
ASSERT_EQ(mac_key_server_.size(), MAC_KEY_SIZE);
|
||||
signature->assign(SHA256_DIGEST_LENGTH, 0);
|
||||
unsigned int sig_len = SHA256_DIGEST_LENGTH;
|
||||
ASSERT_TRUE(HMAC(EVP_sha256(), mac_key_server_.data(), mac_key_server_.size(),
|
||||
data, data_length, signature->data(), &sig_len));
|
||||
}
|
||||
|
||||
void KeyDeriver::ClientSignBuffer(const vector<uint8_t>& buffer,
|
||||
std::vector<uint8_t>* signature) const {
|
||||
ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE);
|
||||
signature->assign(SHA256_DIGEST_LENGTH, 0);
|
||||
unsigned int sig_len = SHA256_DIGEST_LENGTH;
|
||||
ASSERT_TRUE(HMAC(EVP_sha256(), mac_key_client_.data(), mac_key_client_.size(),
|
||||
buffer.data(), buffer.size(), signature->data(), &sig_len));
|
||||
}
|
||||
|
||||
void KeyDeriver::ClientSignPstReport(const vector<uint8_t>& pst_report_buffer,
|
||||
std::vector<uint8_t>* signature) const {
|
||||
ASSERT_EQ(mac_key_client_.size(), MAC_KEY_SIZE);
|
||||
signature->assign(SHA_DIGEST_LENGTH, 0);
|
||||
unsigned int sig_len = SHA_DIGEST_LENGTH;
|
||||
ASSERT_TRUE(HMAC(EVP_sha1(), mac_key_client_.data(), mac_key_client_.size(),
|
||||
&pst_report_buffer[SHA_DIGEST_LENGTH],
|
||||
pst_report_buffer.size() - SHA_DIGEST_LENGTH,
|
||||
signature->data(), &sig_len));
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
93
libwvdrmengine/oemcrypto/test/oec_key_deriver.h
Normal file
93
libwvdrmengine/oemcrypto/test/oec_key_deriver.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
//
|
||||
#ifndef CDM_OEC_KEY_DERIVER_H_
|
||||
#define CDM_OEC_KEY_DERIVER_H_
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "oec_device_features.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "pst_report.h"
|
||||
|
||||
namespace wvoec {
|
||||
|
||||
constexpr size_t kMaxTestRSAKeyLength = 2000; // Rough estimate.
|
||||
constexpr size_t kMaxCoreProvRequest = 150; // Rough estimate.
|
||||
|
||||
// This structure will be signed to simulate a provisioning response from the
|
||||
// server.
|
||||
struct RSAPrivateKeyMessage {
|
||||
uint8_t rsa_key[kMaxTestRSAKeyLength];
|
||||
uint8_t rsa_key_iv[KEY_IV_SIZE];
|
||||
size_t rsa_key_length;
|
||||
uint8_t enc_message_key[kMaxTestRSAKeyLength];
|
||||
size_t enc_message_key_length;
|
||||
uint32_t nonce;
|
||||
};
|
||||
|
||||
// Holds an encryption key and can encrypt a provisioning message. It also can
|
||||
// encrypt short buffers using CBC, such as content keys in a license.
|
||||
class Encryptor {
|
||||
public:
|
||||
Encryptor() : enc_key_(KEY_SIZE, 0) {}
|
||||
Encryptor(const std::vector<uint8_t>& enc_key) { set_enc_key(enc_key); };
|
||||
Encryptor& operator=(const Encryptor&) = default;
|
||||
void set_enc_key(const std::vector<uint8_t>& enc_key);
|
||||
|
||||
// This encrypts an RSAPrivateKeyMessage with encryption_key so that it may be
|
||||
// loaded with OEMCrypto_LoadProvisioningResponse.
|
||||
// This modifies the clear data: it adds padding and generates a random iv.
|
||||
void PadAndEncryptProvisioningMessage(RSAPrivateKeyMessage* data,
|
||||
RSAPrivateKeyMessage* encrypted) const;
|
||||
|
||||
void CBCEncrypt(const uint8_t* data, uint8_t* encrypted_data,
|
||||
size_t data_length, const uint8_t (&iv)[KEY_IV_SIZE]) const;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> enc_key_;
|
||||
};
|
||||
|
||||
// Holds encryption and mac keys derived from a master key.
|
||||
// Can be used to sign a buffer as either a server or client.
|
||||
class KeyDeriver : public Encryptor {
|
||||
public:
|
||||
KeyDeriver()
|
||||
: mac_key_server_(MAC_KEY_SIZE, 0), mac_key_client_(MAC_KEY_SIZE, 0) {}
|
||||
KeyDeriver& operator=(const KeyDeriver&) = default;
|
||||
|
||||
// Generate mac and enc keys give the master key.
|
||||
void DeriveKeys(const uint8_t* master_key,
|
||||
const std::vector<uint8_t>& mac_key_context,
|
||||
const std::vector<uint8_t>& enc_key_context);
|
||||
// Sign the buffer with server's mac key.
|
||||
void ServerSignBuffer(const uint8_t* data, size_t data_length,
|
||||
std::vector<uint8_t>* signature) const;
|
||||
// Sign the buffer with client's known mac key. Known test keys must be
|
||||
// installed first. This uses HMAC with SHA256, so is suitable for a message.
|
||||
void ClientSignBuffer(const std::vector<uint8_t>& buffer,
|
||||
std::vector<uint8_t>* signature) const;
|
||||
// Sign the pst buffer with client's known mac key. Known test keys must be
|
||||
// installed first. This uses HMAC with SHA128, and skips the beginning of the
|
||||
// buffer, so is only suitable for a pst report.
|
||||
void ClientSignPstReport(const std::vector<uint8_t>& pst_report_buffer,
|
||||
std::vector<uint8_t>* signature) const;
|
||||
void set_mac_keys(const uint8_t* mac_keys);
|
||||
|
||||
private:
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
void DeriveKey(const uint8_t* key, const std::vector<uint8_t>& context,
|
||||
int counter, std::vector<uint8_t>* out);
|
||||
|
||||
std::vector<uint8_t> mac_key_server_;
|
||||
std::vector<uint8_t> mac_key_client_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
#endif // CDM_OEC_KEY_DERIVER_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,12 +7,17 @@
|
||||
//
|
||||
// OEMCrypto unit tests
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core_message_deserialize.h"
|
||||
#include "core_message_serialize.h"
|
||||
#include "odk.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "oec_key_deriver.h"
|
||||
#include "oemcrypto_types.h"
|
||||
#include "pst_report.h"
|
||||
|
||||
@@ -29,21 +34,21 @@ void PrintTo(const vector<uint8_t>& value, ostream* os);
|
||||
namespace wvoec {
|
||||
|
||||
// Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp
|
||||
const size_t kMaxNumKeys = 20;
|
||||
constexpr size_t kMaxNumKeys = 30;
|
||||
|
||||
namespace {
|
||||
#if defined(TEST_SPEED_MULTIPLIER) // Can slow test time limits when
|
||||
// debugging is slowing everything.
|
||||
const int kSpeedMultiplier = TEST_SPEED_MULTIPLIER;
|
||||
constexpr int kSpeedMultiplier = TEST_SPEED_MULTIPLIER;
|
||||
#else
|
||||
const int kSpeedMultiplier = 1;
|
||||
constexpr int kSpeedMultiplier = 1;
|
||||
#endif
|
||||
const int kShortSleep = 1 * kSpeedMultiplier;
|
||||
const int kLongSleep = 2 * kSpeedMultiplier;
|
||||
const uint32_t kDuration = 2 * kSpeedMultiplier;
|
||||
const uint32_t kLongDuration = 5 * kSpeedMultiplier;
|
||||
const int32_t kTimeTolerance = 3 * kSpeedMultiplier;
|
||||
const time_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier;
|
||||
constexpr int kShortSleep = 1 * kSpeedMultiplier;
|
||||
constexpr int kLongSleep = 2 * kSpeedMultiplier;
|
||||
constexpr uint32_t kDuration = 2 * kSpeedMultiplier;
|
||||
constexpr uint32_t kLongDuration = 5 * kSpeedMultiplier;
|
||||
constexpr int32_t kTimeTolerance = 3 * kSpeedMultiplier;
|
||||
constexpr int64_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier;
|
||||
} // namespace
|
||||
|
||||
typedef struct {
|
||||
@@ -55,14 +60,12 @@ typedef struct {
|
||||
|
||||
// Note: The API does not specify a maximum key id length. We specify a
|
||||
// maximum just for these tests, so that we have a fixed message size.
|
||||
const size_t kTestKeyIdMaxLength = 16;
|
||||
constexpr size_t kTestKeyIdMaxLength = 16;
|
||||
|
||||
// Most content will use a key id that is 16 bytes long.
|
||||
const int kDefaultKeyIdLength = 16;
|
||||
|
||||
const size_t kMaxTestRSAKeyLength = 2000; // Rough estimate.
|
||||
const size_t kMaxPSTLength = 255; // In specification.
|
||||
const size_t kMaxMessageSize = 8 * 1024; // In specification.
|
||||
constexpr int kDefaultKeyIdLength = 16;
|
||||
constexpr size_t kMaxPSTLength = 255; // In specification.
|
||||
constexpr size_t kMaxCoreMessage = 200 * kMaxNumKeys + 200; // Rough estimate.
|
||||
|
||||
typedef struct {
|
||||
uint8_t key_id[kTestKeyIdMaxLength];
|
||||
@@ -84,37 +87,40 @@ struct MessageData {
|
||||
uint8_t padding[KEY_IV_SIZE];
|
||||
uint8_t mac_keys[2 * MAC_KEY_SIZE];
|
||||
uint8_t pst[kMaxPSTLength];
|
||||
};
|
||||
|
||||
// This structure will be signed to simulate a provisioning response from the
|
||||
// server.
|
||||
struct RSAPrivateKeyMessage {
|
||||
uint8_t rsa_key[kMaxTestRSAKeyLength];
|
||||
uint8_t rsa_key_iv[KEY_IV_SIZE];
|
||||
size_t rsa_key_length;
|
||||
uint32_t nonce;
|
||||
SRM_Restriction_Data srm_restriction_data;
|
||||
};
|
||||
|
||||
struct Test_PST_Report {
|
||||
Test_PST_Report(const std::string& pst_in,
|
||||
OEMCrypto_Usage_Entry_Status status_in)
|
||||
: status(status_in), pst(pst_in), time_created(time(NULL)) {}
|
||||
OEMCrypto_Usage_Entry_Status status_in);
|
||||
|
||||
OEMCrypto_Usage_Entry_Status status;
|
||||
int64_t seconds_since_license_received;
|
||||
int64_t seconds_since_first_decrypt;
|
||||
int64_t seconds_since_last_decrypt;
|
||||
std::string pst;
|
||||
time_t time_created;
|
||||
int64_t time_created;
|
||||
};
|
||||
|
||||
struct EntitledContentKeyData {
|
||||
uint8_t entitlement_key_id[KEY_SIZE];
|
||||
uint8_t content_key_id[KEY_SIZE];
|
||||
uint8_t content_key_data_iv[KEY_SIZE];
|
||||
uint8_t entitlement_key_id[kTestKeyIdMaxLength];
|
||||
size_t entitlement_key_id_length;
|
||||
uint8_t content_key_id[kTestKeyIdMaxLength];
|
||||
size_t content_key_id_length;
|
||||
uint8_t content_key_data_iv[KEY_IV_SIZE];
|
||||
uint8_t content_key_data[KEY_SIZE];
|
||||
uint8_t encrypted_content_key_data[KEY_SIZE];
|
||||
size_t key_index; // Index into the license's key array. Only for testing.
|
||||
};
|
||||
|
||||
// returns 1 on success, -1 if not supported, or 0 if other failure.
|
||||
int GetRandBytes(unsigned char* buf, int num);
|
||||
|
||||
void GenerateSimpleSampleDescription(const std::vector<uint8_t>& in,
|
||||
std::vector<uint8_t>& out,
|
||||
OEMCrypto_SampleDescription* sample,
|
||||
OEMCrypto_SubSampleDescription* subsample);
|
||||
|
||||
// Increment counter for AES-CTR. The CENC spec specifies we increment only
|
||||
// the low 64 bits of the IV counter, and leave the high 64 bits alone. This
|
||||
// is different from the OpenSSL implementation, so we implement the CTR loop
|
||||
@@ -134,6 +140,288 @@ OEMCrypto_Substring GetSubstring(const std::string& message = "",
|
||||
const std::string& field = "",
|
||||
bool set_zero = false);
|
||||
|
||||
class Session;
|
||||
// The prototype of the OEMCrypto function to prepare and sign a request.
|
||||
typedef OEMCryptoResult (*PrepAndSignRequest_t)(
|
||||
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
||||
size_t* core_message_length, uint8_t* signature, size_t* signature_length);
|
||||
|
||||
// A RoundTrip helps generate and verify a request message, helps generate the
|
||||
// corresponding response, and then helps verify loading the response.
|
||||
template <class CoreRequest, PrepAndSignRequest_t PrepAndSignRequest,
|
||||
class CoreResponse, class ResponseData>
|
||||
class RoundTrip {
|
||||
public:
|
||||
RoundTrip() = delete;
|
||||
RoundTrip(Session* session)
|
||||
: session_(session),
|
||||
core_request_(),
|
||||
core_response_(),
|
||||
response_data_(),
|
||||
encrypted_response_data_(),
|
||||
message_size_(sizeof(ResponseData) + kMaxCoreMessage){};
|
||||
virtual ~RoundTrip() {}
|
||||
|
||||
// Have OEMCrypto sign a request message and then verify the signature and the
|
||||
// core message.
|
||||
virtual void SignAndVerifyRequest();
|
||||
// Create a default |response_data| and |core_response|.
|
||||
virtual void CreateDefaultResponse() = 0;
|
||||
// Copy fields from |response_data| to |padded_response_data|, encrypting
|
||||
// those that should be encrypted. Serialize the core message. Then sign the
|
||||
// response.
|
||||
virtual void EncryptAndSignResponse() = 0;
|
||||
// Attempt to load the response and return the error. Short buffer errors are
|
||||
// handled by LoadResponse, not the caller. All other errors should be
|
||||
// handled by the caller.
|
||||
virtual OEMCryptoResult LoadResponse() { return LoadResponse(session_); }
|
||||
// As with LoadResponse, but load into a different session.
|
||||
virtual OEMCryptoResult LoadResponse(Session* session) = 0;
|
||||
|
||||
// Accessors are all read/write because tests modify default values.
|
||||
Session* session() { return session_; }
|
||||
void set_session(Session* session) { session_ = session; }
|
||||
CoreRequest& core_request() { return core_request_; }
|
||||
CoreResponse& core_response() { return core_response_; }
|
||||
ResponseData& response_data() { return response_data_; }
|
||||
ResponseData& encrypted_response_data() { return encrypted_response_data_; }
|
||||
std::vector<uint8_t>& encrypted_response_buffer() {
|
||||
return encrypted_response_;
|
||||
}
|
||||
|
||||
// Set the size of the buffer used the encrypted license.
|
||||
void set_message_size(size_t size) { message_size_ = size; }
|
||||
// The size of the encrypted message.
|
||||
size_t message_size() { return message_size_; }
|
||||
std::vector<uint8_t>& response_signature() { return response_signature_; }
|
||||
const std::string& serialized_core_message() const {
|
||||
return serialized_core_message_;
|
||||
}
|
||||
|
||||
protected:
|
||||
// ----------------------------------------------------------------------
|
||||
// Specialized functionality for each message type.
|
||||
|
||||
// Verify the signature of the request.
|
||||
virtual void VerifyRequestSignature(
|
||||
const vector<uint8_t>& data, const vector<uint8_t>& generated_signature,
|
||||
size_t core_message_length) = 0;
|
||||
// Verify the values of the core response.
|
||||
virtual void FillAndVerifyCoreRequest(
|
||||
const std::string& core_message_string) = 0;
|
||||
// Find the given pointer in the response_data_.
|
||||
virtual OEMCrypto_Substring FindSubstring(const void* pointer, size_t length);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Member variables.
|
||||
Session* session_;
|
||||
CoreRequest core_request_;
|
||||
CoreResponse core_response_;
|
||||
ResponseData response_data_, encrypted_response_data_;
|
||||
size_t message_size_; // How much of the padded message to use.
|
||||
std::vector<uint8_t> response_signature_;
|
||||
std::string serialized_core_message_;
|
||||
std::vector<uint8_t> encrypted_response_;
|
||||
};
|
||||
|
||||
class ProvisioningRoundTrip
|
||||
: public RoundTrip<
|
||||
/* CoreRequest */ oemcrypto_core_message::ODK_ProvisioningRequest,
|
||||
OEMCrypto_PrepAndSignProvisioningRequest,
|
||||
/* CoreResponse */ ODK_ParsedProvisioning,
|
||||
/* ResponseData */ RSAPrivateKeyMessage> {
|
||||
public:
|
||||
ProvisioningRoundTrip(Session* session,
|
||||
const std::vector<uint8_t>& encoded_rsa_key)
|
||||
: RoundTrip(session),
|
||||
allowed_schemes_(kSign_RSASSA_PSS),
|
||||
encryptor_(),
|
||||
encoded_rsa_key_(encoded_rsa_key) {}
|
||||
// Prepare the session for signing the request.
|
||||
virtual void PrepareSession(const wvoec::WidevineKeybox& keybox);
|
||||
void CreateDefaultResponse() override;
|
||||
void EncryptAndSignResponse() override;
|
||||
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
|
||||
OEMCryptoResult LoadResponse(Session* session) override;
|
||||
void VerifyLoadFailed();
|
||||
const std::vector<uint8_t>& encoded_rsa_key() { return encoded_rsa_key_; }
|
||||
const std::vector<uint8_t>& wrapped_rsa_key() { return wrapped_rsa_key_; }
|
||||
void set_allowed_schemes(uint32_t allowed_schemes) {
|
||||
allowed_schemes_ = allowed_schemes;
|
||||
}
|
||||
|
||||
protected:
|
||||
void VerifyRequestSignature(const vector<uint8_t>& data,
|
||||
const vector<uint8_t>& generated_signature,
|
||||
size_t core_message_length) override;
|
||||
// Verify the values of the core response.
|
||||
virtual void FillAndVerifyCoreRequest(
|
||||
const std::string& core_message_string) override;
|
||||
uint32_t allowed_schemes_;
|
||||
Encryptor encryptor_;
|
||||
// The message key used for Prov 3.0.
|
||||
std::vector<uint8_t> message_key_;
|
||||
std::vector<uint8_t> encrypted_message_key_;
|
||||
std::vector<uint8_t> encoded_rsa_key_;
|
||||
std::vector<uint8_t> wrapped_rsa_key_;
|
||||
};
|
||||
|
||||
class LicenseRoundTrip
|
||||
: public RoundTrip<
|
||||
/* CoreRequest */ oemcrypto_core_message::ODK_LicenseRequest,
|
||||
OEMCrypto_PrepAndSignLicenseRequest,
|
||||
/* CoreResponse */ ODK_ParsedLicense,
|
||||
/* ResponseData */ MessageData> {
|
||||
public:
|
||||
LicenseRoundTrip(Session* session)
|
||||
: RoundTrip(session),
|
||||
control_(wvoec::kControlNonceEnabled),
|
||||
num_keys_(4),
|
||||
pst_(""),
|
||||
minimum_srm_version_(0),
|
||||
update_mac_keys_(true),
|
||||
api_version_(kCurrentAPI),
|
||||
expect_request_has_correct_nonce_(true),
|
||||
license_type_(OEMCrypto_ContentLicense) {}
|
||||
void CreateDefaultResponse() override;
|
||||
// Create a license with four keys. Each key is responsible for one of generic
|
||||
// encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key
|
||||
// is allowed only one type of operation.
|
||||
void CreateResponseWithGenericCryptoKeys();
|
||||
// Fill the |core_response| substrings.
|
||||
virtual void FillCoreResponseSubstrings();
|
||||
void EncryptAndSignResponse() override;
|
||||
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
|
||||
OEMCryptoResult LoadResponse(Session* session) override;
|
||||
// Reload an offline license into a different session. This derives new mac
|
||||
// keys and then calls LoadResponse.
|
||||
OEMCryptoResult ReloadResponse(Session* session);
|
||||
void VerifyTestKeys();
|
||||
// Set the default key control block for all keys. This is used in
|
||||
// CreateDefaultResponse. The key control block determines the restrictions
|
||||
// that OEMCrypto should place on a key's use. For example, it specifies the
|
||||
// minimum HDCP requirement and whether the key can only be used with a secure
|
||||
// video path. See the section "Key Control Block" in the document "Widevine
|
||||
// Modular DRM Security Integration Guide for CENC".
|
||||
void set_control(uint32_t control) { control_ = control; }
|
||||
uint32_t control() const { return control_; }
|
||||
// Set the number of keys to use in the license.
|
||||
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
|
||||
uint32_t num_keys() const { return num_keys_; }
|
||||
// Get/Set the pst for the license and usage table entry.
|
||||
const std::string& pst() const { return pst_; }
|
||||
void set_pst(const std::string& pst) { pst_ = pst; }
|
||||
// Set the minimum SRM version for the license.
|
||||
void set_minimum_srm_version(uint32_t minimum_srm_version) {
|
||||
minimum_srm_version_ = minimum_srm_version;
|
||||
}
|
||||
// Change the hash of the core request. This should cause the response to be
|
||||
// rejected.
|
||||
void BreakRequestHash() { core_response_.request_hash[3] ^= 42; }
|
||||
// Set the API version for the license itself. This will be used in
|
||||
// CreateDefaultResponse.
|
||||
void set_api_version(uint32_t api_version) { api_version_ = api_version; }
|
||||
uint32_t api_version() const { return api_version_; }
|
||||
void set_update_mac_keys(bool update_mac_keys) {
|
||||
update_mac_keys_ = update_mac_keys;
|
||||
}
|
||||
void set_license_type(OEMCrypto_LicenseType license_type) {
|
||||
license_type_ = license_type;
|
||||
}
|
||||
// Skip the nonce check when verifying the license request.
|
||||
void skip_nonce_check() { expect_request_has_correct_nonce_ = false; }
|
||||
// This sets the key id of the specified key to the specified string.
|
||||
// This is used to test with different key id lengths.
|
||||
void SetKeyId(size_t index, const string& key_id);
|
||||
|
||||
protected:
|
||||
void VerifyRequestSignature(const vector<uint8_t>& data,
|
||||
const vector<uint8_t>& generated_signature,
|
||||
size_t core_message_length) override;
|
||||
// Verify the values of the core response.
|
||||
virtual void FillAndVerifyCoreRequest(
|
||||
const std::string& core_message_string) override;
|
||||
|
||||
// The default key control bits used with CreateDefaultResponse.
|
||||
uint32_t control_;
|
||||
// The number of keys in the license response.
|
||||
uint32_t num_keys_;
|
||||
// If non-empty, the license's provider session token.
|
||||
std::string pst_;
|
||||
// If non-zero, the minimum SRM version.
|
||||
uint32_t minimum_srm_version_;
|
||||
// If true, the license contains new mac keys for signing renewals.
|
||||
bool update_mac_keys_;
|
||||
// API version for the license itself. If this is 0 when the license request
|
||||
// is signed, it will be set to the same as OEMCrypto's API version. It may
|
||||
// be set to a lower value in order to test backwards compatibility.
|
||||
uint32_t api_version_;
|
||||
// If true, then we expect the nonce in the core request to match that in
|
||||
// session. This is usually true, but when we are testing how OEMCrypto
|
||||
// handles a bad nonce, we don't want to.
|
||||
bool expect_request_has_correct_nonce_;
|
||||
// Whether this is a content license or an entitlement license. Used in
|
||||
// CreateDefaultResponse.
|
||||
OEMCrypto_LicenseType license_type_;
|
||||
};
|
||||
|
||||
class RenewalRoundTrip
|
||||
: public RoundTrip<
|
||||
/* CoreRequest */ oemcrypto_core_message::ODK_RenewalRequest,
|
||||
OEMCrypto_PrepAndSignRenewalRequest,
|
||||
// Renewal response info is same as request:
|
||||
/* CoreResponse */ oemcrypto_core_message::ODK_RenewalRequest,
|
||||
/* ResponseData */ MessageData> {
|
||||
public:
|
||||
RenewalRoundTrip(LicenseRoundTrip* license_messages)
|
||||
: RoundTrip(license_messages->session()),
|
||||
license_messages_(license_messages),
|
||||
refresh_object_(),
|
||||
is_release_(false) {}
|
||||
void CreateDefaultResponse() override;
|
||||
void EncryptAndSignResponse() override;
|
||||
OEMCryptoResult LoadResponse() override { return LoadResponse(session_); }
|
||||
OEMCryptoResult LoadResponse(Session* session) override;
|
||||
void set_is_release(bool is_release) { is_release_ = is_release; }
|
||||
|
||||
protected:
|
||||
void VerifyRequestSignature(const vector<uint8_t>& data,
|
||||
const vector<uint8_t>& generated_signature,
|
||||
size_t core_message_length) override;
|
||||
// Verify the values of the core response.
|
||||
virtual void FillAndVerifyCoreRequest(
|
||||
const std::string& core_message_string) override;
|
||||
LicenseRoundTrip* license_messages_;
|
||||
OEMCrypto_KeyRefreshObject refresh_object_;
|
||||
bool is_release_; // If this is a license release, and not a real renewal.
|
||||
};
|
||||
|
||||
class EntitledMessage {
|
||||
public:
|
||||
EntitledMessage(LicenseRoundTrip* license_messages)
|
||||
: license_messages_(license_messages), num_keys_() {}
|
||||
void FillKeyArray();
|
||||
void MakeOneKey(size_t entitlement_key_index);
|
||||
void LoadKeys(OEMCryptoResult expected_sts);
|
||||
void set_num_keys(uint32_t num_keys) { num_keys_ = num_keys; }
|
||||
uint32_t num_keys() const { return num_keys_; }
|
||||
void SetEntitlementKeyId(unsigned int index, const std::string& key_id);
|
||||
// Verify that key control blocks of the loaded keys.
|
||||
void VerifyEntitlementTestKeys();
|
||||
|
||||
private:
|
||||
// Find the offset of the give pointer, relative to |entitled_key_data_|.
|
||||
OEMCrypto_Substring FindSubstring(const void* ptr, size_t size);
|
||||
|
||||
LicenseRoundTrip* license_messages_;
|
||||
uint32_t num_keys_;
|
||||
// Clear Entitlement key data. This is the backing data for
|
||||
// |entitled_key_array_|.
|
||||
EntitledContentKeyData entitled_key_data_[kMaxNumKeys];
|
||||
// Entitled key object. Pointers are backed by |entitled_key_data_|.
|
||||
OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys];
|
||||
};
|
||||
|
||||
class Session {
|
||||
public:
|
||||
Session();
|
||||
@@ -141,9 +429,9 @@ class Session {
|
||||
|
||||
// Returns the most recently generated nonce.
|
||||
// Valid after call to GenerateNonce.
|
||||
uint32_t get_nonce() { return nonce_; }
|
||||
uint32_t nonce() const { return nonce_; }
|
||||
// Valid after call to open().
|
||||
uint32_t session_id() { return (uint32_t)session_id_; }
|
||||
uint32_t session_id() const { return (uint32_t)session_id_; }
|
||||
// Call OEMCrypto_OpenSession, with GTest ASSERTs.
|
||||
void open();
|
||||
// Call OEMCrypto_CloseSession, with GTest ASSERTs.
|
||||
@@ -155,7 +443,7 @@ class Session {
|
||||
// Generates one nonce. If error_counter is null, this will sleep 1 second
|
||||
// and try again if a nonce flood has been detected. If error_counter is
|
||||
// not null, it will be incremented when a nonce flood is detected.
|
||||
void GenerateNonce(int* error_counter = NULL);
|
||||
void GenerateNonce(int* error_counter = nullptr);
|
||||
// Fill the vectors with test context which generate known mac and enc keys.
|
||||
void FillDefaultContext(vector<uint8_t>* mac_context,
|
||||
vector<uint8_t>* enc_context);
|
||||
@@ -165,87 +453,6 @@ class Session {
|
||||
// Generate known mac and enc keys using OEMCrypto_DeriveKeysFromSessionKey
|
||||
// and also fill out enc_key_, mac_key_server_, and mac_key_client_.
|
||||
void GenerateDerivedKeysFromSessionKey();
|
||||
// Loads and verifies the keys in the message pointed to by message_ptr()
|
||||
// using OEMCrypto_LoadKeys. This message should have already been created
|
||||
// by FillSimpleMessage, modified if needed, and then encrypted and signed by
|
||||
// the server's mac key in EncryptAndSign.
|
||||
void LoadTestKeys(const std::string& pst = "", bool new_mac_keys = true);
|
||||
// Loads the entitlement keys in the message pointed to by message_ptr()
|
||||
// using OEMCrypto_LoadKeys. This message should have already been created
|
||||
// by FillSimpleEntitlementMessage, modified if needed, and then encrypted
|
||||
// and signed by the server's mac key in EncryptAndSign.
|
||||
void LoadEntitlementTestKeys(const std::string& pst = "",
|
||||
bool new_mac_keys = true,
|
||||
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
||||
// Fills an OEMCrypto_EntitledContentKeyObject using the information from
|
||||
// the license_ and randomly generated content keys. This method should be
|
||||
// called after LoadEntitlementTestKeys.
|
||||
void FillEntitledKeyArray();
|
||||
// Encrypts and loads the entitled content keys via
|
||||
// OEMCrypto_LoadEntitledContentKeys.
|
||||
void LoadEntitledContentKeys(
|
||||
OEMCryptoResult expected_sts = OEMCrypto_SUCCESS);
|
||||
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
||||
// have the correct key control data.
|
||||
void VerifyTestKeys();
|
||||
// This uses OEMCrypto_QueryKeyControl to check that the keys in OEMCrypto
|
||||
// have the correct key control data.
|
||||
void VerifyEntitlementTestKeys();
|
||||
// This creates a refresh key or license renewal message, signs it with the
|
||||
// server's mac key, and calls OEMCrypto_RefreshKeys.
|
||||
void RefreshTestKeys(const size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce, OEMCryptoResult expected_result);
|
||||
// This sets the key id in the current message data to the specified string.
|
||||
// This is used to test with different key id lengths.
|
||||
void SetKeyId(int index, const string& key_id);
|
||||
// This fills the data structure license_ with key information. This data
|
||||
// can be modified, and then should be encrypted and signed in EncryptAndSign
|
||||
// before being loaded in LoadTestKeys.
|
||||
void FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce,
|
||||
const std::string& pst = "");
|
||||
// This fills the data structure license_ with entitlement key information.
|
||||
// This data can be modified, and then should be encrypted and signed in
|
||||
// EncryptAndSign before being loaded in LoadEntitlementTestKeys.
|
||||
void FillSimpleEntitlementMessage(
|
||||
uint32_t duration, uint32_t control,
|
||||
uint32_t nonce, const std::string& pst = "");
|
||||
// Like FillSimpleMessage, this fills encrypted_license_ with data. The name
|
||||
// is a little misleading: the license renewal message is not encrypted, it
|
||||
// is just signed. The signature is computed in RefreshTestKeys, above.
|
||||
void FillRefreshMessage(size_t key_count, uint32_t control_bits,
|
||||
uint32_t nonce);
|
||||
// Sets the OEMCrypto_Substring parameters of the LoadKeys method.
|
||||
// Specifically, it sets the |enc_mac_keys_iv|, |enc_mac_keys|, |pst|, and
|
||||
// |srm_restriction_data| in that order. For testing purposes,
|
||||
// |srm_restriction_data| will always be NULL.
|
||||
void SetLoadKeysSubstringParams();
|
||||
// This copies data from license_ to encrypted_license_, and then encrypts
|
||||
// each field in the key array appropriately. It then signes the buffer with
|
||||
// the server mac keys. It then fills out the key_array_ so that pointers in
|
||||
// that array point to the locations in the encrypted message.
|
||||
void EncryptAndSign();
|
||||
// This encrypts an RSAPrivateKeyMessage with encryption_key so that it may be
|
||||
// loaded with OEMCrypto_RewrapDeviceRSAKey.
|
||||
void EncryptProvisioningMessage(RSAPrivateKeyMessage* data,
|
||||
RSAPrivateKeyMessage* encrypted,
|
||||
const vector<uint8_t>& encryption_key);
|
||||
// Sign the buffer with server's mac key.
|
||||
void ServerSignBuffer(const uint8_t* data, size_t data_length,
|
||||
std::vector<uint8_t>* signature);
|
||||
// Sign the buffer with client's known mac key. Known test keys must be
|
||||
// installed first.
|
||||
void ClientSignMessage(const vector<uint8_t>& data,
|
||||
std::vector<uint8_t>* signature);
|
||||
// This checks the signature generated by OEMCrypto_GenerateSignature against
|
||||
// that generaged by ClientSignMessage.
|
||||
void VerifyClientSignature(size_t data_length = 400);
|
||||
// Set the pointers in key_array[*] to point values inside data. This is
|
||||
// needed to satisfy range checks in OEMCrypto_LoadKeys.
|
||||
void FillKeyArray(const MessageData& data, OEMCrypto_KeyObject* key_array);
|
||||
// As in FillKeyArray but for the license renewal message passed to
|
||||
// OEMCrypto_RefreshKeys.
|
||||
void FillRefreshArray(OEMCrypto_KeyRefreshObject* key_array,
|
||||
size_t key_count);
|
||||
// Encrypt a block of data using CTR mode.
|
||||
void EncryptCTR(const vector<uint8_t>& in_buffer, const uint8_t* key,
|
||||
const uint8_t* starting_iv, vector<uint8_t>* out_buffer);
|
||||
@@ -253,24 +460,12 @@ class Session {
|
||||
void TestDecryptCTR(bool select_key_first = true,
|
||||
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
||||
int key_index = 0);
|
||||
// This compares the actual result with the expected result. If OEMCrypto is
|
||||
// an older version, we allow it to report an equivalent error code.
|
||||
void TestDecryptResult(OEMCryptoResult expected_result,
|
||||
OEMCryptoResult actual_result);
|
||||
// Verify that an attempt to select an expired key either succeeds, or gives
|
||||
// an actionable error code.
|
||||
void TestSelectExpired(unsigned int key_index);
|
||||
// Calls OEMCrypto_GetOEMPublicCertificate and loads the OEM cert's public
|
||||
// rsa key into public_rsa_.
|
||||
// Calls OEMCrypto_GetOEMPublicCertificate and OEMCrypto_LoadOEMPrivateKey and
|
||||
// loads the OEM cert's public rsa key into public_rsa_.
|
||||
void LoadOEMCert(bool verify_cert = false);
|
||||
// Creates RSAPrivateKeyMessage for the specified rsa_key, encrypts it with
|
||||
// the specified encryption key, and then signs it with the server's mac key.
|
||||
// If encryption_key is null, use the session's enc_key_.
|
||||
void MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted,
|
||||
size_t message_size, std::vector<uint8_t>* signature,
|
||||
uint32_t allowed_schemes,
|
||||
const vector<uint8_t>& rsa_key,
|
||||
const vector<uint8_t>* encryption_key = NULL);
|
||||
// Calls OEMCrypto_RewrapDeviceRSAKey with the given provisioning response
|
||||
// message. If force is true, we assert that the key loads successfully.
|
||||
void RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted,
|
||||
@@ -278,7 +473,7 @@ class Session {
|
||||
vector<uint8_t>* wrapped_key, bool force);
|
||||
// Loads the specified RSA public key into public_rsa_. If rsa_key is null,
|
||||
// the default test key is loaded.
|
||||
void PreparePublicKey(const uint8_t* rsa_key = NULL,
|
||||
void PreparePublicKey(const uint8_t* rsa_key = nullptr,
|
||||
size_t rsa_key_length = 0);
|
||||
// Verifies the given signature is from the given message and RSA key, pkey.
|
||||
static bool VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message,
|
||||
@@ -306,7 +501,7 @@ class Session {
|
||||
// Creates a new usage entry, and keeps track of the index.
|
||||
// If status is null, we expect success, otherwise status is set to the
|
||||
// return value.
|
||||
void CreateNewUsageEntry(OEMCryptoResult *status = NULL);
|
||||
void CreateNewUsageEntry(OEMCryptoResult* status = nullptr);
|
||||
// Copy encrypted usage entry from other session, and then load it.
|
||||
// This session must already be open.
|
||||
void LoadUsageEntry(uint32_t index, const vector<uint8_t>& buffer);
|
||||
@@ -330,7 +525,7 @@ class Session {
|
||||
}
|
||||
// Generates a usage report for the specified pst. If there is success,
|
||||
// the report's signature is verified, and several fields are given sanity
|
||||
// checks. If other is not null, then the mac keys are copied from other in
|
||||
// checks. If |other| is not null, then the mac keys are copied from other in
|
||||
// order to verify signatures.
|
||||
void GenerateReport(const std::string& pst,
|
||||
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
||||
@@ -355,12 +550,6 @@ class Session {
|
||||
int64_t time_license_received = 0,
|
||||
int64_t time_first_decrypt = 0,
|
||||
int64_t time_last_decrypt = 0);
|
||||
// Same as above, but generates the report with the given status.
|
||||
void GenerateVerifyReport(const std::string& pst,
|
||||
OEMCrypto_Usage_Entry_Status status,
|
||||
int64_t time_license_received = 0,
|
||||
int64_t time_first_decrypt = 0,
|
||||
int64_t time_last_decrypt = 0);
|
||||
// Create an entry in the old usage table based on the given report.
|
||||
void CreateOldEntry(const Test_PST_Report &report);
|
||||
// Create a new entry and copy the old entry into it. Then very the report
|
||||
@@ -370,88 +559,33 @@ class Session {
|
||||
|
||||
// The unencrypted license response or license renewal response.
|
||||
MessageData& license() { return license_; }
|
||||
// The encrypted license response or license renewal response.
|
||||
MessageData& encrypted_license() { return padded_message_; }
|
||||
|
||||
// A pointer to the buffer holding encrypted_license.
|
||||
const uint8_t* message_ptr();
|
||||
void set_license(const MessageData& license) { license_ = license; }
|
||||
|
||||
// An array of key objects for use in LoadKeys.
|
||||
OEMCrypto_KeyObject* key_array() { return key_array_; }
|
||||
|
||||
// An array of key objects for LoadEntitledContentKeys.
|
||||
OEMCrypto_EntitledContentKeyObject* entitled_key_array() {
|
||||
return entitled_key_array_;
|
||||
const KeyDeriver& key_deriver() const { return key_deriver_; }
|
||||
void set_mac_keys(const uint8_t* mac_keys) {
|
||||
key_deriver_.set_mac_keys(mac_keys);
|
||||
}
|
||||
|
||||
// The last signature generated with the server's mac key.
|
||||
std::vector<uint8_t>& signature() { return signature_; }
|
||||
|
||||
// Set the number of keys to use in the license(), encrypted_license()
|
||||
// and key_array().
|
||||
void set_num_keys(int num_keys) { num_keys_ = num_keys; }
|
||||
// The current number of keys to use in the license(), encrypted_license()
|
||||
// and key_array().
|
||||
unsigned int num_keys() const { return num_keys_; }
|
||||
|
||||
// Set the size of the buffer used the encrypted license.
|
||||
// Must be between sizeof(MessageData) and kMaxMessageSize.
|
||||
void set_message_size(size_t size);
|
||||
// The size of the encrypted message.
|
||||
size_t message_size() { return message_size_; }
|
||||
|
||||
// The OEMCrypto_Substrings associated with the encrypted license that are
|
||||
// passed to LoadKeys.
|
||||
vector<OEMCrypto_Substring> load_keys_params() { return load_keys_params_; }
|
||||
OEMCrypto_Substring enc_mac_keys_iv_substr() { return load_keys_params_[0]; }
|
||||
OEMCrypto_Substring enc_mac_keys_substr() { return load_keys_params_[1]; }
|
||||
OEMCrypto_Substring pst_substr() { return load_keys_params_[2]; }
|
||||
OEMCrypto_Substring srm_restriction_data_substr() {
|
||||
return load_keys_params_[3];
|
||||
}
|
||||
|
||||
// Pointer to buffer holding |encrypted_entitled_message_|
|
||||
const uint8_t* encrypted_entitled_message_ptr();
|
||||
|
||||
private:
|
||||
// Generate mac and enc keys give the master key.
|
||||
void DeriveKeys(const uint8_t* master_key,
|
||||
const vector<uint8_t>& mac_key_context,
|
||||
const vector<uint8_t>& enc_key_context);
|
||||
// Internal utility function to derive key using CMAC-128
|
||||
void DeriveKey(const uint8_t* key, const vector<uint8_t>& context,
|
||||
int counter, vector<uint8_t>* out);
|
||||
// This compares the actual result with the expected result. If OEMCrypto is
|
||||
// an older version, we allow it to report an equivalent error code.
|
||||
void TestDecryptResult(OEMCryptoResult expected_result,
|
||||
OEMCryptoResult actual_select_result,
|
||||
OEMCryptoResult actual_decryt_result);
|
||||
|
||||
bool open_;
|
||||
bool forced_session_id_;
|
||||
OEMCrypto_SESSION session_id_;
|
||||
vector<uint8_t> mac_key_server_;
|
||||
vector<uint8_t> mac_key_client_;
|
||||
vector<uint8_t> enc_key_;
|
||||
KeyDeriver key_deriver_;
|
||||
uint32_t nonce_;
|
||||
RSA* public_rsa_;
|
||||
vector<uint8_t> pst_report_buffer_;
|
||||
MessageData license_;
|
||||
struct PaddedMessageData : public MessageData {
|
||||
uint8_t padding[kMaxMessageSize - sizeof(MessageData)];
|
||||
} padded_message_;
|
||||
size_t message_size_; // How much of the padded message to use.
|
||||
OEMCrypto_KeyObject key_array_[kMaxNumKeys];
|
||||
vector<OEMCrypto_Substring> load_keys_params_;
|
||||
std::vector<uint8_t> signature_;
|
||||
unsigned int num_keys_;
|
||||
|
||||
vector<uint8_t> encrypted_usage_entry_;
|
||||
uint32_t usage_entry_number_;
|
||||
string pst_;
|
||||
|
||||
// Clear Entitlement key data. This is the backing data for
|
||||
// |entitled_key_array_|.
|
||||
EntitledContentKeyData entitled_key_data_[kMaxNumKeys];
|
||||
// Message containing data from |key_array| and |entitled_key_data_|.
|
||||
std::string entitled_message_;
|
||||
// Entitled key object. Pointers are backed by |entitled_key_data_|.
|
||||
OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys];
|
||||
std::string encrypted_entitled_message_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -15,73 +15,21 @@ const uint8_t* find(const vector<uint8_t>& message,
|
||||
vector<uint8_t>::const_iterator pos = search(
|
||||
message.begin(), message.end(), substring.begin(), substring.end());
|
||||
if (pos == message.end()) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
return &(*pos);
|
||||
}
|
||||
|
||||
// This creates a wrapped RSA key for devices that have the test keybox
|
||||
// installed. If force is true, we assert that the key loads successfully.
|
||||
void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes,
|
||||
bool force) {
|
||||
// This creates a wrapped RSA key.
|
||||
void SessionUtil::CreateWrappedRSAKey() {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox(keybox_));
|
||||
// Provisioning request would be signed by the client and verified by the
|
||||
// server.
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature());
|
||||
struct RSAPrivateKeyMessage encrypted;
|
||||
std::vector<uint8_t> signature;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.MakeRSACertificate(&encrypted, sizeof(encrypted),
|
||||
&signature, allowed_schemes,
|
||||
encoded_rsa_key_));
|
||||
ASSERT_NO_FATAL_FAILURE(s.RewrapRSAKey(
|
||||
encrypted, sizeof(encrypted), signature, &wrapped_rsa_key_, force));
|
||||
// Verify that the clear key is not contained in the wrapped key.
|
||||
// It should be encrypted.
|
||||
ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_));
|
||||
}
|
||||
|
||||
// This creates a wrapped RSA key for devices using provisioning 3.0. If force
|
||||
// is true, we assert that the key loads successfully.
|
||||
void SessionUtil::CreateWrappedRSAKeyFromOEMCert(
|
||||
uint32_t allowed_schemes, bool force) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert());
|
||||
s.GenerateNonce();
|
||||
struct RSAPrivateKeyMessage encrypted;
|
||||
std::vector<uint8_t> signature;
|
||||
std::vector<uint8_t> message_key;
|
||||
std::vector<uint8_t> encrypted_message_key;
|
||||
s.GenerateRSASessionKey(&message_key, &encrypted_message_key);
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature,
|
||||
allowed_schemes, encoded_rsa_key_, &message_key));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s.RewrapRSAKey30(encrypted, encrypted_message_key,
|
||||
&wrapped_rsa_key_, force));
|
||||
// Verify that the clear key is not contained in the wrapped key.
|
||||
// It should be encrypted.
|
||||
ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_));
|
||||
}
|
||||
|
||||
// If force is true, we assert that the key loads successfully.
|
||||
void SessionUtil::CreateWrappedRSAKey(uint32_t allowed_schemes,
|
||||
bool force) {
|
||||
switch (global_features.provisioning_method) {
|
||||
case OEMCrypto_OEMCertificate:
|
||||
CreateWrappedRSAKeyFromOEMCert(allowed_schemes, force);
|
||||
break;
|
||||
case OEMCrypto_Keybox:
|
||||
CreateWrappedRSAKeyFromKeybox(allowed_schemes, force);
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Cannot generate wrapped RSA key if provision method = "
|
||||
<< wvoec::ProvisioningMethodName(
|
||||
global_features.provisioning_method);
|
||||
}
|
||||
ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_);
|
||||
provisioning_messages.PrepareSession(keybox_);
|
||||
ASSERT_NO_FATAL_FAILURE(provisioning_messages.SignAndVerifyRequest());
|
||||
ASSERT_NO_FATAL_FAILURE(provisioning_messages.CreateDefaultResponse());
|
||||
ASSERT_NO_FATAL_FAILURE(provisioning_messages.EncryptAndSignResponse());
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, provisioning_messages.LoadResponse());
|
||||
wrapped_rsa_key_ = provisioning_messages.wrapped_rsa_key();
|
||||
}
|
||||
|
||||
void SessionUtil::InstallKeybox(const wvoec::WidevineKeybox& keybox,
|
||||
@@ -89,10 +37,9 @@ void SessionUtil::InstallKeybox(const wvoec::WidevineKeybox& keybox,
|
||||
uint8_t wrapped[sizeof(wvoec::WidevineKeybox)];
|
||||
size_t length = sizeof(wvoec::WidevineKeybox);
|
||||
keybox_ = keybox;
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_WrapKeybox(reinterpret_cast<const uint8_t*>(&keybox),
|
||||
sizeof(keybox), wrapped, &length, NULL, 0));
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_WrapKeybox(reinterpret_cast<const uint8_t*>(&keybox),
|
||||
sizeof(keybox), wrapped, &length, nullptr, 0));
|
||||
OEMCryptoResult sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox));
|
||||
if (good) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
@@ -105,18 +52,14 @@ void SessionUtil::EnsureTestKeys() {
|
||||
switch (global_features.derive_key_method) {
|
||||
case DeviceFeatures::LOAD_TEST_KEYBOX:
|
||||
keybox_ = kTestKeybox;
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadTestKeybox(
|
||||
reinterpret_cast<const uint8_t*>(&keybox_),
|
||||
sizeof(keybox_)));
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
OEMCrypto_LoadTestKeybox(reinterpret_cast<const uint8_t*>(&keybox_),
|
||||
sizeof(keybox_)));
|
||||
break;
|
||||
case DeviceFeatures::LOAD_TEST_RSA_KEY:
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey());
|
||||
break;
|
||||
case DeviceFeatures::FORCE_TEST_KEYBOX:
|
||||
keybox_ = kTestKeybox;
|
||||
InstallKeybox(keybox_, true);
|
||||
break;
|
||||
case DeviceFeatures::TEST_PROVISION_30:
|
||||
// Can use oem certificate to install test rsa key.
|
||||
break;
|
||||
@@ -127,26 +70,18 @@ void SessionUtil::EnsureTestKeys() {
|
||||
|
||||
// This makes sure that the derived keys (encryption key and two mac keys)
|
||||
// are installed in OEMCrypto and in the test session.
|
||||
void SessionUtil::InstallTestSessionKeys(Session* s) {
|
||||
if (global_features.uses_certificate) {
|
||||
if (global_features.loads_certificate) {
|
||||
if (wrapped_rsa_key_.size() == 0) {
|
||||
// If we don't have a wrapped key yet, create one.
|
||||
// This wrapped key will be shared by all sessions in the test.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
CreateWrappedRSAKey(kSign_RSASSA_PSS, true));
|
||||
}
|
||||
// Load the wrapped rsa test key.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s->InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
void SessionUtil::InstallTestRSAKey(Session* s) {
|
||||
ASSERT_TRUE(global_features.uses_certificate);
|
||||
if (global_features.loads_certificate) {
|
||||
if (wrapped_rsa_key_.size() == 0) {
|
||||
// If we don't have a wrapped key yet, create one.
|
||||
// This wrapped key will be shared by all sessions in the test.
|
||||
ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey());
|
||||
}
|
||||
// Test RSA key should be loaded.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s->GenerateDerivedKeysFromSessionKey());
|
||||
} else { // Just uses keybox. Test keybox should already be installed.
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
s->GenerateDerivedKeysFromKeybox(keybox_));
|
||||
// Load the wrapped rsa test key.
|
||||
ASSERT_NO_FATAL_FAILURE(s->InstallRSASessionTestKey(wrapped_rsa_key_));
|
||||
}
|
||||
// Test RSA key should be loaded.
|
||||
ASSERT_NO_FATAL_FAILURE(s->GenerateDerivedKeysFromSessionKey());
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -12,33 +12,27 @@ namespace wvoec {
|
||||
|
||||
class SessionUtil {
|
||||
public:
|
||||
SessionUtil()
|
||||
: encoded_rsa_key_(kTestRSAPKCS8PrivateKeyInfo2_2048,
|
||||
kTestRSAPKCS8PrivateKeyInfo2_2048 +
|
||||
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)) {}
|
||||
SessionUtil()
|
||||
: encoded_rsa_key_(kTestRSAPKCS8PrivateKeyInfo2_2048,
|
||||
kTestRSAPKCS8PrivateKeyInfo2_2048 +
|
||||
sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)) {}
|
||||
|
||||
// If force is true, we assert that the key loads successfully.
|
||||
void CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, bool force);
|
||||
// Create a new wrapped DRM Certificate.
|
||||
void CreateWrappedRSAKey();
|
||||
|
||||
// If force is true, we assert that the key loads successfully.
|
||||
void CreateWrappedRSAKeyFromOEMCert(uint32_t allowed_schemes, bool force);
|
||||
// This is used to force installation of a keybox. This overwrites the
|
||||
// production keybox -- it does NOT use OEMCrypto_LoadTestKeybox.
|
||||
void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good);
|
||||
|
||||
// If force is true, we assert that the key loads successfully.
|
||||
void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force);
|
||||
// This loads the test keybox or the test RSA key, using LoadTestKeybox or
|
||||
// LoadTestRSAKey as needed.
|
||||
void EnsureTestKeys();
|
||||
|
||||
// This is used to force installation of a keybox. This overwrites the
|
||||
// production keybox -- it does NOT use OEMCrypto_LoadTestKeybox.
|
||||
void InstallKeybox(const wvoec::WidevineKeybox& keybox, bool good);
|
||||
void InstallTestRSAKey(Session* s);
|
||||
|
||||
// This loads the test keybox or the test RSA key, using LoadTestKeybox or
|
||||
// LoadTestRSAKey as needed.
|
||||
void EnsureTestKeys();
|
||||
|
||||
void InstallTestSessionKeys(Session* s);
|
||||
|
||||
std::vector<uint8_t> encoded_rsa_key_;
|
||||
std::vector<uint8_t> wrapped_rsa_key_;
|
||||
wvoec::WidevineKeybox keybox_;
|
||||
std::vector<uint8_t> encoded_rsa_key_;
|
||||
std::vector<uint8_t> wrapped_rsa_key_;
|
||||
wvoec::WidevineKeybox keybox_;
|
||||
};
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,38 +60,27 @@ TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) {
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidLMPTest, RewrapDeviceRSAKeyImplemented) {
|
||||
if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_RewrapDeviceRSAKey(0, NULL, 0, NULL, 0, NULL, NULL, 0,
|
||||
NULL, NULL, NULL));
|
||||
} else {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_RewrapDeviceRSAKey30(0, NULL, NULL, 0, NULL, 0, NULL,
|
||||
NULL, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
// This verifies that the device can load a DRM Certificate.
|
||||
TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) {
|
||||
ASSERT_NE(
|
||||
OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_GenerateRSASignature(0, NULL, 0, NULL, NULL, kSign_RSASSA_PSS));
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_LoadProvisioning(0, nullptr, 0, 0, nullptr, 0, nullptr,
|
||||
nullptr));
|
||||
}
|
||||
|
||||
// The Generic Crypto API functions are required for Android.
|
||||
TEST_F(OEMCryptoAndroidLMPTest, GenericCryptoImplemented) {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_Generic_Encrypt(0, NULL, 0, NULL,
|
||||
OEMCrypto_AES_CBC_128_NO_PADDING, NULL));
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_Generic_Decrypt(0, NULL, 0, NULL,
|
||||
OEMCrypto_AES_CBC_128_NO_PADDING, NULL));
|
||||
ASSERT_NE(
|
||||
OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_Generic_Sign(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, NULL));
|
||||
OEMCrypto_Generic_Encrypt(0, nullptr, 0, nullptr,
|
||||
OEMCrypto_AES_CBC_128_NO_PADDING, nullptr));
|
||||
ASSERT_NE(
|
||||
OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_Generic_Verify(0, NULL, 0, OEMCrypto_HMAC_SHA256, NULL, 0));
|
||||
OEMCrypto_Generic_Decrypt(0, nullptr, 0, nullptr,
|
||||
OEMCrypto_AES_CBC_128_NO_PADDING, nullptr));
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_Generic_Sign(0, nullptr, 0, OEMCrypto_HMAC_SHA256,
|
||||
nullptr, nullptr));
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_Generic_Verify(0, nullptr, 0, OEMCrypto_HMAC_SHA256,
|
||||
nullptr, 0));
|
||||
}
|
||||
|
||||
// Android requires support of usage table. The usage table is used for Secure
|
||||
@@ -134,15 +123,15 @@ TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) {
|
||||
// Android requires implementation of these functions.
|
||||
TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_GetNumberOfOpenSessions(NULL));
|
||||
OEMCrypto_GetNumberOfOpenSessions(nullptr));
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_GetMaxNumberOfSessions(NULL));
|
||||
OEMCrypto_GetMaxNumberOfSessions(nullptr));
|
||||
}
|
||||
|
||||
// Android requires implementation of these functions.
|
||||
TEST_F(OEMCryptoAndroidMNCTest, QueryKeyControlImplemented) {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_QueryKeyControl(0, NULL, 0, NULL, NULL));
|
||||
OEMCrypto_QueryKeyControl(0, nullptr, 0, nullptr, nullptr));
|
||||
}
|
||||
|
||||
// These tests are required for N Android devices.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "log.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "test_sleep.h"
|
||||
|
||||
static void acknowledge_cast() {
|
||||
std::cout
|
||||
@@ -16,9 +17,7 @@ static void acknowledge_cast() {
|
||||
// because we need to initialize the list of features supported by the device.
|
||||
// Also, the test filter is updated based on the feature list.
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
bool is_cast_receiver = false;
|
||||
bool force_load_test_keybox = false;
|
||||
bool filter_tests = true;
|
||||
int verbosity = 0;
|
||||
// Skip the first element, which is the program name.
|
||||
@@ -31,14 +30,21 @@ int main(int argc, char** argv) {
|
||||
is_cast_receiver = true;
|
||||
}
|
||||
if (arg == "--force_load_test_keybox") {
|
||||
force_load_test_keybox = true;
|
||||
std::cerr << "The argument --force_load_test_keybox is obsolete.\n";
|
||||
return 1;
|
||||
}
|
||||
if (arg == "--no_filter") {
|
||||
filter_tests = false;
|
||||
}
|
||||
if (arg == "--fake_sleep") {
|
||||
wvcdm::TestSleep::set_real_sleep(false);
|
||||
}
|
||||
}
|
||||
wvcdm::g_cutoff = static_cast<wvcdm::LogPriority>(verbosity);
|
||||
wvoec::global_features.Initialize(is_cast_receiver, force_load_test_keybox);
|
||||
wvoec::global_features.Initialize();
|
||||
wvoec::global_features.set_cast_receiver(is_cast_receiver);
|
||||
// Init GTest after device properties has been initialized.
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
// If the user requests --no_filter, we don't change the filter, otherwise, we
|
||||
// filter out features that are not supported.
|
||||
if (filter_tests) {
|
||||
|
||||
Reference in New Issue
Block a user