Files
android/libwvdrmengine/cdm/core/src/license.cpp
Rahul Frias e538c96131 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
2021-03-23 13:06:55 -07:00

1254 lines
42 KiB
C++

// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "license.h"
#include <sstream>
#include <vector>
#include "cdm_session.h"
#include "client_identification.h"
#include "clock.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "device_files.h"
#include "log.h"
#include "platform.h"
#include "policy_engine.h"
#include "privacy_crypto.h"
#include "properties.h"
#include "service_certificate.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace {
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;
const uint32_t kFourCcLittleEndianCbc1 = 0x31636263;
const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
const uint32_t kFourCcCenc = 0x63656e63;
const std::string kEmptyString;
// MAC key in the license are two separate MAC keys (client and server).
constexpr size_t kLicenseMacKeySize = wvcdm::MAC_KEY_SIZE * 2;
} // namespace
namespace wvcdm {
// Protobuf generated classes.
using video_widevine::EncryptedClientIdentification;
using video_widevine::License;
using video_widevine::License_KeyContainer;
using video_widevine::LicenseError;
using video_widevine::LicenseIdentification;
using video_widevine::LicenseRequest;
using video_widevine::LicenseRequest_ContentIdentification;
using video_widevine::LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine::LicenseRequest_ContentIdentification_WebmKeyId;
using video_widevine::LicenseRequest_ContentIdentification_WidevinePsshData;
using video_widevine::SignedMessage;
namespace {
std::vector<CryptoKey> ExtractEntitlementKeys(const License& license) {
std::vector<CryptoKey> key_array;
for (int i = 0; i < license.key_size(); ++i) {
CryptoKey key;
size_t length = 0;
switch (license.key(i).type()) {
case License_KeyContainer::ENTITLEMENT: {
// Strip off PKCS#5 padding - since we know the key is 32 or 48 bytes,
// the padding will always be 16 bytes.
if (license.key(i).key().size() > 32) {
length = license.key(i).key().size() - 16;
} else {
length = 0;
}
key.set_key_data(license.key(i).key().substr(0, length));
key.set_key_data_iv(license.key(i).iv());
key.set_key_id(license.key(i).id());
key.set_track_label(license.key(i).track_label());
if (license.key(i).has_key_control()) {
key.set_key_control(license.key(i).key_control().key_control_block());
key.set_key_control_iv(license.key(i).key_control().iv());
}
uint32_t four_cc = kFourCcCenc;
if (license.has_protection_scheme()) {
four_cc = license.protection_scheme();
}
key.set_track_label(license.key(i).track_label());
switch (four_cc) {
// b/30713238: Android N assumed that the "protection scheme" Four
// CC code, after being extracted from the protobuf, was host byte
// order dependent. Later versions do not assume this, and thus,
// for backwards compatibility, must support both byte orders.
case kFourCcCbc1:
case kFourCcCbcs:
case kFourCcLittleEndianCbc1:
case kFourCcLittleEndianCbcs:
key.set_cipher_mode(kCipherModeCbc);
break;
default:
key.set_cipher_mode(kCipherModeCtr);
break;
}
key_array.push_back(key);
} break;
default:
// Ignore all but ENTITLEMENT key types.
break;
}
}
return key_array;
}
std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> key_array;
// Extract content key(s)
for (int i = 0; i < license.key_size(); ++i) {
CryptoKey key;
size_t length;
switch (license.key(i).type()) {
case License_KeyContainer::CONTENT:
case License_KeyContainer::OPERATOR_SESSION: {
key.set_key_id(license.key(i).id());
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
// the padding will always be 16 bytes.
// TODO(b/111069024): remove this!
if (license.key(i).key().size() > 16) {
length = license.key(i).key().size() - 16;
} else {
length = 0;
}
key.set_key_data(license.key(i).key().substr(0, length));
key.set_key_data_iv(license.key(i).iv());
if (license.key(i).has_key_control()) {
key.set_key_control(license.key(i).key_control().key_control_block());
key.set_key_control_iv(license.key(i).key_control().iv());
}
uint32_t four_cc = kFourCcCenc;
if (license.has_protection_scheme()) {
four_cc = license.protection_scheme();
}
key.set_track_label(license.key(i).track_label());
switch (four_cc) {
// b/30713238: Android N assumed that the "protection scheme" Four
// CC code, after being extracted from the protobuf, was host byte
// order dependent. Later versions do not assume this, and thus,
// for backwards compatibility, must support both byte orders.
case kFourCcCbc1:
case kFourCcCbcs:
case kFourCcLittleEndianCbc1:
case kFourCcLittleEndianCbcs:
key.set_cipher_mode(kCipherModeCbc);
break;
default:
key.set_cipher_mode(kCipherModeCtr);
break;
}
key_array.push_back(key);
break;
}
case License_KeyContainer::KEY_CONTROL:
if (license.key(i).has_key_control()) {
key.set_key_control(license.key(i).key_control().key_control_block());
if (license.key(i).key_control().has_iv()) {
key.set_key_control_iv(license.key(i).key_control().iv());
}
key_array.push_back(key);
}
break;
default:
// Ignore SIGNING and SUB_SESSION key types as they are not content
// related.
break;
}
}
return key_array;
}
} // namespace
CdmLicense::CdmLicense(const CdmSessionId& session_id)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
supports_core_messages_(true),
use_privacy_mode_(false),
clock_(new Clock()),
license_key_type_(kLicenseKeyTypeContent) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
supports_core_messages_(true),
use_privacy_mode_(false),
license_key_type_(kLicenseKeyTypeContent) {
clock_.reset(clock);
}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(bool use_privacy_mode,
const std::string& signed_service_certificate,
CryptoSession* session, PolicyEngine* policy_engine) {
if (!clock_) {
LOGE("Clock parameter not provided");
return false;
}
if (session_id_.empty()) {
LOGE("Session ID not provided");
return false;
}
if (session == nullptr || !session->IsOpen()) {
LOGE("Crypto session not provided or not open");
return false;
}
if (policy_engine == nullptr) {
LOGE("Policy engine not provided");
return false;
}
if (use_privacy_mode && !signed_service_certificate.empty() &&
service_certificate_.Init(signed_service_certificate) != NO_ERROR) {
return false;
}
crypto_session_ = session;
policy_engine_ = policy_engine;
use_privacy_mode_ = use_privacy_mode;
license_nonce_ = 0;
initialized_ = true;
return true;
}
CdmResponseType CdmLicense::SetServiceCertificate(
const std::string& signed_service_certificate) {
return service_certificate_.Init(signed_service_certificate);
}
CdmResponseType CdmLicense::PrepareKeyRequest(
const InitializationData& init_data, const std::string& client_token,
CdmLicenseType license_type, const CdmAppParameterMap& app_parameters,
CdmKeyMessage* signed_request, std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_4;
}
client_token_ = client_token;
if (init_data.IsEmpty() && stored_init_data_) {
InitializationData restored_init_data = *stored_init_data_;
stored_init_data_.reset();
return PrepareKeyRequest(restored_init_data, client_token, license_type,
app_parameters, signed_request, server_url);
}
wrapped_keys_ = init_data.ExtractWrappedKeys();
if (!init_data.is_supported()) {
LOGE("Unsupported init data type: type = %s", init_data.type().c_str());
return INVALID_PARAMETERS_LIC_3;
}
if (init_data.IsEmpty()) {
LOGE("Init data is empty");
return INVALID_PARAMETERS_LIC_4;
}
if (signed_request == nullptr) {
LOGE("Output parameter |signed_request| not provided");
return INVALID_PARAMETERS_LIC_6;
}
if (server_url == nullptr) {
LOGE("Output parameter |server_url| not provided");
return INVALID_PARAMETERS_LIC_7;
}
// If privacy mode and no service certificate, depending on platform
// configuration, request service certificate or declare error
if (use_privacy_mode_ && !service_certificate_.has_certificate()) {
if (!Properties::allow_service_certificate_requests()) {
LOGE("Privacy mode failure: No service certificate");
return PRIVACY_MODE_ERROR_1;
}
stored_init_data_.reset(new InitializationData(init_data));
if (!ServiceCertificate::GetRequest(signed_request)) {
LOGE("Failed to prepare service certificated request");
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
return KEY_MESSAGE;
}
const std::string& request_id = crypto_session_->request_id();
LicenseRequest license_request;
CdmResponseType status;
status = PrepareClientId(app_parameters, kEmptyString, &license_request);
if (NO_ERROR != status) return status;
status =
PrepareContentId(init_data, license_type, request_id, &license_request);
if (NO_ERROR != status) return status;
license_request.set_type(LicenseRequest::NEW);
license_request.set_request_time(clock_->GetCurrentTime());
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
status = crypto_session_->GenerateNonce(&license_nonce_);
switch (status) {
case NO_ERROR:
break;
case SESSION_LOST_STATE_ERROR:
case SYSTEM_INVALIDATED_ERROR:
return status;
default:
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
license_request.set_key_control_nonce(license_nonce_);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
std::string serialized_license_req;
license_request.SerializeToString(&serialized_license_req);
key_request_ = serialized_license_req;
// Derive signing and encryption keys and construct core message and
// signature.
std::string core_message;
std::string license_request_signature;
status = crypto_session_->PrepareAndSignLicenseRequest(
serialized_license_req, &core_message, &license_request_signature);
if (status != NO_ERROR) {
signed_request->clear();
return status;
}
if (license_request_signature.empty()) {
LOGE("License request signature is empty");
signed_request->clear();
return EMPTY_LICENSE_REQUEST;
}
// Put serialized license request and signature together
SignedMessage signed_message;
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature);
signed_message.set_msg(serialized_license_req);
if (core_message.size() > 0) {
signed_message.set_oemcrypto_core_message(core_message);
}
signed_message.SerializeToString(signed_request);
*server_url = server_url_;
return KEY_MESSAGE;
}
// TODO(b/166007195): Remove this.
CdmResponseType CdmLicense::PrepareKeyUpdateReload(CdmSession* cdm_session) {
uint32_t api_version = 0;
if (!crypto_session_->GetApiVersion(&api_version)) {
LOGW("Unknown API Version");
api_version = 15;
}
if (api_version != 16) return NO_ERROR;
// To work around b/166010609, we ask OEMCrypto to prepare an unused renewal
// request. This lets the ODK library update its clock saying when the renewal
// was signed.
constexpr bool is_renewal = true;
const CdmAppParameterMap unused_app_parameters;
CdmKeyMessage unused_request;
std::string unused_url;
return PrepareKeyUpdateRequest(is_renewal, unused_app_parameters, cdm_session,
&unused_request, &unused_url);
}
CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
bool is_renewal, const CdmAppParameterMap& app_parameters,
CdmSession* cdm_session, CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_1;
}
if (signed_request == nullptr) {
LOGE("Output parameter |signed_request| not provided");
return INVALID_PARAMETERS_LIC_1;
}
if (server_url == nullptr) {
LOGE("Output parameter |server_url| not provided");
return INVALID_PARAMETERS_LIC_2;
}
if (is_renewal && !policy_engine_->CanRenew()) {
LOGE("License renewal prohibited");
return LICENSE_RENEWAL_PROHIBITED;
}
if (renew_with_client_id_) {
if (use_privacy_mode_ && !service_certificate_.has_certificate()) {
LOGE("Privacy mode failure: No service certificate");
return PRIVACY_MODE_ERROR_2;
}
}
LicenseRequest license_request;
if (is_renewal)
license_request.set_type(LicenseRequest::RENEWAL);
else
license_request.set_type(LicenseRequest::RELEASE);
license_request.set_request_time(clock_->GetCurrentTime());
if (renew_with_client_id_) {
CdmResponseType status = PrepareClientId(
app_parameters, provider_client_token_, &license_request);
if (NO_ERROR != status) return status;
}
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
license_request.mutable_content_id()->mutable_existing_license();
LicenseIdentification license_id = policy_engine_->license_id();
current_license->mutable_license_id()->CopyFrom(license_id);
int64_t seconds_since_started, seconds_since_last_played;
CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid;
if (!provider_session_token_.empty()) {
if (!is_renewal) {
CdmResponseType status =
crypto_session_->DeactivateUsageInformation(provider_session_token_);
if (NO_ERROR != status) return status;
}
// TODO(rfrias): Refactor to avoid needing to call CdmSession
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return status;
}
std::string usage_report;
CdmResponseType status = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
&seconds_since_started, &seconds_since_last_played);
if (!is_renewal) {
if (NO_ERROR == status)
current_license->set_session_usage_table_entry(usage_report);
else
return GENERATE_USAGE_REPORT_ERROR;
}
}
if (CryptoSession::kUsageDurationsValid != usage_duration_status) {
if (policy_engine_->GetSecondsSinceStarted(&seconds_since_started) &&
policy_engine_->GetSecondsSinceLastPlayed(&seconds_since_last_played))
usage_duration_status = CryptoSession::kUsageDurationsValid;
}
if (CryptoSession::kUsageDurationsValid == usage_duration_status) {
current_license->set_seconds_since_started(seconds_since_started);
current_license->set_seconds_since_last_played(seconds_since_last_played);
}
uint32_t api_version = 0;
if (!crypto_session_->GetApiVersion(&api_version)) {
LOGW("Unknown API Version");
api_version = 15;
}
if (api_version < 16) {
// For a pre-v16 license, get/set the nonce. This value will be reflected
// in the Key Control Block of the license response.
const CdmResponseType status =
crypto_session_->GenerateNonce(&license_nonce_);
switch (status) {
case NO_ERROR:
break;
case SESSION_LOST_STATE_ERROR:
case SYSTEM_INVALIDATED_ERROR:
return status;
default:
return LICENSE_RENEWAL_NONCE_GENERATION_ERROR;
}
}
license_request.set_key_control_nonce(license_nonce_);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
std::string serialized_license_req;
license_request.SerializeToString(&serialized_license_req);
// Construct signature and core message.
std::string core_message;
std::string license_request_signature;
const CdmResponseType status = crypto_session_->PrepareAndSignRenewalRequest(
serialized_license_req, &core_message, &license_request_signature);
if (status != NO_ERROR) return status;
if (license_request_signature.empty()) {
LOGE("License request signature is empty");
return EMPTY_LICENSE_RENEWAL;
}
// Put serialize license request and signature together
SignedMessage signed_message;
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature);
signed_message.set_msg(serialized_license_req);
if (supports_core_messages()) {
// Only include the |core_message| in renewal requests if it is
// already known that the license is v16.
signed_message.set_oemcrypto_core_message(core_message);
}
signed_message.SerializeToString(signed_request);
*server_url = server_url_;
return KEY_MESSAGE;
}
CdmResponseType CdmLicense::HandleKeyResponse(
bool is_restore, const CdmKeyResponse& license_response) {
if (!initialized_) {
LOGE("CdmLicense not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_2;
}
// Clear the latest service version when we receive a new response.
latest_service_version_.Clear();
if (license_response.empty()) {
LOGE("License response is empty");
return EMPTY_LICENSE_RESPONSE_1;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE("Unable to parse signed license response");
return INVALID_LICENSE_RESPONSE;
}
latest_service_version_ = signed_response.service_version_info();
if (use_privacy_mode_ && Properties::allow_service_certificate_requests() &&
signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) {
std::string signed_certificate;
CdmResponseType status = ServiceCertificate::ParseResponse(
license_response, &signed_certificate);
if (status != NO_ERROR) return status;
status = service_certificate_.Init(signed_certificate);
return (status == NO_ERROR) ? NEED_KEY : status;
}
if (signed_response.type() == SignedMessage::ERROR_RESPONSE)
return HandleKeyErrorResponse(signed_response);
if (signed_response.type() != SignedMessage::LICENSE) {
LOGE("Unrecognized signed message type: type = %d",
static_cast<int>(signed_response.type()));
return INVALID_LICENSE_TYPE;
}
if (!signed_response.has_signature()) {
LOGE("License response is not signed");
return LICENSE_RESPONSE_NOT_SIGNED;
}
// Check that the server returned a |core_message|. If missing, then
// the server is assumed to operate as V15. This will imply that the
// |signature| field in the response does not include a core message
// either.
if (!signed_response.has_oemcrypto_core_message() ||
signed_response.oemcrypto_core_message().empty()) {
supports_core_messages_ = false;
}
const std::string& signed_message = signed_response.msg();
const std::string core_message =
signed_response.has_oemcrypto_core_message()
? signed_response.oemcrypto_core_message()
: std::string();
const std::string& signature = signed_response.signature();
License license;
if (!license.ParseFromString(signed_message)) {
LOGE("Unable to parse license response");
return LICENSE_RESPONSE_PARSE_ERROR_1;
}
if (!signed_response.has_session_key()) {
LOGE("Signed response has no session keys present");
return SESSION_KEYS_NOT_FOUND;
}
CdmResponseType status = crypto_session_->GenerateDerivedKeys(
key_request_, signed_response.session_key());
if (status != NO_ERROR) return status;
// Extract mac key
std::string mac_key_iv;
std::string mac_keys;
for (int i = 0; i < license.key_size(); ++i) {
if (license.key(i).type() == License_KeyContainer::SIGNING) {
mac_key_iv.assign(license.key(i).iv());
// Strip off PKCS#5 padding
mac_keys.assign(license.key(i).key().data(), kLicenseMacKeySize);
}
}
if (license.policy().can_renew() ||
(mac_key_iv.size() != 0 || mac_keys.size() != 0)) {
if (mac_key_iv.size() != KEY_IV_SIZE ||
mac_keys.size() != kLicenseMacKeySize) {
LOGE(
"MAC key/IV size error: expected = %zu/%zu, "
"actual = %zu/%zu (key/iv)",
kLicenseMacKeySize, KEY_IV_SIZE, mac_keys.size(), mac_key_iv.size());
return KEY_SIZE_ERROR_1;
}
}
// A license may contain several types of keys (content, entitlement,
// signing, key control and operator sessions); however, it should not
// contain both entitlement keys and content keys. To determine the
// overall type of the license, we check for the existence of either
// type of keys. If both are present, we default to entitlement keys.
CdmLicenseKeyType key_type = kLicenseKeyTypeEntitlement;
std::vector<CryptoKey> key_array = ExtractEntitlementKeys(license);
if (key_array.empty()) {
key_array = ExtractContentKeys(license);
key_type = kLicenseKeyTypeContent;
}
if (key_array.empty()) {
LOGE("No content keys");
return NO_CONTENT_KEY;
}
license_key_type_ = key_type;
if (license.has_provider_client_token())
provider_client_token_ = license.provider_client_token();
if (license.has_srm_update()) {
status = crypto_session_->LoadSrm(license.srm_update());
switch (status) {
case NO_ERROR:
break;
case SYSTEM_INVALIDATED_ERROR:
return status;
default:
break; // Ignore
}
}
if (license.id().type() == video_widevine::OFFLINE &&
license.policy().can_persist())
is_offline_ = true;
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
LOGV("provider_session_token = %s", provider_session_token_.empty()
? "N/A"
: provider_session_token_.c_str());
if (license.policy().has_renewal_server_url()) {
server_url_ = license.policy().renewal_server_url();
}
if (license.policy().has_always_include_client_id()) {
renew_with_client_id_ = license.policy().always_include_client_id();
}
CdmResponseType resp = NO_CONTENT_KEY;
if (kLicenseKeyTypeEntitlement == key_type) {
resp = HandleEntitlementKeyResponse(is_restore, signed_message,
core_message, signature, mac_key_iv,
mac_keys, key_array, license);
} else if (kLicenseKeyTypeContent == key_type) {
resp = HandleContentKeyResponse(is_restore, signed_message, core_message,
signature, mac_key_iv, mac_keys, key_array,
license);
}
return resp;
}
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
bool is_renewal, bool is_restore, const CdmKeyResponse& license_response) {
if (!initialized_) {
LOGE("CdmLicense not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_3;
}
if (license_response.empty()) {
LOGE("License response is empty");
return EMPTY_LICENSE_RESPONSE_2;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE("Unable to parse signed message");
return LICENSE_RESPONSE_PARSE_ERROR_2;
}
switch (signed_response.type()) {
case SignedMessage::LICENSE:
break;
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
LOGE("Unrecognized signed message type: type = %d",
static_cast<int>(signed_response.type()));
return INVALID_LICENSE_TYPE;
}
// At this point of the license life-cycle (handling a renewal), we should
// already know if the license is v15 or not. If license is v16, then a
// renewal should have a |core_message| present; otherwise there might have
// been some tampering with the request or response. On the other hand, a
// release is processed without loading the license, so OEMCrypto does not
// know if it is v15 or v16, and will not add a core message.
if (is_renewal && supports_core_messages() &&
(!signed_response.has_oemcrypto_core_message() ||
signed_response.oemcrypto_core_message().empty())) {
LOGE("Renewal response is missing |core_message| field");
return CORE_MESSAGE_NOT_FOUND;
}
if (!signed_response.has_signature()) {
LOGE("Update key response is missing signature");
return SIGNATURE_NOT_FOUND;
}
const std::string& signed_message = signed_response.msg();
const std::string core_message =
signed_response.has_oemcrypto_core_message()
? signed_response.oemcrypto_core_message()
: std::string();
const std::string& signature = signed_response.signature();
License license;
if (!license.ParseFromString(signed_message)) {
LOGE("Unable to parse license from signed message");
return LICENSE_RESPONSE_PARSE_ERROR_3;
}
if (!license.has_id()) {
LOGE("License ID not present");
return LICENSE_ID_NOT_FOUND;
}
if (license.policy().has_always_include_client_id()) {
renew_with_client_id_ = license.policy().always_include_client_id();
}
if (!is_renewal) {
if (!license.id().has_provider_session_token()) return KEY_ADDED;
provider_session_token_ = license.id().provider_session_token();
return KEY_ADDED;
}
if (license.policy().has_renewal_server_url() &&
license.policy().renewal_server_url().size() > 0) {
server_url_ = license.policy().renewal_server_url();
}
CdmResponseType status;
if (supports_core_messages()) {
status =
crypto_session_->LoadRenewal(signed_message, core_message, signature);
} else {
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
status = crypto_session_->RefreshKeys(signed_message, signature, key_array);
}
if (status == KEY_ADDED) {
policy_engine_->UpdateLicense(license, is_restore);
}
return status;
}
CdmResponseType CdmLicense::HandleEmbeddedKeyData(
const InitializationData& init_data) {
return HandleNewEntitledKeys(init_data.ExtractWrappedKeys());
}
CdmResponseType CdmLicense::RestoreOfflineLicense(
const std::string& client_token, const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response, int64_t playback_start_time,
int64_t last_playback_time, int64_t grace_period_end_time,
CdmSession* cdm_session) {
if (license_request.empty()) {
LOGE("License request is empty");
return EMPTY_LICENSE_REQUEST_2;
}
if (license_response.empty()) {
LOGE("License response is empty");
return EMPTY_LICENSE_RESPONSE_3;
}
client_token_ = client_token;
SignedMessage signed_request;
if (!signed_request.ParseFromString(license_request)) {
LOGE("Failed to parse license request");
return PARSE_REQUEST_ERROR_1;
}
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
LOGE("Unexpected license request type: expected = %d, actual = %d",
static_cast<int>(SignedMessage::LICENSE_REQUEST),
static_cast<int>(signed_request.type()));
return INVALID_LICENSE_REQUEST_TYPE_1;
}
if (!signed_request.has_oemcrypto_core_message() ||
signed_request.oemcrypto_core_message().empty()) {
// Pre V16 license did not include |core_message| components.
// The license response is checked by HandleKeyResponse().
supports_core_messages_ = false;
}
key_request_ = signed_request.msg();
LicenseRequest original_license_request;
if (!original_license_request.ParseFromString(key_request_)) {
LOGW("Could not parse original request.");
} else {
license_nonce_ = original_license_request.key_control_nonce();
}
CdmResponseType sts = HandleKeyResponse(true, license_response);
if (sts != KEY_ADDED) return sts;
if (!license_renewal_response.empty()) {
sts = PrepareKeyUpdateReload(cdm_session);
if (sts != KEY_MESSAGE && sts != NO_ERROR) return sts;
sts = HandleKeyUpdateResponse(true, true, license_renewal_response);
if (sts != KEY_ADDED) return sts;
}
if (!provider_session_token_.empty()) {
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return sts;
}
std::string usage_report;
CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid;
int64_t seconds_since_started, seconds_since_last_played;
sts = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
&seconds_since_started, &seconds_since_last_played);
if (NO_ERROR == sts) {
switch (usage_duration_status) {
case CryptoSession::kUsageDurationPlaybackNotBegun:
playback_start_time = 0;
last_playback_time = 0;
break;
case CryptoSession::kUsageDurationsValid: {
int64_t current_time = clock_->GetCurrentTime();
if (current_time - seconds_since_started > 0)
playback_start_time = current_time - seconds_since_started;
if (current_time - last_playback_time > 0)
last_playback_time = current_time - seconds_since_last_played;
break;
}
default:
break;
}
}
}
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time,
grace_period_end_time);
return NO_ERROR;
}
CdmResponseType CdmLicense::RestoreLicenseForRelease(
const std::string& client_token, const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response) {
if (license_request.empty()) {
LOGE("License request is empty");
return EMPTY_LICENSE_REQUEST_3;
}
if (license_response.empty()) {
LOGE("License response is empty");
return EMPTY_LICENSE_RESPONSE_4;
}
client_token_ = client_token;
SignedMessage signed_request;
if (!signed_request.ParseFromString(license_request)) {
LOGE("Failed to parse signed license request");
return PARSE_REQUEST_ERROR_2;
}
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
LOGE("Unexpected signed license request type: expected = %d, actual = %d",
static_cast<int>(SignedMessage::LICENSE_REQUEST),
static_cast<int>(signed_request.type()));
return INVALID_LICENSE_REQUEST_TYPE_2;
}
if (!signed_request.has_oemcrypto_core_message()) {
// Pre V16 license did not include |core_message| components.
supports_core_messages_ = false;
}
key_request_ = signed_request.msg();
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE("Failed to parse signed license response");
return LICENSE_RESPONSE_PARSE_ERROR_4;
}
if (SignedMessage::LICENSE != signed_response.type()) {
LOGE("Unexpected signed license response type: expected = %d, actual = %d",
static_cast<int>(SignedMessage::LICENSE),
static_cast<int>(signed_response.type()));
return INVALID_LICENSE_TYPE_2;
}
if (!signed_response.has_signature()) {
LOGE("License response is not signed");
return SIGNATURE_NOT_FOUND_2;
}
if (!signed_response.has_oemcrypto_core_message() ||
signed_response.oemcrypto_core_message().empty()) {
// Possible that the request contains a |core_message|, but the
// response does not. This would occur if the licensing server
// is v15.
supports_core_messages_ = false;
}
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("Failed to parse license response");
return LICENSE_RESPONSE_PARSE_ERROR_5;
}
if (license.has_provider_client_token())
provider_client_token_ = license.provider_client_token();
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
if (license.policy().has_always_include_client_id())
renew_with_client_id_ = license.policy().always_include_client_id();
if (!signed_response.has_session_key()) {
LOGE("No session keys present");
return SESSION_KEYS_NOT_FOUND_2;
}
if (!license.id().has_provider_session_token()) {
CdmResponseType result = HandleKeyResponse(false, license_response);
return result == KEY_ADDED ? NO_ERROR : result;
}
if (license.policy().has_renewal_server_url())
server_url_ = license.policy().renewal_server_url();
// If the policy engine already has keys, they will now expire.
// If the policy engine does not already have keys, this will not add any.
policy_engine_->SetLicenseForRelease(license, supports_core_messages());
return NO_ERROR;
}
bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}
bool CdmLicense::ExtractProviderSessionToken(
const CdmKeyResponse& license_response,
std::string* provider_session_token) {
if (license_response.empty()) {
LOGE("License response is empty");
return false;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE("Failed to parse signed license response");
return false;
}
if (signed_response.type() != SignedMessage::LICENSE) {
LOGE("Unexpected signed license response type: expected = %d, actual = %d",
static_cast<int>(SignedMessage::LICENSE),
static_cast<int>(signed_response.type()));
return false;
}
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("Failed to parse license response");
return false;
}
if (license.id().has_provider_session_token() &&
!license.id().provider_session_token().empty()) {
*provider_session_token = license.id().provider_session_token();
return true;
}
return false;
}
CdmResponseType CdmLicense::HandleKeyErrorResponse(
const SignedMessage& signed_message) {
LicenseError license_error;
if (!license_error.ParseFromString(signed_message.msg())) {
LOGE("Failed to parse license error response");
return KEY_ERROR;
}
switch (license_error.error_code()) {
case LicenseError::INVALID_DRM_DEVICE_CERTIFICATE:
return NEED_PROVISIONING;
case LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE:
return DEVICE_REVOKED;
case LicenseError::SERVICE_UNAVAILABLE:
default:
LOGW("Unknown error type: error_code = %d",
static_cast<int>(license_error.error_code()));
return KEY_ERROR;
}
}
CdmResponseType CdmLicense::PrepareClientId(
const CdmAppParameterMap& app_parameters,
const std::string& provider_client_token, LicenseRequest* license_request) {
wvcdm::ClientIdentification id;
if (client_token_.empty()) {
LOGE("Client token not set when preparing client ID");
return CLIENT_TOKEN_NOT_SET;
}
CdmResponseType status = id.Init(client_token_, crypto_session_);
if (status != NO_ERROR) return status;
video_widevine::ClientIdentification* client_id =
license_request->mutable_client_id();
status = id.Prepare(app_parameters, provider_client_token, client_id);
if (status != NO_ERROR) return status;
if (Properties::UsePrivacyMode(session_id_)) {
if (!service_certificate_.has_certificate()) {
LOGE("Service certificate not staged");
return PRIVACY_MODE_ERROR_3;
}
EncryptedClientIdentification* encrypted_client_id =
license_request->mutable_encrypted_client_id();
status = service_certificate_.EncryptClientId(crypto_session_, client_id,
encrypted_client_id);
if (NO_ERROR == status) {
license_request->clear_client_id();
} else {
license_request->clear_encrypted_client_id();
}
return status;
}
return NO_ERROR;
}
CdmResponseType CdmLicense::PrepareContentId(
const InitializationData& init_data, CdmLicenseType license_type,
const std::string& request_id, LicenseRequest* license_request) {
// Content Identification may be a cenc_id, a webm_id or a license_id
LicenseRequest_ContentIdentification* content_id =
license_request->mutable_content_id();
if (init_data.is_cenc() || init_data.is_hls()) {
LicenseRequest_ContentIdentification_WidevinePsshData* widevine_pssh_data =
content_id->mutable_widevine_pssh_data();
if (!init_data.IsEmpty()) {
widevine_pssh_data->add_pssh_data(init_data.data());
} else {
LOGE("ISO-CENC init data not available");
return CENC_INIT_DATA_UNAVAILABLE;
}
if (!SetTypeAndId(license_type, request_id, widevine_pssh_data)) {
return PREPARE_CENC_CONTENT_ID_FAILED;
}
} else if (init_data.is_webm()) {
LicenseRequest_ContentIdentification_WebmKeyId* webm_key_id =
content_id->mutable_webm_key_id();
if (!init_data.IsEmpty()) {
webm_key_id->set_header(init_data.data());
} else {
LOGE("WebM init data not available");
return WEBM_INIT_DATA_UNAVAILABLE;
}
if (!SetTypeAndId(license_type, request_id, webm_key_id)) {
return PREPARE_WEBM_CONTENT_ID_FAILED;
}
} else {
LOGE("Unsupported init data type: type = %s", init_data.type().c_str());
return UNSUPPORTED_INIT_DATA_FORMAT;
}
return NO_ERROR;
}
CdmResponseType CdmLicense::HandleContentKeyResponse(
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty()) {
LOGE("No content keys provided");
return NO_CONTENT_KEY;
}
CdmResponseType resp;
if (supports_core_messages()) {
resp = crypto_session_->LoadLicense(msg, core_message, signature,
kLicenseKeyTypeContent);
} else {
resp = crypto_session_->LoadKeys(
msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_,
license.srm_requirement(), kLicenseKeyTypeContent);
}
if (KEY_ADDED == resp) {
loaded_keys_.clear();
for (std::vector<CryptoKey>::const_iterator it = key_array.begin();
it != key_array.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
}
return resp;
}
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
if (key_array.empty()) {
LOGE("No entitlement keys provided");
return NO_CONTENT_KEY;
}
CdmResponseType resp;
if (supports_core_messages()) {
resp = crypto_session_->LoadLicense(msg, core_message, signature,
kLicenseKeyTypeEntitlement);
} else {
resp = crypto_session_->LoadKeys(
msg, signature, mac_key_iv, mac_key, key_array, provider_session_token_,
license.srm_requirement(), kLicenseKeyTypeEntitlement);
}
if (KEY_ADDED != resp) {
return resp;
}
// Save the entitlement keys for future use to handle key changes.
entitlement_keys_.CopyFrom(license.key());
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
return HandleNewEntitledKeys(wrapped_keys_);
}
CdmResponseType CdmLicense::HandleNewEntitledKeys(
const std::vector<WidevinePsshData_EntitledKey>& wrapped_keys) {
std::vector<CryptoKey> entitled_key_array;
entitled_key_array.reserve(wrapped_keys.size());
for (RepeatedPtrField<License_KeyContainer>::const_iterator kc =
entitlement_keys_.begin();
kc != entitlement_keys_.end(); ++kc) {
if (kc->type() != video_widevine::License::KeyContainer::ENTITLEMENT) {
continue;
}
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator wk =
wrapped_keys.begin();
wk != wrapped_keys.end(); wk++) {
if (wk->entitlement_key_id() == kc->id()) {
// Add a new entry to the key array to load oemcrypto.
entitled_key_array.resize(entitled_key_array.size() + 1);
// Strip PKCS#5 padding from entitled content keys.
std::string content_key = wk->key();
if (content_key.size() < CONTENT_KEY_SIZE) {
LOGE(
"Entitled content key too small: "
"expected = %zu, actual = %zu (bytes)",
CONTENT_KEY_SIZE, content_key.size());
return KEY_SIZE_ERROR_2;
} else if (content_key.size() > CONTENT_KEY_SIZE) {
content_key.resize(CONTENT_KEY_SIZE);
}
CryptoKey& this_entry = entitled_key_array.back();
this_entry.set_key_id(wk->key_id());
this_entry.set_key_data_iv(wk->iv());
this_entry.set_entitlement_key_id(wk->entitlement_key_id());
this_entry.set_key_data(content_key);
}
}
}
CdmResponseType resp =
crypto_session_->LoadEntitledContentKeys(entitled_key_array);
if (KEY_ADDED == resp) {
for (std::vector<WidevinePsshData_EntitledKey>::const_iterator it =
wrapped_keys.begin();
it != wrapped_keys.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
policy_engine_->SetEntitledLicenseKeys(wrapped_keys);
}
return resp;
}
template <typename T>
bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
const std::string& request_id, T* content_id) {
switch (license_type) {
case kLicenseTypeOffline:
content_id->set_license_type(video_widevine::OFFLINE);
break;
case kLicenseTypeStreaming:
case kLicenseTypeTemporary:
content_id->set_license_type(video_widevine::STREAMING);
break;
default:
LOGD("Unknown license type: %d", static_cast<int>(license_type));
return false;
}
content_id->set_request_id(request_id);
return true;
}
} // namespace wvcdm