Source release 19.2.0

This commit is contained in:
Alex Dale
2024-06-25 14:03:53 -07:00
parent b8bdfccebe
commit cd8256726f
89 changed files with 2747 additions and 35949 deletions

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

@@ -238,20 +238,38 @@ TEST_F(CoreIntegrationTest, ProvisioningStableSpoidTest) {
}
/**
* A clear lead without a license loaded.
* A clear lead without a license loaded that contains only one sample.
* The Decrypt() call in crypto_session.cpp is different for decrypting
* one versus multiple subsamples.
*/
TEST_F(CoreIntegrationTest, ClearLead) {
TEST_F(CoreIntegrationTest, ClearLeadOneSample) {
LicenseHolder holder("CDM_Streaming", &cdm_engine_, config_);
const KeyId key_id = "";
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
EXPECT_EQ(NO_ERROR, holder.DecryptClearLead(key_id));
EXPECT_EQ(NO_ERROR, holder.DecryptClearLead(key_id, 1));
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
/**
* Playback clear lead with a license loaded. Playback should succeed.
* A clear lead without a license loaded containing 2 samples.
* The Decrypt() call in crypto_session.cpp is different for decrypting
* one versus multiple subsamples.
*/
TEST_F(CoreIntegrationTest, ClearLeadMultipleSamples) {
LicenseHolder holder("CDM_Streaming", &cdm_engine_, config_);
const KeyId key_id = "";
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
EXPECT_EQ(NO_ERROR, holder.DecryptClearLead(key_id, 2));
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}
/**
* Playback clear lead with a license loaded containing 2 samples. Playback
* should succeed.
*/
TEST_F(CoreIntegrationTest, ClearLeadAfterLicenseLoad) {
LicenseHolder holder("CDM_Streaming", &cdm_engine_, config_);
@@ -260,7 +278,7 @@ TEST_F(CoreIntegrationTest, ClearLeadAfterLicenseLoad) {
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
EXPECT_EQ(NO_ERROR, holder.DecryptClearLead(key_id));
EXPECT_EQ(NO_ERROR, holder.DecryptClearLead(key_id, 2));
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
}

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
@@ -1062,6 +1048,7 @@ class CdmUseCase_LicenseWithRenewal : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
const uint64_t next_renewal =
start_of_playback_ + initial_policy_.renewal_delay;
// Allow playback within the initial renewal window.
@@ -1267,6 +1254,7 @@ class CdmUseCase_LicenseWithRenewalPlayback : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
uint64_t next_renewal = start_of_playback_ + initial_policy_.renewal_delay;
// Allow playback within the initial renewal window.
SleepUntil(start_of_playback_);
@@ -1584,6 +1572,7 @@ class CdmUseCase_RenewOnLicenseLoad : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
// The Renew on License Load feature is only supported on v18+ servers.
if (config_.ServerOlderThan(18) ||
wvoec::global_features.api_version < 18) {
@@ -1731,6 +1720,7 @@ class CdmUseCase_Heartbeat : public RenewalTest {
void SetUp() override {
RenewalTest::SetUp();
if(Test::IsSkipped()) return;
const uint64_t next_renewal =
start_of_playback_ + initial_policy_.renewal_delay;
// Allow playback within the initial renewal window.

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

@@ -83,7 +83,7 @@ void LicenseHolder::FailReloadLicense() {
}
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 =
@@ -92,7 +92,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);
request_in_flight_.reset(new UrlRequest(url));
ASSERT_TRUE(request_in_flight_->is_connected())
<< "Failed for " << content_id();
@@ -117,7 +117,7 @@ void LicenseHolder::LoadRenewal() {
}
void LicenseHolder::GenerateAndPostReleaseRequest(
const std::string& policy_id) {
const std::string& release_policy_id) {
event_listener_.set_renewal_needed(false);
CdmKeyRequest request;
CdmAppParameterMap empty_app_parameters;
@@ -134,7 +134,7 @@ void LicenseHolder::GenerateAndPostReleaseRequest(
// TODO (b/295956275) vickymin: write DumpReleaseRequest function
// MessageDumper::DumpReleaseRequest(request);
}
const std::string url = MakeUrl(config_.renewal_server(), policy_id);
const std::string url = MakeUrl(config_.renewal_server(), release_policy_id);
request_in_flight_.reset(new UrlRequest(url));
ASSERT_TRUE(request_in_flight_->is_connected())
<< "Failed for " << content_id();
@@ -176,17 +176,21 @@ CdmResponseType LicenseHolder::Decrypt(const std::string& key_id) {
return cdm_engine_->DecryptV16(session_id_, params);
}
CdmResponseType LicenseHolder::DecryptClearLead(const std::string& key_id) {
CdmResponseType LicenseHolder::DecryptClearLead(const std::string& key_id,
size_t num_samples) {
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);
CdmDecryptionParametersV16 params(key_id);
params.is_secure = false;
CdmDecryptionSample sample(input.data(), output.data(), 0, input.size(), iv);
CdmDecryptionSubsample subsample(input.size(), 0);
sample.subsamples.push_back(subsample);
params.samples.push_back(sample);
for (size_t i = 0; i < num_samples; i++) {
CdmDecryptionSample sample(input.data(), output.data(), 0, input.size(),
iv);
sample.subsamples.push_back(subsample);
params.samples.push_back(sample);
}
return cdm_engine_->DecryptV16(session_id_, params);
}
@@ -262,22 +266,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

@@ -69,13 +69,13 @@ class LicenseHolder {
// Attempt to reload a license, but expect a failure.
void FailReloadLicense();
// 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().
void LoadRenewal();
// Generate the release request, and send it to the server.
void GenerateAndPostReleaseRequest(const std::string& policy_id);
void GenerateAndPostReleaseRequest(const std::string& release_policy_id);
// Fetch the release response. This can add a few seconds of latency.
void FetchRelease();
// Load the release response that was fetched in FetchRelease().
@@ -88,7 +88,8 @@ class LicenseHolder {
CdmResponseType Decrypt(const std::string& key_id);
// Try to copy the clear lead to a secure buffer. Returns the result of the
// copy buffer operation.
CdmResponseType DecryptClearLead(const std::string& key_id);
CdmResponseType DecryptClearLead(const std::string& key_id,
size_t num_samples);
// Try to decrypt some random data to a secure buffer. If the test harness
// does not allow creating a secure buffer, then this function fails
// immediately. Otherwise, a secure buffer is created and used for a
@@ -137,7 +138,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

@@ -136,6 +136,7 @@ const std::string kFakeKeyTooLong =
const std::string kFakeKeyTooShort = a2bs_hex("06e247e7f924208011");
const std::string kFakeIv = a2bs_hex("3d515a3ee0be1687080ac59da9e0d69a");
const std::string kFakeBuildInfo = "Mock Crypto Session - License Test";
const uint32_t kDefaultOemCryptoVersion = 18;
class MockCryptoSession : public TestCryptoSession {
public:
@@ -215,84 +216,85 @@ class CdmLicenseTest : public WvCdmTestBase {
protected:
CdmLicenseTest(const std::string& pssh = (kCencInitDataHdr + kCencPssh))
: pssh_(pssh) {}
void SetUp() override {
WvCdmTestBase::SetUp();
clock_ = new MockClock();
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
init_data_ = new InitializationData(CENC_INIT_DATA_FORMAT, pssh_);
policy_engine_ = new MockPolicyEngine(crypto_session_);
crypto_session_.reset(new MockCryptoSession(&crypto_metrics_));
ON_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull()))
.WillByDefault(
DoAll(SetArgPointee<0>(kDefaultSupportedCertTypes), Return(true)));
// PolicyEngine will call GetApiVersion() on creation.
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull()))
.WillRepeatedly(
DoAll(SetArgPointee<0>(kDefaultOemCryptoVersion), Return(true)));
policy_engine_.reset(new MockPolicyEngine(crypto_session_.get()));
init_data_ = InitializationData(CENC_INIT_DATA_FORMAT, pssh_);
clock_ = new MockClock();
cdm_license_.reset(new CdmLicenseTestPeer(kCdmSessionId, clock_));
}
void TearDown() override {
delete cdm_license_;
delete policy_engine_;
delete init_data_;
delete crypto_session_;
delete clock_;
}
virtual void CreateCdmLicense() {
cdm_license_ = new CdmLicenseTestPeer(kCdmSessionId, clock_);
// Nullify pointers for objects owned by CdmLicense.
clock_ = nullptr;
cdm_license_.reset();
// Release mock objects used by the CdmLicense.
// Order is important.
policy_engine_.reset();
crypto_session_.reset();
}
CdmLicenseTestPeer* cdm_license_ = nullptr;
MockClock* clock_ = nullptr;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_ = nullptr;
InitializationData* init_data_ = nullptr;
MockPolicyEngine* policy_engine_ = nullptr;
MockClock* clock_ = nullptr; // Owned by |cdm_license_|.
std::unique_ptr<CdmLicenseTestPeer> cdm_license_;
std::unique_ptr<MockPolicyEngine> policy_engine_;
std::unique_ptr<MockCryptoSession> crypto_session_;
InitializationData init_data_;
std::string pssh_;
};
TEST_F(CdmLicenseTest, InitSuccess) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(false, kEmptyServiceCertificate,
crypto_session_, policy_engine_));
crypto_session_.get(), policy_engine_.get()));
}
TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) {
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(false, kEmptyServiceCertificate, nullptr,
policy_engine_));
policy_engine_.get()));
}
TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(false, kEmptyServiceCertificate,
crypto_session_, nullptr));
crypto_session_.get(), nullptr));
}
TEST_F(CdmLicenseTest, InitWithEmptyServiceCert) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kEmptyServiceCertificate,
crypto_session_, policy_engine_));
crypto_session_.get(), policy_engine_.get()));
}
TEST_F(CdmLicenseTest, InitWithInvalidServiceCert) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(true, kInvalidServiceCertificate,
crypto_session_, policy_engine_));
crypto_session_.get(), policy_engine_.get()));
}
TEST_F(CdmLicenseTest, InitWithServiceCert) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_, policy_engine_));
crypto_session_.get(), policy_engine_.get()));
}
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
@@ -335,15 +337,14 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
.WillOnce(
DoAll(SetArgPointee<0>(kWatermarkingConfigurable), Return(true)));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_, policy_engine_));
crypto_session_.get(), policy_engine_.get()));
CdmAppParameterMap app_parameters;
CdmKeyMessage signed_request;
std::string server_url;
EXPECT_EQ(cdm_license_->PrepareKeyRequest(
*init_data_, kToken, kLicenseTypeStreaming, app_parameters,
init_data_, kToken, kLicenseTypeStreaming, app_parameters,
&signed_request, &server_url),
KEY_MESSAGE);
@@ -470,15 +471,14 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidationV15) {
.WillOnce(
DoAll(SetArgPointee<0>(kWatermarkingNotSupported), Return(true)));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_, policy_engine_));
crypto_session_.get(), policy_engine_.get()));
CdmAppParameterMap app_parameters;
CdmKeyMessage signed_request;
std::string server_url;
EXPECT_EQ(cdm_license_->PrepareKeyRequest(
*init_data_, kToken, kLicenseTypeStreaming, app_parameters,
init_data_, kToken, kLicenseTypeStreaming, app_parameters,
&signed_request, &server_url),
KEY_MESSAGE);
@@ -616,9 +616,8 @@ TEST_P(CdmLicenseEntitledKeyTest, LoadsEntitledKeys) {
}
// Set up the CdmLicense with the mocks and fake entitlement key
CreateCdmLicense();
ASSERT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
crypto_session_, policy_engine_));
crypto_session_.get(), policy_engine_.get()));
cdm_license_->set_entitlement_keys(entitlement_license);
// Call the function under test and check its return value

View File

@@ -267,7 +267,7 @@ class PolicyEngineTestV18 : public PolicyEngineTest {
}
};
TEST_F(PolicyEngineTest, NoLicense) {
TEST_F(PolicyEngineTestV16, NoLicense) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
}
@@ -2826,6 +2826,10 @@ TEST_F(PolicyEngineTestV16, PlaybackOk_RestoreWithoutPlaybackTimes) {
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId));
}
TEST_F(PolicyEngineTestV18, NoLicense) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
}
// These tests exercise license policy when OEMCrypto supports v18.
// The following scenarios are from the duration-and-renewal doc.
// Verifies correct reporting of events, OnSessionRenewalNeeded,

View File

@@ -181,6 +181,9 @@ TEST_F(CorePIGTest, OfflineHWSecureRequired) {
* release, and receive the release response.
*/
TEST_F(CorePIGTest, LicenseRelease1) {
if (!wvoec::global_features.usage_table) {
GTEST_SKIP() << "Test for usage table devices only.";
}
LicenseHolder holder("CDM_UnlimitedStreaming_can_persist", &cdm_engine_,
config_);
holder.set_can_persist(true);
@@ -204,6 +207,9 @@ TEST_F(CorePIGTest, LicenseRelease1) {
* release, and receive the release response.
*/
TEST_F(CorePIGTest, LicenseRelease2) {
if (!wvoec::global_features.usage_table) {
GTEST_SKIP() << "Test for usage table devices only.";
}
LicenseHolder holder("CDM_UnlimitedStreaming_can_persist", &cdm_engine_,
config_);
holder.set_can_persist(true);

View File

@@ -12,6 +12,7 @@
#include "crypto_session.h"
#include "device_files.h"
#include "file_store.h"
#include "license_protocol.pb.h"
#include "system_id_extractor.h"
#include "test_base.h"
#include "wv_cdm_types.h"
@@ -264,17 +265,21 @@ const uint8_t kKeyboxData[72] = {
const uint32_t kKeyboxSystemId = 1337;
const std::string kKeyboxDataStr(kKeyboxData,
kKeyboxData + sizeof(kKeyboxData));
constexpr uint32_t kDrmReprovisioningSystemId = 12345;
const char kBadCertificate[] = "bad_certificate_string_that_wont_parse";
class MockCryptoSession : public CryptoSession {
public:
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 +296,18 @@ class MockCryptoSession : public CryptoSession {
(RequestedSecurityLevel, std::string*), (override));
MOCK_METHOD(CdmResponseType, GetTokenFromOemCert,
(RequestedSecurityLevel, std::string*), (override));
MOCK_METHOD(CdmResponseType, GetTokenFromEmbeddedCertificate,
(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 +345,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 +392,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 +402,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 +411,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);
@@ -407,6 +484,94 @@ TEST_F(SystemIdExtractorTest, DrmCertDevice_NullSystemId) {
EXPECT_EQ(system_id, NULL_SYSTEM_ID);
}
TEST_F(SystemIdExtractorTest, DrmReprovisioning_Success) {
video_widevine::SignedDrmCertificate signed_certificate;
video_widevine::DrmCertificate drm_certificate;
drm_certificate.set_system_id(kDrmReprovisioningSystemId);
uint32_t system_id;
ASSERT_TRUE(drm_certificate.SerializeToString(
signed_certificate.mutable_drm_certificate()));
std::string serialized_certificate;
ASSERT_TRUE(signed_certificate.SerializeToString(&serialized_certificate));
EXPECT_CALL(*crypto_session_,
GetTokenFromEmbeddedCertificate(kLevelDefault, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(std::move(serialized_certificate)),
Return(CdmResponseType(NO_ERROR))));
ExpectProvisioningType(kClientTokenDrmCertificateReprovisioning);
ExpectSet(kDrmReprovisioningSystemId);
auto extractor = CreateExtractor(kLevelDefault);
ASSERT_TRUE(extractor);
EXPECT_TRUE(extractor->ExtractSystemId(&system_id));
EXPECT_EQ(system_id, kDrmReprovisioningSystemId);
}
TEST_F(SystemIdExtractorTest, DrmReprovisioning_FailsNoToken) {
EXPECT_CALL(*crypto_session_,
GetTokenFromEmbeddedCertificate(kLevelDefault, NotNull()))
.WillOnce(Return(CdmResponseType(PARAMETER_NULL)));
ExpectProvisioningType(kClientTokenDrmCertificateReprovisioning);
auto extractor = CreateExtractor(kLevelDefault);
ASSERT_TRUE(extractor);
uint32_t system_id;
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest, DrmReprovisioning_FailsToParseSignedCert) {
std::string bad_certificate(kBadCertificate);
EXPECT_CALL(*crypto_session_,
GetTokenFromEmbeddedCertificate(kLevelDefault, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(std::move(bad_certificate)),
Return(CdmResponseType(NO_ERROR))));
ExpectProvisioningType(kClientTokenDrmCertificateReprovisioning);
auto extractor = CreateExtractor(kLevelDefault);
ASSERT_TRUE(extractor);
uint32_t system_id;
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest, DrmReprovisioning_FailsToParseDrmCert) {
video_widevine::SignedDrmCertificate signed_certificate;
signed_certificate.set_drm_certificate(kBadCertificate);
std::string serialized_certificate;
ASSERT_TRUE(signed_certificate.SerializeToString(&serialized_certificate));
EXPECT_CALL(*crypto_session_,
GetTokenFromEmbeddedCertificate(kLevelDefault, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(std::move(serialized_certificate)),
Return(CdmResponseType(NO_ERROR))));
ExpectProvisioningType(kClientTokenDrmCertificateReprovisioning);
auto extractor = CreateExtractor(kLevelDefault);
ASSERT_TRUE(extractor);
uint32_t system_id;
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest, DrmReprovisioning_FailsNoSystemId) {
video_widevine::SignedDrmCertificate signed_certificate;
video_widevine::DrmCertificate drm_certificate;
drm_certificate.clear_system_id();
uint32_t system_id;
std::string serialized_certificate;
ASSERT_TRUE(drm_certificate.SerializeToString(
signed_certificate.mutable_drm_certificate()));
ASSERT_TRUE(signed_certificate.SerializeToString(&serialized_certificate));
EXPECT_CALL(*crypto_session_,
GetTokenFromEmbeddedCertificate(kLevelDefault, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(std::move(serialized_certificate)),
Return(CdmResponseType(NO_ERROR))));
ExpectProvisioningType(kClientTokenDrmCertificateReprovisioning);
auto extractor = CreateExtractor(kLevelDefault);
ASSERT_TRUE(extractor);
EXPECT_FALSE(extractor->ExtractSystemId(&system_id));
}
TEST_F(SystemIdExtractorTest, KeyboxDevice_Success) {
ExpectProvisioningType(kClientTokenKeybox);
EXPECT_CALL(*crypto_session_, GetTokenFromKeybox(kLevelDefault, NotNull()))
@@ -420,6 +585,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

@@ -189,6 +189,16 @@ enum OptionalBool {
bool UnwrapOptionalBool(OptionalBool value, bool default_value) {
return (value == kBoolUnset) ? default_value : (value == kBoolTrue);
}
// 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
// Static WvCdmTestBase variables.
@@ -208,6 +218,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) {
@@ -374,12 +404,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

@@ -76,6 +76,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"