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

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