Add initial support for key rotation through ce cdm interface.

Merge from Widevine repo of http://go/wvgerrit/42941

Bug: 72168544
Test: tested as part of http://go/ag/4674759
Change-Id: I1a2d0f49371e5b3edf1d9dff85b85593f981d1f5
This commit is contained in:
Fred Gylys-Colwell
2018-07-01 17:56:23 -07:00
parent d17199fb83
commit fc4186e4fd
12 changed files with 480 additions and 332 deletions

View File

@@ -16,9 +16,9 @@
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
#include "usage_table_header.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
#include "usage_table_header.h"
namespace {
const size_t kKeySetIdLength = 14;
@@ -27,26 +27,26 @@ const size_t kKeySetIdLength = 14;
namespace wvcdm {
CdmSession::CdmSession(FileSystem* file_system,
metrics::SessionMetrics* metrics) :
metrics_(metrics),
initialized_(false),
closed_(true),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
is_offline_(false),
is_release_(false),
is_temporary_(false),
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_decryption_(true),
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
usage_support_type_(kNonSecureUsageSupport),
usage_table_header_(NULL),
usage_entry_number_(0),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
metrics::SessionMetrics* metrics)
: metrics_(metrics),
initialized_(false),
closed_(true),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
is_offline_(false),
is_release_(false),
is_temporary_(false),
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_decryption_(true),
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
usage_support_type_(kNonSecureUsageSupport),
usage_table_header_(NULL),
usage_entry_number_(0),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(new CryptoSession(crypto_metrics_));
@@ -77,27 +77,22 @@ CdmResponseType CdmSession::Init(
return Init(cdm_client_property_set, NULL, NULL);
}
CdmResponseType CdmSession::Init(
CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener) {
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id,
WvCdmEventListener* event_listener) {
if (initialized_) {
LOGE("CdmSession::Init: Failed due to previous initialization");
return REINIT_ERROR;
}
if (cdm_client_property_set &&
cdm_client_property_set->security_level() ==
QUERY_VALUE_SECURITY_LEVEL_L3) {
if (cdm_client_property_set && cdm_client_property_set->security_level() ==
QUERY_VALUE_SECURITY_LEVEL_L3) {
requested_security_level_ = kLevel3;
security_level_ = kSecurityLevelL3;
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_,
crypto_session_open_,
sts,
requested_security_level_);
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
if (NO_ERROR != sts) return sts;
security_level_ = crypto_session_->GetSecurityLevel();
@@ -137,12 +132,10 @@ CdmResponseType CdmSession::Init(
}
bool load_cert_sts;
M_TIME(
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(
wrapped_key),
crypto_metrics_,
crypto_session_load_certificate_private_key_,
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(wrapped_key),
crypto_metrics_, crypto_session_load_certificate_private_key_,
load_cert_sts);
if(!load_cert_sts) {
if (!load_cert_sts) {
return NEED_PROVISIONING;
}
client_token_type = kClientTokenDrmCert;
@@ -172,17 +165,17 @@ CdmResponseType CdmSession::Init(
if (!mock_license_parser_in_use_)
license_parser_.reset(new CdmLicense(session_id_));
if (!mock_policy_engine_in_use_)
policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get()));
policy_engine_.reset(
new PolicyEngine(session_id_, event_listener, crypto_session_.get()));
std::string service_certificate;
if (!Properties::GetServiceCertificate(session_id_, &service_certificate))
service_certificate.clear();
if (!license_parser_->Init(
client_token, client_token_type, serial_number,
Properties::UsePrivacyMode(session_id_), service_certificate,
crypto_session_.get(), policy_engine_.get()))
if (!license_parser_->Init(client_token, client_token_type, serial_number,
Properties::UsePrivacyMode(session_id_),
service_certificate, crypto_session_.get(),
policy_engine_.get()))
return LICENSE_PARSER_INIT_ERROR;
license_received_ = false;
@@ -214,32 +207,37 @@ CdmResponseType CdmSession::RestoreOfflineSession(
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &grace_period_end_time,
&app_parameters_, &usage_entry_, &usage_entry_number_)) {
LOGE("CdmSession::RestoreOfflineSession: failed to retrieve license. "
"key set id = %s", key_set_id.c_str());
LOGE(
"CdmSession::RestoreOfflineSession: failed to retrieve license. "
"key set id = %s",
key_set_id.c_str());
return GET_LICENSE_ERROR;
}
// Do not restore a released offline license, unless a release retry
if (!(license_type == kLicenseTypeRelease ||
license_state == DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::RestoreOfflineSession: invalid offline license state = "
"%d, type = %d", license_state, license_type);
LOGE(
"CdmSession::RestoreOfflineSession: invalid offline license state = "
"%d, type = %d",
license_state, license_type);
return GET_RELEASED_LICENSE_ERROR;
}
std::string provider_session_token;
if (usage_support_type_ == kUsageEntrySupport) {
if (!license_parser_->ExtractProviderSessionToken(
key_response_, &provider_session_token) ||
key_response_, &provider_session_token) ||
usage_table_header_ == NULL) {
provider_session_token.clear();
} else {
CdmResponseType sts =
usage_table_header_->LoadEntry(crypto_session_.get(), usage_entry_,
usage_entry_number_);
CdmResponseType sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = "
"%d", sts);
LOGE(
"CdmSession::RestoreOfflineSession: failed to load usage entry = "
"%d",
sts);
return sts;
}
}
@@ -264,12 +262,15 @@ CdmResponseType CdmSession::RestoreOfflineSession(
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession failed to update usage entry = "
"%d", sts);
LOGE(
"CdmSession::RestoreOfflineSession failed to update usage entry = "
"%d",
sts);
return sts;
}
if (!StoreLicense(license_state)) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
LOGW(
"CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
@@ -302,7 +303,7 @@ CdmResponseType CdmSession::RestoreUsageSession(
crypto_session_.get(), usage_entry_, usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to load usage entry = %d",
sts);
sts);
return sts;
}
}
@@ -317,11 +318,12 @@ CdmResponseType CdmSession::RestoreUsageSession(
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to update usage entry: %d",
sts);
sts);
return sts;
}
if (!UpdateUsageInfo()) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
LOGW(
"CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
@@ -376,8 +378,12 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal(
case kLicenseTypeRelease:
is_release_ = true;
break;
case kLicenseTypeSubSession:
return license_parser_->HandleSubLicense(init_data);
// TODO(jfore): CdmSession assumes a call to this method once a license has
// been received is a call to generate a license renewal message. Use of
// this enum differentiates the two calls. See "else if (license_received_)"
// below.
case kLicenseTypeEmbeddedKeyData:
return license_parser_->HandleEmbeddedKeyData(init_data);
default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type);
@@ -419,11 +425,9 @@ CdmResponseType CdmSession::GenerateKeyRequestInternal(
app_parameters_ = app_parameters;
CdmResponseType status = license_parser_->PrepareKeyRequest(
init_data, license_type,
app_parameters, &key_request->message,
&key_request->url);
if (status != KEY_MESSAGE)
return status;
init_data, license_type, app_parameters, &key_request->message,
&key_request->url);
if (status != KEY_MESSAGE) return status;
key_request_ = key_request->message;
if (is_offline_) {
@@ -476,13 +480,10 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
// Update or delete entry if usage table header+entries are supported
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty() &&
usage_table_header_ != NULL) {
!provider_session_token.empty() && usage_table_header_ != NULL) {
if (sts != KEY_ADDED) {
CdmResponseType delete_sts =
usage_table_header_->DeleteEntry(usage_entry_number_,
file_handle_.get(),
crypto_metrics_);
CdmResponseType delete_sts = usage_table_header_->DeleteEntry(
usage_entry_number_, file_handle_.get(), crypto_metrics_);
if (delete_sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d",
delete_sts);
@@ -578,17 +579,16 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// Playback may not begin until either the start time passes or the license
// is updated, so we treat this Decrypt call as invalid.
if (params.is_encrypted) {
if (!policy_engine_->CanDecryptContent(*params.key_id)) {
if (policy_engine_->IsLicenseForFuture())
return DECRYPT_NOT_READY;
if (!policy_engine_->IsSufficientOutputProtection(*params.key_id))
return INSUFFICIENT_OUTPUT_PROTECTION;
return NEED_KEY;
}
if (params.is_encrypted &&
!policy_engine_->CanDecryptContent(*params.key_id)) {
if (policy_engine_->IsLicenseForFuture()) return DECRYPT_NOT_READY;
if (!policy_engine_->IsSufficientOutputProtection(*params.key_id))
return INSUFFICIENT_OUTPUT_PROTECTION;
return NEED_KEY;
}
if (!policy_engine_->CanUseKey(*params.key_id, security_level_))
return KEY_PROHIBITED_FOR_SECURITY_LEVEL;
if (!policy_engine_->CanUseKey(*params.key_id, security_level_)) {
return KEY_PROHIBITED_FOR_SECURITY_LEVEL;
}
CdmResponseType status = crypto_session_->Decrypt(params);
@@ -616,8 +616,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// License renewal
// GenerateRenewalRequest() - Construct valid renewal request for the current
// session keys.
CdmResponseType CdmSession::GenerateRenewalRequest(
CdmKeyRequest* key_request) {
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyRequest* key_request) {
if (!initialized_) {
LOGE("CdmSession::GenerateRenewalRequest: not initialized");
return NOT_INITIALIZED_ERROR;
@@ -659,8 +658,7 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
return KEY_ADDED;
}
CdmResponseType CdmSession::GenerateReleaseRequest(
CdmKeyRequest* key_request) {
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) {
if (!initialized_) {
LOGE("CdmSession::GenerateReleaseRequest: not initialized");
return NOT_INITIALIZED_ERROR;
@@ -677,11 +675,13 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
if (has_provider_session_token() &&
usage_support_type_ == kUsageEntrySupport) {
status = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
status =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (status != NO_ERROR) {
LOGE("CdmSession::GenerateReleaseRequest: Update usage entry failed = "
"%d", status);
LOGE(
"CdmSession::GenerateReleaseRequest: Update usage entry failed = "
"%d",
status);
return status;
}
}
@@ -691,8 +691,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
return RELEASE_KEY_REQUEST_ERROR;
} else if (!usage_provider_session_token_.empty()) {
if (usage_support_type_ == kUsageEntrySupport) {
if (!UpdateUsageInfo())
return RELEASE_USAGE_INFO_FAILED;
if (!UpdateUsageInfo()) return RELEASE_USAGE_INFO_FAILED;
}
}
@@ -725,7 +724,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
}
if (usage_support_type_ != kUsageEntrySupport) {
LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d",
usage_support_type_);
usage_support_type_);
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
@@ -734,12 +733,8 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(new CryptoSession(crypto_metrics_));
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_,
crypto_session_open_,
sts,
requested_security_level_);
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
if (sts != NO_ERROR) return sts;
usage_table_header_ = NULL;
@@ -756,8 +751,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
}
return usage_table_header_->DeleteEntry(usage_entry_number,
file_handle_.get(),
crypto_metrics_);
file_handle_.get(), crypto_metrics_);
}
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
@@ -833,11 +827,10 @@ CdmResponseType CdmSession::StoreLicense() {
std::string app_id;
GetApplicationId(&app_id);
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
key_response_,
DeviceFiles::GetUsageInfoFileName(app_id),
key_set_id_, usage_entry_,
usage_entry_number_)) {
if (!file_handle_->StoreUsageInfo(
provider_session_token, key_request_, key_response_,
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id_, usage_entry_,
usage_entry_number_)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
// Usage info file is corrupt. Delete current usage entry and file.
switch (usage_support_type_) {
@@ -969,11 +962,8 @@ CdmResponseType CdmSession::UpdateUsageTableInformation() {
crypto_session_->GetUsageSupportType(&usage_support_type);
if (sts == NO_ERROR && usage_support_type == kUsageTableSupport) {
M_TIME(
sts = crypto_session_->UpdateUsageInformation(),
crypto_metrics_,
crypto_session_update_usage_information_,
sts);
M_TIME(sts = crypto_session_->UpdateUsageInformation(), crypto_metrics_,
crypto_session_update_usage_information_, sts);
return sts;
}
@@ -982,12 +972,12 @@ CdmResponseType CdmSession::UpdateUsageTableInformation() {
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (usage_support_type_ != kUsageEntrySupport ||
!has_provider_session_token() ||
usage_table_header_ == NULL) {
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected state, "
!has_provider_session_token() || usage_table_header_ == NULL) {
LOGE(
"CdmSession::UpdateUsageEntryInformation: Unexpected state, "
"usage support type: %d, PST present: %s, usage table header available"
": %s", usage_support_type_,
has_provider_session_token() ? "yes" : "no",
": %s",
usage_support_type_, has_provider_session_token() ? "yes" : "no",
usage_table_header_ == NULL ? "no" : "yes");
return INCORRECT_USAGE_SUPPORT_TYPE_2;
}
@@ -1003,9 +993,8 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (sts != NO_ERROR) return sts;
if (is_offline_)
StoreLicense(is_release_
? DeviceFiles::kLicenseStateReleasing
: DeviceFiles::kLicenseStateActive);
StoreLicense(is_release_ ? DeviceFiles::kLicenseStateReleasing
: DeviceFiles::kLicenseStateActive);
else if (!usage_provider_session_token_.empty())
UpdateUsageInfo();
@@ -1022,18 +1011,10 @@ CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
return PARAMETER_NULL;
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericEncrypt(
in_buffer,
key_id,
iv,
algorithm,
out_buffer),
crypto_metrics_,
crypto_session_generic_encrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
algorithm);
M_TIME(sts = crypto_session_->GenericEncrypt(in_buffer, key_id, iv, algorithm,
out_buffer),
crypto_metrics_, crypto_session_generic_encrypt_, sts,
metrics::Pow2Bucket(in_buffer.size()), algorithm);
return sts;
}
@@ -1047,18 +1028,10 @@ CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
return PARAMETER_NULL;
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericDecrypt(
in_buffer,
key_id,
iv,
algorithm,
out_buffer),
crypto_metrics_,
crypto_session_generic_decrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
algorithm);
M_TIME(sts = crypto_session_->GenericDecrypt(in_buffer, key_id, iv, algorithm,
out_buffer),
crypto_metrics_, crypto_session_generic_decrypt_, sts,
metrics::Pow2Bucket(in_buffer.size()), algorithm);
return sts;
}
@@ -1072,16 +1045,9 @@ CdmResponseType CdmSession::GenericSign(const std::string& message,
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericSign(
message,
key_id,
algorithm,
signature),
crypto_metrics_,
crypto_session_generic_sign_,
sts,
metrics::Pow2Bucket(message.size()),
algorithm);
sts = crypto_session_->GenericSign(message, key_id, algorithm, signature),
crypto_metrics_, crypto_session_generic_sign_, sts,
metrics::Pow2Bucket(message.size()), algorithm);
return sts;
}
@@ -1090,17 +1056,10 @@ CdmResponseType CdmSession::GenericVerify(const std::string& message,
CdmSigningAlgorithm algorithm,
const std::string& signature) {
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericVerify(
message,
key_id,
algorithm,
signature),
crypto_metrics_,
crypto_session_generic_verify_,
sts,
metrics::Pow2Bucket(message.size()),
algorithm);
M_TIME(sts = crypto_session_->GenericVerify(message, key_id, algorithm,
signature),
crypto_metrics_, crypto_session_generic_verify_, sts,
metrics::Pow2Bucket(message.size()), algorithm);
return sts;
}
@@ -1117,8 +1076,7 @@ bool CdmSession::UpdateUsageInfo() {
usage_data.usage_entry_number = usage_entry_number_;
return file_handle_->UpdateUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
usage_provider_session_token_,
DeviceFiles::GetUsageInfoFileName(app_id), usage_provider_session_token_,
usage_data);
}