Source release v3.5.0

This commit is contained in:
Gene Morgan
2017-11-28 17:42:16 -08:00
parent 501c22890d
commit 31381a1311
155 changed files with 16680 additions and 3816 deletions

View File

@@ -7,6 +7,7 @@
#include <vector>
#include "clock.h"
#include "cdm_session.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "device_files.h"
@@ -35,7 +36,6 @@ const uint32_t kFourCcCbcs = 0x63626373;
const uint32_t kFourCcLittleEndianCbc1 = 0x31636263;
const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
const uint32_t kFourCcCenc = 0x63656e63;
const uint32_t kFourCcCens = 0x63656e73;
} // namespace
@@ -123,7 +123,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
case kFourCcLittleEndianCbcs:
key.set_cipher_mode(kCipherModeCbc);
break;
default:
default:
key.set_cipher_mode(kCipherModeCtr);
break;
}
@@ -149,11 +149,11 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> sub_session_keys = ExtractSubSessionKeys(license);
// Match the track label from the key arrays and add sub_license_key_id to
// the content key array.
LOGI("Received %d subsession keys", sub_session_keys.size());
LOGV("Received %d subsession keys", sub_session_keys.size());
if (!sub_session_keys.empty()) {
for (int i = 0; i < key_array.size(); ++i) {
for (size_t i = 0; i < key_array.size(); ++i) {
if (key_array[i].track_label().empty()) continue;
for (int x = 0; x < sub_session_keys.size(); ++x) {
for (size_t x = 0; x < sub_session_keys.size(); ++x) {
if (sub_session_keys[x].track_label() == key_array[i].track_label()) {
key_array[i].set_sub_session_key_id(sub_session_keys[x].key_id());
key_array[i].set_sub_session_key(sub_session_keys[x].key_data());
@@ -172,6 +172,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
use_privacy_mode_(false),
clock_(new Clock()) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
@@ -180,16 +181,18 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false) {
is_offline_(false),
use_privacy_mode_(false) {
clock_.reset(clock);
}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(
ServiceCertificate* service_certificate, const std::string& client_token,
CdmClientTokenType client_token_type, const std::string& device_id,
CryptoSession* session, PolicyEngine* policy_engine) {
const std::string& client_token, CdmClientTokenType client_token_type,
const std::string& device_id, bool use_privacy_mode,
const std::string& signed_service_certificate, CryptoSession* session,
PolicyEngine* policy_engine) {
if (clock_.get() == NULL) {
LOGE("CdmLicense::Init: clock parameter not provided");
return false;
@@ -211,12 +214,24 @@ bool CdmLicense::Init(
return false;
}
service_certificate_ = service_certificate;
if (use_privacy_mode) {
if (!signed_service_certificate.empty()) {
if (service_certificate_.Init(signed_service_certificate) != NO_ERROR)
return false;
}
if (!service_certificate_.has_certificate() &&
!Properties::allow_service_certificate_requests()) {
LOGE("CdmLicense::Init: Required service certificate not provided");
return false;
}
}
client_token_ = client_token;
client_token_type_ = client_token_type;
device_id_ = device_id;
crypto_session_ = session;
policy_engine_ = policy_engine;
use_privacy_mode_ = use_privacy_mode;
initialized_ = true;
return true;
}
@@ -229,6 +244,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_4;
}
if (init_data.IsEmpty() && stored_init_data_.get()) {
InitializationData restored_init_data = *stored_init_data_;
stored_init_data_.reset();
return PrepareKeyRequest(restored_init_data, license_type, app_parameters,
signed_request, server_url);
}
if (!init_data.is_supported()) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
@@ -247,12 +268,25 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return INVALID_PARAMETERS_LIC_7;
}
// If privacy mode, must have service certificate
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->has_certificate()) {
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
// 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("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_1;
return PRIVACY_MODE_ERROR_1;
}
stored_init_data_.reset(new InitializationData(init_data));
if (!ServiceCertificate::GetRequest(signed_request)) {
LOGE("CdmLicense::PrepareKeyRequest: failed to prepare a service "
"certificated request");
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
return KEY_MESSAGE;
}
std::string request_id;
@@ -284,30 +318,30 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// initialization data.
std::vector<video_widevine::SubLicense> embedded_key_data =
init_data.ExtractEmbeddedKeys();
for (int i = 0; i < embedded_key_data.size(); ++i) {
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
bool exists = false;
if (!crypto_session_->GenerateSubSessionNonce(
embedded_key_data[i].sub_session_key_id(), &exists, &nonce)) {
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
if (exists) {
SignedMessage signed_sub_license;
License_KeyContainer keyc;
// Parse the sub license for this track to extract the label.
if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) ||
!keyc.ParseFromString(signed_sub_license.msg()) ||
keyc.track_label().empty()) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
if (exists) {
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
LicenseRequest::SubSessionData* sub_session_data =
license_request.add_sub_session_data();
sub_session_data->set_sub_session_key_id(
embedded_key_data[i].sub_session_key_id());
sub_session_data->set_nonce(nonce);
sub_session_data->set_track_label(keyc.track_label());
}
SignedMessage signed_sub_license;
License_KeyContainer keyc;
// Parse the sub license for this track to extract the label.
if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) ||
!keyc.ParseFromString(signed_sub_license.msg()) ||
keyc.track_label().empty()) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
}
LicenseRequest::SubSessionData* sub_session_data =
license_request.add_sub_session_data();
sub_session_data->set_sub_session_key_id(
embedded_key_data[i].sub_session_key_id());
sub_session_data->set_nonce(nonce);
sub_session_data->set_track_label(keyc.track_label());
}
license_request.set_protocol_version(video_widevine::VERSION_2_1);
@@ -316,8 +350,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
std::string serialized_license_req;
license_request.SerializeToString(&serialized_license_req);
if (Properties::use_certificates_as_identification())
key_request_ = serialized_license_req;
key_request_ = serialized_license_req;
// Derive signing and encryption keys and construct signature.
std::string license_request_signature;
@@ -347,7 +380,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
bool is_renewal, const CdmAppParameterMap& app_parameters,
CdmKeyMessage* signed_request, std::string* server_url) {
CdmSession* cdm_session, CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_1;
@@ -367,8 +401,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
}
if (renew_with_client_id_) {
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->has_certificate()) {
if (use_privacy_mode_ && !service_certificate_.has_certificate()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_2;
@@ -403,6 +436,13 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
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,
@@ -483,16 +523,26 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return INVALID_LICENSE_RESPONSE;
}
switch (signed_response.type()) {
case SignedMessage::LICENSE:
break;
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
LOGE(
"CdmLicense::HandleKeyResponse: unrecognized signed message type: %d",
signed_response.type());
return INVALID_LICENSE_TYPE;
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("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d",
signed_response.type());
return INVALID_LICENSE_TYPE;
}
if (!signed_response.has_signature()) {
@@ -506,15 +556,13 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return LICENSE_RESPONSE_PARSE_ERROR_1;
}
if (Properties::use_certificates_as_identification()) {
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::HandleKeyResponse: no session keys present");
return SESSION_KEYS_NOT_FOUND;
}
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return GENERATE_DERIVED_KEYS_ERROR;
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::HandleKeyResponse: no session keys present");
return SESSION_KEYS_NOT_FOUND;
}
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return GENERATE_DERIVED_KEYS_ERROR;
// Extract mac key
std::string mac_key_iv;
@@ -544,17 +592,17 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return NO_CONTENT_KEY;
}
if (license.has_srm_update()) crypto_session_->LoadSrm(license.srm_update());
if (license.id().type() == video_widevine::OFFLINE &&
license.policy().can_persist())
is_offline_ = true;
LOGV("Get Provider_session_token:");
if (license.id().has_provider_session_token()) {
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
LOGV("Provider_session_token=%s", provider_session_token_.c_str());
} else {
LOGV("NO 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();
@@ -564,10 +612,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
renew_with_client_id_ = license.policy().always_include_client_id();
}
CdmResponseType resp = NO_ERROR;
resp = crypto_session_->LoadKeys(
CdmResponseType resp = crypto_session_->LoadKeys(
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
key_array, provider_session_token_);
key_array, provider_session_token_, license.srm_requirement());
if (KEY_ADDED == resp) {
loaded_keys_.clear();
@@ -667,7 +714,7 @@ CdmResponseType CdmLicense::HandleSubLicense(
std::set<KeyId> loaded_keys;
// Build a license with the rotated keys.
License license;
for (int i = 0; i < subkeys.size(); ++i) {
for (size_t i = 0; i < subkeys.size(); ++i) {
SignedMessage sm;
if (!sm.ParseFromString(subkeys[i].key_msg())) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
@@ -676,18 +723,30 @@ CdmResponseType CdmLicense::HandleSubLicense(
if (!keyc.ParseFromString(sm.msg())) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
}
size_t length;
std::vector<CryptoKey> keys;
keys.resize(1);
keys[0].set_key_id(keyc.id());
keys[0].set_key_data(keyc.key());
// Strip PKCS#5 padding from sublicense content keys.
// TODO(jfore): Refactor this to use ExtractContentKeys.
if (keyc.key().size() > KEY_SIZE) {
length = keyc.key().size() - KEY_SIZE;
} else {
length = 0;
}
keys[0].set_key_data(keyc.key().substr(0, length));
keys[0].set_key_data_iv(keyc.iv());
keys[0].set_key_control(keyc.key_control().key_control_block());
keys[0].set_key_control_iv(keyc.key_control().iv());
keys[0].set_track_label(keyc.track_label());
//TODO: passing empty cipher_mode and srm_req params - OK?
CdmResponseType result = crypto_session_->LoadKeys(
sm.msg(), sm.signature(), std::string(), std::string(), keys,
std::string());
std::string(), std::string());
if (result != KEY_ADDED) {
LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d",
result);
return result;
}
loaded_keys.insert(keyc.id());
@@ -703,7 +762,8 @@ bool CdmLicense::RestoreOfflineLicense(
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) {
int64_t last_playback_time, int64_t grace_period_end_time,
CdmSession* cdm_session) {
if (license_request.empty() || license_response.empty()) {
LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
@@ -726,13 +786,7 @@ bool CdmLicense::RestoreOfflineLicense(
return false;
}
if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg();
} else {
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
}
key_request_ = signed_request.msg();
CdmResponseType sts = HandleKeyResponse(license_response);
@@ -745,9 +799,16 @@ bool CdmLicense::RestoreOfflineLicense(
}
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 false;
}
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,
@@ -803,13 +864,7 @@ bool CdmLicense::RestoreLicenseForRelease(
return false;
}
if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg();
} else {
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
}
key_request_ = signed_request.msg();
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
@@ -848,19 +903,15 @@ bool CdmLicense::RestoreLicenseForRelease(
if (license.policy().has_always_include_client_id())
renew_with_client_id_ = license.policy().always_include_client_id();
if (Properties::use_certificates_as_identification()) {
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present");
return false;
}
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present");
return false;
}
if (license.id().has_provider_session_token()) {
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return false;
} else {
return KEY_ADDED == HandleKeyResponse(license_response);
}
if (license.id().has_provider_session_token()) {
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return false;
}
if (license.policy().has_renewal_server_url())
@@ -876,6 +927,44 @@ 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("CdmLicense::ExtractProviderSessionToken: empty license response");
return false;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE(
"CdmLicense::ExtractProviderSessionToken: unable to parse signed "
"license response");
return false;
}
if (signed_response.type() != SignedMessage::LICENSE) {
LOGE("CdmLicense::ExtractProviderSessionToken: unrecognized signed message "
"type: %d", signed_response.type());
return false;
}
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::ExtractProviderSessionToken: unable 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;
@@ -969,7 +1058,7 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(b2a_hex(device_id_));
} else if (crypto_session_->GetDeviceUniqueId(&value)) {
} else if (crypto_session_->GetInternalDeviceUniqueId(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(value);
@@ -1041,16 +1130,36 @@ CdmResponseType CdmLicense::PrepareClientId(
}
}
CryptoSession::SupportedCertificateTypes supported_certs;
if (crypto_session_->GetSupportedCertificateTypes(&supported_certs)) {
if (supported_certs.rsa_2048_bit) {
client_capabilities->add_supported_certificate_key_type(
video_widevine::
ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_2048);
}
if (supported_certs.rsa_3072_bit) {
client_capabilities->add_supported_certificate_key_type(
video_widevine::
ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_3072);
}
}
client_capabilities->set_can_update_srm(
crypto_session_->IsSrmUpdateSupported());
uint16_t srm_version;
if (crypto_session_->GetSrmVersion(&srm_version))
client_capabilities->set_srm_version(srm_version);
if (Properties::UsePrivacyMode(session_id_)) {
if (service_certificate_->certificate().empty()) {
if (!service_certificate_.has_certificate()) {
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
return PRIVACY_MODE_ERROR_3;
}
EncryptedClientIdentification* encrypted_client_id =
license_request->mutable_encrypted_client_id();
CdmResponseType status;
status = service_certificate_->EncryptClientId(crypto_session_, client_id,
encrypted_client_id);
status = service_certificate_.EncryptClientId(crypto_session_, client_id,
encrypted_client_id);
if (NO_ERROR == status) {
license_request->clear_client_id();
} else {