Move system ID extraction outside of CryptoSession. am: 9d169a00bb am: 3c82ad605d
Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/18244016 Change-Id: I461ae5b896396aff9949242d897d743fef3609ce Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
#include "ota_keybox_provisioner.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "system_id_extractor.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
@@ -839,6 +840,17 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (query_token == QUERY_KEY_SYSTEM_ID) {
|
||||
SystemIdExtractor extractor(security_level, crypto_session.get(),
|
||||
file_system_);
|
||||
uint32_t system_id;
|
||||
if (!extractor.ExtractSystemId(&system_id)) {
|
||||
LOGW("ExtractSystemId failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
*query_response = std::to_string(system_id);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType status;
|
||||
M_TIME(status = crypto_session->Open(security_level),
|
||||
@@ -857,16 +869,6 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
|
||||
*query_response = device_id;
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (query_token == QUERY_KEY_SYSTEM_ID) {
|
||||
uint32_t system_id;
|
||||
const bool got_id = crypto_session->GetSystemId(&system_id);
|
||||
if (!got_id) {
|
||||
LOGW("QUERY_KEY_SYSTEM_ID unknown failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
*query_response = std::to_string(system_id);
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (query_token == QUERY_KEY_PROVISIONING_ID) {
|
||||
std::string provisioning_id;
|
||||
status = crypto_session->GetProvisioningId(&provisioning_id);
|
||||
|
||||
@@ -72,8 +72,6 @@ constexpr uint32_t kRsaSignatureLength = 256;
|
||||
constexpr size_t kEstimatedInitialUsageTableHeader = 40;
|
||||
const size_t kAes128BlockSize = 16;
|
||||
|
||||
// Constants and utility objects relating to OEM Certificates
|
||||
constexpr const char* kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
|
||||
constexpr int kMaxTerminateCountDown = 5;
|
||||
|
||||
const std::string kStringNotAvailable = "NA";
|
||||
@@ -100,11 +98,6 @@ static_assert(wvutil::ArraySize(kMaxSubsampleRegionSizes) ==
|
||||
|
||||
constexpr size_t kDefaultMaxSubsampleRegionSize = kMaxSubsampleRegionSizes[0];
|
||||
|
||||
// Not a valid system ID. Used as a placeholder for systems without an ID.
|
||||
// Will not be accepted for DRM provisioning requests or license requests.
|
||||
constexpr uint32_t kNullSystemId =
|
||||
static_cast<uint32_t>(std::numeric_limits<int>::max());
|
||||
|
||||
constexpr size_t kMaxExternalDeviceIdLength = 64;
|
||||
|
||||
// This maps a few common OEMCryptoResult to CdmResponseType. Many mappings
|
||||
@@ -269,7 +262,7 @@ OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode) {
|
||||
|
||||
CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
|
||||
: metrics_(metrics),
|
||||
system_id_(-1),
|
||||
system_id_(NULL_SYSTEM_ID),
|
||||
open_(false),
|
||||
pre_provision_token_type_(kClientTokenUninitialized),
|
||||
update_usage_table_after_close_session_(false),
|
||||
@@ -525,76 +518,102 @@ bool CryptoSession::SetUpUsageTableHeader(
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetTokenFromKeybox(std::string* token) {
|
||||
CdmResponseType CryptoSession::GetTokenFromKeybox(
|
||||
RequestedSecurityLevel requested_security_level, std::string* key_data) {
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
RETURN_IF_NULL(token, PARAMETER_NULL);
|
||||
std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0');
|
||||
size_t buf_size = temp_buffer.size();
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
RETURN_IF_NULL(key_data, PARAMETER_NULL);
|
||||
LOGV("requested_security_level = %s",
|
||||
RequestedSecurityLevelToString(requested_security_level));
|
||||
// Devices with an invalid L1 keybox which support OTA keybox
|
||||
// provisioning don't have keybox data.
|
||||
const bool keybox_provisioning_required = WithStaticFieldReadLock(
|
||||
"GetTokenFromKeybox - keybox_provisioning_required", [&] {
|
||||
if (requested_security_level_ != kLevelDefault) return false;
|
||||
return needs_keybox_provisioning_;
|
||||
});
|
||||
if (keybox_provisioning_required) return NEED_PROVISIONING;
|
||||
|
||||
size_t key_data_length = KEYBOX_KEY_DATA_SIZE;
|
||||
key_data->assign(key_data_length, '\0');
|
||||
OEMCryptoResult status;
|
||||
WithOecReadLock("GetTokenFromKeybox", [&] {
|
||||
M_TIME(status =
|
||||
OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_),
|
||||
M_TIME(status = OEMCrypto_GetKeyData(
|
||||
reinterpret_cast<uint8_t*>(&key_data->front()), &key_data_length,
|
||||
requested_security_level),
|
||||
metrics_, oemcrypto_get_key_data_, status,
|
||||
metrics::Pow2Bucket(buf_size));
|
||||
metrics::Pow2Bucket(key_data_length));
|
||||
});
|
||||
|
||||
if (OEMCrypto_SUCCESS == status) {
|
||||
token->swap(temp_buffer);
|
||||
key_data->resize(key_data_length);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
key_data->clear();
|
||||
return MapOEMCryptoResult(status, GET_TOKEN_FROM_KEYBOX_ERROR,
|
||||
"GetTokenFromKeybox");
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetTokenFromOemCert(std::string* token) {
|
||||
CdmResponseType CryptoSession::GetTokenFromOemCert(
|
||||
RequestedSecurityLevel requested_security_level, std::string* oem_cert) {
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
RETURN_IF_NULL(token, PARAMETER_NULL);
|
||||
RETURN_IF_NULL(oem_cert, PARAMETER_NULL);
|
||||
LOGV("requested_security_level = %s",
|
||||
RequestedSecurityLevelToString(requested_security_level));
|
||||
|
||||
OEMCryptoResult status;
|
||||
if (!oem_token_.empty()) {
|
||||
token->assign(oem_token_);
|
||||
return NO_ERROR;
|
||||
}
|
||||
const bool cache_success =
|
||||
WithOecSessionLock("GetTokenFromOemCert - check cached", [&] {
|
||||
if (oem_token_.empty()) {
|
||||
return false;
|
||||
}
|
||||
oem_cert->assign(oem_token_);
|
||||
return true;
|
||||
});
|
||||
if (cache_success) return NO_ERROR;
|
||||
|
||||
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
|
||||
bool retrying = false;
|
||||
while (true) {
|
||||
size_t buf_size = temp_buffer.size();
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
WithOecSessionLock("GetTokenFromOemCert", [&] {
|
||||
status = OEMCrypto_GetOEMPublicCertificate(buf, &buf_size,
|
||||
requested_security_level_);
|
||||
size_t oem_cert_length = CERTIFICATE_DATA_SIZE;
|
||||
oem_cert->assign(oem_cert_length, '\0');
|
||||
OEMCryptoResult status =
|
||||
WithOecReadLock("GetTokenFromOemCert - attempt 1", [&] {
|
||||
return OEMCrypto_GetOEMPublicCertificate(
|
||||
reinterpret_cast<uint8_t*>(&oem_cert->front()), &oem_cert_length,
|
||||
requested_security_level);
|
||||
});
|
||||
metrics_->oemcrypto_get_oem_public_certificate_.Increment(status);
|
||||
if (status == OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||
oem_cert->assign(oem_cert_length, '\0');
|
||||
status = WithOecReadLock("GetTokenFromOemCert - attempt 2", [&] {
|
||||
return OEMCrypto_GetOEMPublicCertificate(
|
||||
reinterpret_cast<uint8_t*>(&oem_cert->front()), &oem_cert_length,
|
||||
requested_security_level);
|
||||
});
|
||||
metrics_->oemcrypto_get_oem_public_certificate_.Increment(status);
|
||||
|
||||
if (OEMCrypto_SUCCESS == status) {
|
||||
temp_buffer.resize(buf_size);
|
||||
oem_token_.assign(temp_buffer);
|
||||
token->assign(temp_buffer);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
if (status == OEMCrypto_ERROR_SHORT_BUFFER && !retrying) {
|
||||
temp_buffer.resize(buf_size);
|
||||
retrying = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
return MapOEMCryptoResult(status, GET_TOKEN_FROM_OEM_CERT_ERROR,
|
||||
"GetTokenFromOemCert");
|
||||
}
|
||||
|
||||
if (status == OEMCrypto_SUCCESS) {
|
||||
oem_cert->resize(oem_cert_length);
|
||||
WithOecSessionLock("GetTokenFromOemCert - set cache",
|
||||
[&] { oem_token_ = *oem_cert; });
|
||||
return NO_ERROR;
|
||||
}
|
||||
oem_cert->clear();
|
||||
return MapOEMCryptoResult(status, GET_TOKEN_FROM_OEM_CERT_ERROR,
|
||||
"GetTokenFromOemCert");
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetProvisioningToken(
|
||||
std::string* token, std::string* additional_token) {
|
||||
RETURN_IF_NOT_OPEN(CRYPTO_SESSION_NOT_OPEN);
|
||||
return GetProvisioningToken(requested_security_level_, token,
|
||||
additional_token);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetProvisioningToken(
|
||||
RequestedSecurityLevel requested_security_level, std::string* token,
|
||||
std::string* additional_token) {
|
||||
if (token == nullptr || additional_token == nullptr) {
|
||||
metrics_->crypto_session_get_token_.Increment(PARAMETER_NULL);
|
||||
RETURN_IF_NULL(token, PARAMETER_NULL);
|
||||
RETURN_IF_NULL(additional_token, PARAMETER_NULL);
|
||||
}
|
||||
|
||||
if (!IsInitialized()) {
|
||||
metrics_->crypto_session_get_token_.Increment(
|
||||
CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
@@ -603,11 +622,12 @@ CdmResponseType CryptoSession::GetProvisioningToken(
|
||||
|
||||
CdmResponseType status = UNKNOWN_CLIENT_TOKEN_TYPE;
|
||||
if (pre_provision_token_type_ == kClientTokenKeybox) {
|
||||
status = GetTokenFromKeybox(token);
|
||||
status = GetTokenFromKeybox(requested_security_level, token);
|
||||
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
status = GetTokenFromOemCert(token);
|
||||
status = GetTokenFromOemCert(requested_security_level, token);
|
||||
} else if (pre_provision_token_type_ == kClientTokenBootCertChain) {
|
||||
status = GetBootCertificateChain(token, additional_token);
|
||||
status = GetBootCertificateChain(requested_security_level, token,
|
||||
additional_token);
|
||||
}
|
||||
metrics_->crypto_session_get_token_.Increment(status);
|
||||
return status;
|
||||
@@ -689,7 +709,7 @@ CdmResponseType CryptoSession::GetInternalDeviceUniqueId(
|
||||
|
||||
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED &&
|
||||
pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
return GetTokenFromOemCert(device_id);
|
||||
return GetTokenFromOemCert(requested_security_level_, device_id);
|
||||
}
|
||||
|
||||
const bool use_null_device_id = WithStaticFieldReadLock(
|
||||
@@ -772,84 +792,23 @@ bool CryptoSession::GetApiMinorVersion(RequestedSecurityLevel security_level,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetSystemId(uint32_t* system_id) {
|
||||
bool CryptoSession::GetCachedSystemId(uint32_t* system_id) {
|
||||
RETURN_IF_NULL(system_id, false);
|
||||
RETURN_IF_UNINITIALIZED(false);
|
||||
RETURN_IF_NOT_OPEN(false);
|
||||
if (system_id_ == NULL_SYSTEM_ID) return false;
|
||||
*system_id = system_id_;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This method gets the system id from the keybox key data.
|
||||
// This method assumes that OEMCrypto has been initialized before making this
|
||||
// call.
|
||||
CdmResponseType CryptoSession::GetSystemIdInternal(uint32_t* system_id) {
|
||||
RETURN_IF_NULL(system_id, PARAMETER_NULL);
|
||||
|
||||
if (pre_provision_token_type_ == kClientTokenKeybox) {
|
||||
const bool use_null_system_id = WithStaticFieldReadLock(
|
||||
"GetSystemIdInternal() use_null_system_id", [&] {
|
||||
// Devices with an invalid L1 keybox which support OTA keybox
|
||||
// provisioning require a placeholder system ID while waiting for
|
||||
// keybox.
|
||||
if (requested_security_level_ != kLevelDefault) return false;
|
||||
return needs_keybox_provisioning_;
|
||||
});
|
||||
if (use_null_system_id) {
|
||||
LOGD("Using null system ID");
|
||||
*system_id = kNullSystemId;
|
||||
return NO_ERROR;
|
||||
}
|
||||
std::string token;
|
||||
const CdmResponseType status = GetTokenFromKeybox(&token);
|
||||
if (status != NO_ERROR) return status;
|
||||
if (token.size() < 2 * sizeof(uint32_t)) {
|
||||
LOGE("Keybox token size too small: token_size = %zu", token.size());
|
||||
return KEYBOX_TOKEN_TOO_SHORT;
|
||||
}
|
||||
|
||||
// Decode 32-bit int encoded as network-byte-order byte array starting at
|
||||
// index 4.
|
||||
const uint32_t* id = reinterpret_cast<const uint32_t*>(&token[4]);
|
||||
*system_id = ntohl(*id);
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
// Get the OEM Cert
|
||||
std::string oem_cert;
|
||||
CdmResponseType status = GetTokenFromOemCert(&oem_cert);
|
||||
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
if (!ExtractSystemIdFromOemCert(oem_cert, system_id))
|
||||
return EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (pre_provision_token_type_ == kClientTokenDrmCert) {
|
||||
// TODO(blueeyes): Support loading the system id from a pre-provisioned
|
||||
// Drm certificate.
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (pre_provision_token_type_ == kClientTokenBootCertChain) {
|
||||
// A system id can not be inferred from BCC. If the provisioning process has
|
||||
// come to the second stage, we may read system id from the stored OEM cert.
|
||||
return NO_ERROR;
|
||||
}
|
||||
LOGE("Unsupported pre-provision token type: %d",
|
||||
static_cast<int>(pre_provision_token_type_));
|
||||
return UNKNOWN_CLIENT_TOKEN_TYPE;
|
||||
}
|
||||
|
||||
bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
||||
uint32_t* system_id) {
|
||||
return ExtractExtensionValueFromCertificate(
|
||||
oem_cert, kWidevineSystemIdExtensionOid, /* cert_index */ 1, system_id);
|
||||
void CryptoSession::SetSystemId(uint32_t system_id) {
|
||||
if (!IsOpen()) return; // Ignore silently.
|
||||
system_id_ = system_id;
|
||||
metrics_->crypto_session_system_id_.Record(system_id_);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
||||
RETURN_IF_NULL(provisioning_id, PARAMETER_NULL);
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
RETURN_IF_NOT_OPEN(CRYPTO_SESSION_NOT_OPEN);
|
||||
|
||||
if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
// OEM Cert devices have no provisioning-unique ID embedded in them, so we
|
||||
@@ -868,7 +827,8 @@ CdmResponseType CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
||||
}
|
||||
if (pre_provision_token_type_ == kClientTokenKeybox) {
|
||||
std::string token;
|
||||
CdmResponseType status = GetTokenFromKeybox(&token);
|
||||
CdmResponseType status =
|
||||
GetTokenFromKeybox(requested_security_level_, &token);
|
||||
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
@@ -940,16 +900,6 @@ CdmResponseType CryptoSession::Open(
|
||||
LOGV("Opened session: id = %u", oec_session_id_);
|
||||
open_ = true;
|
||||
|
||||
// Get System ID and save it.
|
||||
result = GetSystemIdInternal(&system_id_);
|
||||
if (result == NO_ERROR) {
|
||||
metrics_->crypto_session_system_id_.Record(system_id_);
|
||||
} else {
|
||||
LOGE("Failed to fetch system ID");
|
||||
metrics_->crypto_session_system_id_.SetError(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Set up request ID
|
||||
uint64_t request_id_base;
|
||||
OEMCryptoResult random_sts;
|
||||
@@ -989,6 +939,8 @@ void CryptoSession::Close() {
|
||||
// Clear cached values.
|
||||
has_usage_info_support_ = kBooleanUnset;
|
||||
oem_token_.clear();
|
||||
system_id_ = NULL_SYSTEM_ID;
|
||||
pre_provision_token_type_ = kClientTokenUninitialized;
|
||||
|
||||
if (close_sts != OEMCrypto_SUCCESS) {
|
||||
LOGW("OEMCrypto_CloseSession failed: status = %d",
|
||||
@@ -1412,13 +1364,26 @@ CdmResponseType CryptoSession::LoadCertificatePrivateKey(
|
||||
|
||||
CdmResponseType CryptoSession::GetBootCertificateChain(
|
||||
std::string* bcc, std::string* additional_signature) {
|
||||
RETURN_IF_NOT_OPEN(CRYPTO_SESSION_NOT_OPEN);
|
||||
return GetBootCertificateChain(requested_security_level_, bcc,
|
||||
additional_signature);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetBootCertificateChain(
|
||||
RequestedSecurityLevel requested_security_level, std::string* bcc,
|
||||
std::string* additional_signature) {
|
||||
RETURN_IF_NULL(bcc, PARAMETER_NULL);
|
||||
RETURN_IF_NULL(additional_signature, PARAMETER_NULL);
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
LOGV("GetBootCertificateChain");
|
||||
LOGV("requested_security_level = %s",
|
||||
RequestedSecurityLevelToString(requested_security_level));
|
||||
if (pre_provision_token_type_ != kClientTokenBootCertChain) {
|
||||
return PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR;
|
||||
}
|
||||
if (requested_security_level != kLevelDefault) {
|
||||
LOGE("CDM only supports L1 BCC");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
}
|
||||
|
||||
size_t bcc_length = 0;
|
||||
size_t additional_signature_length = 0;
|
||||
@@ -3351,12 +3316,6 @@ CdmResponseType CryptoSession::LoadOtaProvisioning(
|
||||
WithOecWriteLock("LoadOtaProvisioning",
|
||||
[&] { needs_keybox_provisioning_ = false; });
|
||||
}
|
||||
CdmResponseType result = GetSystemIdInternal(&system_id_);
|
||||
if (result == NO_ERROR) {
|
||||
LOGD("New system id is %d", system_id_);
|
||||
} else {
|
||||
LOGE("Failed to fetch system ID");
|
||||
}
|
||||
return MapOEMCryptoResult(status, UNKNOWN_ERROR, "LoadOtaProvisioning");
|
||||
}
|
||||
|
||||
|
||||
190
libwvdrmengine/cdm/core/src/system_id_extractor.cpp
Normal file
190
libwvdrmengine/cdm/core/src/system_id_extractor.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine License
|
||||
// Agreement.
|
||||
#include "system_id_extractor.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
#include "privacy_crypto.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
namespace {
|
||||
// Offset within the keybox data where the device ID is stored.
|
||||
constexpr size_t kKeyboxSystemIdOffset = 4;
|
||||
// Index of certificate within cerificate chain which contains the
|
||||
// system ID (0 = leaf/device cert, 1 = intermediate/device family cert).
|
||||
constexpr size_t kOemCertSystemIdIndex = 1;
|
||||
// OID of X.509 certificate extension containing the Widevine system ID.
|
||||
const std::string kWidevineSystemIdExtensionOid = "1.3.6.1.4.1.11129.4.1.1";
|
||||
|
||||
constexpr size_t kSystemIdLength = sizeof(uint32_t);
|
||||
|
||||
constexpr bool IsSupportedSecurityLevel(CdmSecurityLevel security_level) {
|
||||
return security_level == kSecurityLevelL1 ||
|
||||
security_level == kSecurityLevelL2 ||
|
||||
security_level == kSecurityLevelL3;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
SystemIdExtractor::SystemIdExtractor(RequestedSecurityLevel security_level,
|
||||
CryptoSession* crypto_session,
|
||||
wvutil::FileSystem* fs)
|
||||
: security_level_(security_level),
|
||||
crypto_session_(crypto_session),
|
||||
fs_(fs) {
|
||||
assert(crypto_session != nullptr);
|
||||
assert(fs != nullptr);
|
||||
}
|
||||
|
||||
bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) {
|
||||
if (system_id == nullptr) {
|
||||
LOGE("Output |system_id| is null");
|
||||
return false;
|
||||
}
|
||||
if (crypto_session_->GetCachedSystemId(system_id)) {
|
||||
return true;
|
||||
}
|
||||
CdmClientTokenType type = kClientTokenUninitialized;
|
||||
const CdmResponseType status =
|
||||
crypto_session_->GetProvisioningMethod(security_level_, &type);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get provisioning method: security_level = %s, status = %d",
|
||||
RequestedSecurityLevelToString(security_level_),
|
||||
static_cast<int>(status));
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
switch (type) {
|
||||
case kClientTokenDrmCert:
|
||||
LOGE("Cannot get a system ID from a DRM certificate");
|
||||
return false;
|
||||
case kClientTokenKeybox:
|
||||
success = ExtractSystemIdProv20(system_id);
|
||||
break;
|
||||
case kClientTokenOemCert:
|
||||
success = ExtractSystemIdProv30(system_id);
|
||||
break;
|
||||
case kClientTokenBootCertChain:
|
||||
success = ExtractSystemIdProv40(system_id);
|
||||
break;
|
||||
case kClientTokenUninitialized:
|
||||
default:
|
||||
LOGE("Unexpected token type: %d", type);
|
||||
return false;
|
||||
}
|
||||
if (success && *system_id != NULL_SYSTEM_ID) {
|
||||
crypto_session_->SetSystemId(*system_id);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SystemIdExtractor::ExtractSystemIdFromKeyboxData(
|
||||
const std::string& key_data, uint32_t* system_id) {
|
||||
if (system_id == nullptr) return false;
|
||||
if (key_data.size() < (kKeyboxSystemIdOffset + kSystemIdLength)) {
|
||||
LOGE("Keybox data does not contain system ID: key_data_size = %zu",
|
||||
key_data.size());
|
||||
return false;
|
||||
}
|
||||
uint32_t system_id_nbo = 0;
|
||||
memcpy(&system_id_nbo, &key_data[kKeyboxSystemIdOffset],
|
||||
sizeof(system_id_nbo));
|
||||
*system_id = ntohl(system_id_nbo);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SystemIdExtractor::ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
||||
uint32_t* system_id) {
|
||||
if (system_id == nullptr) return false;
|
||||
return ExtractExtensionValueFromCertificate(oem_cert,
|
||||
kWidevineSystemIdExtensionOid,
|
||||
kOemCertSystemIdIndex, system_id);
|
||||
}
|
||||
|
||||
bool SystemIdExtractor::ExtractSystemIdProv20(uint32_t* system_id) {
|
||||
std::string key_data;
|
||||
const CdmResponseType status =
|
||||
crypto_session_->GetTokenFromKeybox(security_level_, &key_data);
|
||||
if (status == NEED_PROVISIONING) {
|
||||
LOGD("Keybox provisioning required, using null system ID");
|
||||
*system_id = NULL_SYSTEM_ID;
|
||||
return true;
|
||||
}
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get keybox data: security_level = %s, status = %d",
|
||||
RequestedSecurityLevelToString(security_level_),
|
||||
static_cast<int>(status));
|
||||
return false;
|
||||
}
|
||||
if (!ExtractSystemIdFromKeyboxData(key_data, system_id)) {
|
||||
LOGE("Failed to extract system ID from keybox data");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemIdExtractor::ExtractSystemIdProv30(uint32_t* system_id) {
|
||||
std::string oem_cert;
|
||||
const CdmResponseType status =
|
||||
crypto_session_->GetTokenFromOemCert(security_level_, &oem_cert);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get OEM certificate: security_level = %s, status = %d",
|
||||
RequestedSecurityLevelToString(security_level_),
|
||||
static_cast<int>(status));
|
||||
return false;
|
||||
}
|
||||
if (!ExtractSystemIdFromOemCert(oem_cert, system_id)) {
|
||||
LOGE("Failed to extract system ID from OEM certificate");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemIdExtractor::ExtractSystemIdProv40(uint32_t* system_id) {
|
||||
const CdmSecurityLevel security_level =
|
||||
crypto_session_->GetSecurityLevel(security_level_);
|
||||
if (!IsSupportedSecurityLevel(security_level)) {
|
||||
LOGE("Unsupported security level: %s",
|
||||
CdmSecurityLevelToString(security_level));
|
||||
return false;
|
||||
}
|
||||
DeviceFiles real_device_files(fs_);
|
||||
// Mock DeviceFiles for testing.
|
||||
DeviceFiles& device_files =
|
||||
(test_device_files_ ? *test_device_files_ : real_device_files);
|
||||
if (!device_files.Init(security_level)) {
|
||||
LOGE("Failed to initialize device files: security_level = %s",
|
||||
CdmSecurityLevelToString(security_level));
|
||||
return false;
|
||||
}
|
||||
std::string oem_cert;
|
||||
CryptoWrappedKey wrapped_private_key_unused;
|
||||
const DeviceFiles::CertificateState cert_state =
|
||||
device_files.RetrieveOemCertificate(&oem_cert,
|
||||
&wrapped_private_key_unused);
|
||||
if (cert_state == DeviceFiles::kCertificateNotFound) {
|
||||
LOGD("No OEM certificate available, using null system ID");
|
||||
*system_id = NULL_SYSTEM_ID;
|
||||
return true;
|
||||
}
|
||||
if (cert_state != DeviceFiles::kCertificateValid) {
|
||||
LOGE(
|
||||
"Failed to retrieve OEM certificate: "
|
||||
"security_level = %s, cert_state = %s",
|
||||
CdmSecurityLevelToString(security_level),
|
||||
DeviceFiles::CertificateStateToString(cert_state));
|
||||
return false;
|
||||
}
|
||||
if (!ExtractSystemIdFromOemCert(oem_cert, system_id)) {
|
||||
LOGE("Failed to extract system ID from OEM certificate");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace wvcdm
|
||||
Reference in New Issue
Block a user