Add DRM reprovisioning request generation

[ Merge of http://go/wvgerrit/192010 ]

Updates the CDM to add support for DRM reprovisioning request creation.
- Load the baked-in certificate for use as the client token.
- Add functions to build and sign a drm reprovisioning request.
- Update the Rikers L3 OEMCrypto implementation to support signing
  provisioning requests and getting embedded certificate.
- Update client id token to handle DRM reprovisioning.
- Add OEMCrypto function to load the baked-in device certificate in
  Rikers CDMs and stubs for non-Rikers CDMs.
- Add dynamic adapter support for getting embedded device certificate
  only on L3.

Bug: 305093063
Test: WVTS
Change-Id: I9a0ecf95e27213b046f03baa0781fb164179323b
This commit is contained in:
Rahul Frias
2024-03-07 14:39:46 -08:00
parent 6499e7063d
commit af2ffca5fa
16 changed files with 164 additions and 7 deletions

View File

@@ -104,7 +104,8 @@ CdmResponseType ClientIdentification::InitForOtaKeyboxProvisioning(
/*
* Return the ClientIdentification message token type for provisioning request.
* NOTE: a DRM Cert should never be presented to the provisioning server.
* 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,
@@ -403,9 +404,11 @@ bool ClientIdentification::GetProvisioningTokenType(
}
return true;
}
case kClientTokenDrmCert:
// TODO: b/305093063 - Add token for DRM reprovisioning requests.
case kClientTokenDrmReprovisioning:
*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));

View File

@@ -81,6 +81,7 @@ extern "C" OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION session,
#endif
namespace wvcdm {
namespace {
using UsageTableLock = std::unique_lock<std::recursive_mutex>;
@@ -346,6 +347,9 @@ CdmResponseType CryptoSession::GetProvisioningMethod(
case OEMCrypto_BootCertificateChain:
type = kClientTokenBootCertChain;
break;
case OEMCrypto_DrmReprovisioning:
type = kClientTokenDrmReprovisioning;
break;
case OEMCrypto_ProvisioningError:
default:
if (static_cast<int>(method) == 0 && needs_keybox_provisioning_) {
@@ -662,6 +666,8 @@ CdmResponseType CryptoSession::GetProvisioningToken(
} else if (pre_provision_token_type_ == kClientTokenBootCertChain) {
status = GetBootCertificateChain(requested_security_level, token,
additional_token);
} else if (pre_provision_token_type_ == kClientTokenDrmReprovisioning) {
status = GetTokenFromEmbeddedCertificate(token);
}
metrics_->crypto_session_get_token_.Increment(status);
return status;
@@ -1253,6 +1259,7 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
}
OEMCryptoResult sts;
// TODO: b/305093063 - Refactor to a switch statement to improve readability
if (pre_provision_token_type_ == kClientTokenKeybox) {
should_specify_algorithm = false;
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
@@ -1268,6 +1275,10 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest(
should_specify_algorithm = true;
// Do nothing here. The key to signing the provisioning 4.0 request for each
// stage has been loaded already when it was generated by OEMCrypto.
} else if (pre_provision_token_type_ == kClientTokenDrmReprovisioning) {
should_specify_algorithm = false;
// Do nothing here. The baked-in certificate used as the token has already
// been loaded when the EncryptedClientId was filled in.
} else {
LOGE("Unknown method %d", pre_provision_token_type_);
return CdmResponseType(UNKNOWN_CLIENT_TOKEN_TYPE);
@@ -1439,6 +1450,59 @@ CdmResponseType CryptoSession::GetBootCertificateChain(
return CdmResponseType(NO_ERROR);
}
CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate(
std::string* token) {
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
RETURN_IF_NULL(token, PARAMETER_NULL);
CdmClientTokenType token_type = kClientTokenUninitialized;
const CdmResponseType sts =
GetProvisioningMethod(requested_security_level_, &token_type);
if (sts != NO_ERROR) {
LOGE("Failed to get token type");
return sts;
}
if (token_type != kClientTokenDrmReprovisioning) {
token->clear();
return CdmResponseType(NO_ERROR);
}
size_t certificate_length = CERTIFICATE_DATA_SIZE;
std::string embedded_certificate(certificate_length, '\0');
OEMCryptoResult status =
WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 1", [&] {
return OEMCrypto_GetEmbeddedDrmCertificate(
reinterpret_cast<uint8_t*>(&embedded_certificate.front()),
&certificate_length);
});
if (status == OEMCrypto_ERROR_SHORT_BUFFER) {
embedded_certificate.assign(certificate_length, '\0');
status =
WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 2", [&] {
return OEMCrypto_GetEmbeddedDrmCertificate(
reinterpret_cast<uint8_t*>(&embedded_certificate.front()),
&certificate_length);
});
}
if (status == OEMCrypto_SUCCESS) {
// TODO: b/312782308 - Store embedded certificate as SignedDrmCertificate
video_widevine_client::sdk::HashedFile hashed_file;
video_widevine_client::sdk::File file;
embedded_certificate.resize(certificate_length);
if (hashed_file.ParseFromString(embedded_certificate) &&
file.ParseFromString(hashed_file.file())) {
*token = file.device_certificate().certificate();
return CdmResponseType(NO_ERROR);
}
}
token->clear();
return MapOEMCryptoResult(status, GET_TOKEN_FROM_EMBEDDED_CERT_ERROR,
"GetTokenFromEmbeddedCertificate");
}
CdmResponseType CryptoSession::GetDeviceInformation(
RequestedSecurityLevel requested_security_level, std::string* device_info) {
RETURN_IF_NULL(device_info, PARAMETER_NULL);

View File

@@ -3191,3 +3191,16 @@ extern "C" OEMCrypto_WatermarkingSupport OEMCrypto_GetWatermarkingSupport(
extern "C" OEMCryptoResult OEMCrypto_ProductionReady(void) {
return wvcdm::OEMCrypto_ProductionReady(kLevelDefault);
}
extern "C" OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate(
uint8_t* public_cert, size_t* public_cert_length) {
if (!gAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
const FunctionPointers* fcn = gAdapter->GetFunctionPointers(kLevelDefault);
if (!fcn) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
// Only supported for internal L3. Ignore L1 calls.
if (fcn->security_level != wvcdm::kSecurityLevelL3) {
if (public_cert_length) *public_cert_length = 0;
return OEMCrypto_SUCCESS;
}
return Level3_GetEmbeddedDrmCertificate(public_cert, public_cert_length);
}

View File

@@ -0,0 +1,12 @@
// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
#include "OEMCryptoCENC.h"
OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate(
uint8_t* public_cert, size_t* public_cert_length) {
(void)public_cert;
(void)public_cert_length;
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -59,7 +59,7 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) {
bool success = false;
switch (type) {
case kClientTokenDrmCert:
// TODO: b/305093063 - Extract system id when handling DRM reprovisioning.
// TODO: b/309675153 - Extract system id when using DRM reprovisioning.
case kClientTokenDrmReprovisioning:
LOGW(
"Cannot get a system ID from a DRM certificate, "

View File

@@ -749,6 +749,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) {
return "GET_TOKEN_FROM_KEYBOX_ERROR";
case KEYBOX_TOKEN_TOO_SHORT:
return "KEYBOX_TOKEN_TOO_SHORT";
case GET_TOKEN_FROM_EMBEDDED_CERT_ERROR:
return "GET_TOKEN_FROM_EMBEDDED_CERT_ERROR";
case EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR:
return "EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR";
case RSA_SIGNATURE_GENERATION_ERROR: