Source release 18.6.0

This commit is contained in:
Alex Dale
2024-06-27 12:54:34 -07:00
parent 28ec8548c6
commit 20c0587dcb
56 changed files with 1191 additions and 35538 deletions

View File

@@ -115,7 +115,7 @@ class MockCdmEngineImpl : public CdmEngine {
class WvCdmEngineMetricsImplTest : public ::testing::Test {
public:
void SetUp() override {
file_system_.reset(CreateTestFileSystem());
file_system_ = CreateTestFileSystem();
std::shared_ptr<EngineMetrics> engine_metrics(new EngineMetrics);
test_cdm_metrics_engine_.reset(
new CdmEngineMetricsImpl<StrictMock<MockCdmEngineImpl>>(

View File

@@ -21,8 +21,6 @@
#include "license_holder.h"
#include "license_request.h"
#include "log.h"
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
#include "oec_device_features.h"
#include "properties.h"
#include "string_conversions.h"
#include "test_base.h"
@@ -147,12 +145,6 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
WvCdmEngineTest() {}
void SetUp() override {
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
if (wvoec::global_features.provisioning_method ==
OEMCrypto_DrmReprovisioning) {
GTEST_SKIP()
<< "Skipping until Drm Reprovisioning server support is implemented.";
}
WvCdmEnginePreProvTest::SetUp();
session_opened_ = false;
WvCdmEnginePreProvTest::OpenSession();
@@ -191,7 +183,6 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
EXPECT_EQ(KEY_MESSAGE, result);
key_msg_ = request.message;
server_url_ = request.url;
}
std::string GetKeyRequestResponse(const std::string& server_url,
@@ -295,8 +286,6 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
<< "Expected request type " << key_request_type << " was not found. "
<< "metrics: " << wvutil::b2a_hex(serialized_metrics);
}
std::string server_url_;
};
// Tests to validate service certificate
@@ -401,22 +390,15 @@ TEST_F(WvCdmEngineTest, LoadKey) {
// command line.
TEST_F(WvCdmEngineTest, LicenseRenewalSpecifiedServer) {
EnsureProvisioned();
GenerateKeyRequest(binary_key_id(), kCencMimeType);
VerifyNewKeyResponse(config_.license_server(), config_.client_auth());
GenerateRenewalRequest();
if (!server_url_.empty()) {
// If the license server put a URL for the renewal in the license, we should
// be able to verify the renewal against that server.
VerifyRenewalKeyResponse(server_url_, config_.client_auth());
} else {
// If the license server did not give us a URL, we won't verify it.
LOGE("License server did not set renewal URL. license_url=%s.",
config_.license_server().c_str());
// This is OK when you are using a local, debug, license server, but it
// should NOT happen if using UAT or UAT nightly.
EXPECT_EQ(std::string::npos, config_.license_server().find("proxy.uat"));
}
LicenseHolder holder("CDM_SpecifyRenewalUrl", &cdm_engine_, config_);
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
CdmKeyRequest request;
const CdmResponseType result =
cdm_engine_.GenerateRenewalRequest(holder.session_id(), &request);
ASSERT_EQ(KEY_MESSAGE, result);
EXPECT_EQ(request.url, "url_as_specified_in_integration_console");
}
// This test generates a renewal and then requests it from the server specified

View File

@@ -5,11 +5,13 @@
#ifndef CDM_TEST_CREATE_TEST_FILE_SYSTEM_H_
#define CDM_TEST_CREATE_TEST_FILE_SYSTEM_H_
#include <memory>
#include "file_store.h"
// Create a new FileSystem object that is suitable for using to create a new
// CdmEngine object. How this is implemented is platform-specific. The caller
// owns the returned pointer and is responsible for deleting it.
wvutil::FileSystem* CreateTestFileSystem();
std::unique_ptr<wvutil::FileSystem> CreateTestFileSystem();
#endif // CDM_TEST_CREATE_TEST_FILE_SYSTEM_H_

View File

@@ -128,12 +128,6 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine,
// appended to it.
void SetUp() override {
WvCdmTestBase::SetUp();
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
if (wvoec::global_features.provisioning_method ==
OEMCrypto_DrmReprovisioning) {
GTEST_SKIP()
<< "Skipping until Drm Reprovisioning server support is implemented.";
}
EnsureProvisioned();
license_holder_.set_can_persist(GetParam());
ASSERT_NO_FATAL_FAILURE(license_holder_.OpenSession());
@@ -158,14 +152,6 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine,
}
void TearDown() override {
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
if (wvoec::global_features.provisioning_method ==
OEMCrypto_DrmReprovisioning) {
// Since the session was not opened above. We can skip closing the session
// here too. This should be removed when EnsureProvisioning no longer
// skips this test.
return;
}
license_holder_.CloseSession();
// Log the time used in this test suite. When this comment was written,
// these tests took over three hours. If we want to improve that, we need to

View File

@@ -40,12 +40,6 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine {
if (!wvoec::global_features.generic_crypto) {
GTEST_SKIP() << "Test for devices with generic crypto API only";
}
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
if (wvoec::global_features.provisioning_method ==
OEMCrypto_DrmReprovisioning) {
GTEST_SKIP()
<< "Skipping until Drm Reprovisioning server support is implemented.";
}
EnsureProvisioned();
ASSERT_NO_FATAL_FAILURE(holder_.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder_.FetchLicense());
@@ -75,8 +69,6 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine {
}
void TearDown() override {
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
if (IsSkipped()) return;
holder_.CloseSession();
}

View File

@@ -76,7 +76,7 @@ void LicenseHolder::ReloadLicense() {
}
void LicenseHolder::GenerateAndPostRenewalRequest(
const std::string& policy_id) {
const std::string& renewal_policy_id) {
event_listener_.set_renewal_needed(false);
CdmKeyRequest request;
const CdmResponseType result =
@@ -85,7 +85,7 @@ void LicenseHolder::GenerateAndPostRenewalRequest(
if (config_.dump_golden_data()) {
MessageDumper::DumpRenewalRequest(request);
}
const std::string url = MakeUrl(config_.renewal_server(), policy_id);
const std::string url = MakeUrl(config_.renewal_server(), renewal_policy_id);
renewal_in_flight_.reset(new UrlRequest(url));
ASSERT_TRUE(renewal_in_flight_->is_connected())
<< "Failed for " << content_id();
@@ -134,10 +134,13 @@ CdmResponseType LicenseHolder::DecryptClearLead(const std::string& key_id) {
const std::vector<uint8_t> iv(KEY_IV_SIZE, 0);
CdmDecryptionParametersV16 params(key_id);
params.is_secure = false;
CdmDecryptionSample sample(input.data(), output.data(), 0, input.size(), iv);
CdmDecryptionSample sample1(input.data(), output.data(), 0, input.size(), iv);
CdmDecryptionSample sample2(input.data(), output.data(), 0, input.size(), iv);
CdmDecryptionSubsample subsample(input.size(), 0);
sample.subsamples.push_back(subsample);
params.samples.push_back(sample);
sample1.subsamples.push_back(subsample);
sample2.subsamples.push_back(subsample);
params.samples.push_back(sample1);
params.samples.push_back(sample2);
return cdm_engine_->DecryptV16(session_id_, params);
}
@@ -213,22 +216,16 @@ void LicenseHolder::GenerateKeyRequest(const InitializationData& init_data,
}
std::string LicenseHolder::MakeUrl(const std::string& server_url,
const std::string& policy_id) {
const std::string& renewal_policy_id) {
// For tests, we want to specify the policy, but the UAT server only allows us
// to set the content id as the video_id. So each policy is matched to a
// single license with the same name. The local license server, on the other
// hand, wants to see the policy id in the url. So we have to guess which
// format to use based on the name of the server.
const std::string path = server_url + config_.client_auth();
std::string video_query;
if (!policy_id.empty()) {
if (path.find("proxy.uat") != std::string::npos) {
// This is uat or uat-nightly. Set the video_id.
video_query = "video_id=" + policy_id;
} else {
// This is probably a local license server. Set the policy.
video_query = "policy=" + policy_id;
}
if (!renewal_policy_id.empty()) {
const std::string video_query =
"video_id=" + content_id() + "&renewal_policy=" + renewal_policy_id;
// If there is already a parameter, then we don't need to add another
// question mark.
return path + ((path.find('?') == std::string::npos) ? '?' : '&') +

View File

@@ -67,7 +67,7 @@ class LicenseHolder {
// key_set_id is set by calling LoadLicense(), or by calling set_key_set_id().
void ReloadLicense();
// Generate the renewal request, and send it to the server.
void GenerateAndPostRenewalRequest(const std::string& policy_id);
void GenerateAndPostRenewalRequest(const std::string& renewal_policy_id);
// Fetch the renewal response. This can add a few seconds of latency.
void FetchRenewal();
// Load the renewal response that was fetched in FetchRenewal().
@@ -129,7 +129,7 @@ class LicenseHolder {
// Generate a URL for the specified policy. The license request should be sent
// to this url.
std::string MakeUrl(const std::string& server_url,
const std::string& policy_id);
const std::string& renewal_policy_id);
// Fetch the key response from the server.
void GetKeyResponse(const CdmKeyRequest& key_request);
};

View File

@@ -22,6 +22,7 @@ using wvutil::TestSleep;
namespace wvcdm {
FileSystem* RebootTest::file_system_;
std::unique_ptr<FileSystem> RebootTest::default_file_system_;
namespace {
// How much fudge or round off error do we allow in license durations for reboot
@@ -31,7 +32,10 @@ constexpr int64_t kFudge = 10;
void RebootTest::SetUp() {
WvCdmTestBase::SetUp();
if (!file_system_) file_system_ = CreateTestFileSystem();
if (!file_system_) {
if (!default_file_system_) default_file_system_ = CreateTestFileSystem();
file_system_ = default_file_system_.get();
}
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();

View File

@@ -6,6 +6,7 @@
#define WVCDM_CORE_REBOOT_TEST_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -37,6 +38,7 @@ class RebootTest : public WvCdmTestBaseWithEngine {
// This is used to store each test's persistent data.
static wvutil::FileSystem* file_system_;
static std::unique_ptr<wvutil::FileSystem> default_file_system_;
// The persistent data for the current test.
std::map<std::string, std::string> persistent_data_;

View File

@@ -270,11 +270,13 @@ class MockCryptoSession : public CryptoSession {
MockCryptoSession(metrics::CryptoMetrics* metrics) : CryptoSession(metrics) {}
// ~MockCryptoSession() override {}
bool IsOpen() override { return true; }
CdmSecurityLevel GetSecurityLevel() override { return kSecurityLevelL1; }
bool IsOpen() override { return is_open_; }
CdmSecurityLevel GetSecurityLevel() override {
return is_open_ ? open_security_level_ : kSecurityLevelUninitialized;
}
CdmSecurityLevel GetSecurityLevel(
RequestedSecurityLevel security_level) override {
return security_level == kLevelDefault ? kSecurityLevelL1
return security_level == kLevelDefault ? default_security_level_
: kSecurityLevelL3;
}
@@ -291,6 +293,16 @@ class MockCryptoSession : public CryptoSession {
(RequestedSecurityLevel, std::string*), (override));
MOCK_METHOD(CdmResponseType, GetTokenFromOemCert,
(RequestedSecurityLevel, std::string*), (override));
// These default values should represent good values of a
// CryptoSession used for system ID extractions.
// Test cases should modify them if needing to test edge cases.
bool is_open_ = false;
// Security level which is returned if the session is opened.
CdmSecurityLevel open_security_level_ = kSecurityLevelL1;
// Security level of the underlying default OEMCrypto engine.
CdmSecurityLevel default_security_level_ = kSecurityLevelL1;
};
class MockDeviceFiles : public DeviceFiles {
@@ -328,13 +340,27 @@ class SystemIdExtractorTest : public WvCdmTestBase {
return extractor;
}
void ExpectProvisioningType(CdmClientTokenType type) {
EXPECT_CALL(*crypto_session_, GetCachedSystemId).WillOnce(Return(false));
void ExpectProvisioningType(CdmClientTokenType type, bool is_open = false) {
crypto_session_->is_open_ = is_open;
if (is_open) {
// Extractor should only call GetCachedSystemId if session
// is opened.
EXPECT_CALL(*crypto_session_, GetCachedSystemId).WillOnce(Return(false));
} else {
EXPECT_CALL(*crypto_session_, GetCachedSystemId).Times(0);
}
EXPECT_CALL(*crypto_session_, GetProvisioningMethod(_, NotNull()))
.WillOnce(
DoAll(SetArgPointee<1>(type), Return(CdmResponseType(NO_ERROR))));
}
void ExpectFromCached(uint32_t system_id) {
crypto_session_->is_open_ = true;
EXPECT_CALL(*crypto_session_, GetCachedSystemId(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(system_id), Return(true)));
}
void ExpectSet(uint32_t system_id) {
EXPECT_CALL(*crypto_session_, SetSystemId(system_id));
}
@@ -361,8 +387,7 @@ TEST_F(SystemIdExtractorTest, ExtractSystemIdFromKeyboxData) {
TEST_F(SystemIdExtractorTest, CachedSystemId) {
const uint32_t kCachedSystemId = 1234;
EXPECT_CALL(*crypto_session_, GetCachedSystemId(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kCachedSystemId), Return(true)));
ExpectFromCached(kCachedSystemId);
auto extractor = CreateExtractor(kLevelDefault);
ASSERT_TRUE(extractor);
uint32_t system_id;
@@ -372,6 +397,7 @@ TEST_F(SystemIdExtractorTest, CachedSystemId) {
TEST_F(SystemIdExtractorTest, SetSystemIdMetrics) {
const uint32_t kSystemId = 4321;
crypto_session_->is_open_ = true; // Must be open to set system ID.
crypto_session_->SetSystemIdBase(kSystemId);
drm_metrics::WvCdmMetrics::CryptoMetrics metrics_proto;
crypto_metrics_.Serialize(&metrics_proto);
@@ -380,8 +406,54 @@ TEST_F(SystemIdExtractorTest, SetSystemIdMetrics) {
EXPECT_EQ(recorded_system_id, kSystemId);
}
TEST_F(SystemIdExtractorTest,
BadSecurityLevelExpectations_InvalidRequestedSecurityLevel) {
// Extractor caller is using an invalid requested security level.
const RequestedSecurityLevel kBadRequestedSecurityLevel =
static_cast<RequestedSecurityLevel>(9999);
auto extractor = CreateExtractor(kBadRequestedSecurityLevel);
uint32_t system_id;
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest,
BadSecurityLevelExpectations_UnexpectedSessionSecurityLevel) {
// CryptoSession is returning an unexpected result for its security
// level.
crypto_session_->is_open_ = true;
crypto_session_->open_security_level_ = kSecurityLevelUnknown;
auto extractor = CreateExtractor(kLevelDefault);
uint32_t system_id;
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest,
BadSecurityLevelExpectations_UnexpectedExtractorSecurityLevel) {
// OEMCrypto (via session-less CryptoSession) is returning an
// unexpected result for the default security level.
crypto_session_->is_open_ = true;
crypto_session_->default_security_level_ = kSecurityLevelUnknown;
auto extractor = CreateExtractor(kLevelDefault);
uint32_t system_id;
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest,
BadSecurityLevelExpectations_MismatchedSessionSecurityLevel) {
// CryptoSession and Extractor are different security levels.
crypto_session_->is_open_ = true;
// Case 1: Session L3, extractor L1
crypto_session_->open_security_level_ = kSecurityLevelL3;
auto extractor = CreateExtractor(kLevelDefault);
uint32_t system_id;
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
// Case 2: Session L1, extractor L3
crypto_session_->open_security_level_ = kSecurityLevelL1;
extractor = CreateExtractor(kLevel3);
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest, GetProvisioningMethod_Failed) {
EXPECT_CALL(*crypto_session_, GetCachedSystemId).WillOnce(Return(false));
EXPECT_CALL(*crypto_session_, GetProvisioningMethod(_, NotNull()))
.WillOnce(Return(CdmResponseType(UNKNOWN_ERROR)));
auto extractor = CreateExtractor(kLevelDefault);
@@ -420,6 +492,19 @@ TEST_F(SystemIdExtractorTest, KeyboxDevice_Success) {
EXPECT_EQ(system_id, kKeyboxSystemId);
}
TEST_F(SystemIdExtractorTest, KeyboxDevice_Success_WithCallToGetCached) {
ExpectProvisioningType(kClientTokenKeybox, /* is_open = */ true);
EXPECT_CALL(*crypto_session_, GetTokenFromKeybox(kLevelDefault, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(kKeyboxDataStr),
Return(CdmResponseType(NO_ERROR))));
ExpectSet(kKeyboxSystemId);
auto extractor = CreateExtractor(kLevelDefault);
ASSERT_TRUE(extractor);
uint32_t system_id;
EXPECT_TRUE(extractor->ExtractSystemId(&system_id));
EXPECT_EQ(system_id, kKeyboxSystemId);
}
TEST_F(SystemIdExtractorTest, KeyboxDevice_NeedsOtaKeyboxProvisioning) {
ExpectProvisioningType(kClientTokenKeybox);
EXPECT_CALL(*crypto_session_, GetTokenFromKeybox(kLevelDefault, NotNull()))

View File

@@ -149,6 +149,17 @@ void show_menu(const char* prog_name, const std::string& extra_help_text) {
std::cout << extra_help_text << std::endl;
}
// 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 BoringSSL implementation, so we implement the CTR loop
// ourselves.
void ctr128_inc64(int64_t increaseBy, std::vector<uint8_t>& iv) {
uint64_t* counterBuffer = reinterpret_cast<uint64_t*>(&(iv[8]));
(*counterBuffer) =
wvutil::htonll64(wvutil::ntohll64(*counterBuffer) + increaseBy);
}
} // namespace
std::unique_ptr<ConfigTestEnv> WvCdmTestBase::default_config_;
@@ -162,6 +173,26 @@ void WvCdmTestBase::StripeBuffer(std::vector<uint8_t>* buffer, size_t size,
}
}
// Encrypt a block of data using CTR mode.
std::vector<uint8_t> WvCdmTestBase::Aes128CtrEncrypt(
const std::vector<uint8_t>& key, const std::vector<uint8_t>& starting_iv,
const std::vector<uint8_t>& in_buffer) {
AES_KEY aes_key;
AES_set_encrypt_key(key.data(), AES_BLOCK_SIZE * 8, &aes_key);
std::vector<uint8_t> out_buffer(in_buffer.size());
std::vector<uint8_t> iv = starting_iv;
size_t l = 0; // byte index into encrypted subsample.
while (l < in_buffer.size()) {
uint8_t aes_output[AES_BLOCK_SIZE];
AES_encrypt(iv.data(), aes_output, &aes_key);
for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) {
out_buffer[l] = aes_output[n] ^ in_buffer[l];
}
ctr128_inc64(1, iv);
}
return out_buffer;
}
std::string WvCdmTestBase::Aes128CbcEncrypt(std::vector<uint8_t> key,
const std::vector<uint8_t>& clear,
std::vector<uint8_t> iv) {
@@ -330,12 +361,6 @@ void WvCdmTestBase::Provision() {
}
void WvCdmTestBase::EnsureProvisioned() {
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
if (wvoec::global_features.provisioning_method ==
OEMCrypto_DrmReprovisioning) {
GTEST_SKIP()
<< "Skipping until Drm Reprovisioning server support is implemented.";
}
CdmSessionId session_id;
std::unique_ptr<wvutil::FileSystem> file_system(CreateTestFileSystem());
// OpenSession will check if a DRM certificate exists, while

View File

@@ -70,6 +70,10 @@ class WvCdmTestBase : public ::testing::Test {
const std::vector<uint8_t>& clear,
std::vector<uint8_t> iv);
// Helper method for doing cryptography.
static std::vector<uint8_t> Aes128CtrEncrypt(
const std::vector<uint8_t>& key, const std::vector<uint8_t>& starting_iv,
const std::vector<uint8_t>& in_buffer);
// Helper method for doing cryptography.
static std::string SignHMAC(const std::string& message,
const std::vector<uint8_t>& key);

View File

@@ -6,9 +6,10 @@
#include <errno.h>
#include <gtest/gtest.h>
#include <sstream>
#include <gtest/gtest.h>
#include "http_socket.h"
#include "log.h"
#include "string_conversions.h"