Merges to android Pi release (part 6)

These are a set of CLs merged from the wv cdm repo to the android repo.

* Enable Cast for Android Things build.

  Author: Thoren Paulson <thoren@google.com>

  [ Merge of http://go/wvgerrit/29941 ]

  Added a path to make_cast_libwvlevel3 for Android Things. Added the new
  system id to the preprocessor guards in android_keybox.cpp. Guarded the
  references to stderr in page_allocator.cpp because for some reason they
  don't get resolved when we link against the resulting library.

  BUG: 63443584

* Resolve memory leaks in use of OpenSSL.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/32700 ]

  Use of EVP_CIPHER_CTX requires a call to EVP_CIPHER_CTX_cleanup().

* Memory leak in OpenSSL RSA key handling.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/32621 ]

  This fixes a range of tests. --gtest_filter="CdmDecrypt*" runs
  five tests and still loses 5 objects totalling 1320 bytes (down
  from 6200 bytes).

* Unit test and mock OEMCrypto memory leaks.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/32640 ]

  More memory leak cleanup. All remaining leaks are due
  to calls to CRYPTO_malloc() without the matching free
  (i.e., calls into openssl).

* Clean up memory leaks in tests.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/32600 ]

  This is the first pass at cleaning up memory leaks. These leaks
  were affecting a lot of tests, making it hard to identify more
  serious leaks.

  Switch to unique_ptr<> pointers for CdmEngine in
  generic_crypto_unittest tests for FileSystem object in
  mock OEMCrypto's CryptoEngine object.

* Fix broken tests - linux-only & address sanitizer failures.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/32460 ]

  Fix broken test:
    WvCdmEnginePreProvTestStaging.ServiceCertificateInitialNoneTest

  Fix failures found by address sanitizer:
    DeviceFilesUsageInfoTest.RetrieveByProviderSessionToken
    DeviceFilesUsageInfoTest.UpdateUsageInfo

  NOTE: address sanitizer cannot handle EXPECT_CALL macros containing
  a call with a Contains matcher as an argument, e.g.:

  EXPECT_CALL(file,
              Write(Contains(certificate, wrapped_private_key, 0),
                    Gt(certificate.size() + wrapped_private_key.size())))

  The address sanitizer reports a crash, issues a report, and stops. A
  temporary fix is to replace the "Contains()" argument with "_".

* Usage license handling corrections

  Author: Rahul Frias <rfrias@google.com>

  [ Merge of http://go/wvgerrit/28540 ]

  Validate that offline licenses that do not contain a provider session
  token are not handled by the TEE.

  BUG: 38490468

  Test: WV Unit/integration tests, GtsMediaTestCases,
        WvCdmRequestLicenseTest.ReleaseRetryL3OfflineKeySessionUsageDisabledTest

* UsageTableEntry::CopyOldUsageEntry memcpy read out of range.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/32220 ]

  The function copies the pst from a variable length input vector
  into a 256 byte character array. But the length argument was a
  fixed value - MAC_KEY_SIZE. Depending on the actual PST length this
  can lead to memcpy reading out of bounds or the PST getting truncated.

BUG: 71650075
Test: Not currently passing. Will be addressed in a subsequent
  commit in the chain.

Change-Id: I81a4593d7d04d0ef6069ce48d0601b6fbdd85de9
This commit is contained in:
Rahul Frias
2018-01-09 22:56:21 -08:00
parent b7c9ad57c9
commit 00da44bb68
63 changed files with 977 additions and 582 deletions

View File

@@ -16,7 +16,6 @@
#include "file_store.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "metrics_front_end.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
@@ -188,7 +187,8 @@ CdmResponseType CdmEngine::OpenSession(
CloseExpiredReleaseSessions();
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_,
metrics_.AddSession()));
CdmResponseType sts = new_session->Init(&service_certificate_, property_set,
forced_session_id, event_listener);
if (sts != NO_ERROR) {
@@ -545,13 +545,13 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
const std::string& query_token,
std::string* query_response) {
LOGI("CdmEngine::QueryStatus");
CryptoSession crypto_session(&metrics_);
CryptoSession crypto_session(metrics_.GetCryptoMetrics());
if (security_level == kLevel3) {
CdmResponseType status;
M_TIME(
status = crypto_session.Open(
kLevel3),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
kLevel3);
@@ -567,7 +567,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
CdmSecurityLevel level;
M_TIME(
level = crypto_session.GetSecurityLevel(),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_get_security_level_,
level);
switch (level) {
@@ -594,7 +594,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
M_TIME(
got_id = crypto_session.GetExternalDeviceUniqueId(
&deviceId),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_get_device_unique_id_,
got_id);
if (!got_id) {
@@ -609,7 +609,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
M_TIME(
got_id = crypto_session.GetSystemId(
&system_id),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_get_system_id_,
got_id,
system_id);
@@ -646,7 +646,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
M_TIME(
got_info = crypto_session.UsageInformationSupport(
&supports_usage_reporting),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_usage_information_support_,
got_info);
if (!got_info) {
@@ -854,7 +854,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
if (NULL == cert_provisioning_.get()) {
cert_provisioning_.reset(
new CertificateProvisioning(&metrics_, &service_certificate_));
new CertificateProvisioning(metrics_.GetCryptoMetrics(),
&service_certificate_));
}
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_type, cert_authority,
@@ -896,12 +897,12 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
if (NULL == cert_provisioning_.get()) {
// Certificate provisioning object has been released. Check if a concurrent
// provisioning attempt has succeeded before declaring failure.
CryptoSession crypto_session(&metrics_);
CryptoSession crypto_session(metrics_.GetCryptoMetrics());
CdmResponseType status;
M_TIME(
status = crypto_session.Open(
cert_provisioning_requested_security_level_),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
cert_provisioning_requested_security_level_);
@@ -914,7 +915,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
CdmSecurityLevel security_level;
M_TIME(
security_level = crypto_session.GetSecurityLevel(),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_get_security_level_,
security_level);
if (!IsProvisioned(security_level)) {
@@ -968,14 +969,15 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
}
CdmResponseType CdmEngine::DeleteUsageTable(CdmSecurityLevel security_level) {
scoped_ptr<CryptoSession> crypto_session(new CryptoSession(&metrics_));
scoped_ptr<CryptoSession> crypto_session(
new CryptoSession(metrics_.GetCryptoMetrics()));
CdmResponseType status;
M_TIME(
status = crypto_session->Open(
security_level == kSecurityLevelL3 ?
kLevel3 :
kLevelDefault),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
@@ -986,11 +988,12 @@ CdmResponseType CdmEngine::DeleteUsageTable(CdmSecurityLevel security_level) {
}
M_TIME(
status = crypto_session->DeleteAllUsageReports(),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_delete_all_usage_reports_,
status);
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageTable: error deleteing usage reports: %d", status);
LOGE("CdmEngine::DeleteUsageTable: error deleteing usage reports: %d",
status);
}
return status;
}
@@ -1049,7 +1052,8 @@ CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
}
// Got provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession(&metrics_));
scoped_ptr<CryptoSession> crypto_session(
new CryptoSession(metrics_.GetCryptoMetrics()));
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
@@ -1079,7 +1083,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
}
usage_property_set_->set_security_level(kLevelDefault);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(file_system_));
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error: %d", status);
@@ -1099,7 +1103,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
ssid, &usage_data)) {
usage_property_set_->set_security_level(kLevel3);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(file_system_));
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
@@ -1176,7 +1180,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
usage_property_set_->set_security_level(requested_security_level);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(file_system_));
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
@@ -1257,7 +1261,8 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(
}
// Got at least one provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession(&metrics_));
scoped_ptr<CryptoSession> crypto_session(
new CryptoSession(metrics_.GetCryptoMetrics()));
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
@@ -1285,7 +1290,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
? kLevel3
: kLevelDefault;
usage_property_set_->set_security_level(security_level);
usage_session_.reset(new CdmSession(file_system_));
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
usage_session_->Init(usage_property_set_.get());
switch (usage_session_->get_usage_support_type()) {
@@ -1730,19 +1735,20 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
if (!file_system_->Exists(device_base_path_level1) &&
!file_system_->Exists(device_base_path_level3)) {
scoped_ptr<CryptoSession> crypto_session(new CryptoSession(&metrics_));
scoped_ptr<CryptoSession> crypto_session(
new CryptoSession(metrics_.GetCryptoMetrics()));
CdmResponseType status;
M_TIME(
status = crypto_session->Open(
cert_provisioning_requested_security_level_),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
cert_provisioning_requested_security_level_);
if (NO_ERROR == status) {
M_TIME(
status = crypto_session->DeleteAllUsageReports(),
&metrics_,
metrics_.GetCryptoMetrics(),
crypto_session_delete_all_usage_reports_,
status);
if (NO_ERROR != status) {

View File

@@ -12,7 +12,6 @@
#include "clock.h"
#include "file_store.h"
#include "log.h"
#include "metrics_front_end.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
@@ -25,10 +24,10 @@ const size_t kKeySetIdLength = 14;
namespace wvcdm {
CdmSession::CdmSession(FileSystem* file_system) :
CdmSession::CdmSession(FileSystem* file_system,
metrics::SessionMetrics* metrics) :
metrics_(metrics),
initialized_(false),
closed_(false),
crypto_session_(new CryptoSession(&metrics_)),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
is_offline_(false),
@@ -45,6 +44,9 @@ CdmSession::CdmSession(FileSystem* file_system) :
usage_entry_number_(0),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(new CryptoSession(crypto_metrics_));
life_span_.Start();
}
@@ -55,7 +57,10 @@ CdmSession::~CdmSession() {
}
Properties::RemoveSessionPropertySet(session_id_);
M_RECORD(&metrics_, cdm_session_life_span_, life_span_.AsMs());
if (metrics_) {
M_RECORD(metrics_, cdm_session_life_span_, life_span_.AsMs());
metrics_->SetCompleted();
}
}
CdmResponseType CdmSession::Init(
@@ -80,16 +85,15 @@ CdmResponseType CdmSession::Init(
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->Open(
requested_security_level_),
&metrics_,
sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_,
crypto_session_open_,
sts,
requested_security_level_);
if (NO_ERROR != sts) return sts;
M_TIME(
security_level_ = crypto_session_->GetSecurityLevel(),
&metrics_,
crypto_metrics_,
crypto_session_get_security_level_,
security_level_);
if (!file_handle_->Init(security_level_)) {
@@ -126,7 +130,7 @@ CdmResponseType CdmSession::Init(
M_TIME(
get_client_token_sts = crypto_session_->GetClientToken(
&client_token),
&metrics_,
crypto_metrics_,
crypto_session_get_token_,
get_client_token_sts);
if (!get_client_token_sts) {
@@ -144,7 +148,7 @@ CdmResponseType CdmSession::Init(
M_TIME(
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(
wrapped_key),
&metrics_,
crypto_metrics_,
crypto_session_load_certificate_private_key_,
load_cert_sts);
if(!load_cert_sts) {
@@ -166,6 +170,7 @@ CdmResponseType CdmSession::Init(
session_id_ =
Properties::AlwaysUseKeySetIds() ? key_set_id_ : GenerateSessionId();
metrics_->SetSessionId(session_id_);
if (session_id_.empty()) {
LOGE("CdmSession::Init: empty session ID");
@@ -222,14 +227,20 @@ CdmResponseType CdmSession::RestoreOfflineSession(
return GET_RELEASED_LICENSE_ERROR;
}
std::string provider_session_token;
if (usage_support_type_ == kUsageEntrySupport) {
CdmResponseType sts = usage_table_header_->LoadEntry(crypto_session_.get(),
usage_entry_,
usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = %d",
sts);
return sts;
if (!license_parser_->ExtractProviderSessionToken(
key_response_, &provider_session_token)) {
provider_session_token.clear();
} else {
CdmResponseType sts =
usage_table_header_->LoadEntry(crypto_session_.get(), usage_entry_,
usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = "
"%d", sts);
return sts;
}
}
}
@@ -246,7 +257,8 @@ CdmResponseType CdmSession::RestoreOfflineSession(
}
}
if (usage_support_type_ == kUsageEntrySupport) {
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty()) {
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
@@ -432,7 +444,8 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
if (sts != KEY_ADDED) {
CdmResponseType sts =
usage_table_header_->DeleteEntry(usage_entry_number_,
file_handle_.get(), &metrics_);
file_handle_.get(),
crypto_metrics_);
if (sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d", sts);
}
@@ -449,8 +462,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
license_parser_->provider_session_token().c_str());
if (is_offline_ || has_provider_session_token()) {
if (usage_support_type_ == kUsageEntrySupport)
if (has_provider_session_token() &&
usage_support_type_ == kUsageEntrySupport) {
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
}
if (!is_offline_)
usage_provider_session_token_ =
@@ -614,7 +629,8 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
if (KEY_MESSAGE != status) return status;
if (usage_support_type_ == kUsageEntrySupport) {
if (has_provider_session_token() &&
usage_support_type_ == kUsageEntrySupport) {
status = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
if (status != NO_ERROR) {
@@ -645,7 +661,8 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
if (is_offline_ || has_provider_session_token()) {
DeleteLicense();
if (usage_support_type_ == kUsageEntrySupport) {
if (usage_support_type_ == kUsageEntrySupport &&
has_provider_session_token()) {
sts = DeleteUsageEntry(usage_entry_number_);
if (NO_ERROR != sts) return sts;
}
@@ -654,7 +671,8 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
}
CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
if (usage_support_type_ != kUsageEntrySupport) {
if (usage_support_type_ != kUsageEntrySupport ||
!has_provider_session_token()) {
LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d",
usage_support_type_);
return INCORRECT_USAGE_SUPPORT_TYPE_1;
@@ -664,10 +682,10 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
// it, so close and reopen session.
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(new CryptoSession(&metrics_));
crypto_session_.reset(new CryptoSession(crypto_metrics_));
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
&metrics_,
crypto_metrics_,
crypto_session_open_,
sts,
requested_security_level_);
@@ -688,7 +706,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
return usage_table_header_->DeleteEntry(usage_entry_number,
file_handle_.get(),
&metrics_);
crypto_metrics_);
}
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
@@ -838,7 +856,7 @@ CdmResponseType CdmSession::DeleteMultipleUsageInformation(
M_TIME(
sts = crypto_session_->DeleteMultipleUsageInformation(
provider_session_tokens),
&metrics_,
crypto_metrics_,
crypto_session_delete_multiple_usage_information_,
sts);
return sts;
@@ -852,7 +870,7 @@ CdmResponseType CdmSession::UpdateUsageTableInformation() {
if (sts == NO_ERROR && usage_support_type == kUsageTableSupport) {
M_TIME(
sts = crypto_session_->UpdateUsageInformation(),
&metrics_,
crypto_metrics_,
crypto_session_update_usage_information_,
sts);
return sts;
@@ -862,7 +880,8 @@ CdmResponseType CdmSession::UpdateUsageTableInformation() {
}
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (usage_support_type_ != kUsageEntrySupport) {
if (usage_support_type_ != kUsageEntrySupport ||
!has_provider_session_token()) {
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected usage type "
"supported: %d", usage_support_type_);
return INCORRECT_USAGE_SUPPORT_TYPE_2;
@@ -900,7 +919,7 @@ CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
iv,
algorithm,
out_buffer),
&metrics_,
crypto_metrics_,
crypto_session_generic_encrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
@@ -925,7 +944,7 @@ CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
iv,
algorithm,
out_buffer),
&metrics_,
crypto_metrics_,
crypto_session_generic_decrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
@@ -948,7 +967,7 @@ CdmResponseType CdmSession::GenericSign(const std::string& message,
key_id,
algorithm,
signature),
&metrics_,
crypto_metrics_,
crypto_session_generic_sign_,
sts,
metrics::Pow2Bucket(message.size()),
@@ -967,7 +986,7 @@ CdmResponseType CdmSession::GenericVerify(const std::string& message,
key_id,
algorithm,
signature),
&metrics_,
crypto_metrics_,
crypto_session_generic_verify_,
sts,
metrics::Pow2Bucket(message.size()),

View File

@@ -12,7 +12,6 @@
#include "crypto_key.h"
#include "log.h"
#include "metrics_front_end.h"
#include "openssl/sha.h"
#include "properties.h"
#include "pst_report.h"
@@ -45,7 +44,7 @@ uint64_t CryptoSession::request_id_index_ = 0;
UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL;
UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL;
CryptoSession::CryptoSession(metrics::MetricsGroup* metrics)
CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics)
: metrics_(metrics),
open_(false),
update_usage_table_after_close_session_(false),
@@ -234,34 +233,43 @@ bool CryptoSession::GetProvisioningToken(std::string* token) {
CdmSecurityLevel CryptoSession::GetSecurityLevel() {
LOGV("CryptoSession::GetSecurityLevel");
if (!initialized_) {
M_RECORD(metrics_, oemcrypto_security_level_, 0,
kSecurityLevelUninitialized, requested_security_level_);
return kSecurityLevelUninitialized;
}
std::string security_level;
M_TIME(
security_level = OEMCrypto_SecurityLevel(
requested_security_level_),
metrics_,
oemcrypto_security_level_,
security_level,
requested_security_level_);
wvcdm::metrics::TimerMetric timer;
timer.Start();
std::string security_level =
OEMCrypto_SecurityLevel(requested_security_level_);
double clock_time = timer.AsUs();
if ((security_level.size() != 2) || (security_level.at(0) != 'L')) {
M_RECORD(metrics_, oemcrypto_security_level_, clock_time,
kSecurityLevelUnknown, requested_security_level_);
return kSecurityLevelUnknown;
}
CdmSecurityLevel cdm_security_level;
switch (security_level.at(1)) {
case '1':
return kSecurityLevelL1;
cdm_security_level = kSecurityLevelL1;
break;
case '2':
return kSecurityLevelL2;
cdm_security_level = kSecurityLevelL2;
break;
case '3':
return kSecurityLevelL3;
cdm_security_level = kSecurityLevelL3;
break;
default:
return kSecurityLevelUnknown;
cdm_security_level = kSecurityLevelUnknown;
break;
}
return kSecurityLevelUnknown;
M_RECORD(metrics_, oemcrypto_security_level_, clock_time,
cdm_security_level, requested_security_level_);
return cdm_security_level;
}
bool CryptoSession::GetInternalDeviceUniqueId(std::string* device_id) {
@@ -444,8 +452,10 @@ uint8_t CryptoSession::GetSecurityPatchLevel() {
}
CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
LOGV("CryptoSession::Open: Lock: requested_security_level: %d",
requested_security_level);
LOGD("CryptoSession::Open: Lock: requested_security_level: %s",
requested_security_level == kLevel3
? QUERY_VALUE_SECURITY_LEVEL_L3.c_str()
: QUERY_VALUE_SECURITY_LEVEL_DEFAULT.c_str());
AutoLock auto_lock(crypto_lock_);
if (!initialized_) return UNKNOWN_ERROR;
if (open_) return NO_ERROR;
@@ -719,6 +729,10 @@ CdmResponseType CryptoSession::LoadKeys(
} else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) {
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
result = INSUFFICIENT_CRYPTO_RESOURCES;
} else if (OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE == sts) {
// Handle vendor specific error
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
result = NEED_PROVISIONING;
} else {
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
result = LOAD_KEY_ERROR;
@@ -1750,11 +1764,15 @@ bool CryptoSession::GetSrmVersion(uint16_t* srm_version) {
}
OEMCryptoResult status = OEMCrypto_GetCurrentSRMVersion(srm_version);
if (OEMCrypto_SUCCESS != status) {
LOGW("OEMCrypto_GetCurrentSRMVersion fails with %d", status);
return false;
switch (status) {
case OEMCrypto_SUCCESS:
return true;
case OEMCrypto_ERROR_NOT_IMPLEMENTED:
return false;
default:
LOGW("OEMCrypto_GetCurrentSRMVersion fails with %d", status);
return false;
}
return true;
}
bool CryptoSession::IsSrmUpdateSupported() {

View File

@@ -163,7 +163,7 @@ bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
uint32_t* system_id) {
LOGI("ExtractDeviceInfo Entry");
if (!serial_number && !system_id) {
LOGE("Invalid paramters to DeviceFiles::ExtractDeviceInfo");
LOGE("DeviceFiles::ExtractDeviceInfo: invalid parameter.");
return false;
}
@@ -1239,7 +1239,7 @@ bool DeviceFiles::FileExists(const std::string& name) {
bool DeviceFiles::ListFiles(std::vector<std::string>* names) {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
LOGW("DeviceFiles::ListFiles: Unable to get base path");
return false;
}
return file_system_->List(path, names);

View File

@@ -486,13 +486,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
license.policy().can_persist())
is_offline_ = true;
LOGV("Get Provider_session_token:");
if (license.id().has_provider_session_token()) {
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
LOGV("Provider_session_token=%s", provider_session_token_.c_str());
} else {
LOGV("NO Provider_session_token");
}
LOGV("provider_session_token: %s", provider_session_token_.empty() ?
"N/A" : provider_session_token_.c_str());
if (license.policy().has_renewal_server_url()) {
server_url_ = license.policy().renewal_server_url();
@@ -803,7 +801,8 @@ bool CdmLicense::ExtractProviderSessionToken(
return false;
}
if (license.id().has_provider_session_token()) {
if (license.id().has_provider_session_token() &&
!license.id().provider_session_token().empty()) {
*provider_session_token = license.id().provider_session_token();
return true;
}

View File

@@ -29,8 +29,7 @@
#include "level3.h"
#include "lock.h"
#include "log.h"
#include "metrics_front_end.h"
#include "metrics_group.h"
#include "metrics_collections.h"
#include "properties.h"
#include "wv_cdm_constants.h"
@@ -41,6 +40,8 @@ using wvcdm::kLevel3;
namespace {
static const size_t kMaxGenericEncryptChunkSize = 100*1024;
const OEMCryptoResult kOemCryptoResultVendorSpecificError1 =
static_cast<OEMCryptoResult>(10008);
typedef struct {
const uint8_t* key_id;
@@ -375,6 +376,162 @@ void clear_cache_function(void *page, size_t len) {
#endif
}
// The WatchDog looks after a worker thread that is trying to initialize L3.
// Once in a rare while, the L3 init does not finish and eats up CPU cycles.
// If that happens, the watchdog thread will give up and return an error.
class WatchDog {
public:
// Created by main thread.
WatchDog() {
pthread_mutex_init(&mutex_, NULL);
pthread_cond_init(&condition_, NULL);
status_ = OEMCrypto_SUCCESS;
gave_up_ = false;
}
// Deleted by either thread.
~WatchDog() {
pthread_cond_destroy(&condition_);
}
// Starts worker thread.
void StartThread() {
running_ = true;
if(pthread_create(&thread_, NULL, RunWatchDog, this)) {
LOGE("Could not create watch dog thread.");
status_ = OEMCrypto_ERROR_INIT_FAILED;
running_ = false;
return;
}
}
// Function called by new worker thread in pthread_create.
static void *RunWatchDog(void *watcher) {
WatchDog* dog = reinterpret_cast<WatchDog *>(watcher);
dog->DoInit();
dog->SignalDoneAndCleanUp();
return NULL;
}
// Called by worker thread.
void DoInit() {
std::string base_path;
wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&base_path);
status_ = Level3_Initialize(clear_cache_function,
base_path.c_str());
}
std::string FailureFilename() {
std::string path;
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGW("WatchDog::FailureFilename: Unable to get base path");
return "/data/l3_failure_file";
}
path += "l3_failure_file";
return path;
}
// Check to see if the failure file was created before that last abort.
void CheckForPreviousFailure(wvcdm::metrics::CryptoMetrics* metrics) {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
if (!file_system.Exists(filename)) return;
wvcdm::File* file = file_system.Open(filename, file_system.kReadOnly);
if (file) {
uint32_t flag = 0;
ssize_t size = sizeof(flag);
ssize_t size_read = file->Read(reinterpret_cast<char*>(&flag), size);
file->Close();
file_system.Remove(filename);
if (size == size_read && flag) {
LOGE("Previous L3 Init failed.");
if (metrics == nullptr) return;
M_RECORD(
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_L3_INITIALIZATION_FAILED);
}
}
}
// Save the failure file before we abort.
void SaveFailureInformation() {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
LOGD("failure filename = %s", filename.c_str());
wvcdm::File* file = file_system.Open(
filename, file_system.kCreate | file_system.kTruncate);
if (!file) {
LOGE("Could not create file %s", filename.c_str());
return;
}
uint32_t flag = 0x6261640a; // bad
ssize_t size = sizeof(flag);
ssize_t size_written = file->Write(reinterpret_cast<char*>(&flag), size);
file->Close();
if (size != size_written) {
LOGE("Wrote %d bytes, not %d, to file %s", size_written, size,
filename.c_str());
} else {
LOGE("I wrote %d to %s", size_written, filename.c_str());
}
}
// Called by worker thread after DoInit has finshed.
void SignalDoneAndCleanUp() {
pthread_mutex_lock(&mutex_);
running_ = false;
pthread_cond_signal(&condition_);
// If the main thread gave up, it won't delete this, so we must.
bool should_delete = gave_up_;
pthread_mutex_unlock(&mutex_);
// https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
if (should_delete) delete this;
}
// Called by main thread to wait for worker thread.
OEMCryptoResult WaitForStatusAndCleanUp() {
pthread_mutex_lock(&mutex_);
struct timespec time_to_giveup;
clock_gettime(CLOCK_REALTIME, &time_to_giveup);
time_to_giveup.tv_sec += 5; // wait 5 seconds.
if (running_) {
pthread_cond_timedwait(&condition_, &mutex_, &time_to_giveup);
}
if (running_) {
gave_up_ = true;
status_ = OEMCrypto_ERROR_INIT_FAILED;
LOGE("XXX WATCH DOG ERROR XXX");
SaveFailureInformation();
// This is controversial. The argument for an abort here is that if we
// do not abort, we will suck all the life out of the user's battery. By
// saving information to the file system, above, we can still track
// metrics.
abort();
}
// If we gave up waiting for init thread, we should not delete the mutex
// out from under it.
bool should_delete = !gave_up_;
OEMCryptoResult status = status_;
pthread_mutex_unlock(&mutex_);
if (should_delete) delete this;
return status;
}
OEMCryptoResult status() { return status_; }
private:
OEMCryptoResult status_;
pthread_t thread_;
pthread_mutex_t mutex_;
pthread_cond_t condition_;
bool running_;
bool gave_up_;
};
struct LevelSession {
FunctionPointers* fcn;
OEMCrypto_SESSION session;
@@ -423,17 +580,20 @@ class Adapter {
* To avoid changing the function signature and function contract - declare
* a one-off metrics group to collect detailed information about how
* oemcrypto was intialized.
*
* TODO(blueeyes): Refactor this to allow Initialize to provide the
* details to the caller or to use the metrics instance provided by
* the caller.
*/
wvcdm::metrics::MetricsGroup metrics;
wvcdm::metrics::CryptoMetrics metrics;
level1_ = FunctionPointers(); // start with all null pointers.
level3_ = FunctionPointers(); // start with all null pointers.
LoadLevel3();
std::string base_path;
wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&base_path);
OEMCryptoResult result = Level3_Initialize(clear_cache_function,
base_path.c_str());
WatchDog *watcher = new WatchDog();
watcher->CheckForPreviousFailure(&metrics);
watcher->StartThread();
OEMCryptoResult result = watcher->WaitForStatusAndCleanUp();
if (Level3_IsInApp()) {
M_RECORD(
&metrics,
@@ -472,7 +632,7 @@ class Adapter {
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_L1_OPEN_FAILED);
return result;
}
if (LoadLevel1(metrics)) {
if (LoadLevel1(&metrics)) {
LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1.");
} else {
level1_ = FunctionPointers(); // revert to all null pointers.
@@ -483,7 +643,10 @@ class Adapter {
return result;
}
bool LoadLevel1(wvcdm::metrics::MetricsGroup& metrics) {
bool LoadLevel1(wvcdm::metrics::CryptoMetrics* metrics) {
if (metrics == nullptr) {
return false;
}
level1_valid_ = true;
const uint32_t kMinimumVersion = 8;
const uint32_t kMaximumVersion = 13;
@@ -493,7 +656,7 @@ class Adapter {
LOOKUP_ALL(8, Terminate, OEMCrypto_Terminate);
if (!level1_valid_) {
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_INVALID_L1);
@@ -503,7 +666,7 @@ class Adapter {
if (st != OEMCrypto_SUCCESS) {
LOGW("Could not initialize L1. Falling Back to L3.");
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INITIALIZE_L1);
@@ -511,7 +674,7 @@ class Adapter {
}
level1_.version = level1_.APIVersion();
M_RECORD(
&metrics,
metrics,
oemcrypto_l1_api_version_,
NO_TIME,
level1_.version,
@@ -520,7 +683,7 @@ class Adapter {
LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.",
level1_.version, kMinimumVersion);
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_WRONG_L1_VERSION);
@@ -598,7 +761,7 @@ class Adapter {
// If we have a valid keybox, initialization is done. We're good.
if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) {
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX);
@@ -610,7 +773,7 @@ class Adapter {
if (level1_.version > 11 &&
(level1_.GetProvisioningMethod() == OEMCrypto_OEMCertificate)) {
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0);
@@ -630,13 +793,13 @@ class Adapter {
LOGE("OEMCrypto uses cert as identification, but cdm does not!");
LOGE("This will not work on a production device.");
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_CERTIFICATE_MIX);
} else {
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_CERTIFICATE);
@@ -649,7 +812,7 @@ class Adapter {
LOGW("Bad Level 1 Keybox. Falling Back to L3.");
level1_.Terminate();
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX);
@@ -661,7 +824,7 @@ class Adapter {
LOGW("Could not open %s. Falling Back to L3.", filename.c_str());
level1_.Terminate();
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX);
@@ -675,7 +838,7 @@ class Adapter {
filename.c_str());
level1_.Terminate();
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX);
@@ -683,7 +846,7 @@ class Adapter {
}
LOGI("Installed keybox from %s", filename.c_str());
M_RECORD(
&metrics,
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX);
@@ -1156,18 +1319,26 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
} else {
if (pair.fcn->LoadKeys_V9_or_V10 == NULL)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->LoadKeys_V9_or_V10(pair.session, message, message_length,
signature, signature_length,
enc_mac_key_iv, enc_mac_key, num_keys,
&key_array_v10[0], pst, pst_length);
OEMCryptoResult result = pair.fcn->LoadKeys_V9_or_V10(
pair.session, message, message_length, signature, signature_length,
enc_mac_key_iv, enc_mac_key, num_keys, &key_array_v10[0], pst,
pst_length);
// Convert a vendor specific error, to make it actionable
if (result == kOemCryptoResultVendorSpecificError1)
result = OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE;
return result;
}
} else {
if (pair.fcn->version < 13) {
if (pair.fcn->LoadKeys_V11_or_V12 == NULL)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->LoadKeys_V11_or_V12(
OEMCryptoResult result = pair.fcn->LoadKeys_V11_or_V12(
pair.session, message, message_length, signature, signature_length,
enc_mac_key_iv, enc_mac_key, num_keys, key_array, pst, pst_length);
// Convert a vendor specific error, to make it actionable
if (result == kOemCryptoResultVendorSpecificError1)
result = OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE;
return result;
} else {
if (pair.fcn->LoadKeys == NULL) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->LoadKeys(pair.session, message, message_length,

View File

@@ -94,6 +94,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
reinterpret_cast<uint8_t*>(&(*iv)[0])) == 0) {
LOGE("AesCbcKey::Encrypt: AES CBC setup failure: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(&ctx);
return false;
}
@@ -105,6 +106,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
in.size()) == 0) {
LOGE("AesCbcKey::Encrypt: encryption failure: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(&ctx);
return false;
}
@@ -113,8 +115,10 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
&padding) == 0) {
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_cleanup(&ctx);
return false;
}
EVP_CIPHER_CTX_cleanup(&ctx);
out->resize(out_length + padding);
return true;
@@ -178,6 +182,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
return false;
}
FreeKey(key);
return true;
}
@@ -256,10 +261,15 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
return false;
}
EVP_PKEY *pkey = EVP_PKEY_new();
if (pkey == NULL ||
EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
if (pkey == NULL) {
LOGE("RsaPublicKey::VerifySignature: EVP_PKEY allocation failed");
FreeKey(rsa_key);
return false;
}
if (EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY");
FreeKey(rsa_key);
EVP_PKEY_free(pkey);
return false;
}
FreeKey(rsa_key);

View File

@@ -5,7 +5,6 @@
#include "crypto_session.h"
#include "license.h"
#include "log.h"
#include "metrics_group.h"
namespace {
std::string kEmptyString;
@@ -139,7 +138,7 @@ CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session,
CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number,
DeviceFiles* handle,
metrics::MetricsGroup* metrics) {
metrics::CryptoMetrics* metrics) {
LOGV("UsageTableHeader::DeleteEntry: Lock");
AutoLock auto_lock(usage_table_header_lock_);
if (usage_entry_number >= usage_entry_info_.size())
@@ -188,14 +187,13 @@ CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number,
number_of_entries_to_be_deleted =
usage_entry_info_.size() - swap_entry_number;
}
return Shrink(metrics, number_of_entries_to_be_deleted);
}
CdmResponseType UsageTableHeader::MoveEntry(
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
uint32_t to_usage_entry_number, DeviceFiles* handle,
metrics::MetricsGroup* metrics) {
metrics::CryptoMetrics* metrics) {
LOGV("UsageTableHeader::MoveEntry");
// crypto_session points to an object whose scope is this method or a test
@@ -372,7 +370,7 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
}
CdmResponseType UsageTableHeader::Shrink(
metrics::MetricsGroup* metrics,
metrics::CryptoMetrics* metrics,
uint32_t number_of_usage_entries_to_delete) {
if (usage_entry_info_.empty()) {
LOGE("UsageTableHeader::Shrink: usage entry info table unexpectedly empty");
@@ -413,14 +411,14 @@ CdmResponseType UsageTableHeader::Shrink(
}
CdmResponseType UsageTableHeader::UpgradeFromUsageTable(
DeviceFiles* handle, metrics::MetricsGroup* metrics) {
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
UpgradeLicensesFromUsageTable(handle, metrics);
UpgradeUsageInfoFromUsageTable(handle, metrics);
return NO_ERROR;
}
bool UsageTableHeader::UpgradeLicensesFromUsageTable(
DeviceFiles* handle, metrics::MetricsGroup* metrics) {
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
// Fetch the key set IDs for each offline license. For each license
// * retrieve the provider session token,
// * create a new usage entry
@@ -509,7 +507,7 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable(
}
bool UsageTableHeader::UpgradeUsageInfoFromUsageTable(
DeviceFiles* handle, metrics::MetricsGroup* metrics) {
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
// Fetch all usage files. For each file retrieve all the usage info records
// within the file. For each piece of usage information
// * create a new usage entry