Merge "Add DRM reprovisioning request generation" into main
This commit is contained in:
@@ -112,6 +112,9 @@ class CryptoSession {
|
||||
// Only valid for OEM certificate-based based devices.
|
||||
virtual CdmResponseType GetTokenFromOemCert(
|
||||
RequestedSecurityLevel requested_security_level, std::string* oem_cert);
|
||||
// Retrieves the embedded public certificate from OEMCrypto.
|
||||
// Only valid for L3 devices with embedded (baked-in) certificates.
|
||||
virtual CdmResponseType GetTokenFromEmbeddedCertificate(std::string* token);
|
||||
|
||||
// The overloaded methods with |requested_level| may be called
|
||||
// without a preceding call to Open. The other method must call Open first.
|
||||
|
||||
@@ -463,6 +463,7 @@ enum CdmResponseEnum : int32_t {
|
||||
SESSION_NOT_FOUND_24 = 397,
|
||||
GET_DEVICE_INFORMATION_ERROR = 398,
|
||||
GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR = 399,
|
||||
GET_TOKEN_FROM_EMBEDDED_CERT_ERROR = 400,
|
||||
// Don't forget to add new values to
|
||||
// * core/src/wv_cdm_types.cpp
|
||||
// * android/include/mapErrors-inl.h
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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, "
|
||||
|
||||
@@ -751,6 +751,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:
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "license_holder.h"
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
|
||||
#include "oec_device_features.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "test_base.h"
|
||||
@@ -152,6 +154,12 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest {
|
||||
WvCdmEngineTest() {}
|
||||
|
||||
void SetUp() override {
|
||||
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
|
||||
if (wvoec::global_features.provisioning_method ==
|
||||
OEMCrypto_DrmReprovisioning) {
|
||||
GTEST_SKIP()
|
||||
<< "Skipping until Drm Reprovisioning server support is implemented.";
|
||||
}
|
||||
WvCdmEnginePreProvTest::SetUp();
|
||||
session_opened_ = false;
|
||||
WvCdmEnginePreProvTest::OpenSession();
|
||||
|
||||
@@ -481,8 +481,8 @@ TEST_P(CertificateProvisioningTest, ProvisioningResponseSuccess) {
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
CertificateProvisioningTests, CertificateProvisioningTest,
|
||||
// TODO: b/305093063 - Add Drm Reprovisioning to Values once implemented.
|
||||
testing::Values(kClientTokenKeybox, kClientTokenOemCert),
|
||||
testing::Values(kClientTokenKeybox, kClientTokenOemCert,
|
||||
kClientTokenDrmReprovisioning),
|
||||
[](const testing::TestParamInfo<CertificateProvisioningTest::ParamType>&
|
||||
param_type) {
|
||||
return CdmClientTokenTypeToString(param_type.param);
|
||||
|
||||
@@ -99,6 +99,9 @@ TEST_F(CryptoSessionMetricsTest, OpenSessionValidMetrics) {
|
||||
} else if (token_type == kClientTokenBootCertChain) {
|
||||
EXPECT_EQ(OEMCrypto_BootCertificateChain,
|
||||
metrics_proto.oemcrypto_provisioning_method().int_value());
|
||||
} else if (token_type == kClientTokenDrmReprovisioning) {
|
||||
EXPECT_EQ(OEMCrypto_DrmReprovisioning,
|
||||
metrics_proto.oemcrypto_provisioning_method().int_value());
|
||||
} else {
|
||||
FAIL() << "Unexpected token type: " << token_type;
|
||||
}
|
||||
@@ -140,6 +143,9 @@ TEST_F(CryptoSessionMetricsTest, GetProvisioningTokenValidMetrics) {
|
||||
} else if (token_type == kClientTokenBootCertChain) {
|
||||
EXPECT_EQ(OEMCrypto_BootCertificateChain,
|
||||
metrics_proto.oemcrypto_provisioning_method().int_value());
|
||||
} else if (token_type == kClientTokenDrmReprovisioning) {
|
||||
EXPECT_EQ(OEMCrypto_DrmReprovisioning,
|
||||
metrics_proto.oemcrypto_provisioning_method().int_value());
|
||||
} else {
|
||||
ASSERT_EQ(0, metrics_proto.crypto_session_get_token().size());
|
||||
}
|
||||
|
||||
@@ -40,6 +40,12 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine {
|
||||
if (!wvoec::global_features.generic_crypto) {
|
||||
GTEST_SKIP() << "Test for devices with generic crypto API only";
|
||||
}
|
||||
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
|
||||
if (wvoec::global_features.provisioning_method ==
|
||||
OEMCrypto_DrmReprovisioning) {
|
||||
GTEST_SKIP()
|
||||
<< "Skipping until Drm Reprovisioning server support is implemented.";
|
||||
}
|
||||
EnsureProvisioned();
|
||||
ASSERT_NO_FATAL_FAILURE(holder_.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder_.FetchLicense());
|
||||
@@ -68,7 +74,11 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine {
|
||||
iv_ = std::string(iv_vector_.begin(), iv_vector_.end());
|
||||
}
|
||||
|
||||
void TearDown() override { holder_.CloseSession(); }
|
||||
void TearDown() override {
|
||||
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
|
||||
if (IsSkipped()) return;
|
||||
holder_.CloseSession();
|
||||
}
|
||||
|
||||
protected:
|
||||
LicenseHolder holder_;
|
||||
|
||||
@@ -374,6 +374,12 @@ void WvCdmTestBase::Provision() {
|
||||
}
|
||||
|
||||
void WvCdmTestBase::EnsureProvisioned() {
|
||||
// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented.
|
||||
if (wvoec::global_features.provisioning_method ==
|
||||
OEMCrypto_DrmReprovisioning) {
|
||||
GTEST_SKIP()
|
||||
<< "Skipping until Drm Reprovisioning server support is implemented.";
|
||||
}
|
||||
CdmSessionId session_id;
|
||||
std::unique_ptr<wvutil::FileSystem> file_system(CreateTestFileSystem());
|
||||
// OpenSession will check if a DRM certificate exists, while
|
||||
|
||||
@@ -70,6 +70,11 @@ OEMCryptoResult Level3_QueryKeyControl(OEMCrypto_SESSION session,
|
||||
key_control_block_length);
|
||||
}
|
||||
|
||||
OEMCryptoResult Level3_GetEmbeddedDrmCertificate(
|
||||
uint8_t* public_cert, size_t* public_cert_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
OEMCryptoResult Level3_DecryptCENC_V17(
|
||||
OEMCrypto_SESSION session, const OEMCrypto_SampleDescription* samples,
|
||||
size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) {
|
||||
|
||||
@@ -521,6 +521,8 @@ typedef enum OEMCrypto_ProvisioningMethod {
|
||||
OEMCrypto_OEMCertificate = 3,
|
||||
// Device has Boot Certificate Chain (BCC).
|
||||
OEMCrypto_BootCertificateChain = 4,
|
||||
// Device has baked in DRM certificate with reprovisioning (level 3 only).
|
||||
OEMCrypto_DrmReprovisioning = 5
|
||||
} OEMCrypto_ProvisioningMethod;
|
||||
|
||||
/**
|
||||
@@ -740,6 +742,7 @@ typedef enum OEMCrypto_SignatureHashAlgorithm {
|
||||
#define OEMCrypto_GetUsageEntryInfo _oecc148
|
||||
#define OEMCrypto_GetBCCType _oecc149
|
||||
#define OEMCrypto_LoadRelease _oecc150
|
||||
#define OEMCrypto_GetEmbeddedDrmCertificate _oecc151
|
||||
// clang-format on
|
||||
|
||||
/// @addtogroup initcontrol
|
||||
@@ -6108,6 +6111,22 @@ OEMCryptoResult OEMCrypto_LoadProvisioning_V18(
|
||||
|
||||
/****************************************************************************/
|
||||
/****************************************************************************/
|
||||
/* The following functions are used by internal L3 CDMs and are not required by
|
||||
* other CDM implementations.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the embedded Drm Certificate used by internal L3 CDMs.
|
||||
*
|
||||
* @param[out] public_cert where the certificate is stored.
|
||||
* @param[in,out] public_cert_length the length, in bytes, of the certificate.
|
||||
*
|
||||
* @retval OEMCrypto_SUCCESS on success
|
||||
* @retval OEMCrypto_ERROR_SHORT_BUFFER if public_cert_length is too small.
|
||||
* @retval OEMCrypto_ERROR_NOT_IMPLEMENTED
|
||||
*/
|
||||
OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate(uint8_t* public_cert,
|
||||
size_t* public_cert_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
#define Level3_GetUsageEntryInfo _lcc148
|
||||
#define Level3_GetBCCType _lcc149
|
||||
#define Level3_LoadRelease _lcc150
|
||||
#define Level3_GetEmbeddedDrmCertificate _lcc151
|
||||
#else
|
||||
#define Level3_Initialize _oecc01
|
||||
#define Level3_Terminate _oecc02
|
||||
@@ -233,6 +234,8 @@
|
||||
#define Level3_GetUsageEntryInfo _oecc148
|
||||
#define Level3_GetBCCType _oecc149
|
||||
#define Level3_LoadRelease _oecc150
|
||||
// Internal-only.
|
||||
#define Level3_GetEmbeddedDrmCertificate _oecc151
|
||||
#endif
|
||||
|
||||
#define Level3_GetInitializationState _oecl3o01
|
||||
@@ -527,6 +530,8 @@ OEMCryptoResult Level3_LoadProvisioningCast(
|
||||
size_t signature_length, uint8_t* wrapped_private_key,
|
||||
size_t* wrapped_private_key_length);
|
||||
OEMCryptoResult Level3_GetBCCType(OEMCrypto_BCCType* bcc_type);
|
||||
OEMCryptoResult Level3_GetEmbeddedDrmCertificate(uint8_t* public_cert,
|
||||
size_t* public_cert_length);
|
||||
|
||||
// The following are specific to Google's Level 3 implementation and are not
|
||||
// required.
|
||||
|
||||
Reference in New Issue
Block a user