Files
android/libwvdrmengine/cdm/core/src/client_identification.cpp
Rahul Frias 6d448bdd48 Added an OTA keybox provisioner.
[ Merge of http://go/wvgerrit/133729 ]

The OtaKeyboxProvisioner is a system-wide provisioner for sharing the
provisioning workflow between CDM engines.

Bug: 189232882
Test: GtsMediaTestCases
Change-Id: I873af3087cc05e1831bdd1d2c14fb002b73e6902

Added keybox provisioning proto fields.

[ Merge of http://go/wvgerrit/133730 and http://go/ag/15113032 ]

This CL copies over the required license_protocol.proto changes that
are required for OTA keybox provisioning.  These fields are defined in
the server-side certificate_provisioning.proto, defined in
http://cl/377533774.

Note, changes are slightly different from server proto due to the RVC
version of license_protocol.proto being out of date with SC and newer
changes.

Bug: 189232882
Test: run_x86_64_tests
Change-Id: I55fcf6a7ac2ba4b6026b9acc63e822ff33c431d9

Added OTA keybox provisioning device files.

[ Merge of http://go/wvgerrit/133743 and http://go/ag/15421141 ]

This change adds a new set of proto messages/fields the CDM's device
files for recording device and engine information around OTA keybox
provisioning (OKP).

To make cleanup and thread protection possible, there is a single file
which will contain all the information for the device as a whole and
each CDM engine tied to an app/origin.

Bug: 189232882
Test: Linux unit tests
Change-Id: Iaf80cd6342f32657e04416750d9b278d935821a5

Client ID for OKP requests.

[ Merge of http://go/wvgerrit/133744 and http://go/ag/15645331 ]

Extended the CDM ClientIdentification class to support a subset of
client info used for OKP requests.

Bug: 189232882
Test: Android unit tests
Change-Id: I6aafb4f2164efe69bc733ece0a912f0e91893b91
2021-10-11 17:39:21 -07:00

349 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 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::InitForProvisioning(
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::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 (!is_okp_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;
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);
}
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(
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)) {
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);
}
}
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