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:
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user