[ Merge of http://go/wvgerrit/164077 ] This CL makes major changes to the names of variables and types that are related to the usage table, header, entries, entry indexes, and other related data. The renaming followed these rules: 1) "Usage table header" will exclusively refer to the header blob that is OEMCrypto specific. The CDM class "UsageTableHeader" is the CDM-layer's abstraction around the "usage table" concept. The name has been updated to reflect that. 2) The "Cdm" prefix is only used for the CDM-specific data types for the usage table and entry info. It has been removed from OEMCrypto-specific types. - UsageTableHeader -> CdmUsageTable - CdmUsageTableHeader -> UsageTableHeader - CdmUsageEntry -> UsageEntry 3) The "usage_" prefix has been removed from variables when the usage table or usage entries are the subject of the function or class. 4) UsageEntryIndex is the type for entry indexes, instead of directly using uint32_t. This matches how we wrap other types in "wv_cdm_types.h" 5) Changed entry "number" to entry "index". 6) Vectors of elements have been renamed to be either pluralized or have a suffix "_list". 7) "Usage info" was occasionally being used to refer to the usage table or entries generally, rather than specifically secure-stop. - CryptoSession::HasUsageInfoSupport() -> HasUsageTableSupport() The most major change is that the files "usage_table_header*" have been renamed to be "cdm_usage_table*". Bug: 242914226 Test: run_x86_64_tests and request_license_test Change-Id: Iee98446b71f4f2934d3c9e0fb949eb05b84d1f8c
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.Enum());
|
|
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
|