Files
android/libwvdrmengine/cdm/core/src/client_identification.cpp
Alex Dale 78f4bca3a9 Restrict reserved Client ID fields.
[ Merge of http://go/wvgerrit/108904 ]

Client ID name-value fields in the license request share the same
namespace with app parameters and WV standard device information.  As
a result, it was possible for applications to provide parameters that
could potentially fool the license server with spoof values.

This CL restricts the use of the fields that are common across both the
Android CDM and CE CDM.  Currently, Android specific fields are
restricted by the MediaDrmPlugin layer, and there are no CE CDM
specific fields currently defined.

The non-HIDL DRM plugin does not restrict these fields; however, it
will be removed in S.

Bug: 171723566
Test: Android integration test
Change-Id: I5ad9ead73c5aff712dff8133953de5ddc3296452
2020-12-04 12:34:01 -08:00

336 lines
12 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 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, 9> kReservedProperties = {
kKeyCompanyName,
kKeyModelName,
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 video_widevine::ClientIdentification_ClientCapabilities;
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::Init(CryptoSession* crypto_session) {
if (crypto_session == nullptr) {
LOGE("Crypto session not provided");
return PARAMETER_NULL;
}
is_license_request_ = false;
crypto_session_ = crypto_session;
return NO_ERROR;
}
CdmResponseType ClientIdentification::Init(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;
}
/*
* 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 {
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;
CdmResponseType status = crypto_session_->GetProvisioningToken(&token);
if (status != NO_ERROR) {
LOGE("Failed to get provisioning token: status = %d",
static_cast<int>(status));
return status;
}
client_id->set_token(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::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);
}
ClientIdentification_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_->UsageInformationSupport(&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(
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
break;
case HDCP_V1:
client_capabilities->set_max_hdcp_version(
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
break;
case HDCP_V2:
client_capabilities->set_max_hdcp_version(
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
break;
case HDCP_V2_1:
client_capabilities->set_max_hdcp_version(
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
break;
case HDCP_V2_2:
client_capabilities->set_max_hdcp_version(
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
break;
case HDCP_V2_3:
client_capabilities->set_max_hdcp_version(
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_3);
break;
case HDCP_NO_DIGITAL_OUTPUT:
client_capabilities->set_max_hdcp_version(
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_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(
video_widevine::
ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_2048);
}
if (supported_certs.rsa_3072_bit) {
client_capabilities->add_supported_certificate_key_type(
video_widevine::
ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_3072);
}
}
if (is_license_request_) {
client_capabilities->set_can_update_srm(
crypto_session_->IsSrmUpdateSupported());
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)) {
video_widevine::ClientIdentification_ClientCapabilities_AnalogOutputCapabilities
capabilities = video_widevine::
ClientIdentification_ClientCapabilities_AnalogOutputCapabilities_ANALOG_OUTPUT_NONE;
if (can_support_output) {
if (can_support_cgms_a) {
capabilities = video_widevine::
ClientIdentification_ClientCapabilities_AnalogOutputCapabilities_ANALOG_OUTPUT_SUPPORTS_CGMS_A;
} else {
capabilities = video_widevine::
ClientIdentification_ClientCapabilities_AnalogOutputCapabilities_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(
video_widevine::
ClientIdentification_ClientCapabilities_AnalogOutputCapabilities_ANALOG_OUTPUT_UNKNOWN);
}
uint32_t version, tier;
if (crypto_session_->GetApiVersion(&version)) {
if (version >= OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER) {
if (crypto_session_->GetResourceRatingTier(&tier)) {
client_capabilities->set_resource_rating_tier(tier);
}
}
}
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 kClientTokenDrmCert:
default:
// shouldn't happen
LOGE("Unexpected provisioning type: %d", static_cast<int>(token));
return false;
}
}
} // namespace wvcdm