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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user