Files
android/libwvdrmengine/cdm/core/src/client_identification.cpp
Alex Dale 97f3544866 Added CDM support for Watermarking reporting.
[ Merge of http://go/wvgerrit/148552 ]

Extended the CDM layer to report OEMCrypto's watermarking support.
The reporting of watermarking comes in three (3) mechanisms:
1) ClientCapabilities in license requests
2) CryptoSession metrics when queried to OEMCrypto
3) String property query by apps

If OEMCrypto implementents OEMCrypto_GetWatermarkingSupport(), then
the reported watermarking support by the CDM will match that of
OEMCrypto.

If OEMCrypto does not implement OEMCrypto_GetWatermarkingSupport()
or an error occurs, it is assumed that OEMCrypto does not support
watermarking, and the CDM will report "Not Supported".

Bug: 226443788
Test: run_x86_64_tests request_license_test and license_unittest
Change-Id: Id929a356c395e6bcf45d371ee6887eec40d35329
2022-04-01 14:24:44 -07:00

395 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 PARAMETER_NULL;
}
is_license_request_ = false;
client_token_ = client_token;
crypto_session_ = crypto_session;
return NO_ERROR;
}
CdmResponseType ClientIdentification::InitForLicenseRequest(
const std::string& client_token, CryptoSession* crypto_session) {
if (crypto_session == nullptr) {
LOGE("Crypto session not provided");
return PARAMETER_NULL;
}
if (client_token.empty()) {
LOGE("Client token is empty");
return PARAMETER_NULL;
}
is_license_request_ = true;
client_token_ = client_token;
crypto_session_ = crypto_session;
return NO_ERROR;
}
CdmResponseType ClientIdentification::InitForOtaKeyboxProvisioning(
CryptoSession* crypto_session) {
if (crypto_session == nullptr) {
LOGE("Crypto session not provided");
return PARAMETER_NULL;
}
is_okp_request_ = true;
crypto_session_ = crypto_session;
return 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 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",
static_cast<int>(status));
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 NO_ERROR;
}
ClientCapabilities* client_capabilities =
client_id->mutable_client_capabilities();
client_capabilities->set_client_token(true);
if (is_license_request_) {
bool supports_usage_information;
if (crypto_session_->HasUsageInfoSupport(&supports_usage_information)) {
client_capabilities->set_session_token(supports_usage_information);
}
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;
}
}
return 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