Revise a few metrics and add unit tests.

This is a merge of Widevine cl 39040.

A few of the metrics were not implemented, or implemented incorrectly in
O MR1. This cleans them up

Bug: 64001676

Test: Re-ran unit tests and added some additional tests. GPlay Movies check.

Change-Id: I1e8bcc36fecd76e72d853306075bc46d82f45161
This commit is contained in:
Adam Stone
2018-01-23 17:43:35 -08:00
parent 7bbe8e3bf1
commit 795cf8a624
12 changed files with 320 additions and 89 deletions

View File

@@ -23,6 +23,7 @@
#include "wv_cdm_constants.h"
namespace {
// Encode unsigned integer into a big endian formatted string
std::string EncodeUint32(unsigned int u) {
std::string s;
@@ -32,6 +33,7 @@ std::string EncodeUint32(unsigned int u) {
s.append(1, (u >> 0) & 0xFF);
return s;
}
size_t GetOffset(std::string message, std::string field) {
size_t pos = message.find(field);
if (pos == std::string::npos) {
@@ -40,6 +42,7 @@ size_t GetOffset(std::string message, std::string field) {
}
return pos;
}
void GenerateMacContext(const std::string& input_context,
std::string* deriv_context) {
if (!deriv_context) {
@@ -677,6 +680,7 @@ class SubLicenseKeySession : public KeySession {
CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
: metrics_(metrics),
system_id_(-1),
open_(false),
update_usage_table_after_close_session_(false),
is_destination_buffer_type_valid_(false),
@@ -700,9 +704,11 @@ CryptoSession::~CryptoSession() {
}
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
OEMCrypto_ProvisioningMethod method;
OEMCrypto_ProvisioningMethod method =
OEMCrypto_GetProvisioningMethod(requested_security_level_);
metrics_->oemcrypto_provisioning_method_.Record(method);
CdmClientTokenType type;
switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) {
switch (method) {
case OEMCrypto_OEMCertificate:
type = kClientTokenOemCert;
break;
@@ -714,7 +720,8 @@ bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
break;
case OEMCrypto_ProvisioningError:
default:
LOGE("OEMCrypto_GetProvisioningMethod failed", method);
LOGE("OEMCrypto_GetProvisioningMethod failed. %d", method);
metrics_->oemcrypto_provisioning_method_.SetError(method);
return false;
}
*token_type = type;
@@ -805,13 +812,14 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) {
size_t buf_size = temp_buffer.size();
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
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 true;
}
if (OEMCrypto_ERROR_SHORT_BUFFER && !retrying) {
if (status == OEMCrypto_ERROR_SHORT_BUFFER && !retrying) {
temp_buffer.resize(buf_size);
retrying = true;
continue;
@@ -821,42 +829,28 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) {
}
}
bool CryptoSession::GetClientToken(std::string* token) {
if (!token) {
LOGE("CryptoSession::GetClientToken : No token passed to method.");
return false;
}
LOGV("CryptoSession::GetClientToken: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
// Only keybox is used for client token. All other cases use DRM Cert.
if (pre_provision_token_type_ != kClientTokenKeybox) {
return false;
}
return GetTokenFromKeybox(token);
}
bool CryptoSession::GetProvisioningToken(std::string* token) {
if (!token) {
LOGE("CryptoSession::GetProvisioningToken : No token passed to method.");
metrics_->crypto_session_get_token_.Increment(false);
return false;
}
LOGV("CryptoSession::GetProvisioningToken: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
metrics_->crypto_session_get_token_.Increment(false);
return false;
}
bool result = false;
if (pre_provision_token_type_ == kClientTokenKeybox) {
return GetTokenFromKeybox(token);
result = GetTokenFromKeybox(token);
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
return GetTokenFromOemCert(token);
} else {
return false;
result = GetTokenFromOemCert(token);
}
metrics_->crypto_session_get_token_.Increment(result);
return result;
}
CdmSecurityLevel CryptoSession::GetSecurityLevel() {
@@ -977,7 +971,19 @@ bool CryptoSession::GetSystemId(uint32_t* system_id) {
LOGV("CryptoSession::GetSystemId: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
if (!initialized_ || !open_) {
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 and that
// the caller has acquired the crypto_lock_ before making this call.
bool CryptoSession::GetSystemIdInternal(uint32_t* system_id) {
if (!system_id) {
LOGE("CryptoSession::GetSystemIdInternal: No system_id passed to method.");
return false;
}
@@ -991,8 +997,8 @@ bool CryptoSession::GetSystemId(uint32_t* system_id) {
metrics_, oemcrypto_get_key_data_, sts, metrics::Pow2Bucket(buf_size));
if (OEMCrypto_SUCCESS != sts) {
LOGE("CryptoSession::GetSystemId: OEMCrypto_GetKeyData failed with %d",
sts);
LOGE("CryptoSession::GetSystemIdInternal: "
"OEMCrypto_GetKeyData failed with %d", sts);
return false;
}
@@ -1010,9 +1016,13 @@ bool CryptoSession::GetSystemId(uint32_t* system_id) {
} else {
return false;
}
} else if (pre_provision_token_type_ == kClientTokenDrmCert) {
// TODO(blueeyes): Support loading the system id from a pre-provisioned
// Drm certificate.
return true;
} else {
LOGE("CryptoSession::GetSystemId: Unsupported pre-provision token type %d",
pre_provision_token_type_);
LOGE("CryptoSession::GetSystemIdInternal: "
"Unsupported pre-provision token type %d", pre_provision_token_type_);
return false;
}
}
@@ -1023,7 +1033,8 @@ bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
const boringssl_ptr<STACK_OF(X509), DeleteX509Stack> x509_stack(
sk_X509_new_null());
if (x509_stack.get() == NULL) {
LOGE("CryptoSession::GetSystemId: Unable to allocate X509 Stack.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to allocate X509 Stack.");
return false;
}
@@ -1031,7 +1042,8 @@ bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
CBS_init(&pkcs7, reinterpret_cast<const uint8_t*>(oem_cert.data()),
oem_cert.size());
if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) {
LOGE("CryptoSession::GetSystemId: Error getting certificate chain.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Error getting certificate chain.");
return false;
}
@@ -1039,14 +1051,16 @@ bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
// Get the Widevine intermediate cert from the stack
if (sk_X509_num(certs) != 2) {
LOGE("CryptoSession::GetSystemId: Expected 2 certificates in chain, got %d",
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Expected 2 certificates in chain, got %d",
sk_X509_num(certs));
return false;
}
X509* const intermediate_cert = sk_X509_value(certs, 1);
if (!intermediate_cert) {
LOGE("CryptoSession::GetSystemId: Unable to get intermediate cert.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get intermediate cert.");
return false;
}
@@ -1055,16 +1069,15 @@ bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
for (int i = 0; i < extension_count; ++i) {
X509_EXTENSION* const extension = X509_get_ext(intermediate_cert, i);
if (!extension) {
LOGE("CryptoSession::GetSystemId: Unable to get cert extension %d", i);
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get cert extension %d", i);
continue;
}
ASN1_OBJECT* const extension_object = X509_EXTENSION_get_object(extension);
if (!extension_object) {
LOGE(
"CryptoSession::GetSystemId: Unable to get object of cert "
"extension %d",
i);
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get object of cert extension %d", i);
continue;
}
@@ -1078,35 +1091,31 @@ bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
ASN1_OCTET_STRING* const octet_str = X509_EXTENSION_get_data(extension);
if (!octet_str) {
LOGE(
"CryptoSession::GetSystemId: Unable to get data of Widevine System "
"ID extension.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to get data of Widevine System ID extension.");
return false;
}
const unsigned char* data = octet_str->data;
if (!data) {
LOGE(
"CryptoSession::GetSystemId: Null data in Widevine System ID "
"extension.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Null data in Widevine System ID extension.");
return false;
}
ASN1_INTEGER* const asn1_integer =
d2i_ASN1_INTEGER(NULL, &data, octet_str->length);
if (!asn1_integer) {
LOGE(
"CryptoSession::GetSystemId: Unable to decode data in Widevine "
"System ID extension.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to decode data in Widevine System ID extension.");
return false;
}
const long system_id_long = ASN1_INTEGER_get(asn1_integer);
ASN1_INTEGER_free(asn1_integer);
if (system_id_long == -1) {
LOGE(
"CryptoSession::GetSystemId: Unable to decode ASN integer in "
"Widevine System ID extension.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Unable to decode ASN integer in Widevine System ID extension.");
return false;
}
@@ -1114,7 +1123,8 @@ bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
return true;
}
LOGE("CryptoSession::GetSystemId: Widevine System ID extension not found.");
LOGE("CryptoSession::ExtractSystemIdFromOemCert: "
"Widevine System ID extension not found.");
return false;
}
@@ -1191,13 +1201,25 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
session_count_, (int)initialized_);
return UNKNOWN_ERROR;
}
// Get System ID and save it.
if (GetSystemIdInternal(&system_id_)) {
metrics_->crypto_session_system_id_.Record(system_id_);
} else {
LOGE("CryptoSession::Open: Failed to fetch system id.");
metrics_->crypto_session_system_id_.SetError(LOAD_SYSTEM_ID_ERROR);
return LOAD_SYSTEM_ID_ERROR;
}
OEMCryptoResult random_sts = OEMCrypto_GetRandom(
reinterpret_cast<uint8_t*>(&request_id_base_), sizeof(request_id_base_));
metrics_->oemcrypto_get_random_.Increment(random_sts);
++request_id_index_;
CdmUsageSupportType usage_support_type;
if (GetUsageSupportType(&usage_support_type) == NO_ERROR) {
CdmResponseType result = GetUsageSupportType(&usage_support_type);
if (result == NO_ERROR) {
metrics_->oemcrypto_usage_table_support_.Record(usage_support_type);
if (usage_support_type == kUsageEntrySupport) {
CdmSecurityLevel security_level = GetSecurityLevel();
if (security_level == kSecurityLevelL1 ||
@@ -1224,6 +1246,8 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
usage_table_header_ = *header;
}
}
} else {
metrics_->oemcrypto_usage_table_support_.SetError(result);
}
// TODO(gmorgan, jfore): resolve handling of usage records in sublicenses
@@ -1361,6 +1385,7 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
OEMCryptoResult sts =
OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
metrics_->oemcrypto_get_oem_public_certificate_.Increment(sts);
LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_);
M_TIME(
@@ -2032,10 +2057,15 @@ bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message,
// Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key
// and 0 as wrapped_rsa_key_length.
size_t wrapped_private_key_length = 0;
OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv, NULL,
&wrapped_private_key_length);
OEMCryptoResult status;
M_TIME(
status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv, NULL,
&wrapped_private_key_length),
metrics_,
oemcrypto_rewrap_device_rsa_key_30_,
status);
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGE("OEMCrypto_RewrapDeviceRSAKey30 failed getting wrapped key length");
@@ -2043,11 +2073,15 @@ bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message,
}
wrapped_private_key->resize(wrapped_private_key_length);
status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv,
reinterpret_cast<uint8_t*>(&(*wrapped_private_key)[0]),
&wrapped_private_key_length);
M_TIME(
status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv,
reinterpret_cast<uint8_t*>(&(*wrapped_private_key)[0]),
&wrapped_private_key_length),
metrics_,
oemcrypto_rewrap_device_rsa_key_30_,
status);
wrapped_private_key->resize(wrapped_private_key_length);