Add client information to release and renewal messages
[ Merge of go/wvgerrit/14240 ] Client information is reported in release and renewal messages based on flag in the license. License proto has been updated to match server updates. There are two caveats * Client IDs will be reported unencrypted when usage reports are requested. * Release requests that enable privacy mode (encrypted client IDs) but do not specify a service certificate are not supported. b/19247020 Change-Id: I95e709922122370f310936fbad3d312262128e49
This commit is contained in:
@@ -107,7 +107,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
key_set_id, &license_state, &offline_init_data_, &key_request_,
|
||||
&key_response_, &offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_, &offline_release_server_url_,
|
||||
&playback_start_time, &last_playback_time)) {
|
||||
&playback_start_time, &last_playback_time, &app_parameters_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return GET_LICENSE_ERROR;
|
||||
@@ -208,6 +208,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
return KEY_REQUEST_ERROR_1;
|
||||
}
|
||||
|
||||
app_parameters_ = app_parameters;
|
||||
if (!license_parser_->PrepareKeyRequest(init_data, license_type,
|
||||
app_parameters, session_id_,
|
||||
key_request, server_url)) {
|
||||
@@ -361,8 +362,8 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
CdmResponseType status =
|
||||
license_parser_->PrepareKeyUpdateRequest(true, key_request, server_url);
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
true, app_parameters_, session_id_, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
@@ -389,8 +390,8 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
is_release_ = true;
|
||||
CdmResponseType status =
|
||||
license_parser_->PrepareKeyUpdateRequest(false, key_request, server_url);
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
false, app_parameters_, session_id_, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
@@ -499,7 +500,7 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
key_set_id_, state, offline_init_data_, key_request_, key_response_,
|
||||
offline_key_renewal_request_, offline_key_renewal_response_,
|
||||
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
|
||||
policy_engine_->GetLastPlaybackTime());
|
||||
policy_engine_->GetLastPlaybackTime(), app_parameters_);
|
||||
}
|
||||
|
||||
bool CdmSession::DeleteLicense() {
|
||||
@@ -507,8 +508,8 @@ bool CdmSession::DeleteLicense() {
|
||||
return false;
|
||||
|
||||
if (!license_parser_->provider_session_token().empty()) {
|
||||
if(crypto_session_->DeleteUsageInformation(
|
||||
license_parser_->provider_session_token()) != NO_ERROR) {
|
||||
if (crypto_session_->DeleteUsageInformation(
|
||||
license_parser_->provider_session_token()) != NO_ERROR) {
|
||||
LOGE("CdmSession::DeleteLicense: error deleting usage info");
|
||||
}
|
||||
}
|
||||
@@ -555,8 +556,8 @@ void CdmSession::GetApplicationId(std::string* app_id) {
|
||||
|
||||
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
return crypto_session_
|
||||
->DeleteMultipleUsageInformation(provider_session_tokens);
|
||||
return crypto_session_->DeleteMultipleUsageInformation(
|
||||
provider_session_tokens);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageInformation() {
|
||||
|
||||
@@ -27,6 +27,7 @@ using video_widevine_client::sdk::HashedFile;
|
||||
using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
using video_widevine_client::sdk::NameValue;
|
||||
using video_widevine_client::sdk::UsageInfo;
|
||||
using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
||||
|
||||
@@ -176,16 +177,14 @@ bool DeviceFiles::RemoveCertificate(const std::string& origin) {
|
||||
return RemoveFile(GetCertificateFileName(origin));
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
const CdmInitData& pssh_data,
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url,
|
||||
int64_t playback_start_time,
|
||||
int64_t last_playback_time) {
|
||||
bool DeviceFiles::StoreLicense(
|
||||
const std::string& key_set_id, const LicenseState state,
|
||||
const CdmInitData& pssh_data, const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url, int64_t playback_start_time,
|
||||
int64_t last_playback_time, const CdmAppParameterMap& app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreLicense: not initialized");
|
||||
return false;
|
||||
@@ -218,6 +217,13 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
license->set_release_server_url(release_server_url);
|
||||
license->set_playback_start_time(playback_start_time);
|
||||
license->set_last_playback_time(last_playback_time);
|
||||
NameValue* app_params;
|
||||
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
|
||||
iter != app_parameters.end(); ++iter) {
|
||||
app_params = license->add_app_parameters();
|
||||
app_params->set_name(iter->first);
|
||||
app_params->set_value(iter->second);
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
@@ -230,7 +236,7 @@ bool DeviceFiles::RetrieveLicense(
|
||||
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url, int64_t* playback_start_time,
|
||||
int64_t* last_playback_time) {
|
||||
int64_t* last_playback_time, CdmAppParameterMap* app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: not initialized");
|
||||
return false;
|
||||
@@ -285,6 +291,10 @@ bool DeviceFiles::RetrieveLicense(
|
||||
*release_server_url = license.release_server_url();
|
||||
*playback_start_time = license.playback_start_time();
|
||||
*last_playback_time = license.last_playback_time();
|
||||
for (int i = 0; i < license.app_parameters_size(); ++i) {
|
||||
(*app_parameters)[license.app_parameters(i).name()] =
|
||||
license.app_parameters(i).value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,11 @@ package video_widevine_client.sdk;
|
||||
// need this if we are using libprotobuf-cpp-2.3.0-lite
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message NameValue {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
message DeviceCertificate {
|
||||
optional bytes certificate = 1;
|
||||
optional bytes wrapped_private_key = 2;
|
||||
@@ -33,6 +38,7 @@ message License {
|
||||
optional bytes release_server_url = 7;
|
||||
optional int64 playback_start_time = 8 [default = 0];
|
||||
optional int64 last_playback_time = 9 [default = 0];
|
||||
repeated NameValue app_parameters = 10;
|
||||
}
|
||||
|
||||
message UsageInfo {
|
||||
|
||||
@@ -129,7 +129,10 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
}
|
||||
|
||||
CdmLicense::CdmLicense()
|
||||
: session_(NULL), initialized_(false), clock_(new Clock()) {}
|
||||
: session_(NULL),
|
||||
initialized_(false),
|
||||
renew_with_client_id_(false),
|
||||
clock_(new Clock()) {}
|
||||
|
||||
CdmLicense::CdmLicense(Clock* clock) : session_(NULL), initialized_(false) {
|
||||
if (NULL == clock) {
|
||||
@@ -194,184 +197,23 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string service_certificate;
|
||||
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id);
|
||||
std::string serialized_service_certificate;
|
||||
if (!Properties::GetServiceCertificate(session_id,
|
||||
&serialized_service_certificate) ||
|
||||
serialized_service_certificate.empty())
|
||||
serialized_service_certificate = service_certificate_;
|
||||
|
||||
if (privacy_mode_enabled && serialized_service_certificate.empty()) {
|
||||
stored_init_data_ = init_data.data();
|
||||
return PrepareServiceCertificateRequest(signed_request, server_url);
|
||||
if (privacy_mode_enabled) {
|
||||
if (!GetServiceCertificate(session_id, &service_certificate)) {
|
||||
stored_init_data_ = init_data.data();
|
||||
return PrepareServiceCertificateRequest(signed_request, server_url);
|
||||
}
|
||||
}
|
||||
|
||||
std::string request_id;
|
||||
session_->GenerateRequestId(&request_id);
|
||||
|
||||
LicenseRequest license_request;
|
||||
ClientIdentification* client_id = license_request.mutable_client_id();
|
||||
|
||||
if (Properties::use_certificates_as_identification())
|
||||
client_id->set_type(ClientIdentification::DEVICE_CERTIFICATE);
|
||||
else
|
||||
client_id->set_type(ClientIdentification::KEYBOX);
|
||||
client_id->set_token(token_);
|
||||
|
||||
ClientIdentification_NameValue* client_info;
|
||||
CdmAppParameterMap::const_iterator iter;
|
||||
for (iter = app_parameters.begin(); iter != app_parameters.end(); iter++) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(iter->first);
|
||||
client_info->set_value(iter->second);
|
||||
}
|
||||
std::string value;
|
||||
if (Properties::GetCompanyName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kCompanyNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetModelName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kModelNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetArchitectureName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kArchitectureNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetDeviceName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetProductName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kProductNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetBuildInfo(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kBuildInfoKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (session_->GetDeviceUniqueId(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetWVCdmVersion(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kWVCdmVersionKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
|
||||
ClientIdentification_ClientCapabilities* client_capabilities =
|
||||
client_id->mutable_client_capabilities();
|
||||
bool supports_usage_information;
|
||||
if (session_->UsageInformationSupport(&supports_usage_information)) {
|
||||
client_capabilities->set_session_token(supports_usage_information);
|
||||
}
|
||||
|
||||
client_capabilities->set_anti_rollback_usage_table(
|
||||
session_->IsAntiRollbackHwPresent());
|
||||
|
||||
uint32_t api_version = 0;
|
||||
if (session_->GetApiVersion(&api_version)) {
|
||||
client_capabilities->set_oem_crypto_api_version(api_version);
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability current_version, max_version;
|
||||
if (session_->GetHdcpCapabilities(¤t_version, &max_version)) {
|
||||
switch (max_version) {
|
||||
case HDCP_NONE:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
|
||||
break;
|
||||
case HDCP_V1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
|
||||
break;
|
||||
case HDCP_V2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
|
||||
break;
|
||||
case HDCP_V2_1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
|
||||
break;
|
||||
case HDCP_V2_2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
|
||||
break;
|
||||
case HDCP_NO_DIGITAL_OUTPUT:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NO_DIGITAL_OUTPUT);
|
||||
break;
|
||||
default:
|
||||
LOGW(
|
||||
"CdmLicense::PrepareKeyRequest: unexpected HDCP max capability "
|
||||
"version %d",
|
||||
max_version);
|
||||
}
|
||||
}
|
||||
|
||||
if (privacy_mode_enabled) {
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
license_request.mutable_encrypted_client_id();
|
||||
DeviceCertificate service_certificate;
|
||||
|
||||
if (!service_certificate.ParseFromString(serialized_service_certificate)) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareKeyRequest: unable to parse retrieved "
|
||||
"service certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (service_certificate.type() !=
|
||||
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareKeyRequest: retrieved certificate not of type"
|
||||
" service, %d",
|
||||
service_certificate.type());
|
||||
return false;
|
||||
}
|
||||
encrypted_client_id->set_service_id(service_certificate.service_id());
|
||||
encrypted_client_id->set_service_certificate_serial_number(
|
||||
service_certificate.serial_number());
|
||||
|
||||
std::string iv(KEY_IV_SIZE, 0);
|
||||
std::string key(KEY_SIZE, 0);
|
||||
|
||||
if (!session_->GetRandom(key.size(), reinterpret_cast<uint8_t*>(&key[0]))) {
|
||||
return false;
|
||||
}
|
||||
if (!session_->GetRandom(iv.size(), reinterpret_cast<uint8_t*>(&iv[0]))) {
|
||||
return false;
|
||||
}
|
||||
std::string id, enc_id, enc_key;
|
||||
client_id->SerializeToString(&id);
|
||||
|
||||
AesCbcKey aes;
|
||||
if (!aes.Init(key)) return false;
|
||||
if (!aes.Encrypt(id, &enc_id, &iv)) return false;
|
||||
|
||||
RsaPublicKey rsa;
|
||||
if (!rsa.Init(service_certificate.public_key())) return false;
|
||||
if (!rsa.Encrypt(key, &enc_key)) return false;
|
||||
|
||||
encrypted_client_id->set_encrypted_client_id_iv(iv);
|
||||
encrypted_client_id->set_encrypted_privacy_key(enc_key);
|
||||
encrypted_client_id->set_encrypted_client_id(enc_id);
|
||||
license_request.clear_client_id();
|
||||
}
|
||||
CdmResponseType status =
|
||||
PrepareClientId(privacy_mode_enabled, service_certificate, app_parameters,
|
||||
&license_request);
|
||||
if (NO_ERROR != status) return false;
|
||||
|
||||
// Content Identification may be a cenc_id, a webm_id or a license_id
|
||||
LicenseRequest_ContentIdentification* content_id =
|
||||
@@ -463,7 +305,9 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
bool is_renewal, CdmKeyMessage* signed_request, std::string* server_url) {
|
||||
bool is_renewal, const CdmAppParameterMap& app_parameters,
|
||||
const CdmSessionId& session_id, CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized");
|
||||
return LICENSE_PARSER_NOT_INITIALIZED_1;
|
||||
@@ -485,6 +329,22 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
|
||||
license_request.set_request_time(clock_->GetCurrentTime());
|
||||
|
||||
if (renew_with_client_id_) {
|
||||
std::string service_certificate;
|
||||
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id);
|
||||
if (privacy_mode_enabled) {
|
||||
if (!GetServiceCertificate(session_id, &service_certificate)) {
|
||||
return PrepareServiceCertificateRequest(signed_request, server_url)
|
||||
? KEY_MESSAGE
|
||||
: SERVICE_CERTIFICATE_REQUEST_GENERATE_ERROR;
|
||||
}
|
||||
}
|
||||
CdmResponseType status =
|
||||
PrepareClientId(privacy_mode_enabled, service_certificate,
|
||||
app_parameters, &license_request);
|
||||
if (NO_ERROR != status) return status;
|
||||
}
|
||||
|
||||
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
|
||||
license_request.mutable_content_id()->mutable_license();
|
||||
LicenseIdentification license_id = policy_engine_->license_id();
|
||||
@@ -653,6 +513,10 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
|
||||
policy_engine_->SetLicense(license);
|
||||
|
||||
if (license.policy().has_renew_with_client_id()) {
|
||||
renew_with_client_id_ = license.policy().renew_with_client_id();
|
||||
}
|
||||
|
||||
CdmResponseType resp = session_->LoadKeys(
|
||||
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
|
||||
key_array, provider_session_token_);
|
||||
@@ -684,8 +548,19 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
return LICENSE_RESPONSE_PARSE_ERROR_2;
|
||||
}
|
||||
|
||||
if (signed_response.type() == SignedMessage::ERROR_RESPONSE) {
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
switch (signed_response.type()) {
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE:
|
||||
return CdmLicense::HandleServiceCertificateResponse(signed_response);
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
default:
|
||||
LOGE(
|
||||
"CdmLicense::HandleKeyUpdateResponse: unrecognized signed message "
|
||||
"type: %d",
|
||||
signed_response.type());
|
||||
return INVALID_LICENSE_TYPE;
|
||||
}
|
||||
|
||||
if (!signed_response.has_signature()) {
|
||||
@@ -708,6 +583,10 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
if (license.policy().has_renew_with_client_id()) {
|
||||
renew_with_client_id_ = license.policy().renew_with_client_id();
|
||||
}
|
||||
|
||||
if (!is_renewal) {
|
||||
if (!license.id().has_provider_session_token()) return KEY_ADDED;
|
||||
|
||||
@@ -842,27 +721,32 @@ bool CdmLicense::RestoreLicenseForRelease(
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(license_response)) {
|
||||
LOGE("CdmLicense::RestoreLicenseForRelease: unable to parse signed license"
|
||||
" response");
|
||||
LOGE(
|
||||
"CdmLicense::RestoreLicenseForRelease: unable to parse signed license"
|
||||
" response");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SignedMessage::LICENSE != signed_response.type()) {
|
||||
LOGE("CdmLicense::RestoreLicenseForRelease: unrecognized signed message "
|
||||
"type: %d", signed_response.type());
|
||||
LOGE(
|
||||
"CdmLicense::RestoreLicenseForRelease: unrecognized signed message "
|
||||
"type: %d",
|
||||
signed_response.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!signed_response.has_signature()) {
|
||||
LOGE("CdmLicense::RestoreLicenseForRelease: license response is not"
|
||||
" signed");
|
||||
LOGE(
|
||||
"CdmLicense::RestoreLicenseForRelease: license response is not"
|
||||
" signed");
|
||||
return false;
|
||||
}
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg())) {
|
||||
LOGE("CdmLicense::RestoreLicenseForRelease: unable to parse license"
|
||||
" response");
|
||||
LOGE(
|
||||
"CdmLicense::RestoreLicenseForRelease: unable to parse license"
|
||||
" response");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -994,6 +878,183 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::PrepareClientId(
|
||||
bool encrypt, const std::string& certificate,
|
||||
const CdmAppParameterMap& app_parameters, LicenseRequest* license_request) {
|
||||
ClientIdentification* client_id = license_request->mutable_client_id();
|
||||
|
||||
if (Properties::use_certificates_as_identification())
|
||||
client_id->set_type(ClientIdentification::DEVICE_CERTIFICATE);
|
||||
else
|
||||
client_id->set_type(ClientIdentification::KEYBOX);
|
||||
client_id->set_token(token_);
|
||||
|
||||
ClientIdentification_NameValue* client_info;
|
||||
CdmAppParameterMap::const_iterator iter;
|
||||
for (iter = app_parameters.begin(); iter != app_parameters.end(); iter++) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(iter->first);
|
||||
client_info->set_value(iter->second);
|
||||
}
|
||||
std::string value;
|
||||
if (Properties::GetCompanyName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kCompanyNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetModelName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kModelNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetArchitectureName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kArchitectureNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetDeviceName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetProductName(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kProductNameKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetBuildInfo(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kBuildInfoKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (session_->GetDeviceUniqueId(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (Properties::GetWVCdmVersion(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kWVCdmVersionKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
|
||||
ClientIdentification_ClientCapabilities* client_capabilities =
|
||||
client_id->mutable_client_capabilities();
|
||||
bool supports_usage_information;
|
||||
if (session_->UsageInformationSupport(&supports_usage_information)) {
|
||||
client_capabilities->set_session_token(supports_usage_information);
|
||||
}
|
||||
|
||||
client_capabilities->set_anti_rollback_usage_table(
|
||||
session_->IsAntiRollbackHwPresent());
|
||||
|
||||
uint32_t api_version = 0;
|
||||
if (session_->GetApiVersion(&api_version)) {
|
||||
client_capabilities->set_oem_crypto_api_version(api_version);
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability current_version, max_version;
|
||||
if (session_->GetHdcpCapabilities(¤t_version, &max_version)) {
|
||||
switch (max_version) {
|
||||
case HDCP_NONE:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
|
||||
break;
|
||||
case HDCP_V1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
|
||||
break;
|
||||
case HDCP_V2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
|
||||
break;
|
||||
case HDCP_V2_1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
|
||||
break;
|
||||
case HDCP_V2_2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
|
||||
break;
|
||||
case HDCP_NO_DIGITAL_OUTPUT:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NO_DIGITAL_OUTPUT);
|
||||
break;
|
||||
default:
|
||||
LOGW(
|
||||
"CdmLicense::PrepareClientId: unexpected HDCP max capability "
|
||||
"version %d",
|
||||
max_version);
|
||||
}
|
||||
}
|
||||
|
||||
if (encrypt) {
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
license_request->mutable_encrypted_client_id();
|
||||
DeviceCertificate service_certificate;
|
||||
|
||||
if (!service_certificate.ParseFromString(certificate)) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareClientId: unable to parse retrieved "
|
||||
"service certificate");
|
||||
return PARSE_SERVICE_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
if (service_certificate.type() !=
|
||||
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareClientId: retrieved certificate not of type"
|
||||
" service, %d",
|
||||
service_certificate.type());
|
||||
return SERVICE_CERTIFICATE_TYPE_ERROR;
|
||||
}
|
||||
encrypted_client_id->set_service_id(service_certificate.service_id());
|
||||
encrypted_client_id->set_service_certificate_serial_number(
|
||||
service_certificate.serial_number());
|
||||
|
||||
std::string iv(KEY_IV_SIZE, 0);
|
||||
std::string key(KEY_SIZE, 0);
|
||||
|
||||
if (!session_->GetRandom(key.size(), reinterpret_cast<uint8_t*>(&key[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
if (!session_->GetRandom(iv.size(), reinterpret_cast<uint8_t*>(&iv[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
std::string id, enc_id, enc_key;
|
||||
client_id->SerializeToString(&id);
|
||||
|
||||
AesCbcKey aes;
|
||||
if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR;
|
||||
if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR;
|
||||
|
||||
RsaPublicKey rsa;
|
||||
if (!rsa.Init(service_certificate.public_key()))
|
||||
return CLIENT_ID_RSA_INIT_ERROR;
|
||||
if (!rsa.Encrypt(key, &enc_key)) return CLIENT_ID_RSA_ENCRYPT_ERROR;
|
||||
|
||||
encrypted_client_id->set_encrypted_client_id_iv(iv);
|
||||
encrypted_client_id->set_encrypted_privacy_key(enc_key);
|
||||
encrypted_client_id->set_encrypted_client_id(enc_id);
|
||||
license_request->clear_client_id();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool CdmLicense::GetServiceCertificate(const CdmSessionId& session_id,
|
||||
std::string* service_certificate) {
|
||||
if (!Properties::GetServiceCertificate(session_id, service_certificate) ||
|
||||
service_certificate->empty())
|
||||
*service_certificate = service_certificate_;
|
||||
|
||||
if (service_certificate->size() > 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id,
|
||||
|
||||
@@ -78,6 +78,10 @@ message License {
|
||||
// Indicates that the license shall be sent for renewal when usage is
|
||||
// started.
|
||||
optional bool renew_with_usage = 11 [default = false];
|
||||
|
||||
// Indicates to client that license renewal and release requests ought to
|
||||
// include ClientIdentification (client_id).
|
||||
optional bool renew_with_client_id = 12 [default = false];
|
||||
}
|
||||
|
||||
message KeyContainer {
|
||||
@@ -167,6 +171,8 @@ message License {
|
||||
optional KeyType type = 4;
|
||||
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
|
||||
optional OutputProtection required_protection = 6;
|
||||
// NOTE: Use of requested_protection is not recommended as it is only
|
||||
// supported on a small number of platforms.
|
||||
optional OutputProtection requested_protection = 7;
|
||||
optional KeyControl key_control = 8;
|
||||
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
|
||||
@@ -174,7 +180,13 @@ message License {
|
||||
// content being decrypted/decoded falls within one of the specified ranges,
|
||||
// the optional required_protections may be applied. Otherwise an error will
|
||||
// be reported.
|
||||
// NOTE: Use of this feature is not recommended, as it is only supported on
|
||||
// a small number of platforms.
|
||||
repeated VideoResolutionConstraint video_resolution_constraints = 10;
|
||||
// Optional flag to indicate the key must only be used if the client
|
||||
// supports anti rollback of the user table. Content provider can query the
|
||||
// client capabilities to determine if the client support this feature.
|
||||
optional bool anti_rollback_usage_table = 11 [default = false];
|
||||
}
|
||||
|
||||
optional LicenseIdentification id = 1;
|
||||
@@ -286,36 +298,6 @@ message SignedMessage {
|
||||
optional RemoteAttestation remote_attestation = 5;
|
||||
}
|
||||
|
||||
// This message is used to pass optional data on initial license issuance.
|
||||
message SessionInit {
|
||||
optional bytes session_id = 1;
|
||||
optional bytes purchase_id = 2;
|
||||
// master_signing_key should be 128 bits in length.
|
||||
optional bytes master_signing_key = 3;
|
||||
// signing_key should be 512 bits in length to be split into two
|
||||
// (server || client) HMAC-SHA256 keys.
|
||||
optional bytes signing_key = 4;
|
||||
optional int64 license_start_time = 5;
|
||||
// Client token for the session. This session is for use by the license
|
||||
// provider, and is akin to a client cookie. It will be copied to
|
||||
// License::provider_client_token, and sent back by the client in
|
||||
// ClientIdentification::provider_client_token in all license requests
|
||||
// thereafter.
|
||||
optional bytes provider_client_token = 6;
|
||||
// Session token for the session. This token is for use by the license
|
||||
// provider, and is akin to a session cookie. It will be copied to
|
||||
// LicenseIdentfication::provider_session_token, and sent back in all
|
||||
// license renewal and release requests for the session thereafter.
|
||||
optional bytes provider_session_token = 7;
|
||||
}
|
||||
|
||||
// This message is used by the server to preserve and restore session state.
|
||||
message SessionState {
|
||||
optional LicenseIdentification license_id = 1;
|
||||
optional bytes signing_key = 2;
|
||||
optional uint32 keybox_system_id = 3;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// certificate_provisioning.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user