Android CDM: Restored secure stop tests.

[ Semi-revert of http://ag/20183443 ]
[ Merge of http://go/wvgerrit/168898 ]

These tests were removed from Android last quarter; however, they
now need to be restored.  These tests will be removed in Android V.

To help with ambiguity around where the CDM is operating on a single
or set of usage info messages, the variables have been renamed to
propery indicate plurality.

Bug: 263319220
Test: cdm_extended_duration_test
Test: request_license_test
Test: libwvdrmdrmplugin_hal_test
Change-Id: I38b16dd5811069fafaeab5ffc19d0f8a8095f0cf
This commit is contained in:
Alex Dale
2023-03-23 22:03:21 -07:00
parent 5ed89d16e2
commit e928670c85
11 changed files with 1842 additions and 343 deletions

View File

@@ -50,7 +50,7 @@ const int kHttpOk = 200;
const uint32_t kMinute = 60;
const uint32_t kClockTolerance = 10;
const uint32_t kMaxUsageTableSize = 50;
const uint32_t kMaxUsageTableSize = 300;
const std::string kEmptyServiceCertificate;
constexpr int64_t kUnlimitedDurationValue = LLONG_MAX;
@@ -71,7 +71,7 @@ struct SubSampleInfo {
};
// clang-format off
SubSampleInfo kEncryptedStreamingNoPstSubSample = {
const SubSampleInfo kEncryptedStreamingNoPstSubSample = {
// key SD, encrypted, 256b
true, 1, true, true, false,
wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"),
@@ -95,7 +95,7 @@ SubSampleInfo kEncryptedStreamingNoPstSubSample = {
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"),
wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, 3};
SubSampleInfo kEncryptedStreamingClip8SubSample = {
const SubSampleInfo kEncryptedStreamingClip8SubSample = {
true, 1, true, true, false,
wvutil::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"),
wvutil::a2b_hex(
@@ -118,7 +118,7 @@ SubSampleInfo kEncryptedStreamingClip8SubSample = {
"37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"),
wvutil::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3};
SubSampleInfo kEncryptedStreamingClip5SubSample = {
const SubSampleInfo kEncryptedStreamingClip5SubSample = {
true, 1, true, true, false,
wvutil::a2bs_hex("3AE243D83B93B3311A1D777FF5FBE01A"),
wvutil::a2b_hex(
@@ -141,7 +141,7 @@ SubSampleInfo kEncryptedStreamingClip5SubSample = {
"ed0716cd8bceb80cf59166a217006bd147c51b04dfb183088ce3f51e9b9f759e"),
wvutil::a2b_hex("b358ab21ac90455bbf60490daad457e3"), 0, 3};
SubSampleInfo kEncryptedOfflineClip2SubSample = {
const SubSampleInfo kEncryptedOfflineClip2SubSample = {
true, 1, true, true, false,
wvutil::a2bs_hex("3260F39E12CCF653529990168A3583FF"),
wvutil::a2b_hex(
@@ -164,13 +164,13 @@ SubSampleInfo kEncryptedOfflineClip2SubSample = {
"944B5080B998BB0FE6E566AAFAE2328B37FD189F1920A964434ECF18E11AC81E"),
wvutil::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3};
std::string kStreamingClip8PstInitData = wvutil::a2bs_hex(
const std::string kStreamingClip8PstInitData = wvutil::a2bs_hex(
"000000427073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
"08011a0d7769646576696e655f74657374220f73" // pssh data
"747265616d696e675f636c697038");
std::string kOfflineClip2PstInitData = wvutil::a2bs_hex(
const std::string kOfflineClip2PstInitData = wvutil::a2bs_hex(
"000000407073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id
"08011a0d7769646576696e655f74657374220d6f" // pssh data
@@ -399,30 +399,30 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
// Post a request and extract the signed provisioning message from
// the HTTP response.
std::string GetUsageInfoResponse(const std::string& server_url,
const std::string& client_auth,
const std::string& usage_info_request) {
std::string GetSecureStopResponse(const std::string& server_url,
const std::string& client_auth,
const std::string& secure_stop_request) {
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth);
EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to "
<< server_url << client_auth;
url_request.PostRequest(usage_info_request);
EXPECT_TRUE(url_request.is_connected())
<< "Fail to connect to " << server_url << client_auth;
url_request.PostRequest(secure_stop_request);
std::string message;
EXPECT_TRUE(url_request.GetResponse(&message));
int http_status_code = url_request.GetStatusCode(message);
const int http_status_code = url_request.GetStatusCode(message);
if (kHttpOk != http_status_code) {
LogResponseError(message, http_status_code);
}
EXPECT_EQ(kHttpOk, http_status_code);
std::string usage_info;
std::string secure_stop_response;
if (kHttpOk == http_status_code) {
LicenseRequest license;
license.GetDrmMessage(message, usage_info);
LOGV("HTTP response body: (%zu bytes)", usage_info.size());
license.GetDrmMessage(message, secure_stop_response);
LOGV("HTTP response body: (%zu bytes)", secure_stop_response.size());
}
return usage_info;
return secure_stop_response;
}
void VerifyKeyRequestResponse(const std::string& server_url,
@@ -554,7 +554,6 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
license_renewal.type());
EXPECT_LT(0, license_renewal.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version());
EXPECT_TRUE(license_renewal.has_key_control_nonce());
}
void ValidateReleaseRequest(std::string& usage_msg, bool license_used,
@@ -635,7 +634,6 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
license_renewal.type());
EXPECT_LT(0, license_renewal.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version());
EXPECT_TRUE(license_renewal.has_key_control_nonce());
}
void ValidateHasUpdateUsageEntry(
@@ -909,7 +907,6 @@ TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) {
license_renewal.type());
EXPECT_LT(0, license_renewal.request_time());
EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version());
EXPECT_TRUE(license_renewal.has_key_control_nonce());
decryptor_->CloseSession(session_id_);
}
@@ -955,7 +952,7 @@ TEST_F(WvCdmExtendedDurationTest, DecryptionCloseSessionConcurrencyTest) {
(delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) {
result = nanosleep(&delay_remaining, &delay_remaining);
}
SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
@@ -986,6 +983,67 @@ TEST_F(WvCdmExtendedDurationTest, DecryptionCloseSessionConcurrencyTest) {
decryptor_->CloseSession(session_id);
}
TEST_F(WvCdmExtendedDurationTest, DISABLED_UsageOverflowTest) {
Provision();
TestWvCdmClientPropertySet client_property_set;
TestWvCdmClientPropertySet* property_set = nullptr;
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
FileSystem file_system;
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::vector<std::string> provider_session_tokens;
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(""), &provider_session_tokens));
const std::string key_id = wvutil::a2bs_hex(
"000000427073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
"08011a0d7769646576696e655f74657374220f73" // pssh data
"747265616d696e675f636c697035");
for (size_t i = 0; i < kMaxUsageTableSize + 100; ++i) {
decryptor_->OpenSession(config_.key_system(), property_set,
kDefaultCdmIdentifier, nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
decryptor_->CloseSession(session_id_);
}
CdmUsageReportList usage_reports;
CdmResponseType status =
decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status);
int error_count = 0;
while (!usage_reports.empty()) {
// Release each of the listed secure stops.
for (size_t i = 0; i < usage_reports.size(); ++i) {
const CdmUsageReport& usage_report = usage_reports[i];
const CdmKeyMessage release_msg = GetSecureStopResponse(
config_.license_server(), config_.client_auth(), usage_report);
EXPECT_EQ(NO_ERROR,
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier))
<< i << "/" << usage_reports.size() << " (err " << (error_count++) << ")"
<< release_msg;
}
ASSERT_LE(error_count, 100); // Give up after 100 failures.
// Get an updated list. The CDM might not list all entries at once.
status = decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
switch (status.code()) {
case KEY_MESSAGE:
EXPECT_FALSE(usage_reports.empty());
break;
case NO_ERROR:
EXPECT_TRUE(usage_reports.empty());
break;
default:
FAIL() << "GetUsageInfo failed with error " << static_cast<int>(status);
break;
}
}
}
// This test verifies that sessions allocated internally during key release
// message generation are deallocated after their time to live period expires
// by timer events (if other sessions are open).
@@ -1218,7 +1276,7 @@ TEST_P(WvCdmStreamingNoPstTest, UsageTest) {
for (size_t i = 0; i < GetParam(); ++i) {
// Decrypt data
SubSampleInfo* data = &kEncryptedStreamingNoPstSubSample;
const SubSampleInfo* data = &kEncryptedStreamingNoPstSubSample;
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
@@ -1282,6 +1340,218 @@ TEST_P(WvCdmStreamingNoPstTest, UsageTest) {
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingNoPstTest,
::testing::Values(0, 1, 2));
class WvCdmStreamingPstTest : public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {};
// TODO(b275651559): Re-enable test once the issue with "license remaining
// duration" is addressed.
TEST_P(WvCdmStreamingPstTest, DISABLED_UsageTest) {
Unprovision();
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kStreamingClip8PstInitData, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
ValidateResponse(video_widevine::STREAMING, true);
int64_t initial_license_duration_remaining = 0;
int64_t initial_playback_duration_remaining = 0;
QueryKeyStatus(true, false, &initial_license_duration_remaining,
&initial_playback_duration_remaining);
sleep(kMinute);
int64_t expected_seconds_since_license_received = kMinute;
int64_t expected_seconds_since_initial_playback = 0;
int64_t expected_seconds_since_last_playback = 0;
const size_t minutes = GetParam();
for (size_t m = 0; m < minutes; ++m) {
// Decrypt data
const SubSampleInfo& data = kEncryptedStreamingClip8SubSample;
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&data.key_id, &data.encrypt_data.front(),
data.encrypt_data.size(), &data.iv,
data.block_offset, decrypt_buffer.data());
decryption_parameters.is_encrypted = data.is_encrypted;
decryption_parameters.is_secure = data.is_secure;
decryption_parameters.subsample_flags = data.subsample_flags;
EXPECT_EQ(NO_ERROR,
decryptor_->Decrypt(session_id_, data.validate_key_id,
decryption_parameters));
EXPECT_EQ(data.decrypt_data, decrypt_buffer);
sleep(kMinute);
expected_seconds_since_license_received += kMinute;
expected_seconds_since_initial_playback += kMinute;
expected_seconds_since_last_playback = kMinute;
}
// Query and validate usage information
int64_t license_duration_remaining = 0;
int64_t playback_duration_remaining = 0;
QueryKeyStatus(true, false, &license_duration_remaining,
&playback_duration_remaining);
if (initial_license_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining,
expected_seconds_since_license_received, kClockTolerance)
<< "initial_license_duration_remaining = "
<< initial_license_duration_remaining
<< ", license_duration_remaining = " << license_duration_remaining;
}
if (initial_playback_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining,
expected_seconds_since_initial_playback, kClockTolerance)
<< "initial_playback_duration_remaining = "
<< initial_playback_duration_remaining
<< ", playback_duration_remaining = " << playback_duration_remaining;
}
decryptor_->CloseSession(session_id_);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingPstTest, ::testing::Values(0, 1, 2));
class WvCdmStreamingUsageReportTest
: public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {};
// TODO(b275651559): Re-enable test once the issue with "license remaining
// duration" is addressed.
TEST_P(WvCdmStreamingUsageReportTest, DISABLED_UsageTest) {
Unprovision();
Provision();
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(kStreamingClip8PstInitData, kLicenseTypeStreaming);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(),
false);
ValidateResponse(video_widevine::STREAMING, true);
int64_t initial_license_duration_remaining = 0;
int64_t initial_playback_duration_remaining = 0;
QueryKeyStatus(true, false, &initial_license_duration_remaining,
&initial_playback_duration_remaining);
sleep(kMinute);
int64_t expected_seconds_since_license_received = kMinute;
int64_t expected_seconds_since_initial_playback = 0;
int64_t expected_seconds_since_last_playback = 0;
const size_t minutes = GetParam();
for (size_t m = 0; m < minutes; ++m) {
// Decrypt data
const SubSampleInfo& data = kEncryptedStreamingClip8SubSample;
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&data.key_id, &data.encrypt_data.front(),
data.encrypt_data.size(), &data.iv,
data.block_offset, decrypt_buffer.data());
decryption_parameters.is_encrypted = data.is_encrypted;
decryption_parameters.is_secure = data.is_secure;
decryption_parameters.subsample_flags = data.subsample_flags;
EXPECT_EQ(NO_ERROR,
decryptor_->Decrypt(session_id_, data.validate_key_id,
decryption_parameters));
EXPECT_EQ(data.decrypt_data, decrypt_buffer);
sleep(kMinute);
expected_seconds_since_license_received += kMinute;
expected_seconds_since_initial_playback += kMinute;
expected_seconds_since_last_playback = kMinute;
}
// Query and validate usage information
int64_t license_duration_remaining = 0;
int64_t playback_duration_remaining = 0;
QueryKeyStatus(true, false, &license_duration_remaining,
&playback_duration_remaining);
// For unlimited "rental durations", the "license duration" will
// effectively be unlimited. Remaining license duration in this
// case is represented by |kUnlimitedDurationValue| and will not
// change over time.
if (initial_license_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining,
expected_seconds_since_license_received, kClockTolerance)
<< "initial_license_duration_remaining = "
<< initial_license_duration_remaining
<< ", license_duration_remaining = " << license_duration_remaining;
}
if (initial_playback_duration_remaining == kUnlimitedDurationValue) {
EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue);
} else {
EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining,
expected_seconds_since_initial_playback, kClockTolerance)
<< "initial_playback_duration_remaining = "
<< initial_playback_duration_remaining
<< ", playback_duration_remaining = " << playback_duration_remaining;
}
decryptor_->CloseSession(session_id_);
// Create usage report and validate
CdmUsageReportList usage_reports;
CdmResponseType status =
decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status);
int error_count = 0;
while (!usage_reports.empty()) {
for (size_t i = 0; i < usage_reports.size(); ++i) {
ValidateReleaseRequest(usage_reports[i],
expected_seconds_since_initial_playback != 0,
expected_seconds_since_license_received,
expected_seconds_since_initial_playback,
expected_seconds_since_last_playback);
const CdmKeyResponse release_msg = GetSecureStopResponse(config_.license_server(),
config_.client_auth(), usage_reports[i]);
EXPECT_EQ(NO_ERROR,
decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier))
<< i << "/" << usage_reports.size() << " (err " << (error_count++) << ")"
<< release_msg;
}
ASSERT_LE(error_count, 100); // Give up after 100 failures.
status = decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports);
switch (status.code()) {
case KEY_MESSAGE:
EXPECT_FALSE(usage_reports.empty());
break;
case NO_ERROR:
EXPECT_TRUE(usage_reports.empty());
break;
default:
FAIL() << "GetUsageInfo failed with error " << static_cast<int>(status);
break;
}
}
// Validate that update usage table entry is exercised.
drm_metrics::WvCdmMetrics metrics;
ASSERT_EQ(NO_ERROR, decryptor_->GetMetrics(kDefaultCdmIdentifier, &metrics));
ValidateHasUpdateUsageEntry(metrics);
}
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest,
::testing::Values(0, 1, 2));
class WvCdmOfflineUsageReportTest
: public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {};
@@ -1350,7 +1620,7 @@ TEST_P(WvCdmOfflineUsageReportTest, UsageTest) {
}
// Decrypt data
SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
@@ -1464,7 +1734,7 @@ TEST_F(WvCdmExtendedDurationTest, MaxUsageEntryOfflineRecoveryTest) {
++number_of_valid_offline_sessions;
// Decrypt data
SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&data->key_id, &data->encrypt_data.front(),