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:
Rahul Frias
2015-05-04 11:39:57 -07:00
parent 2229e51c18
commit 20fc54e384
14 changed files with 1252 additions and 524 deletions

View File

@@ -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(&current_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(&current_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,