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:
Alex Dale
2022-05-13 18:39:19 +00:00
committed by Automerger Merge Worker
12 changed files with 945 additions and 431 deletions

View File

@@ -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);

View File

@@ -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");
}

View 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