Save and retrieve key information from licenses and usage records

[ Merge of http://go/wvgerrit/120512 ]

Wrapped DRM private keys are loaded when a key request is made or when
offline/usage sessions are restored. They were earlier loaded when a
session was opened.

For streaming sessions, key material will be fetched from the default
or legacy certificates and loaded when a key request is made.

For offline and usage sessions, key material may be retrieved from
license or usage records if available. If not available, information
associated with the legacy certificate will be loaded.

Certificate and wrapped keys are also written out when an offline
license or usage record is saved.

Bug: 169740403
Test: WV unit/integration tests
      WvCdmRequestLicenseTest.ProvisioningWithExpiringCertTest
      WvCdmRequestLicenseTest.StreamingWithExpiringCertTest
      WvCdmRequestLicenseTest.RestoreOfflineKeysWithExpiringCertTest
Change-Id: Ice0154c632170c46da171cbbb23a97380c610a98
This commit is contained in:
Rahul Frias
2021-03-23 13:06:55 -07:00
parent 30ebbefb40
commit e538c96131
9 changed files with 412 additions and 253 deletions

View File

@@ -51,6 +51,8 @@ namespace {
// HTTP response codes.
const int kHttpOk = 200;
const int kDrmCertificateExpiryPeriod = 150;
const std::string kCencMimeType = "video/mp4";
const wvcdm::CdmIdentifier kExampleIdentifier = {
wvcdm::EMPTY_SPOID, "com.example", "com.example", 7, 9};
@@ -1747,6 +1749,13 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
client_auth->assign(config.client_auth());
}
CdmResponseType GenerateKeyRequest() {
CdmAppParameterMap app_parameters;
return GenerateKeyRequest(kCencMimeType, binary_key_id(), app_parameters,
kLicenseTypeStreaming, kDefaultCdmIdentifier,
nullptr);
}
void GenerateKeyRequest(const std::string& init_data,
CdmLicenseType license_type) {
GenerateKeyRequest(init_data, license_type, nullptr);
@@ -1794,6 +1803,24 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
CdmLicenseType license_type,
const CdmIdentifier& cdm_identifier,
CdmClientPropertySet* property_set) {
CdmResponseType status =
GenerateKeyRequest(init_data_type, init_data, app_parameters,
license_type, cdm_identifier, property_set);
EXPECT_EQ(expected_response, status)
<< "session_id_ " << session_id_ << std::endl
<< "init_data (hex) " << wvcdm::b2a_hex(init_data) << std::endl
<< "cdm_identifier.origin " << cdm_identifier.origin << std::endl
<< "cdm_identifier.app_package_name " << cdm_identifier.app_package_name
<< std::endl
<< "cdm_identifier.unique_id " << cdm_identifier.unique_id << std::endl;
}
CdmResponseType GenerateKeyRequest(const std::string& init_data_type,
const std::string& init_data,
CdmAppParameterMap& app_parameters,
CdmLicenseType license_type,
const CdmIdentifier& cdm_identifier,
CdmClientPropertySet* property_set) {
CdmKeyRequest key_request;
std::string key_set_id;
license_type_ = license_type;
@@ -1801,20 +1828,13 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
InitializationData parsed_init_data(init_data_type, init_data);
parsed_init_data.DumpToLogs();
}
EXPECT_EQ(
expected_response,
decryptor_->GenerateKeyRequest(
session_id_, key_set_id, init_data_type, init_data, license_type,
app_parameters, property_set, cdm_identifier, &key_request))
<< "session_id_ " << session_id_ << std::endl
<< "init_data (hex) " << wvcdm::b2a_hex(init_data) << std::endl
<< "key_set_id " << key_set_id << std::endl
<< "cdm_identifier.origin " << cdm_identifier.origin << std::endl
<< "cdm_identifier.app_package_name " << cdm_identifier.app_package_name
<< std::endl
<< "cdm_identifier.unique_id " << cdm_identifier.unique_id << std::endl;
CdmResponseType status = decryptor_->GenerateKeyRequest(
session_id_, key_set_id, init_data_type, init_data, license_type,
app_parameters, property_set, cdm_identifier, &key_request);
key_msg_ = key_request.message;
EXPECT_EQ(0u, key_request.url.size());
return status;
}
void GenerateRenewalRequest(CdmLicenseType license_type,
@@ -2027,6 +2047,40 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
decryptor_->Unprovision(kSecurityLevelL3, kDefaultCdmIdentifier));
}
bool IsProvisioned(const CdmIdentifier& identifier,
SecurityLevel requested_security_level) {
TestWvCdmClientPropertySet property_set_L3;
TestWvCdmClientPropertySet* property_set = nullptr;
if (kLevel3 == requested_security_level) {
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
property_set = &property_set_L3;
}
CdmResponseType status = decryptor_->OpenSession(
config_.key_system(), property_set, identifier, nullptr, &session_id_);
if (status == NO_ERROR) {
wvcdm::CdmAppParameterMap app_parameters;
status =
GenerateKeyRequest(kCencMimeType, binary_key_id(), app_parameters,
kLicenseTypeStreaming, identifier, nullptr);
}
decryptor_->CloseSession(session_id_);
switch (status) {
case NO_ERROR:
case KEY_MESSAGE:
return true;
case NEED_PROVISIONING:
return false;
default:
EXPECT_EQ(NO_ERROR, status);
return false;
}
}
void Provision() { Provision(kDefaultCdmIdentifier, kLevelDefault); }
void Provision(const CdmIdentifier& identifier,
@@ -2040,24 +2094,13 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
property_set = &property_set_L3;
}
CdmResponseType status = decryptor_->OpenSession(
config_.key_system(), property_set, identifier, nullptr, &session_id_);
switch (status) {
case NO_ERROR:
decryptor_->CloseSession(session_id_);
return;
case NEED_PROVISIONING:
break;
default:
EXPECT_EQ(NO_ERROR, status);
return;
}
if (IsProvisioned(identifier, requested_security_level)) return;
std::string provisioning_server;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
status = decryptor_->GetProvisioningRequest(
CdmResponseType status = decryptor_->GetProvisioningRequest(
cert_type, cert_authority, identifier, kEmptyServiceCertificate,
requested_security_level, &key_msg_, &provisioning_server);
EXPECT_EQ(wvcdm::NO_ERROR, status);
@@ -2169,10 +2212,8 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
Unprovision();
EXPECT_EQ(
NEED_PROVISIONING,
decryptor_->OpenSession(config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr, &session_id_));
EXPECT_FALSE(IsProvisioned(kDefaultCdmIdentifier, kLevelDefault));
std::string provisioning_server;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
@@ -2195,11 +2236,9 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
}
TEST_F(WvCdmRequestLicenseTest, ProvisioningTestWithServiceCertificate) {
CdmResponseType status =
decryptor_->OpenSession(config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr, &session_id_);
EXPECT_TRUE(status == NEED_PROVISIONING || status == NO_ERROR)
<< "Failure to open session. error: " << status;
Unprovision();
EXPECT_FALSE(IsProvisioned(kDefaultCdmIdentifier, kLevelDefault));
std::string provisioning_server;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
@@ -2222,6 +2261,7 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTestWithServiceCertificate) {
}
TEST_F(WvCdmRequestLicenseTest, L3ProvisioningTest) {
Unprovision();
TestWvCdmClientPropertySet property_set_L3;
property_set_L3.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3);
std::string provisioning_server;
@@ -2246,19 +2286,28 @@ TEST_F(WvCdmRequestLicenseTest, L3ProvisioningTest) {
}
TEST_F(WvCdmRequestLicenseTest, PerOriginProvisioningTest) {
Unprovision();
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
Provision();
// Verify the global identifier is provisioned.
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->OpenSession(
config_.key_system(), nullptr,
kDefaultCdmIdentifier, nullptr, &session_id_));
CdmAppParameterMap app_parameters;
EXPECT_EQ(KEY_MESSAGE,
GenerateKeyRequest(kCencMimeType, binary_key_id(), app_parameters,
kLicenseTypeStreaming, kDefaultCdmIdentifier,
nullptr));
decryptor_->CloseSession(session_id_);
// The other identifier should not be provisioned.
EXPECT_EQ(wvcdm::NEED_PROVISIONING,
decryptor_->OpenSession(config_.key_system(), nullptr,
kExampleIdentifier, nullptr, &session_id_));
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
}
TEST_F(WvCdmRequestLicenseTest, PerOriginProvisioningSupportsOldPaths) {
@@ -2588,6 +2637,42 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningRevocationTest) {
}
}
TEST_F(WvCdmRequestLicenseTest, ProvisioningWithExpiringCertTest) {
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier));
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
ConfigTestEnv config(kContentProtectionStagingServer, true);
// Provision
std::string provisioning_server;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest(
cert_type, cert_authority, kExampleIdentifier,
kEmptyServiceCertificate, kLevelDefault,
&key_msg_, &provisioning_server));
std::string response = GetCertRequestResponse(config.provisioning_server());
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
kExampleIdentifier, response, kLevelDefault,
&cert, &wrapped_key));
EXPECT_EQ(0, static_cast<int>(cert.size()));
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
decryptor_->CloseSession(session_id_);
// Make sure it is provisioned, then wait for certificate expiry period
EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault));
sleep(kDrmCertificateExpiryPeriod);
// Verify that it is no longer provisioned after the certificate expires
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
}
TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
TestWvCdmClientPropertySet property_set_L1;
TestWvCdmClientPropertySet property_set_L3;
@@ -2760,6 +2845,48 @@ TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) {
decryptor_->CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, StreamingWithExpiringCertTest) {
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier));
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
ConfigTestEnv config(kContentProtectionStagingServer, true);
// Provision
std::string provisioning_server;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest(
cert_type, cert_authority, kExampleIdentifier,
kEmptyServiceCertificate, kLevelDefault,
&key_msg_, &provisioning_server));
std::string response = GetCertRequestResponse(config.provisioning_server());
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
kExampleIdentifier, response, kLevelDefault,
&cert, &wrapped_key));
EXPECT_EQ(0, static_cast<int>(cert.size()));
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault));
// Fetch a streaming license
EXPECT_EQ(NO_ERROR,
decryptor_->OpenSession(config_.key_system(), nullptr,
kExampleIdentifier, nullptr, &session_id_));
CdmAppParameterMap app_parameters;
GenerateKeyRequest(KEY_MESSAGE, kCencMimeType, binary_key_id(),
app_parameters, kLicenseTypeStreaming, kExampleIdentifier,
nullptr);
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
decryptor_->CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
Unprovision();
Provision();
@@ -2801,6 +2928,68 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
decryptor_->CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeysWithExpiringCertTest) {
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL1, kExampleIdentifier));
EXPECT_EQ(NO_ERROR,
decryptor_->Unprovision(kSecurityLevelL3, kExampleIdentifier));
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
ConfigTestEnv config(kContentProtectionStagingServer, true);
// Provision
std::string provisioning_server;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->GetProvisioningRequest(
cert_type, cert_authority, kExampleIdentifier,
kEmptyServiceCertificate, kLevelDefault,
&key_msg_, &provisioning_server));
std::string response = GetCertRequestResponse(config.provisioning_server());
EXPECT_NE(0, static_cast<int>(response.size()));
EXPECT_EQ(wvcdm::NO_ERROR, decryptor_->HandleProvisioningResponse(
kExampleIdentifier, response, kLevelDefault,
&cert, &wrapped_key));
EXPECT_EQ(0, static_cast<int>(cert.size()));
EXPECT_EQ(0, static_cast<int>(wrapped_key.size()));
EXPECT_TRUE(IsProvisioned(kExampleIdentifier, kLevelDefault));
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
// Fetch offline license
EXPECT_EQ(NO_ERROR,
decryptor_->OpenSession(config_.key_system(), nullptr,
kExampleIdentifier, nullptr, &session_id_));
CdmAppParameterMap app_parameters;
GenerateKeyRequest(KEY_MESSAGE, kCencMimeType, key_id, app_parameters,
kLicenseTypeOffline, kExampleIdentifier, nullptr);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_->CloseSession(session_id_);
session_id_.clear();
// Wait till certificate expires
sleep(kDrmCertificateExpiryPeriod);
// Make sure the certificate has expired and the device is not provisioned
EXPECT_FALSE(IsProvisioned(kExampleIdentifier, kLevelDefault));
// Restore offline license
decryptor_->OpenSession(config_.key_system(), nullptr, kExampleIdentifier,
nullptr, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_->RestoreKey(session_id_, key_set_id));
decryptor_->CloseSession(session_id_);
}
TEST_F(WvCdmRequestLicenseTest, DisallowMultipleRestoreOfflineKeyTest) {
Unprovision();
Provision();
@@ -2987,11 +3176,7 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseRetryL3OfflineKeyTest) {
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
CdmResponseType sts =
decryptor_->OpenSession(config_.key_system(), &property_set,
kDefaultCdmIdentifier, nullptr, &session_id_);
if (NEED_PROVISIONING == sts) {
if (!IsProvisioned(kDefaultCdmIdentifier, kLevel3)) {
std::string provisioning_server;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
@@ -3009,8 +3194,6 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseRetryL3OfflineKeyTest) {
EXPECT_EQ(NO_ERROR, decryptor_->OpenSession(
config_.key_system(), &property_set,
kDefaultCdmIdentifier, nullptr, &session_id_));
} else {
EXPECT_EQ(NO_ERROR, sts);
}
decryptor_->OpenSession(config_.key_system(), &property_set,
@@ -3063,11 +3246,7 @@ TEST_F(WvCdmRequestLicenseTest,
GetOfflineConfiguration(&key_id, &client_auth);
key_id[key_id.size() - 1] = '1';
CdmResponseType sts =
decryptor_->OpenSession(config_.key_system(), &property_set,
kDefaultCdmIdentifier, nullptr, &session_id_);
if (NEED_PROVISIONING == sts) {
if (!IsProvisioned(kDefaultCdmIdentifier, kLevel3)) {
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority, cert, wrapped_key;
@@ -3085,8 +3264,6 @@ TEST_F(WvCdmRequestLicenseTest,
EXPECT_EQ(NO_ERROR, decryptor_->OpenSession(
config_.key_system(), &property_set,
kDefaultCdmIdentifier, nullptr, &session_id_));
} else {
EXPECT_EQ(NO_ERROR, sts);
}
decryptor_->OpenSession(config_.key_system(), &property_set,