[ Merge of http://go/wvgerrit/168397 ] When CdmResponseType (enum) was transformed to CdmResponseType (struct), the test printers where not updated to print the result of failed comparisons. In addition, several logs statements were updated haphazardly, leaving inconsistencies and potential compiler-specific behavior. This CL replaces CdmResponseType std::string operator with a ToString() method. This is to make it consistent with Google's C++ style guide on conversion operators vs methods. The string conversion function is now defined in wv_cdm_types.cpp instead of inline in the header file. The PrintTo function has been implemented along with the other CDM test printers in test_printers.cpp. Bug: 273989359 Test: run_x86_64_tests Test: MediaDrmParameterizedTests on redfin Test: Forrest drm_compliance Change-Id: Ibfaa17029046b75b1c8c278f7bd7e04a24379848
394 lines
14 KiB
C++
394 lines
14 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 "client_identification.h"
|
|
|
|
#include <string>
|
|
|
|
#include "crypto_session.h"
|
|
#include "license_protocol.pb.h"
|
|
#include "log.h"
|
|
#include "properties.h"
|
|
#include "string_conversions.h"
|
|
#include "wv_cdm_constants.h"
|
|
|
|
namespace wvcdm {
|
|
namespace {
|
|
const std::string kKeyCompanyName = "company_name";
|
|
const std::string kKeyModelName = "model_name";
|
|
const std::string kKeyModelYear = "model_year";
|
|
const std::string kKeyArchitectureName = "architecture_name";
|
|
const std::string kKeyDeviceName = "device_name";
|
|
const std::string kKeyProductName = "product_name";
|
|
const std::string kKeyBuildInfo = "build_info";
|
|
const std::string kKeyWvCdmVersion = "widevine_cdm_version";
|
|
const std::string kKeyOemCryptoSecurityPatchLevel =
|
|
"oem_crypto_security_patch_level";
|
|
const std::string kKeyOemCryptoBuildInformation =
|
|
"oem_crypto_build_information";
|
|
|
|
// These client identification keys are used by the CDM for relaying
|
|
// important device information that cannot be overwritten by the app.
|
|
const std::array<std::string, 10> kReservedProperties = {
|
|
kKeyCompanyName,
|
|
kKeyModelName,
|
|
kKeyModelYear,
|
|
kKeyArchitectureName,
|
|
kKeyDeviceName,
|
|
kKeyProductName,
|
|
kKeyBuildInfo,
|
|
kKeyWvCdmVersion,
|
|
kKeyOemCryptoSecurityPatchLevel,
|
|
kKeyOemCryptoBuildInformation,
|
|
// TODO(b/148813171,b/142280599): include "origin" and "application_name"
|
|
// to this list once collection of this information has been moved
|
|
// to the core CDM.
|
|
};
|
|
|
|
// Checks if the client-provided |prop_name| is reserved for CDM device
|
|
// identification with the license server. Property keys which are
|
|
// reserved should be dropped from the request.
|
|
bool IsPropertyKeyReserved(const std::string& prop_name) {
|
|
for (const std::string& reserved_prop_name : kReservedProperties) {
|
|
if (prop_name == reserved_prop_name) return true;
|
|
}
|
|
return false;
|
|
}
|
|
} // namespace
|
|
|
|
// Protobuf generated classes.
|
|
using ClientCapabilities =
|
|
video_widevine::ClientIdentification::ClientCapabilities;
|
|
using AnalogOutputCapabilities = ClientCapabilities::AnalogOutputCapabilities;
|
|
using video_widevine::ClientIdentification_NameValue;
|
|
using video_widevine::EncryptedClientIdentification;
|
|
using video_widevine::ProvisioningOptions;
|
|
using video_widevine::ProvisioningRequest;
|
|
using video_widevine::ProvisioningResponse;
|
|
using video_widevine::SignedProvisioningMessage;
|
|
|
|
CdmResponseType ClientIdentification::InitForProvisioningRequest(
|
|
const std::string& client_token, CryptoSession* crypto_session) {
|
|
if (crypto_session == nullptr) {
|
|
LOGE("Crypto session not provided");
|
|
return CdmResponseType(PARAMETER_NULL);
|
|
}
|
|
is_license_request_ = false;
|
|
client_token_ = client_token;
|
|
crypto_session_ = crypto_session;
|
|
return CdmResponseType(NO_ERROR);
|
|
}
|
|
|
|
CdmResponseType ClientIdentification::InitForLicenseRequest(
|
|
const std::string& client_token, CryptoSession* crypto_session) {
|
|
if (crypto_session == nullptr) {
|
|
LOGE("Crypto session not provided");
|
|
return CdmResponseType(PARAMETER_NULL);
|
|
}
|
|
if (client_token.empty()) {
|
|
LOGE("Client token is empty");
|
|
return CdmResponseType(PARAMETER_NULL);
|
|
}
|
|
is_license_request_ = true;
|
|
client_token_ = client_token;
|
|
crypto_session_ = crypto_session;
|
|
return CdmResponseType(NO_ERROR);
|
|
}
|
|
|
|
CdmResponseType ClientIdentification::InitForOtaKeyboxProvisioning(
|
|
CryptoSession* crypto_session) {
|
|
if (crypto_session == nullptr) {
|
|
LOGE("Crypto session not provided");
|
|
return CdmResponseType(PARAMETER_NULL);
|
|
}
|
|
is_okp_request_ = true;
|
|
crypto_session_ = crypto_session;
|
|
return CdmResponseType(NO_ERROR);
|
|
}
|
|
|
|
/*
|
|
* Return the ClientIdentification message token type for provisioning request.
|
|
* NOTE: a DRM Cert should never be presented to the provisioning server.
|
|
*/
|
|
CdmResponseType ClientIdentification::Prepare(
|
|
const CdmAppParameterMap& app_parameters,
|
|
const std::string& provider_client_token,
|
|
video_widevine::ClientIdentification* client_id) {
|
|
if (is_license_request_) {
|
|
client_id->set_type(
|
|
video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE);
|
|
client_id->set_token(client_token_);
|
|
} else if (!client_token_.empty()) {
|
|
// A token has been provided via InitForProvisioningRequest. This can only
|
|
// happen in provisioning 4 (second stage) where an OEM cert is provided.
|
|
client_id->set_type(
|
|
video_widevine::ClientIdentification::OEM_DEVICE_CERTIFICATE);
|
|
client_id->set_token(client_token_);
|
|
} else if (!is_okp_request_) {
|
|
// An OTA Keybox Provisioning request does not have a client id.
|
|
// Otherwise this is a regular provisioning request.
|
|
video_widevine::ClientIdentification::TokenType token_type;
|
|
if (!GetProvisioningTokenType(&token_type)) {
|
|
LOGE("Failed to get provisioning token type");
|
|
return CdmResponseType(CLIENT_IDENTIFICATION_TOKEN_ERROR_1);
|
|
}
|
|
client_id->set_type(token_type);
|
|
|
|
std::string token;
|
|
std::string additional_token;
|
|
CdmResponseType status =
|
|
crypto_session_->GetProvisioningToken(&token, &additional_token);
|
|
if (status != NO_ERROR) {
|
|
LOGE("Failed to get provisioning token: status = %d", status.ToInt());
|
|
return status;
|
|
}
|
|
client_id->set_token(token);
|
|
if (!additional_token.empty()) {
|
|
client_id->mutable_device_credentials()->set_token(additional_token);
|
|
}
|
|
}
|
|
|
|
ClientIdentification_NameValue* client_info;
|
|
if (is_license_request_) {
|
|
CdmAppParameterMap::const_iterator iter;
|
|
for (iter = app_parameters.begin(); iter != app_parameters.end(); ++iter) {
|
|
if (IsPropertyKeyReserved(iter->first)) {
|
|
LOGD("Discarding client property: name = \"%s\", value = \"%s\"",
|
|
iter->first.c_str(), iter->second.c_str());
|
|
continue;
|
|
}
|
|
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(kKeyCompanyName);
|
|
client_info->set_value(value);
|
|
}
|
|
if (Properties::GetModelName(&value)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyModelName);
|
|
client_info->set_value(value);
|
|
}
|
|
if (Properties::GetModelYear(&value)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyModelYear);
|
|
client_info->set_value(value);
|
|
}
|
|
if (Properties::GetArchitectureName(&value)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyArchitectureName);
|
|
client_info->set_value(value);
|
|
}
|
|
if (Properties::GetDeviceName(&value)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyDeviceName);
|
|
client_info->set_value(value);
|
|
}
|
|
if (Properties::GetProductName(&value)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyProductName);
|
|
client_info->set_value(value);
|
|
}
|
|
if (Properties::GetBuildInfo(&value)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyBuildInfo);
|
|
client_info->set_value(value);
|
|
}
|
|
if (Properties::GetWVCdmVersion(&value)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyWvCdmVersion);
|
|
client_info->set_value(value);
|
|
}
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyOemCryptoSecurityPatchLevel);
|
|
client_info->set_value(
|
|
std::to_string((uint32_t)crypto_session_->GetSecurityPatchLevel()));
|
|
std::string oec_build_info;
|
|
if (crypto_session_->GetBuildInformation(&oec_build_info)) {
|
|
client_info = client_id->add_client_info();
|
|
client_info->set_name(kKeyOemCryptoBuildInformation);
|
|
client_info->set_value(oec_build_info);
|
|
}
|
|
if (!provider_client_token.empty()) {
|
|
client_id->set_provider_client_token(provider_client_token);
|
|
}
|
|
|
|
if (is_okp_request_) {
|
|
// Capabilities is not important for OTA keybox provisionining.
|
|
return CdmResponseType(NO_ERROR);
|
|
}
|
|
|
|
ClientCapabilities* client_capabilities =
|
|
client_id->mutable_client_capabilities();
|
|
|
|
client_capabilities->set_client_token(true);
|
|
|
|
if (is_license_request_) {
|
|
bool supports_usage_table;
|
|
if (crypto_session_->HasUsageTableSupport(&supports_usage_table)) {
|
|
client_capabilities->set_session_token(supports_usage_table);
|
|
}
|
|
|
|
client_capabilities->set_anti_rollback_usage_table(
|
|
crypto_session_->IsAntiRollbackHwPresent());
|
|
}
|
|
|
|
uint32_t api_version = 0;
|
|
if (crypto_session_->GetApiVersion(&api_version)) {
|
|
client_capabilities->set_oem_crypto_api_version(api_version);
|
|
}
|
|
|
|
if (is_license_request_) {
|
|
CryptoSession::HdcpCapability current_version, max_version;
|
|
if (crypto_session_->GetHdcpCapabilities(¤t_version, &max_version) ==
|
|
NO_ERROR) {
|
|
switch (max_version) {
|
|
case HDCP_NONE:
|
|
client_capabilities->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_NONE);
|
|
break;
|
|
case HDCP_V1:
|
|
client_capabilities->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_V1);
|
|
break;
|
|
case HDCP_V2:
|
|
client_capabilities->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_V2);
|
|
break;
|
|
case HDCP_V2_1:
|
|
client_capabilities->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_V2_1);
|
|
break;
|
|
case HDCP_V2_2:
|
|
client_capabilities->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_V2_2);
|
|
break;
|
|
case HDCP_V2_3:
|
|
client_capabilities->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_V2_3);
|
|
break;
|
|
case HDCP_NO_DIGITAL_OUTPUT:
|
|
client_capabilities->set_max_hdcp_version(
|
|
ClientCapabilities::HDCP_NO_DIGITAL_OUTPUT);
|
|
break;
|
|
default:
|
|
LOGW("Unexpected HDCP max capability version: max_version = %d",
|
|
static_cast<int>(max_version));
|
|
}
|
|
}
|
|
}
|
|
|
|
CryptoSession::SupportedCertificateTypes supported_certs;
|
|
if (crypto_session_->GetSupportedCertificateTypes(&supported_certs)) {
|
|
if (supported_certs.rsa_2048_bit) {
|
|
client_capabilities->add_supported_certificate_key_type(
|
|
ClientCapabilities::RSA_2048);
|
|
}
|
|
if (supported_certs.rsa_3072_bit) {
|
|
client_capabilities->add_supported_certificate_key_type(
|
|
ClientCapabilities::RSA_3072);
|
|
}
|
|
if (supported_certs.ecc_secp256r1) {
|
|
client_capabilities->add_supported_certificate_key_type(
|
|
ClientCapabilities::ECC_SECP256R1);
|
|
}
|
|
if (supported_certs.ecc_secp384r1) {
|
|
client_capabilities->add_supported_certificate_key_type(
|
|
ClientCapabilities::ECC_SECP384R1);
|
|
}
|
|
if (supported_certs.ecc_secp521r1) {
|
|
client_capabilities->add_supported_certificate_key_type(
|
|
ClientCapabilities::ECC_SECP521R1);
|
|
}
|
|
}
|
|
|
|
if (is_license_request_) {
|
|
client_capabilities->set_can_update_srm(false);
|
|
uint16_t srm_version;
|
|
if (crypto_session_->GetSrmVersion(&srm_version) == NO_ERROR)
|
|
client_capabilities->set_srm_version(srm_version);
|
|
}
|
|
bool can_support_output;
|
|
bool can_disable_output;
|
|
bool can_support_cgms_a;
|
|
if (crypto_session_->GetAnalogOutputCapabilities(
|
|
&can_support_output, &can_disable_output, &can_support_cgms_a)) {
|
|
AnalogOutputCapabilities capabilities =
|
|
ClientCapabilities::ANALOG_OUTPUT_NONE;
|
|
|
|
if (can_support_output) {
|
|
if (can_support_cgms_a) {
|
|
capabilities = ClientCapabilities::ANALOG_OUTPUT_SUPPORTS_CGMS_A;
|
|
} else {
|
|
capabilities = ClientCapabilities::ANALOG_OUTPUT_SUPPORTED;
|
|
}
|
|
}
|
|
client_capabilities->set_analog_output_capabilities(capabilities);
|
|
client_capabilities->set_can_disable_analog_output(can_disable_output);
|
|
} else {
|
|
client_capabilities->set_analog_output_capabilities(
|
|
ClientCapabilities::ANALOG_OUTPUT_UNKNOWN);
|
|
}
|
|
|
|
if (api_version >= OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER) {
|
|
uint32_t tier;
|
|
if (crypto_session_->GetResourceRatingTier(&tier)) {
|
|
client_capabilities->set_resource_rating_tier(tier);
|
|
}
|
|
}
|
|
|
|
if (is_license_request_) {
|
|
CdmWatermarkingSupport support;
|
|
if (!crypto_session_->GetWatermarkingSupport(&support)) {
|
|
// Assume not supported.
|
|
support = kWatermarkingNotSupported;
|
|
}
|
|
switch (support) {
|
|
case kWatermarkingNotSupported:
|
|
client_capabilities->set_watermarking_support(
|
|
ClientCapabilities::WATERMARKING_NOT_SUPPORTED);
|
|
break;
|
|
case kWatermarkingConfigurable:
|
|
client_capabilities->set_watermarking_support(
|
|
ClientCapabilities::WATERMARKING_CONFIGURABLE);
|
|
break;
|
|
case kWatermarkingAlwaysOn:
|
|
client_capabilities->set_watermarking_support(
|
|
ClientCapabilities::WATERMARKING_ALWAYS_ON);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return CdmResponseType(NO_ERROR);
|
|
}
|
|
|
|
bool ClientIdentification::GetProvisioningTokenType(
|
|
video_widevine::ClientIdentification::TokenType* token_type) {
|
|
CdmClientTokenType token = crypto_session_->GetPreProvisionTokenType();
|
|
switch (token) {
|
|
case kClientTokenKeybox:
|
|
*token_type = video_widevine::ClientIdentification::KEYBOX;
|
|
return true;
|
|
case kClientTokenOemCert:
|
|
*token_type =
|
|
video_widevine::ClientIdentification::OEM_DEVICE_CERTIFICATE;
|
|
return true;
|
|
case kClientTokenBootCertChain:
|
|
*token_type =
|
|
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
|
|
return true;
|
|
case kClientTokenDrmCert:
|
|
default:
|
|
// shouldn't happen
|
|
LOGE("Unexpected provisioning type: %d", static_cast<int>(token));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} // namespace wvcdm
|