Snap for 7196743 from 0c87234d45 to tm-release
Change-Id: I8f8f50a0f7e72472d5e6a38c514d6b98dd794e08
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "crypto_session.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -1745,11 +1746,11 @@ CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
static_cast<int>(pst_report.pst_length()));
|
||||
LOGV("OEMCrypto_PST_Report.padding: %d\n",
|
||||
static_cast<int>(pst_report.padding()));
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_license_received: %ld\n",
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_license_received: %" PRId64 "\n",
|
||||
pst_report.seconds_since_license_received());
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_first_decrypt: %ld\n",
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_first_decrypt: %" PRId64 "\n",
|
||||
pst_report.seconds_since_first_decrypt());
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %ld\n",
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %" PRId64 "\n",
|
||||
pst_report.seconds_since_last_decrypt());
|
||||
LOGV("OEMCrypto_PST_Report: %s\n", b2a_hex(*usage_report).c_str());
|
||||
|
||||
|
||||
@@ -558,13 +558,16 @@ bool InitializationData::ConstructWidevineInitData(
|
||||
|
||||
// Now format as Widevine init data protobuf
|
||||
WidevinePsshData cenc_header;
|
||||
// TODO(rfrias): The algorithm is a deprecated field, but proto changes
|
||||
// have not yet been pushed to production. Set until then.
|
||||
// TODO(rfrias): The algorithm and provider are deprecated fields, but proto
|
||||
// changes have not yet been pushed to production. Set until then.
|
||||
CORE_UTIL_IGNORE_DEPRECATED
|
||||
cenc_header.set_algorithm(WidevinePsshData_Algorithm_AESCTR);
|
||||
cenc_header.set_provider(provider);
|
||||
CORE_UTIL_RESTORE_WARNINGS
|
||||
|
||||
for (size_t i = 0; i < key_ids.size(); ++i) {
|
||||
cenc_header.add_key_ids(key_ids[i]);
|
||||
}
|
||||
cenc_header.set_provider(provider);
|
||||
cenc_header.set_content_id(content_id);
|
||||
if (method == kHlsMethodAes128)
|
||||
cenc_header.set_protection_scheme(kFourCcCbc1);
|
||||
|
||||
@@ -225,7 +225,9 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
|
||||
|
||||
int status_code = url_request.GetStatusCode(response);
|
||||
if (expect_success)
|
||||
EXPECT_EQ(kHttpOk, status_code) << "Error response: " << response;
|
||||
EXPECT_EQ(kHttpOk, status_code)
|
||||
<< "Error response from " << server_url << ":\n"
|
||||
<< response;
|
||||
|
||||
if (status_code != kHttpOk) {
|
||||
return "";
|
||||
@@ -419,14 +421,14 @@ TEST_F(WvCdmEngineTest, LicenseRenewalSpecifiedServer) {
|
||||
}
|
||||
}
|
||||
|
||||
// This test generates a renewal and then requests the renewal from the same
|
||||
// server from which we requested the original license.
|
||||
TEST_F(WvCdmEngineTest, LicenseRenewalSameServer) {
|
||||
// This test generates a renewal and then requests it from the server specified
|
||||
// by the current test configuration.
|
||||
TEST_F(WvCdmEngineTest, LicenseRenewal) {
|
||||
GenerateKeyRequest(binary_key_id(), kCencMimeType);
|
||||
VerifyNewKeyResponse(config_.license_server(), config_.client_auth());
|
||||
|
||||
GenerateRenewalRequest();
|
||||
VerifyRenewalKeyResponse(config_.license_server(), config_.client_auth());
|
||||
VerifyRenewalKeyResponse(config_.renewal_server(), config_.client_auth());
|
||||
}
|
||||
|
||||
TEST_F(WvCdmEngineTest, ParseDecryptHashStringTest) {
|
||||
|
||||
@@ -309,25 +309,13 @@ ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id, bool streaming,
|
||||
}
|
||||
}
|
||||
|
||||
ConfigTestEnv& ConfigTestEnv::operator=(const ConfigTestEnv& other) {
|
||||
this->server_id_ = other.server_id_;
|
||||
this->client_auth_ = other.client_auth_;
|
||||
this->key_id_ = other.key_id_;
|
||||
this->key_system_ = other.key_system_;
|
||||
this->license_server_ = other.license_server_;
|
||||
this->provisioning_server_ = other.provisioning_server_;
|
||||
this->license_service_certificate_ = other.license_service_certificate_;
|
||||
this->provisioning_service_certificate_ =
|
||||
other.provisioning_service_certificate_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ConfigTestEnv::Init(ServerConfigurationId server_id) {
|
||||
this->server_id_ = server_id;
|
||||
client_auth_ = license_servers[server_id].client_tag;
|
||||
key_id_ = license_servers[server_id].key_id;
|
||||
key_system_ = kWidevineKeySystem;
|
||||
license_server_ = license_servers[server_id].license_server_url;
|
||||
renewal_server_.clear();
|
||||
provisioning_server_ = license_servers[server_id].provisioning_server_url;
|
||||
license_service_certificate_ =
|
||||
a2bs_hex(license_servers[server_id].license_service_certificate);
|
||||
|
||||
@@ -61,9 +61,12 @@ class ConfigTestEnv {
|
||||
ConfigTestEnv(ServerConfigurationId server_id, bool streaming);
|
||||
ConfigTestEnv(ServerConfigurationId server_id, bool streaming, bool renew,
|
||||
bool release);
|
||||
// Allow copy and assign. Performance is not an issue in test initialization.
|
||||
ConfigTestEnv(const ConfigTestEnv &other) { *this = other; };
|
||||
ConfigTestEnv& operator=(const ConfigTestEnv &other);
|
||||
// Allow copy, assign, and move. Performance is not an issue in test
|
||||
// initialization.
|
||||
ConfigTestEnv(const ConfigTestEnv&) = default;
|
||||
ConfigTestEnv(ConfigTestEnv&&) = default;
|
||||
ConfigTestEnv& operator=(const ConfigTestEnv&) = default;
|
||||
ConfigTestEnv& operator=(ConfigTestEnv&&) = default;
|
||||
|
||||
~ConfigTestEnv() {};
|
||||
|
||||
@@ -72,6 +75,12 @@ class ConfigTestEnv {
|
||||
const KeyId& key_id() const { return key_id_; }
|
||||
const CdmKeySystem& key_system() const { return key_system_; }
|
||||
const std::string& license_server() const { return license_server_; }
|
||||
const std::string& renewal_server() const {
|
||||
if (!renewal_server_.empty())
|
||||
return renewal_server_;
|
||||
else
|
||||
return license_server_;
|
||||
}
|
||||
const std::string& provisioning_server() const {
|
||||
return provisioning_server_;
|
||||
}
|
||||
@@ -93,6 +102,9 @@ class ConfigTestEnv {
|
||||
void set_license_server(const std::string& license_server) {
|
||||
license_server_.assign(license_server);
|
||||
}
|
||||
void set_renewal_server(const std::string& renewal_server) {
|
||||
renewal_server_.assign(renewal_server);
|
||||
}
|
||||
void set_license_service_certificate(
|
||||
const std::string& license_service_certificate) {
|
||||
license_service_certificate_.assign(license_service_certificate);
|
||||
@@ -113,6 +125,7 @@ class ConfigTestEnv {
|
||||
KeyId key_id_;
|
||||
CdmKeySystem key_system_;
|
||||
std::string license_server_;
|
||||
std::string renewal_server_;
|
||||
std::string provisioning_server_;
|
||||
std::string license_service_certificate_;
|
||||
std::string provisioning_service_certificate_;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -716,8 +716,13 @@ TEST_P(HlsConstructionTest, InitData) {
|
||||
if (param.success_) {
|
||||
WidevinePsshData cenc_header;
|
||||
EXPECT_TRUE(cenc_header.ParseFromString(value));
|
||||
|
||||
CORE_UTIL_IGNORE_DEPRECATED
|
||||
EXPECT_EQ(video_widevine::WidevinePsshData_Algorithm_AESCTR,
|
||||
cenc_header.algorithm());
|
||||
EXPECT_EQ(param.provider_, cenc_header.provider());
|
||||
CORE_UTIL_RESTORE_WARNINGS
|
||||
|
||||
for (size_t i = 0; i < param.key_ids_.size(); ++i) {
|
||||
bool key_id_found = false;
|
||||
if (param.key_ids_[i].size() != 32) continue;
|
||||
@@ -729,7 +734,6 @@ TEST_P(HlsConstructionTest, InitData) {
|
||||
}
|
||||
EXPECT_TRUE(key_id_found);
|
||||
}
|
||||
EXPECT_EQ(param.provider_, cenc_header.provider());
|
||||
std::vector<uint8_t> param_content_id_vec(Base64Decode(param.content_id_));
|
||||
EXPECT_EQ(
|
||||
std::string(param_content_id_vec.begin(), param_content_id_vec.end()),
|
||||
@@ -822,6 +826,8 @@ TEST_P(HlsParseTest, Parse) {
|
||||
|
||||
WidevinePsshData cenc_header;
|
||||
EXPECT_TRUE(cenc_header.ParseFromString(init_data.data()));
|
||||
|
||||
CORE_UTIL_IGNORE_DEPRECATED
|
||||
EXPECT_EQ(video_widevine::WidevinePsshData_Algorithm_AESCTR,
|
||||
cenc_header.algorithm());
|
||||
if (param.key_.compare(kJsonProvider) == 0) {
|
||||
@@ -831,6 +837,7 @@ TEST_P(HlsParseTest, Parse) {
|
||||
} else if (param.key_.compare(kJsonKeyIds) == 0) {
|
||||
EXPECT_EQ(param.value_, b2a_hex(cenc_header.key_ids(0)));
|
||||
}
|
||||
CORE_UTIL_RESTORE_WARNINGS
|
||||
|
||||
EXPECT_EQ(kHlsIvHexValue, b2a_hex(init_data.hls_iv()));
|
||||
} else {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
#include "metrics_collections.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "test_base.h"
|
||||
#include "test_printers.h"
|
||||
#include "test_sleep.h"
|
||||
@@ -115,22 +116,30 @@ class CorePIGTest : public WvCdmTestBaseWithEngine {
|
||||
const std::vector<uint8_t> input(buffer_size, 0);
|
||||
std::vector<uint8_t> output(buffer_size, 0);
|
||||
const std::vector<uint8_t> iv(KEY_IV_SIZE, 0);
|
||||
Decrypt(session_id, key_id, input, iv, &output, NO_ERROR);
|
||||
ASSERT_EQ(NO_ERROR, Decrypt(session_id, key_id, input, iv, &output));
|
||||
}
|
||||
|
||||
// Try to use the key to decrypt, but expect the key has expired.
|
||||
void FailDecrypt(const CdmSessionId& session_id, const KeyId& key_id) {
|
||||
void FailDecrypt(const CdmSessionId& session_id, const KeyId& key_id,
|
||||
CdmResponseType expected_status) {
|
||||
constexpr size_t buffer_size = 500;
|
||||
const std::vector<uint8_t> input(buffer_size, 0);
|
||||
std::vector<uint8_t> output(buffer_size, 0);
|
||||
const std::vector<uint8_t> iv(KEY_IV_SIZE, 0);
|
||||
Decrypt(session_id, key_id, input, iv, &output, NEED_KEY);
|
||||
CdmResponseType status = Decrypt(session_id, key_id, input, iv, &output);
|
||||
// If the server knows we cannot handle the key, it would not have given us
|
||||
// the key. In that case, the status should indicate no key.
|
||||
if (status != NEED_KEY) {
|
||||
// Otherwise, we should have gotten the expected error.
|
||||
ASSERT_EQ(expected_status, status);
|
||||
}
|
||||
}
|
||||
|
||||
void Decrypt(const CdmSessionId& session_id, const KeyId& key_id,
|
||||
const std::vector<uint8_t>& input,
|
||||
const std::vector<uint8_t>& iv, std::vector<uint8_t>* output,
|
||||
CdmResponseType expected_status) {
|
||||
// Decrypt or fail to decrypt, with the expected status.
|
||||
CdmResponseType Decrypt(const CdmSessionId& session_id, const KeyId& key_id,
|
||||
const std::vector<uint8_t>& input,
|
||||
const std::vector<uint8_t>& iv,
|
||||
std::vector<uint8_t>* output) {
|
||||
CdmDecryptionParametersV16 params(key_id);
|
||||
params.is_secure = false;
|
||||
CdmDecryptionSample sample(input.data(), output->data(), 0, input.size(),
|
||||
@@ -138,9 +147,54 @@ class CorePIGTest : public WvCdmTestBaseWithEngine {
|
||||
CdmDecryptionSubsample subsample(0, input.size());
|
||||
sample.subsamples.push_back(subsample);
|
||||
params.samples.push_back(sample);
|
||||
return cdm_engine_.DecryptV16(session_id, params);
|
||||
}
|
||||
|
||||
// Use the key to decrypt to a secure buffer.
|
||||
void DecryptSecure(const CdmSessionId& session_id, const KeyId& key_id) {
|
||||
ASSERT_TRUE(wvoec::global_features.test_secure_buffers);
|
||||
constexpr size_t buffer_size = 500;
|
||||
const std::vector<uint8_t> input(buffer_size, 0);
|
||||
const std::vector<uint8_t> iv(KEY_IV_SIZE, 0);
|
||||
|
||||
// To create a secure buffer, we need to know the OEMCrypto session id.
|
||||
CdmQueryMap query_map;
|
||||
cdm_engine_.QueryOemCryptoSessionId(session_id, &query_map);
|
||||
const std::string oec_session_id_string =
|
||||
query_map[QUERY_KEY_OEMCRYPTO_SESSION_ID];
|
||||
uint32_t oec_session_id = std::stoi(oec_session_id_string);
|
||||
|
||||
int secure_buffer_fid;
|
||||
OEMCrypto_DestBufferDesc output_descriptor;
|
||||
output_descriptor.type = OEMCrypto_BufferType_Secure;
|
||||
output_descriptor.buffer.secure.handle_length = buffer_size;
|
||||
ASSERT_EQ(
|
||||
OEMCrypto_AllocateSecureBuffer(oec_session_id, buffer_size,
|
||||
&output_descriptor, &secure_buffer_fid),
|
||||
OEMCrypto_SUCCESS);
|
||||
|
||||
ASSERT_NE(output_descriptor.buffer.secure.handle, nullptr);
|
||||
// It is OK if OEMCrypto changes the maximum size, but there must
|
||||
// still be enough room for our data.
|
||||
ASSERT_GE(output_descriptor.buffer.secure.handle_length, buffer_size);
|
||||
output_descriptor.buffer.secure.offset = 0;
|
||||
|
||||
// Now create a sample array for the CDM layer.
|
||||
CdmDecryptionParametersV16 params(key_id);
|
||||
params.is_secure = true;
|
||||
CdmDecryptionSample sample(input.data(),
|
||||
output_descriptor.buffer.secure.handle, 0,
|
||||
input.size(), iv);
|
||||
CdmDecryptionSubsample subsample(0, input.size());
|
||||
sample.subsamples.push_back(subsample);
|
||||
params.samples.push_back(sample);
|
||||
CdmResponseType status = cdm_engine_.DecryptV16(session_id, params);
|
||||
ASSERT_EQ(expected_status, status);
|
||||
|
||||
// Free the secure buffer before we check the return status.
|
||||
OEMCrypto_FreeSecureBuffer(oec_session_id, &output_descriptor,
|
||||
secure_buffer_fid);
|
||||
|
||||
ASSERT_EQ(status, NO_ERROR);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -192,4 +246,36 @@ TEST_F(CorePIGTest, OfflineWithPST) {
|
||||
ASSERT_NO_FATAL_FAILURE(CloseSession(session_id));
|
||||
}
|
||||
|
||||
// This test verifies that the system can download and install license with a
|
||||
// key that requires secure buffers. It also verifies that we cannot decrypt to
|
||||
// a non-secure buffer using this key, but that we can decrypt to a secure
|
||||
// buffer, if the test harness supports secure buffers.
|
||||
TEST_F(CorePIGTest, OfflineHWSecureRequired) {
|
||||
const std::string content_id = "GTS_HW_SECURE_ALL";
|
||||
const KeyId key_id = "0000000000000002";
|
||||
|
||||
const CdmLicenseType license_type = kLicenseTypeOffline;
|
||||
CdmSessionId session_id;
|
||||
ASSERT_NO_FATAL_FAILURE(OpenSession(&session_id));
|
||||
CdmKeyRequest key_request;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
GenerateKeyRequest(session_id, content_id, &key_request, license_type));
|
||||
std::string key_response;
|
||||
ASSERT_NO_FATAL_FAILURE(GetKeyResponse(key_request, &key_response));
|
||||
CdmKeySetId key_set_id;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
AddKey(session_id, key_response, license_type, &key_set_id));
|
||||
// First we try to decrypt to a non-secure buffer and verify that it fails.
|
||||
// TODO(b/164517875): This error code should be something actionable.
|
||||
ASSERT_NO_FATAL_FAILURE(FailDecrypt(session_id, key_id, DECRYPT_ERROR));
|
||||
// Next, if possible, we try to decrypt to a secure buffer, and verify
|
||||
// success.
|
||||
if (wvoec::global_features.test_secure_buffers) {
|
||||
ASSERT_NO_FATAL_FAILURE(DecryptSecure(session_id, key_id));
|
||||
} else {
|
||||
LOGI("Test harness cannot create secure buffers. test skipped.");
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(CloseSession(session_id));
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -93,6 +93,19 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) {
|
||||
<< " in the url" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
std::cout << " --renewal_server_url=<url>" << std::endl;
|
||||
std::cout << " configure the renewal server url, please include http[s] "
|
||||
"in the url"
|
||||
<< std::endl
|
||||
<< " If not set, this defaults to be the same url used by the "
|
||||
"license server."
|
||||
<< std::endl
|
||||
<< " Some tests, such as LicenseRenewalSpecifiedServer, will "
|
||||
"ignore this setting. "
|
||||
<< std::endl
|
||||
<< " See comments in the code for an explanation." << std::endl
|
||||
<< std::endl;
|
||||
|
||||
std::cout << " --provisioning_server_url=<url>" << std::endl;
|
||||
std::cout
|
||||
<< " configure the provisioning server url, please include http[s]"
|
||||
@@ -457,6 +470,8 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
default_config_.set_provisioning_service_certificate(certificate);
|
||||
} else if (arg_prefix == "--license_server_url") {
|
||||
default_config_.set_license_server(arg_value);
|
||||
} else if (arg_prefix == "--renewal_server_url") {
|
||||
default_config_.set_renewal_server(arg_value);
|
||||
} else if (arg_prefix == "--provisioning_server_url") {
|
||||
default_config_.set_provisioning_server(arg_value);
|
||||
} else {
|
||||
@@ -480,6 +495,8 @@ bool WvCdmTestBase::Initialize(int argc, const char* const argv[],
|
||||
<< default_config_.provisioning_server() << std::endl;
|
||||
std::cout << "Default License Server: " << default_config_.license_server()
|
||||
<< std::endl;
|
||||
std::cout << "Default Renewal Server: " << default_config_.renewal_server()
|
||||
<< std::endl;
|
||||
std::cout << "Default KeyID: " << default_config_.key_id() << std::endl
|
||||
<< std::endl;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "test_base.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (!wvcdm::WvCdmTestBase::Initialize(argc, argv)) return 0;
|
||||
if (!wvcdm::WvCdmTestBase::Initialize(argc, argv)) return 1;
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -78,9 +78,7 @@ struct LoggingUidSetter {
|
||||
// unit tests.
|
||||
CORE_UTIL_EXPORT void InitLogging();
|
||||
|
||||
// Only enable format specifier warnings on LP64 systems. There is
|
||||
// no easy portable method to handle format specifiers for int64_t.
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && defined(__LP64__)
|
||||
#ifdef __GNUC__
|
||||
[[gnu::format(printf, 5, 6)]] CORE_UTIL_EXPORT void Log(const char* file,
|
||||
const char* function,
|
||||
int line,
|
||||
|
||||
@@ -11,12 +11,23 @@
|
||||
# else
|
||||
# define CORE_UTIL_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# define CORE_UTIL_IGNORE_DEPRECATED
|
||||
# define CORE_UTIL_RESTORE_WARNINGS
|
||||
#else
|
||||
# ifdef CORE_UTIL_IMPLEMENTATION
|
||||
# define CORE_UTIL_EXPORT __attribute__((visibility("default")))
|
||||
# else
|
||||
# define CORE_UTIL_EXPORT
|
||||
# endif
|
||||
# ifdef __GNUC__
|
||||
# define CORE_UTIL_IGNORE_DEPRECATED \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||
# define CORE_UTIL_RESTORE_WARNINGS _Pragma("GCC diagnostic pop")
|
||||
# else
|
||||
# define CORE_UTIL_IGNORE_DEPRECATED
|
||||
# define CORE_UTIL_RESTORE_WARNINGS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif // WVCDM_UTIL_UTIL_COMMON_H_
|
||||
|
||||
@@ -254,6 +254,8 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
||||
std::string HexEncode(const uint8_t* in_buffer, unsigned int size) {
|
||||
static const char kHexChars[] = "0123456789ABCDEF";
|
||||
if (size == 0) return "";
|
||||
constexpr unsigned int kMaxSafeSize = 2048;
|
||||
if (size > kMaxSafeSize) size = kMaxSafeSize;
|
||||
// Each input byte creates two output hex characters.
|
||||
std::string out_buffer(size * 2, '\0');
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
# include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
@@ -73,10 +76,13 @@ bool TestSleep::RollbackSystemTime(int seconds) {
|
||||
file_time.dwHighDateTime = long_time >> 32;
|
||||
if (!FileTimeToSystemTime(&file_time, &time)) return false;
|
||||
if (!SetSystemTime(&time)) return false;
|
||||
#elif TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
LOGE("iOS time rollback: cannot set system time.");
|
||||
return false;
|
||||
#else
|
||||
auto time = std::chrono::system_clock::now();
|
||||
auto modified_time = time - std::chrono::seconds(seconds);
|
||||
;
|
||||
|
||||
timespec time_spec;
|
||||
time_spec.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
modified_time.time_since_epoch())
|
||||
@@ -140,6 +146,9 @@ bool TestSleep::CanChangeSystemTime() {
|
||||
}
|
||||
LOGE("Win32 time rollback: cannot set system time.");
|
||||
return false;
|
||||
#elif TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
LOGE("iOS time rollback: cannot set system time.");
|
||||
return false;
|
||||
#else
|
||||
// Otherwise, the test needs to be run as root.
|
||||
const uid_t uid = getuid();
|
||||
|
||||
406
libwvdrmengine/oemcrypto/ref/test/oemcrypto_rsa_key_unittest.cpp
Normal file
406
libwvdrmengine/oemcrypto/ref/test/oemcrypto_rsa_key_unittest.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine License
|
||||
// Agreement.
|
||||
//
|
||||
// Reference implementation of OEMCrypto APIs
|
||||
//
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "log.h"
|
||||
#include "oemcrypto_ref_test_utils.h"
|
||||
#include "oemcrypto_rsa_key.h"
|
||||
|
||||
namespace wvoec_ref {
|
||||
constexpr size_t kMessageSize = 4 * 1024; // 4 kB
|
||||
constexpr size_t kCastMessageSize = 83; // Special max size.
|
||||
|
||||
class OEMCryptoRsaKeyTest : public ::testing::TestWithParam<RsaFieldSize> {
|
||||
public:
|
||||
void SetUp() override {
|
||||
// RSA key generation is slow (~2 seconds) compared to the
|
||||
// operations they perform (<50 ms). Each key type is generated
|
||||
// once and globally stored in serialized form.
|
||||
// Caching the instance may result in test failures for
|
||||
// memory-leak detection.
|
||||
const RsaFieldSize field_size = GetParam();
|
||||
std::lock_guard<std::mutex> rsa_key_lock(rsa_key_mutex_);
|
||||
// Use of a switch case is intentional to cause compiler warnings
|
||||
// if a new field size is introduced without updating the test.
|
||||
switch (field_size) {
|
||||
case kRsa2048Bit: {
|
||||
if (!rsa_2048_key_data_.empty()) {
|
||||
key_ = RsaPrivateKey::Load(rsa_2048_key_data_);
|
||||
}
|
||||
if (!key_) {
|
||||
key_ = RsaPrivateKey::New(kRsa2048Bit);
|
||||
}
|
||||
if (rsa_2048_key_data_.empty() && key_) {
|
||||
rsa_2048_key_data_ = key_->Serialize();
|
||||
}
|
||||
} break;
|
||||
case kRsa3072Bit: {
|
||||
if (!rsa_3072_key_data_.empty()) {
|
||||
key_ = RsaPrivateKey::Load(rsa_3072_key_data_);
|
||||
}
|
||||
if (!key_) {
|
||||
key_ = RsaPrivateKey::New(kRsa3072Bit);
|
||||
}
|
||||
if (rsa_3072_key_data_.empty() && key_) {
|
||||
rsa_3072_key_data_ = key_->Serialize();
|
||||
}
|
||||
} break;
|
||||
case kRsaFieldUnknown: // Suppress compiler warnings
|
||||
LOGE("RSA test was incorrectly instantiation");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() override { key_.reset(); }
|
||||
|
||||
protected:
|
||||
std::unique_ptr<RsaPrivateKey> key_;
|
||||
static std::mutex rsa_key_mutex_;
|
||||
static std::vector<uint8_t> rsa_2048_key_data_;
|
||||
static std::vector<uint8_t> rsa_3072_key_data_;
|
||||
};
|
||||
|
||||
std::mutex OEMCryptoRsaKeyTest::rsa_key_mutex_;
|
||||
std::vector<uint8_t> OEMCryptoRsaKeyTest::rsa_2048_key_data_;
|
||||
std::vector<uint8_t> OEMCryptoRsaKeyTest::rsa_3072_key_data_;
|
||||
|
||||
// Basic verification of RSA private key generation.
|
||||
TEST_P(OEMCryptoRsaKeyTest, KeyProperties) {
|
||||
ASSERT_TRUE(key_);
|
||||
const RsaFieldSize expected_field_size = GetParam();
|
||||
|
||||
EXPECT_EQ(key_->field_size(), expected_field_size);
|
||||
EXPECT_NE(nullptr, key_->GetRsaKey());
|
||||
}
|
||||
|
||||
// Checks that the private key serialization APIs are compatible
|
||||
// and performing in a manner that is similar to other OEMCrypto methods
|
||||
// that retrieve data.
|
||||
TEST_P(OEMCryptoRsaKeyTest, SerializePrivateKey) {
|
||||
ASSERT_TRUE(key_);
|
||||
|
||||
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
|
||||
size_t buffer_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> buffer(buffer_size);
|
||||
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
key_->Serialize(buffer.data(), &buffer_size));
|
||||
EXPECT_GT(buffer_size, kInitialBufferSize);
|
||||
|
||||
buffer.resize(buffer_size);
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, key_->Serialize(buffer.data(), &buffer_size));
|
||||
buffer.resize(buffer_size);
|
||||
|
||||
const std::vector<uint8_t> direct_key_data = key_->Serialize();
|
||||
EXPECT_FALSE(direct_key_data.empty());
|
||||
ASSERT_EQ(buffer.size(), direct_key_data.size());
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << std::to_string(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that a private key that is serialized can be deserialized and
|
||||
// reload. Also checks that the serialization of a key produces the
|
||||
// same data to ensure consistency.
|
||||
TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPrivateKey) {
|
||||
ASSERT_TRUE(key_);
|
||||
|
||||
const std::vector<uint8_t> key_data = key_->Serialize();
|
||||
std::unique_ptr<RsaPrivateKey> loaded_key = RsaPrivateKey::Load(key_data);
|
||||
ASSERT_TRUE(loaded_key);
|
||||
|
||||
EXPECT_EQ(key_->field_size(), loaded_key->field_size());
|
||||
|
||||
const std::vector<uint8_t> loaded_key_data = loaded_key->Serialize();
|
||||
ASSERT_EQ(key_data.size(), loaded_key_data.size());
|
||||
for (size_t i = 0; i < key_data.size(); i++) {
|
||||
ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << std::to_string(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that a private key with explicitly indicated schemes include
|
||||
// the scheme fields in the reserialized key.
|
||||
TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPrivateKeyWithAllowedSchemes) {
|
||||
ASSERT_TRUE(key_);
|
||||
|
||||
const std::vector<uint8_t> raw_key_data = key_->Serialize();
|
||||
std::vector<uint8_t> key_data = {'S', 'I', 'G', 'N', 0x00, 0x00, 0x00, 0x03};
|
||||
key_data.insert(key_data.end(), raw_key_data.begin(), raw_key_data.end());
|
||||
|
||||
std::unique_ptr<RsaPrivateKey> explicit_key = RsaPrivateKey::Load(key_data);
|
||||
ASSERT_TRUE(explicit_key);
|
||||
EXPECT_EQ(key_->field_size(), explicit_key->field_size());
|
||||
EXPECT_EQ(explicit_key->allowed_schemes(), 0x03);
|
||||
|
||||
const std::vector<uint8_t> explicit_key_data = explicit_key->Serialize();
|
||||
ASSERT_EQ(key_data.size(), explicit_key_data.size());
|
||||
ASSERT_EQ(key_data, explicit_key_data);
|
||||
}
|
||||
|
||||
// Checks that a public key can be created from the private key.
|
||||
TEST_P(OEMCryptoRsaKeyTest, DerivePublicKey) {
|
||||
ASSERT_TRUE(key_);
|
||||
|
||||
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
EXPECT_TRUE(key_->IsMatchingPublicKey(*pub_key));
|
||||
}
|
||||
|
||||
// Checks that the public key serialization APIs are compatible
|
||||
// and performing in a manner that is similar to other OEMCrypto methods
|
||||
// that retrieve data.
|
||||
TEST_P(OEMCryptoRsaKeyTest, SerializePublicKey) {
|
||||
ASSERT_TRUE(key_);
|
||||
|
||||
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
|
||||
size_t buffer_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> buffer(buffer_size);
|
||||
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
pub_key->Serialize(buffer.data(), &buffer_size));
|
||||
EXPECT_GT(buffer_size, kInitialBufferSize);
|
||||
|
||||
buffer.resize(buffer_size);
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->Serialize(buffer.data(), &buffer_size));
|
||||
buffer.resize(buffer_size);
|
||||
|
||||
const std::vector<uint8_t> direct_key_data = pub_key->Serialize();
|
||||
EXPECT_FALSE(direct_key_data.empty());
|
||||
ASSERT_EQ(buffer.size(), direct_key_data.size());
|
||||
for (size_t i = 0; i < buffer.size(); i++) {
|
||||
ASSERT_EQ(buffer[i], direct_key_data[i]) << "i = " << std::to_string(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that a public key that is serialized can be deserialized and
|
||||
// reload. Also checks that the serialization of a key produces the
|
||||
// same data to ensure consistency.
|
||||
TEST_P(OEMCryptoRsaKeyTest, SerializeAndReloadPublicKey) {
|
||||
ASSERT_TRUE(key_);
|
||||
|
||||
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
const std::vector<uint8_t> key_data = pub_key->Serialize();
|
||||
|
||||
std::unique_ptr<RsaPublicKey> loaded_key = RsaPublicKey::Load(key_data);
|
||||
ASSERT_TRUE(loaded_key);
|
||||
|
||||
EXPECT_EQ(pub_key->field_size(), loaded_key->field_size());
|
||||
EXPECT_EQ(pub_key->allowed_schemes(), loaded_key->allowed_schemes());
|
||||
|
||||
const std::vector<uint8_t> loaded_key_data = loaded_key->Serialize();
|
||||
ASSERT_EQ(key_data.size(), loaded_key_data.size());
|
||||
for (size_t i = 0; i < key_data.size(); i++) {
|
||||
ASSERT_EQ(key_data[i], loaded_key_data[i]) << "i = " << std::to_string(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the RSA signature generating API operates similar to
|
||||
// existing signature generation functions.
|
||||
TEST_P(OEMCryptoRsaKeyTest, GenerateSignature) {
|
||||
ASSERT_TRUE(key_);
|
||||
|
||||
const std::vector<uint8_t> message = RandomData(kMessageSize);
|
||||
ASSERT_FALSE(message.empty()) << "CdmRandom failed";
|
||||
|
||||
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
|
||||
size_t signature_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
EXPECT_EQ(
|
||||
OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
key_->GenerateSignature(message.data(), message.size(), kRsaPssDefault,
|
||||
signature.data(), &signature_size));
|
||||
EXPECT_GT(signature_size, kInitialBufferSize);
|
||||
|
||||
signature.resize(signature_size);
|
||||
EXPECT_EQ(
|
||||
OEMCrypto_SUCCESS,
|
||||
key_->GenerateSignature(message.data(), message.size(), kRsaPssDefault,
|
||||
signature.data(), &signature_size));
|
||||
signature.resize(signature_size);
|
||||
|
||||
EXPECT_LE(signature_size, key_->SignatureSize());
|
||||
}
|
||||
|
||||
// Checks that RSA signatures can be verified by an RSA public key.
|
||||
TEST_P(OEMCryptoRsaKeyTest, VerifySignature) {
|
||||
ASSERT_TRUE(key_);
|
||||
const std::vector<uint8_t> message = RandomData(kMessageSize);
|
||||
ASSERT_FALSE(message.empty()) << "CdmRandom failed";
|
||||
|
||||
const std::vector<uint8_t> signature = key_->GenerateSignature(message);
|
||||
|
||||
std::unique_ptr<RsaPublicKey> pub_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS, pub_key->VerifySignature(message, signature));
|
||||
|
||||
// Check with different message.
|
||||
const std::vector<uint8_t> message_two = RandomData(kMessageSize);
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
|
||||
pub_key->VerifySignature(message_two, signature));
|
||||
|
||||
// Check with bad signature.
|
||||
const std::vector<uint8_t> bad_signature = RandomData(signature.size());
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE,
|
||||
pub_key->VerifySignature(message, bad_signature));
|
||||
}
|
||||
|
||||
// Checks that the special CAST receiver signature scheme works
|
||||
// to the degree that it is possible to test.
|
||||
TEST_P(OEMCryptoRsaKeyTest, GenerateAndVerifyRsaSignature) {
|
||||
ASSERT_TRUE(key_);
|
||||
// Key must be enabled for PKCS1 Block 1 padding scheme.
|
||||
// To do so, the key is serialized and the padding scheme is
|
||||
// added to the key data.
|
||||
const std::vector<uint8_t> key_data = key_->Serialize();
|
||||
ASSERT_FALSE(key_data.empty());
|
||||
std::vector<uint8_t> pkcs_enabled_key_data = {
|
||||
'S', 'I', 'G', 'N', 0x00, 0x00, 0x00, kSign_PKCS1_Block1};
|
||||
pkcs_enabled_key_data.insert(pkcs_enabled_key_data.end(), key_data.begin(),
|
||||
key_data.end());
|
||||
std::unique_ptr<RsaPrivateKey> pkcs_enabled_key =
|
||||
RsaPrivateKey::Load(pkcs_enabled_key_data);
|
||||
ASSERT_TRUE(pkcs_enabled_key);
|
||||
|
||||
// The actual cast message is a domain specific hash of the message,
|
||||
// however, random data works for testing purposes.
|
||||
const std::vector<uint8_t> message = RandomData(kCastMessageSize);
|
||||
|
||||
// Generate signature.
|
||||
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
|
||||
size_t signature_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> signature(signature_size);
|
||||
EXPECT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
pkcs_enabled_key->GenerateSignature(message.data(), message.size(),
|
||||
kRsaPkcs1Cast, signature.data(),
|
||||
&signature_size));
|
||||
EXPECT_GT(signature_size, kInitialBufferSize);
|
||||
signature.resize(signature_size);
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
pkcs_enabled_key->GenerateSignature(message.data(), message.size(),
|
||||
kRsaPkcs1Cast, signature.data(),
|
||||
&signature_size));
|
||||
signature.resize(signature_size);
|
||||
|
||||
EXPECT_LE(signature_size, pkcs_enabled_key->SignatureSize());
|
||||
|
||||
// Verify signature.
|
||||
std::unique_ptr<RsaPublicKey> pub_key = pkcs_enabled_key->MakePublicKey();
|
||||
ASSERT_TRUE(pub_key);
|
||||
|
||||
EXPECT_EQ(OEMCrypto_SUCCESS,
|
||||
pub_key->VerifySignature(message, signature, kRsaPkcs1Cast));
|
||||
}
|
||||
|
||||
// Verifies the session key exchange protocol used by the licensing
|
||||
// server.
|
||||
TEST_P(OEMCryptoRsaKeyTest, ShareSessionKey) {
|
||||
constexpr size_t kSessionKeySize = 16;
|
||||
ASSERT_TRUE(key_);
|
||||
std::unique_ptr<RsaPublicKey> public_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(public_key);
|
||||
|
||||
// Generate session key.
|
||||
const std::vector<uint8_t> session_key = RandomData(kSessionKeySize);
|
||||
ASSERT_FALSE(session_key.empty());
|
||||
|
||||
// Server's perspective.
|
||||
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
|
||||
size_t enc_session_key_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> enc_session_key(enc_session_key_size);
|
||||
OEMCryptoResult result = public_key->EncryptSessionKey(
|
||||
session_key.data(), session_key.size(), enc_session_key.data(),
|
||||
&enc_session_key_size);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
|
||||
enc_session_key.resize(enc_session_key_size);
|
||||
result = public_key->EncryptSessionKey(session_key.data(), session_key.size(),
|
||||
enc_session_key.data(),
|
||||
&enc_session_key_size);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, result);
|
||||
enc_session_key.resize(enc_session_key_size);
|
||||
|
||||
// Client's perspective.
|
||||
size_t received_session_key_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> received_session_key(received_session_key_size);
|
||||
result = key_->DecryptSessionKey(
|
||||
enc_session_key.data(), enc_session_key.size(),
|
||||
received_session_key.data(), &received_session_key_size);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
|
||||
received_session_key.resize(received_session_key_size);
|
||||
result = key_->DecryptSessionKey(
|
||||
enc_session_key.data(), enc_session_key.size(),
|
||||
received_session_key.data(), &received_session_key_size);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, result);
|
||||
received_session_key.resize(received_session_key_size);
|
||||
|
||||
// Compare keys.
|
||||
ASSERT_EQ(session_key.size(), received_session_key.size());
|
||||
ASSERT_EQ(session_key, received_session_key);
|
||||
}
|
||||
|
||||
// Verifies the encryption key exchange protocol used by the licensing
|
||||
// server.
|
||||
TEST_P(OEMCryptoRsaKeyTest, ShareEncryptionKey) {
|
||||
constexpr size_t kEncryptionKeySize = 16;
|
||||
ASSERT_TRUE(key_);
|
||||
std::unique_ptr<RsaPublicKey> public_key = key_->MakePublicKey();
|
||||
ASSERT_TRUE(public_key);
|
||||
|
||||
// Generate session key.
|
||||
const std::vector<uint8_t> encryption_key = RandomData(kEncryptionKeySize);
|
||||
ASSERT_FALSE(encryption_key.empty());
|
||||
|
||||
// Server's perspective.
|
||||
constexpr size_t kInitialBufferSize = 10; // Definitely too small.
|
||||
size_t enc_encryption_key_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> enc_encryption_key(enc_encryption_key_size);
|
||||
OEMCryptoResult result = public_key->EncryptEncryptionKey(
|
||||
encryption_key.data(), encryption_key.size(), enc_encryption_key.data(),
|
||||
&enc_encryption_key_size);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
|
||||
enc_encryption_key.resize(enc_encryption_key_size);
|
||||
result = public_key->EncryptEncryptionKey(
|
||||
encryption_key.data(), encryption_key.size(), enc_encryption_key.data(),
|
||||
&enc_encryption_key_size);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, result);
|
||||
enc_encryption_key.resize(enc_encryption_key_size);
|
||||
|
||||
// Client's perspective.
|
||||
size_t received_encryption_key_size = kInitialBufferSize;
|
||||
std::vector<uint8_t> received_encryption_key(received_encryption_key_size);
|
||||
result = key_->DecryptEncryptionKey(
|
||||
enc_encryption_key.data(), enc_encryption_key.size(),
|
||||
received_encryption_key.data(), &received_encryption_key_size);
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, result);
|
||||
received_encryption_key.resize(received_encryption_key_size);
|
||||
result = key_->DecryptEncryptionKey(
|
||||
enc_encryption_key.data(), enc_encryption_key.size(),
|
||||
received_encryption_key.data(), &received_encryption_key_size);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, result);
|
||||
received_encryption_key.resize(received_encryption_key_size);
|
||||
|
||||
// Compare keys.
|
||||
ASSERT_EQ(encryption_key.size(), received_encryption_key.size());
|
||||
ASSERT_EQ(encryption_key, received_encryption_key);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(AllFieldSizes, OEMCryptoRsaKeyTest,
|
||||
::testing::Values(kRsa2048Bit, kRsa3072Bit));
|
||||
|
||||
} // namespace wvoec_ref
|
||||
@@ -106,8 +106,8 @@ adb_shell_run request_license_test $PROVISIONING_ARG
|
||||
# Additional tests
|
||||
adb_shell_run base64_test
|
||||
adb_shell_run buffer_reader_test
|
||||
adb_shell_run cdm_engine_test
|
||||
adb_shell_run cdm_engine_metrics_decorator_unittest
|
||||
adb_shell_run cdm_engine_test
|
||||
adb_shell_run cdm_session_unittest
|
||||
adb_shell_run certificate_provisioning_unittest
|
||||
adb_shell_run counter_metric_unittest
|
||||
|
||||
Reference in New Issue
Block a user