Files
android/libwvdrmengine/cdm/core/src/client_identification.cpp
Geoffrey Alexander e9e4b76817 Rename and clarify Drm Reprovisioning token types
[ Merge of http://go/wvgerrit/194374 ]

Renames and adds clarifying comments to Drm Reprovisioning token types.
All provisioning methods can be forced to reprovision by apps which can
cause reprovisioning to be an overloaded term. Renaming token types
used by the Drm Reprovisioning method to more clearly state they are
used for Drm Certificate Reprovisioning should help to avoid confusion.
This change also adds comments to help clarify when and where Drm
Reprovisioning is used as a provisioning type.

Bug: b/305093063
Test: WVTS

* Added dependency to dynamic perf tests to fix missing header build
  error.

Change-Id: I158eb5672ad9e655a60bc68e0f4f2f7a0d464b4e
2024-03-20 11:15:58 -07:00

420 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;
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 unless
* DRM re-provisioning is being used.
*/
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;
// Include app parameters for license and provisioning requests
if (!is_okp_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(&current_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;
}
}
if (api_version >=
OEM_CRYPTO_API_VERSION_SUPPORTS_INITIAL_RENEWAL_DELAY_BASE) {
client_capabilities->set_initial_renewal_delay_base(true);
}
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: {
OEMCrypto_BCCType bcc_type;
const CdmResponseType result =
crypto_session_->GetProvisioning40TokenType(&bcc_type);
if (result == NOT_IMPLEMENTED_ERROR) {
// Default to CBOR BCC for OEMCrypto that doesn't support GetBCCType().
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
return true;
}
if (result != NO_ERROR) return false;
if (bcc_type == OEMCrypto_CBOR) {
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN;
} else if (bcc_type == OEMCrypto_X509) {
*token_type =
video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN_X509;
} else {
// shouldn't happen
LOGE("Unexpected BCC type: %d", static_cast<int>(bcc_type));
return false;
}
return true;
}
case kClientTokenDrmCertificateReprovisioning:
*token_type =
video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE;
return true;
case kClientTokenDrmCert:
default:
// shouldn't happen
LOGE("Unexpected provisioning type: %d", static_cast<int>(token));
return false;
}
}
} // namespace wvcdm