Source release 17.1.2

This commit is contained in:
John "Juce" Bruce
2023-06-23 15:37:42 -07:00
parent a10f13a2dc
commit 2baa7c6e2b
353 changed files with 12903 additions and 2305 deletions

View File

@@ -31,6 +31,8 @@
'../core/test/http_socket.cpp',
'../core/test/license_holder.cpp',
'../core/test/license_request.cpp',
'../core/test/message_dumper.cpp',
'../core/test/provisioning_holder.cpp',
'../core/test/reboot_test.cpp',
'../core/test/test_base.cpp',
'../core/test/test_printers.cpp',

View File

@@ -24,12 +24,14 @@
'../core/test/license_keys_unittest.cpp',
'../core/test/license_request.cpp',
'../core/test/license_unittest.cpp',
'../core/test/message_dumper.cpp',
'../core/test/okp_fallback_policy_test.cpp',
'../core/test/ota_keybox_provisioner_test.cpp',
'../core/test/parallel_operations_test.cpp',
'../core/test/policy_engine_constraints_unittest.cpp',
'../core/test/policy_engine_unittest.cpp',
'../core/test/policy_integration_test.cpp',
'../core/test/provisioning_holder.cpp',
'../core/test/rw_lock_test.cpp',
'../core/test/service_certificate_unittest.cpp',
'../core/test/system_id_extractor_unittest.cpp',

View File

@@ -530,6 +530,19 @@ class CDM_EXPORT Cdm : public ITimerClient {
virtual Status getStatusForHdcpVersion(HdcpVersion hdcp,
KeyStatus* key_status) = 0;
// Checks if the given initialization data contains embedded, entitled keys.
// Sets the variable pointed to by |contains_keys| to true if the init data
// contains embedded keys or false if it does not. This function is useful if
// apps choose to handle such initialization data differently, such as in the
// case of key rotation or loading an offline entitlement license.
//
// For PSSH init data, this function accepts the full concatenated blob of
// PSSH boxes from the stream. It will return true if any of the PSSHs contain
// embedded Widevine keys.
virtual Status initDataContainsEmbeddedKeys(InitDataType init_data_type,
const std::string& init_data,
bool* contains_keys) = 0;
// Creates a new session.
// Do not use this to load an existing persistent session (use load()).
// If successful, the session ID is returned via |session_id|.
@@ -875,6 +888,35 @@ class CDM_EXPORT Cdm : public ITimerClient {
// and apps to gather these metrics to send them back to Google for analysis.
virtual Status getMetrics(std::string* serialized_metrics) = 0;
// Creates a Cast Provisioning Request message.
// This method is only useful on devices that implement support for Google
// Cast. Calling this method will generate a provisioning request that can be
// used to provision the device's Cast certificate. The request should be sent
// to the provisioning server just like a request from
// getProvisioningRequest(). However, the response should be given to
// handleCastProvisioningResponse() in order to extract the additional,
// Cast-specific fields.
virtual Status getCastProvisioningRequest(std::string* request) = 0;
// Handles a Cast provisioning response.
// This method is only useful on devices that implement support for Google
// Cast. It returns both the Cast public certificate and a wrapped private key
// that can be used with castSign(). Handling a Cast provisioning response
// does not affect the device's Widevine provisioning status. See
// handleProvisioningResponse() for handling Widevine provisioning.
virtual Status handleCastProvisioningResponse(const std::string& response,
std::string* cert,
std::string* wrapped_key) = 0;
// Signs a method for Cast usage.
// This method is only useful on devices that support Google Cast and after
// receiving a wrapped private key from handleCastProvisioningResponse(). This
// method generates a signature for the message using the given private key in
// PKCS#1 with block type 1 padding.
virtual Status castSign(const std::string& wrapped_key,
const std::string& message,
std::string* signature) = 0;
protected:
Cdm() {}
};

View File

@@ -13,7 +13,7 @@
# define CDM_VERSION_MINOR 1
#endif
#ifndef CDM_VERSION_PATCH
# define CDM_VERSION_PATCH 1
# define CDM_VERSION_PATCH 2
#endif
#ifndef CDM_VERSION_TAG
# define CDM_VERSION_TAG ""

View File

@@ -1,35 +0,0 @@
# Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine License
# Agreement.
{
'includes': [
'platform_properties.gypi',
],
'variables': {
# Directory where OEMCrypto header, test, and reference files lives.
'oemcrypto_dir': '../oemcrypto',
# Directory where widevine utilities live.
'util_dir': '../util',
},
'targets': [
{
'target_name': 'oec_ref',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'includes': [
'../oemcrypto/ref/oec_ref.gypi',
],
'dependencies': [
'<(oemcrypto_dir)/util/oec_ref_util.gyp:oec_ref_util',
],
},
{
'target_name': 'oec_ref_shared',
'type': 'shared_library',
'dependencies': [
'oec_ref'
],
},
],
}

View File

@@ -50,6 +50,9 @@ constexpr char kNoSandboxId[] = "";
const int64_t kPolicyTimerDurationMilliseconds = 5000;
void* const kPolicyTimerContext = nullptr;
const std::string kEmptyAuthority;
const std::string kCastAuthority = "cast.google.com";
struct HostType {
Cdm::IStorage* storage;
Cdm::IClock* clock;
@@ -228,6 +231,10 @@ class CdmImpl final : public Cdm, public WvCdmEventListener {
Status getStatusForHdcpVersion(HdcpVersion hdcp,
KeyStatus* key_status) override;
Status initDataContainsEmbeddedKeys(InitDataType init_data_type,
const std::string& init_data,
bool* contains_keys) override;
Status createSession(SessionType session_type,
std::string* session_id) override;
@@ -301,6 +308,15 @@ class CdmImpl final : public Cdm, public WvCdmEventListener {
Status getMetrics(std::string* serialized_metrics) override;
Status getCastProvisioningRequest(std::string* request) override;
Status handleCastProvisioningResponse(const std::string& response,
std::string* cert,
std::string* wrapped_key) override;
Status castSign(const std::string& wrapped_key, const std::string& message,
std::string* signature) override;
// ITimerClient:
void onTimerExpired(void* context) override;
@@ -322,6 +338,14 @@ class CdmImpl final : public Cdm, public WvCdmEventListener {
GenericSigningAlgorithmType algorithm);
Cdm::Status ConvertHdcpLevel(const std::string& query_value,
Cdm::HdcpVersion* result);
Status getProvisioningRequest(CdmCertificateType cert_type,
const std::string& authority,
std::string* request);
Status handleProvisioningResponse(const std::string& response,
std::string* cert,
std::string* wrapped_key);
Cdm::Status ConvertStatusCode(CdmResponseType inner_error) const;
IEventListener* listener_;
bool policy_timer_enabled_;
@@ -430,15 +454,9 @@ Cdm::Status CdmImpl::getRobustnessLevel(RobustnessLevel* level) {
}
std::string level_string;
const CdmResponseType result = cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_SECURITY_LEVEL, &level_string);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
const auto result = ConvertStatusCode(cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_SECURITY_LEVEL, &level_string));
if (result != kSuccess) return result;
if (level_string == QUERY_VALUE_SECURITY_LEVEL_L1) {
*level = kL1;
@@ -461,15 +479,9 @@ Cdm::Status CdmImpl::getSystemId(uint32_t* id) {
}
std::string id_string;
const CdmResponseType result =
cdm_engine_->QueryStatus(kLevelDefault, QUERY_KEY_SYSTEM_ID, &id_string);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
const auto result = ConvertStatusCode(cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_SYSTEM_ID, &id_string));
if (result != kSuccess) return result;
*id = static_cast<uint32_t>(std::stoul(id_string));
return kSuccess;
@@ -482,15 +494,9 @@ Cdm::Status CdmImpl::getResourceRatingTier(uint32_t* tier) {
}
std::string tier_string;
const CdmResponseType result = cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_RESOURCE_RATING_TIER, &tier_string);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
const auto result = ConvertStatusCode(cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_RESOURCE_RATING_TIER, &tier_string));
if (result != kSuccess) return result;
const uint32_t parsed_tier = static_cast<uint32_t>(std::stoul(tier_string));
if (parsed_tier <= 0) {
@@ -507,18 +513,8 @@ Cdm::Status CdmImpl::getOemCryptoBuildInfo(std::string* build_info) {
LOGE("Missing build_info parameter to receive build info.");
return kTypeError;
}
CdmResponseType result = cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, build_info);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return ConvertStatusCode(cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, build_info));
}
Cdm::ProvisioningStatus CdmImpl::getProvisioningStatus() {
@@ -540,51 +536,18 @@ Cdm::ProvisioningStatus CdmImpl::getProvisioningStatus() {
}
Cdm::Status CdmImpl::getProvisioningRequest(std::string* request) {
std::string empty_authority;
std::string ignored_base_url;
CdmResponseType result = cdm_engine_->GetProvisioningRequest(
kCertificateWidevine, empty_authority, provisioning_service_certificate_,
kLevelDefault, request, &ignored_base_url);
if (result == CERT_PROVISIONING_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kResourceContention;
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return getProvisioningRequest(kCertificateWidevine, kEmptyAuthority, request);
}
Cdm::Status CdmImpl::handleProvisioningResponse(const std::string& response) {
std::string ignored_cert;
std::string ignored_wrapped_key;
CdmResponseType result = cdm_engine_->HandleProvisioningResponse(
response, kLevelDefault, &ignored_cert, &ignored_wrapped_key);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return handleProvisioningResponse(response, &ignored_cert,
&ignored_wrapped_key);
}
Cdm::Status CdmImpl::removeProvisioning() {
CdmResponseType result = cdm_engine_->Unprovision(kSecurityLevelL1);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return ConvertStatusCode(cdm_engine_->Unprovision(kSecurityLevelL1));
}
Cdm::Status CdmImpl::listStoredLicenses(std::vector<std::string>* key_set_ids) {
@@ -592,16 +555,8 @@ Cdm::Status CdmImpl::listStoredLicenses(std::vector<std::string>* key_set_ids) {
LOGE("Missing vector parameter to receive key_set_ids.");
return kTypeError;
}
CdmResponseType result =
cdm_engine_->ListStoredLicenses(kSecurityLevelL1, key_set_ids);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return ConvertStatusCode(cdm_engine_->ListStoredLicenses(
kSecurityLevelL1, key_set_ids));
}
Cdm::Status CdmImpl::listUsageRecords(std::vector<std::string>* ksids) {
@@ -609,51 +564,26 @@ Cdm::Status CdmImpl::listUsageRecords(std::vector<std::string>* ksids) {
LOGE("Missing vector parameter to receive KSIDs.");
return kTypeError;
}
CdmResponseType result = cdm_engine_->ListUsageIds(
property_set_.app_id(), kSecurityLevelL1, ksids, nullptr);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return ConvertStatusCode(cdm_engine_->ListUsageIds(
property_set_.app_id(), kSecurityLevelL1, ksids, nullptr));
}
Cdm::Status CdmImpl::deleteUsageRecord(const std::string& key_set_id) {
CdmResponseType result = cdm_engine_->DeleteUsageRecord(
property_set_.app_id(), kSecurityLevelL1, key_set_id);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return ConvertStatusCode(cdm_engine_->DeleteUsageRecord(
property_set_.app_id(), kSecurityLevelL1, key_set_id));
}
Cdm::Status CdmImpl::deleteAllUsageRecords() {
CdmResponseType result =
cdm_engine_->RemoveAllUsageInfo(property_set_.app_id(), kSecurityLevelL1);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
return ConvertStatusCode(cdm_engine_->RemoveAllUsageInfo(
property_set_.app_id(), kSecurityLevelL1));
}
Cdm::Status CdmImpl::getStatusForHdcpVersion(Cdm::HdcpVersion hdcp,
Cdm::KeyStatus* key_status) {
std::string query_value;
if (cdm_engine_->QueryStatus(kLevelDefault, QUERY_KEY_MAX_HDCP_LEVEL,
&query_value) != NO_ERROR) {
return kUnexpectedError;
}
auto result = ConvertStatusCode(cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_MAX_HDCP_LEVEL, &query_value));
if (result != kSuccess) return result;
if (query_value == QUERY_VALUE_HDCP_NONE ||
query_value == QUERY_VALUE_HDCP_LEVEL_UNKNOWN) {
@@ -662,15 +592,46 @@ Cdm::Status CdmImpl::getStatusForHdcpVersion(Cdm::HdcpVersion hdcp,
*key_status = Cdm::kUsable;
} else {
Cdm::HdcpVersion max_hdcp;
if (ConvertHdcpLevel(query_value, &max_hdcp) != kSuccess) {
return kUnexpectedError;
}
result = ConvertHdcpLevel(query_value, &max_hdcp);
if (result != kSuccess) return result;
*key_status = (hdcp <= max_hdcp ? Cdm::kUsable : Cdm::kOutputRestricted);
}
return kSuccess;
}
Cdm::Status CdmImpl::initDataContainsEmbeddedKeys(InitDataType init_data_type,
const std::string& init_data,
bool* contains_keys) {
if (contains_keys == nullptr) {
LOGE("Missing pointer to result variable.");
return kTypeError;
}
if (init_data_type != kCenc) {
// Only PSSHs can contain embedded keys.
*contains_keys = false;
return kSuccess;
}
std::string oec_version;
auto result = ConvertStatusCode(cdm_engine_->QueryStatus(
wvcdm::kLevelDefault, QUERY_KEY_OEMCRYPTO_API_VERSION, &oec_version));
if (result != kSuccess) return result;
InitializationData init_data_obj(CENC_INIT_DATA_FORMAT, init_data,
oec_version);
if (init_data_obj.IsEmpty()) {
// Note that InitializationData's idea of "empty" includes "failed to find
// and parse a Widevine PSSH".
LOGE("Failed to parse init data. It may not contain a Widevine PSSH.");
return kNotSupported;
}
*contains_keys = init_data_obj.contains_entitled_keys();
return kSuccess;
}
Cdm::Status CdmImpl::createSession(SessionType session_type,
std::string* session_id) {
if (session_id == nullptr) {
@@ -691,25 +652,18 @@ Cdm::Status CdmImpl::createSession(SessionType session_type,
return kNotSupported;
}
CdmResponseType result = cdm_engine_->OpenSession(
"com.widevine.alpha", &property_set_, this, session_id);
switch (result) {
case NO_ERROR:
sessions_[*session_id].type = session_type;
return kSuccess;
case NEED_PROVISIONING:
// The Session ID may have been set by the CDM Engine despite this
// failure. We clear the ID here to ensure that we don't communicate a
// misleading ID to the application.
session_id->clear();
return kNeedsDeviceCertificate;
case SYSTEM_INVALIDATED_ERROR:
LOGE("System invalidated");
return kSystemStateLost;
default:
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
const auto result = ConvertStatusCode(cdm_engine_->OpenSession(
"com.widevine.alpha", &property_set_, this, session_id));
if (result != kSuccess) {
// The Session ID may have been set by the CDM Engine despite this
// failure. We clear the ID here to ensure that we don't communicate a
// misleading ID to the application.
session_id->clear();
return result;
}
sessions_[*session_id].type = session_type;
return kSuccess;
}
Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
@@ -767,11 +721,10 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
}
std::string oec_version;
if (cdm_engine_->QueryStatus(wvcdm::kLevelDefault,
QUERY_KEY_OEMCRYPTO_API_VERSION,
&oec_version) != NO_ERROR) {
return kUnexpectedError;
}
auto result = ConvertStatusCode(cdm_engine_->QueryStatus(
wvcdm::kLevelDefault, QUERY_KEY_OEMCRYPTO_API_VERSION, &oec_version));
if (result != kSuccess) return result;
InitializationData init_data_obj(init_data_type_name, init_data, oec_version);
if (init_data_obj.IsEmpty()) {
// Note that InitializationData's idea of "empty" includes "failed to find
@@ -782,31 +735,10 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
}
CdmKeyRequest key_request;
const CdmResponseType result = cdm_engine_->GenerateKeyRequest(
result = ConvertStatusCode(cdm_engine_->GenerateKeyRequest(
session_id, session_id, init_data_obj, license_type, app_parameters_,
&key_request);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kResourceContention;
} else if (result == NEED_PROVISIONING) {
LOGE("Device not provisioned");
return kNeedsDeviceCertificate;
} else if (result == PRIVACY_MODE_ERROR_1 || result == PRIVACY_MODE_ERROR_2 ||
result == PRIVACY_MODE_ERROR_3) {
LOGE("No licensing service certificate installed");
return kNeedsServiceCertificate;
} else if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
&key_request));
if (result != kSuccess) return result;
sessions_[session_id].callable = true;
assert(key_request.type == kKeyRequestTypeInitial);
@@ -827,20 +759,9 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
return kQuotaExceeded;
}
CdmResponseType result = cdm_engine_->OpenSession(
"com.widevine.alpha", &property_set_, session_id, this);
switch (result) {
case NO_ERROR:
break;
case SYSTEM_INVALIDATED_ERROR:
LOGE("System invalidated");
return kSystemStateLost;
case NEED_PROVISIONING:
return kNeedsDeviceCertificate;
default:
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
auto result = ConvertStatusCode(cdm_engine_->OpenSession(
"com.widevine.alpha", &property_set_, session_id, this));
if (result != kSuccess) return result;
DeviceFiles f(&file_system_);
if (!f.Init(kSecurityLevelUnknown)) {
@@ -851,20 +772,11 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
if (!f.LicenseExists(session_id)) {
// This might be a usage record session which needs to be loaded.
CdmKeyMessage ignored_release_message;
result =
cdm_engine_->LoadUsageSession(session_id, &ignored_release_message);
if (result != KEY_MESSAGE) {
result = ConvertStatusCode(cdm_engine_->LoadUsageSession(
session_id, &ignored_release_message));
if (result != kSuccess) {
cdm_engine_->CloseSession(session_id);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == LOAD_USAGE_INFO_MISSING) {
LOGE("Unable to load license: %s", session_id.c_str());
return kSessionNotFound;
} else {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
return result;
}
sessions_[session_id].type = kPersistentUsageRecord;
@@ -872,17 +784,8 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
return kSuccess;
}
result = cdm_engine_->RestoreKey(session_id, session_id);
if (result == GET_RELEASED_LICENSE_ERROR) {
// This was partially removed already.
// The EME spec states that we should be able to load it, but not use it.
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != KEY_ADDED) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
result = ConvertStatusCode(cdm_engine_->RestoreKey(session_id, session_id));
if (result != kSuccess) return result;
if (!policy_timer_enabled_) {
policy_timer_enabled_ = true;
@@ -916,31 +819,14 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
// AddKey(), it will internally delegate to RenewKey().
CdmKeySetId key_set_id = session_id;
CdmLicenseType license_type = {}; // Required for AddKey. Unused otherwise.
const CdmResponseType result =
const CdmResponseType inner_error =
cdm_engine_->AddKey(session_id, response, &license_type, &key_set_id);
// result should only be NEED_KEY after server certificate provisioning, which
// should no longer happen in this version of the CDM.
assert(result != NEED_KEY);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == OFFLINE_LICENSE_PROHIBITED) {
LOGE("A temporary session cannot be used for a persistent license.");
return kRangeError;
} else if (result == STORAGE_PROHIBITED) {
LOGE("A temporary session cannot be used for a persistent usage records.");
return kRangeError;
} else if (result == NEED_PROVISIONING) {
LOGE("The device needs to reprovision.");
return kNeedsDeviceCertificate;
} else if (result != KEY_ADDED) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
assert(inner_error != NEED_KEY);
const auto result = ConvertStatusCode(inner_error);
if (result != kSuccess) return result;
if (!policy_timer_enabled_) {
policy_timer_enabled_ = true;
@@ -970,11 +856,10 @@ Cdm::Status CdmImpl::loadEmbeddedKeys(const std::string& session_id,
}
std::string oec_version;
if (cdm_engine_->QueryStatus(wvcdm::kLevelDefault,
QUERY_KEY_OEMCRYPTO_API_VERSION,
&oec_version) != NO_ERROR) {
return kUnexpectedError;
}
auto result = ConvertStatusCode(cdm_engine_->QueryStatus(
wvcdm::kLevelDefault, QUERY_KEY_OEMCRYPTO_API_VERSION, &oec_version));
if (result != kSuccess) return result;
InitializationData init_data_obj(CENC_INIT_DATA_FORMAT, init_data,
oec_version);
if (init_data_obj.IsEmpty()) {
@@ -986,25 +871,9 @@ Cdm::Status CdmImpl::loadEmbeddedKeys(const std::string& session_id,
}
CdmKeyRequest key_request;
const CdmResponseType result = cdm_engine_->GenerateKeyRequest(
return ConvertStatusCode(cdm_engine_->GenerateKeyRequest(
session_id, session_id, init_data_obj, kLicenseTypeEmbeddedKeyData,
app_parameters_, &key_request);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kResourceContention;
} else if (result != KEY_ADDED) {
LOGE("Unexpected Failure: GenerateKeyRequest() returned %d",
static_cast<int>(result));
return kUnexpectedError;
}
return kSuccess;
app_parameters_, &key_request));
}
Cdm::Status CdmImpl::getExpiration(const std::string& session_id,
@@ -1050,24 +919,9 @@ Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& session_id,
}
CdmKeyAllowedUsage usage_for_key;
CdmResponseType result =
cdm_engine_->QueryKeyAllowedUsage(session_id, key_id, &usage_for_key);
if (result != NO_ERROR) {
// TODO(b/114435278): There are multiple KEY_NOT_FOUND_* errors that should
// probably all turn into kNoKey. Here, and below, and everywhere.
if (result == KEY_NOT_FOUND_1) {
return kNoKey;
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
}
const auto result = ConvertStatusCode(cdm_engine_->QueryKeyAllowedUsage(
session_id, key_id, &usage_for_key));
if (result != kSuccess) return result;
*usage_flags = KeyAllowedFlags(usage_for_key);
return kSuccess;
@@ -1081,24 +935,9 @@ Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& key_id,
}
CdmKeyAllowedUsage usage_for_key;
const CdmResponseType result =
cdm_engine_->QueryKeyAllowedUsage(key_id, &usage_for_key);
if (result != NO_ERROR) {
if (result == KEY_NOT_FOUND_1 || result == KEY_NOT_FOUND_2) {
return kNoKey;
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == KEY_CONFLICT_1) {
return kTypeError;
} else {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
}
const auto result = ConvertStatusCode(cdm_engine_->QueryKeyAllowedUsage(
key_id, &usage_for_key));
if (result != kSuccess) return result;
*usage_flags = KeyAllowedFlags(usage_for_key);
return kSuccess;
@@ -1146,16 +985,11 @@ Cdm::Status CdmImpl::close(const std::string& session_id) {
return kSessionNotFound;
}
const CdmResponseType result = cdm_engine_->CloseSession(session_id);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
const auto result = ConvertStatusCode(cdm_engine_->CloseSession(session_id));
if (result == kSuccess) {
sessions_.erase(session_id);
}
sessions_.erase(session_id);
return kSuccess;
return result;
}
Cdm::Status CdmImpl::remove(const std::string& session_id) {
@@ -1185,23 +1019,12 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
it->second = kReleased;
}
const CdmResponseType result = cdm_engine_->GenerateKeyRequest(
const auto result = ConvertStatusCode(cdm_engine_->GenerateKeyRequest(
session_id, session_id, empty_initialization_data, kLicenseTypeRelease,
app_parameters_, &key_request);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kResourceContention;
} else if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", static_cast<int>(result));
app_parameters_, &key_request));
if (result != kSuccess) {
cdm_engine_->CloseSession(session_id);
return kUnexpectedError;
return result;
}
LOGI("A license release has been generated.");
@@ -1226,18 +1049,8 @@ Cdm::Status CdmImpl::forceRemove(const std::string& session_id) {
return kRangeError;
}
const CdmResponseType result = cdm_engine_->RemoveLicense(session_id);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
const auto result = ConvertStatusCode(cdm_engine_->RemoveLicense(session_id));
if (result != kSuccess) return result;
sessions_.erase(session_id);
cdm_engine_->CloseSession(session_id);
@@ -1330,41 +1143,10 @@ Cdm::Status CdmImpl::decrypt(const std::string& session_id,
return cdm_sample;
});
const CdmResponseType result =
cdm_engine_->DecryptV16(session_id, parameters);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == OUTPUT_TOO_LARGE_ERROR) {
LOGE("Output too large");
return kOutputTooLarge;
}
if (result == NEED_KEY || result == KEY_NOT_FOUND_3 ||
result == SESSION_NOT_FOUND_FOR_DECRYPT) {
LOGE("Key not available.");
return kNoKey;
}
if (result == INSUFFICIENT_OUTPUT_PROTECTION) {
LOGE("Key usage blocked due to HDCP or display resolution constraints.");
return kKeyUsageBlockedByPolicy;
}
LOGE("Decrypt error: %d", static_cast<int>(result));
return kDecryptError;
const auto result =
ConvertStatusCode(cdm_engine_->DecryptV16(session_id, parameters));
if (result == kUnexpectedError) return kDecryptError;
return result;
}
Cdm::Status CdmImpl::genericEncrypt(const std::string& session_id,
@@ -1381,33 +1163,8 @@ Cdm::Status CdmImpl::genericEncrypt(const std::string& session_id,
return kNotSupported;
}
CdmResponseType result = cdm_engine_->GenericEncrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == OUTPUT_TOO_LARGE_ERROR) {
LOGE("Output too large");
return kOutputTooLarge;
}
if (result == SESSION_NOT_FOUND_13) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_3 || result == NEED_KEY) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
return ConvertStatusCode(cdm_engine_->GenericEncrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer));
}
Cdm::Status CdmImpl::genericDecrypt(const std::string& session_id,
@@ -1424,33 +1181,8 @@ Cdm::Status CdmImpl::genericDecrypt(const std::string& session_id,
return kNotSupported;
}
const CdmResponseType result = cdm_engine_->GenericDecrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == OUTPUT_TOO_LARGE_ERROR) {
LOGE("Output too large");
return kOutputTooLarge;
}
if (result == SESSION_NOT_FOUND_14) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_4 || result == NEED_KEY) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
return ConvertStatusCode(cdm_engine_->GenericDecrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer));
}
Cdm::Status CdmImpl::genericSign(const std::string& session_id,
@@ -1464,29 +1196,8 @@ Cdm::Status CdmImpl::genericSign(const std::string& session_id,
return kNotSupported;
}
const CdmResponseType result = cdm_engine_->GenericSign(
session_id, message, key_id, cdm_algorithm, signature);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == SESSION_NOT_FOUND_15) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_5 || result == NEED_KEY) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
return ConvertStatusCode(cdm_engine_->GenericSign(
session_id, message, key_id, cdm_algorithm, signature));
}
Cdm::Status CdmImpl::genericVerify(const std::string& session_id,
@@ -1500,29 +1211,8 @@ Cdm::Status CdmImpl::genericVerify(const std::string& session_id,
return kNotSupported;
}
const CdmResponseType result = cdm_engine_->GenericVerify(
session_id, message, key_id, cdm_algorithm, signature);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == SESSION_NOT_FOUND_16) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_6 || result == NEED_KEY) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
return ConvertStatusCode(cdm_engine_->GenericVerify(
session_id, message, key_id, cdm_algorithm, signature));
}
Cdm::Status CdmImpl::setVideoResolution(const std::string& session_id,
@@ -1555,6 +1245,23 @@ Cdm::Status CdmImpl::getMetrics(std::string* serialized_metrics) {
return kSuccess;
}
Cdm::Status CdmImpl::getCastProvisioningRequest(std::string* request) {
return getProvisioningRequest(kCertificateX509, kCastAuthority, request);
}
Cdm::Status CdmImpl::handleCastProvisioningResponse(const std::string& response,
std::string* cert,
std::string* wrapped_key) {
return handleProvisioningResponse(response, cert, wrapped_key);
}
Cdm::Status CdmImpl::castSign(const std::string& wrapped_key,
const std::string& message,
std::string* signature) {
return ConvertStatusCode(cdm_engine_->SignRsa(wrapped_key, message, signature,
kSign_PKCS1_Block1));
}
void CdmImpl::onTimerExpired(void* context) {
if (context == kPolicyTimerContext) {
if (policy_timer_enabled_) {
@@ -1693,6 +1400,116 @@ Cdm::Status CdmImpl::ConvertHdcpLevel(const std::string& query_value,
return kSuccess;
}
Cdm::Status CdmImpl::getProvisioningRequest(CdmCertificateType cert_type,
const std::string& authority,
std::string* request) {
std::string ignored_base_url;
return ConvertStatusCode(cdm_engine_->GetProvisioningRequest(
cert_type, authority, provisioning_service_certificate_, kLevelDefault,
request, &ignored_base_url));
}
Cdm::Status CdmImpl::handleProvisioningResponse(const std::string& response,
std::string* cert,
std::string* wrapped_key) {
return ConvertStatusCode(cdm_engine_->HandleProvisioningResponse(
response, kLevelDefault, cert, wrapped_key));
}
Cdm::Status CdmImpl::ConvertStatusCode(CdmResponseType inner_error) const {
switch (inner_error) {
case GET_RELEASED_LICENSE_ERROR:
// This was partially removed already.
// The EME spec states that we should be able to load it, but not use it.
case KEY_ADDED:
case KEY_MESSAGE:
case NO_ERROR:
return kSuccess;
case KEY_CONFLICT_1:
LOGE("Multiple sessions contain given key (inner_error=%d)", inner_error);
return kTypeError;
case NEED_PROVISIONING:
LOGE("Device not provisioned (inner_error=%d)", inner_error);
return kNeedsDeviceCertificate;
case LOAD_USAGE_INFO_MISSING:
LOGE("Unable to load license (inner_error=%d)", inner_error);
return kSessionNotFound;
case SESSION_NOT_FOUND_1:
case SESSION_NOT_FOUND_2:
case SESSION_NOT_FOUND_3:
case SESSION_NOT_FOUND_4:
case SESSION_NOT_FOUND_5:
case SESSION_NOT_FOUND_6:
case SESSION_NOT_FOUND_7:
case SESSION_NOT_FOUND_8:
case SESSION_NOT_FOUND_9:
case SESSION_NOT_FOUND_10:
case SESSION_NOT_FOUND_11:
case SESSION_NOT_FOUND_12:
case SESSION_NOT_FOUND_13:
case SESSION_NOT_FOUND_14:
case SESSION_NOT_FOUND_15:
case SESSION_NOT_FOUND_16:
//case SESSION_NOT_FOUND_17: // undefined
case SESSION_NOT_FOUND_18:
case SESSION_NOT_FOUND_19:
case SESSION_NOT_FOUND_20:
case SESSION_NOT_FOUND_21:
case SESSION_NOT_FOUND_22:
case SESSION_NOT_FOUND_23:
LOGE("Session not found (inner_error=%d)", inner_error);
return kSessionNotFound;
case KEY_NOT_FOUND_1:
case KEY_NOT_FOUND_2:
case KEY_NOT_FOUND_3:
case KEY_NOT_FOUND_4:
case KEY_NOT_FOUND_5:
case KEY_NOT_FOUND_6:
case NEED_KEY:
case NO_CONTENT_KEY:
case NO_CONTENT_KEY_2:
case NO_CONTENT_KEY_3:
case SESSION_NOT_FOUND_FOR_DECRYPT:
LOGE("Key not found (inner_error=%d)", inner_error);
return kNoKey;
case INSUFFICIENT_OUTPUT_PROTECTION:
LOGE("Key usage blocked due to HDCP or display resolution constraints.");
return kKeyUsageBlockedByPolicy;
case OFFLINE_LICENSE_PROHIBITED:
LOGE("A temporary session cannot be used for a persistent license.");
return kRangeError;
case STORAGE_PROHIBITED:
LOGE(
"A temporary session cannot be used for a persistent usage records.");
return kRangeError;
case CERT_PROVISIONING_NONCE_GENERATION_ERROR:
case LICENSE_REQUEST_NONCE_GENERATION_ERROR:
LOGE("Nonce quota exceeded (inner_error=%d)", inner_error);
return kResourceContention;
case SESSION_LOST_STATE_ERROR:
LOGE("Session invalidated (inner_error=%d)", inner_error);
return kSessionStateLost;
case SYSTEM_INVALIDATED_ERROR:
LOGE("System invalidated (inner_error=%d)", inner_error);
return kSystemStateLost;
case OUTPUT_TOO_LARGE_ERROR:
LOGE("Output too large (inner_error=%d)", inner_error);
return kOutputTooLarge;
case PRIVACY_MODE_ERROR_1:
case PRIVACY_MODE_ERROR_2:
case PRIVACY_MODE_ERROR_3:
LOGE("No service certificate installed (inner_error=%d)", inner_error);
return kNeedsServiceCertificate;
default:
LOGE("Unknown error: %d", inner_error);
return kUnexpectedError;
}
}
} // namespace
// static

View File

@@ -191,7 +191,10 @@ void Properties::InitOnce() {
allow_restore_of_offline_licenses_with_release_ = true;
delay_oem_crypto_termination_ = false;
SetClientInfo();
session_property_set_.reset(new CdmClientPropertySetMap());
{
std::unique_lock<std::mutex> lock(session_mutex_);
session_property_set_.reset(new CdmClientPropertySetMap());
}
}
// static

View File

@@ -11,9 +11,13 @@
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "OEMCryptoCENC.h"
#include "cdm.h"
@@ -204,6 +208,12 @@ const std::string kNewValue = "A New Value";
const std::string kParamName = "PARAM";
const std::string kParamName2 = "PARAM2";
const std::string kFakeCastMessage = a2bs_hex(
// ASN.1 SHA-1 identifier
"3021300906052b0e03021a05000414"
// Fake SHA-1 digest (actually just random bytes)
"96b34d11727bb41089e989ea51588666f924a40e");
class CdmTest : public WvCdmTestBase, public Cdm::IEventListener {
public:
CdmTest() {}
@@ -2431,6 +2441,33 @@ TEST_F(CdmTest, EncryptedPlaybackWithoutALicense) {
EXPECT_EQ(Cdm::kNoKey, status);
}
TEST_F(CdmTest, CheckInitDataEmbeddedKeys) {
bool contains_embedded_keys;
// WebM (cannot contain embedded keys)
EXPECT_EQ(cdm_->initDataContainsEmbeddedKeys(Cdm::kWebM, kWebMInitData,
&contains_embedded_keys),
Cdm::kSuccess);
EXPECT_FALSE(contains_embedded_keys);
// PSSH without embedded keys
EXPECT_EQ(cdm_->initDataContainsEmbeddedKeys(Cdm::kCenc, kCencInitData,
&contains_embedded_keys),
Cdm::kSuccess);
EXPECT_FALSE(contains_embedded_keys);
// PSSH with embedded keys
EXPECT_EQ(cdm_->initDataContainsEmbeddedKeys(
Cdm::kCenc, kCencEntitlementInitData1, &contains_embedded_keys),
Cdm::kSuccess);
EXPECT_TRUE(contains_embedded_keys);
// Null out pointer
EXPECT_NE(
cdm_->initDataContainsEmbeddedKeys(Cdm::kCenc, kCencInitData, nullptr),
Cdm::kSuccess);
}
TEST_F(CdmTest, GetEmptyMetrics) {
std::string metrics;
Cdm::Status status = cdm_->getMetrics(&metrics);
@@ -2691,6 +2728,72 @@ TEST_F(CdmIndividualizationTest, NoLoadWithoutProvisioning) {
EXPECT_EQ(Cdm::kNeedsDeviceCertificate, cdm_->load(kBogusSessionId));
}
TEST_F(CdmIndividualizationTest, CastReceiverProvisionAndSign) {
if (!CheckProvisioningSupport()) {
GTEST_SKIP() << "OEMCrypto does not support provisioning";
}
if (!wvoec::global_features.cast_receiver) {
GTEST_SKIP() << "OEMCrypto does not support Cast Receiver functionality";
}
ASSERT_NO_FATAL_FAILURE(ProvisionDevice());
// Perform Cast provisioning and store the public cert and wrapped private key
// for use later in the test
std::string cast_prov_request;
ASSERT_EQ(cdm_->getCastProvisioningRequest(&cast_prov_request),
Cdm::kSuccess);
const std::string cast_prov_response =
GetProvisioningResponse(cast_prov_request);
ASSERT_FALSE(cast_prov_response.empty());
std::string cert;
std::string wrapped_key;
ASSERT_EQ(cdm_->handleCastProvisioningResponse(cast_prov_response, &cert,
&wrapped_key),
Cdm::kSuccess);
EXPECT_FALSE(cert.empty());
ASSERT_FALSE(wrapped_key.empty());
// Perform cast signing using the wrapped private key
std::string signature;
ASSERT_EQ(cdm_->castSign(wrapped_key, kFakeCastMessage, &signature),
Cdm::kSuccess);
// Verify the signature against the public key
//
// 1) Load the public key into an RSA struct
std::unique_ptr<BIO, void (*)(BIO*)> bio(BIO_new(BIO_s_mem()), BIO_free_all);
ASSERT_NE(bio, nullptr);
ASSERT_EQ(BIO_write(bio.get(), cert.data(), static_cast<int>(cert.size())),
static_cast<int>(cert.size()));
std::unique_ptr<X509, void (*)(X509*)> x509(
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), X509_free);
ASSERT_NE(x509, nullptr);
std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)> pubkey(
X509_get_pubkey(x509.get()), EVP_PKEY_free);
ASSERT_NE(pubkey, nullptr);
// Not a std::unique_ptr because "get0" returns a non-owning pointer.
RSA* const rsa = EVP_PKEY_get0_RSA(pubkey.get());
ASSERT_NE(rsa, nullptr);
// 2) Decrypt the signature
std::string decrypted_digest(RSA_size(rsa), 0);
const int decrypted_length = RSA_public_decrypt(
static_cast<int>(signature.size()),
reinterpret_cast<const uint8_t*>(signature.data()),
reinterpret_cast<uint8_t*>(&decrypted_digest[0]), rsa, RSA_PKCS1_PADDING);
ASSERT_GT(decrypted_length, 0);
// 3) Compare the digests
decrypted_digest.resize(decrypted_length);
EXPECT_EQ(decrypted_digest, kFakeCastMessage);
}
class CdmProv40IndividualizationTest : public CdmTest {};
TEST_F(CdmProv40IndividualizationTest, NeedsOemCertProvisioning) {

View File

@@ -20,7 +20,7 @@
int argc = 1;
testing::InitGoogleTest(&argc, argv);
XCTAssertEqual(widevine::PerfTestMain(&widevine::Cdm::initialize, &widevine::Cdm::create, cert), 0);
XCTAssertEqual(widevine::PerfTestMain(&widevine::Cdm::initialize, &widevine::Cdm::create, ""), 0);
}
@end