Source release v3.0.0-0-g8d3792b-ce + third_party

Change-Id: I399e71ddfffcd436171d1c60283c63ab4658e0b1
This commit is contained in:
Joey Parrish
2015-06-19 15:13:34 -07:00
parent 58aba6b2ec
commit 0546ee6732
965 changed files with 426663 additions and 12897 deletions

View File

@@ -21,16 +21,43 @@
namespace {
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
const size_t kUsageReportsPerRequest = 1;
} // unnamed namespace
} // namespace
namespace wvcdm {
class UsagePropertySet : public CdmClientPropertySet {
public:
UsagePropertySet() {}
virtual ~UsagePropertySet() {}
void set_security_level(SecurityLevel security_level) {
if (kLevel3 == security_level)
security_level_ = QUERY_VALUE_SECURITY_LEVEL_L3;
else
security_level_.clear();
}
virtual const std::string& security_level() const { return security_level_; }
virtual bool use_privacy_mode() const { return false; }
virtual const std::string& service_certificate() const { return empty_; }
virtual void set_service_certificate(const std::string&) {}
virtual bool is_session_sharing_enabled() const { return false; }
virtual uint32_t session_sharing_id() const { return 0; }
virtual void set_session_sharing_id(uint32_t /* id */) {}
virtual const std::string& app_id() const { return app_id_; }
void set_app_id(const std::string& appId) { app_id_ = appId; }
private:
std::string app_id_;
std::string security_level_;
const std::string empty_;
};
bool CdmEngine::seeded_ = false;
CdmEngine::CdmEngine()
: cert_provisioning_requested_security_level_(kLevelDefault),
: cert_provisioning_(NULL),
cert_provisioning_requested_security_level_(kLevelDefault),
usage_session_(NULL),
last_usage_information_update_time(0) {
last_usage_information_update_time_(0) {
Properties::Init();
if (!seeded_) {
Clock clock;
@@ -40,6 +67,7 @@ CdmEngine::CdmEngine()
}
CdmEngine::~CdmEngine() {
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator i(sessions_.begin());
for (; i != sessions_.end(); ++i) {
delete i->second;
@@ -48,24 +76,34 @@ CdmEngine::~CdmEngine() {
}
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
const CdmClientPropertySet* property_set,
CdmClientPropertySet* property_set,
const std::string& origin,
WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id,
CdmSessionId* session_id) {
LOGI("CdmEngine::OpenSession");
if (!ValidateKeySystem(key_system)) {
LOGI("CdmEngine::OpenSession: invalid key_system = %s", key_system.c_str());
return KEY_ERROR;
return INVALID_KEY_SYSTEM;
}
if (!session_id) {
LOGE("CdmEngine::OpenSession: no session ID destination provided");
return KEY_ERROR;
return INVALID_PARAMETERS_ENG_1;
}
scoped_ptr<CdmSession> new_session(new CdmSession(property_set));
if (forced_session_id) {
if (sessions_.find(*forced_session_id) != sessions_.end()) {
return DUPLICATE_SESSION_ID_SPECIFIED;
}
}
scoped_ptr<CdmSession> new_session(
new CdmSession(property_set, origin, event_listener, forced_session_id));
if (new_session->session_id().empty()) {
LOGE("CdmEngine::OpenSession: failure to generate session ID");
return UNKNOWN_ERROR;
return EMPTY_SESSION_ID;
}
CdmResponseType sts = new_session->Init();
@@ -74,25 +112,30 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
cert_provisioning_requested_security_level_ =
new_session->GetRequestedSecurityLevel();
} else {
LOGE("CdmEngine::OpenSession: bad session init: %u", sts);
LOGE("CdmEngine::OpenSession: bad session init: %d", sts);
}
return sts;
}
*session_id = new_session->session_id();
AutoLock lock(session_list_lock_);
sessions_[*session_id] = new_session.release();
return NO_ERROR;
}
CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
CdmResponseType CdmEngine::OpenKeySetSession(
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
const std::string& origin, WvCdmEventListener* event_listener) {
LOGI("CdmEngine::OpenKeySetSession");
if (key_set_id.empty()) {
LOGE("CdmEngine::OpenKeySetSession: invalid key set id");
return KEY_ERROR;
return EMPTY_KEYSET_ID_ENG_1;
}
CdmSessionId session_id;
CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id);
CdmResponseType sts =
OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
NULL /* forced_session_id */, &session_id);
if (sts != NO_ERROR) return sts;
@@ -102,13 +145,12 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::CloseSession");
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_1;
}
CdmSession* session = iter->second;
sessions_.erase(session_id);
delete session;
@@ -122,7 +164,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
key_set_id.c_str());
return KEY_ERROR;
return KEYSET_ID_NOT_FOUND_1;
}
CdmResponseType sts = CloseSession(iter->second);
@@ -130,33 +172,42 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
return sts;
}
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
return iter != sessions_.end();
}
CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const InitializationData& init_data, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url, CdmKeySetId* key_set_id_out) {
CdmKeyRequestType* key_request_type, std::string* server_url,
CdmKeySetId* key_set_id_out) {
LOGI("CdmEngine::GenerateKeyRequest");
CdmSessionId id = session_id;
CdmResponseType sts;
if (license_type == kLicenseTypeRelease) {
// NOTE: If AlwaysUseKeySetIds() is true, there is no need to consult the
// release_key_sets_ map for release licenses.
if (license_type == kLicenseTypeRelease &&
!Properties::AlwaysUseKeySetIds()) {
if (key_set_id.empty()) {
LOGE("CdmEngine::GenerateKeyRequest: invalid key set ID");
return UNKNOWN_ERROR;
return EMPTY_KEYSET_ID_ENG_2;
}
if (!session_id.empty()) {
LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s",
session_id.c_str());
return UNKNOWN_ERROR;
return INVALID_SESSION_ID;
}
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
key_set_id.c_str());
return UNKNOWN_ERROR;
return KEYSET_ID_NOT_FOUND_2;
}
id = iter->second;
@@ -166,12 +217,12 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_2;
}
if (!key_request) {
LOGE("CdmEngine::GenerateKeyRequest: no key request destination provided");
return KEY_ERROR;
return INVALID_PARAMETERS_ENG_2;
}
key_request->clear();
@@ -179,27 +230,23 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
if (license_type == kLicenseTypeRelease) {
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
if (sts != KEY_ADDED) {
LOGE(
"CdmEngine::GenerateKeyRequest: key release restoration failed,"
"sts = %d",
(int)sts);
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
"sts = %d", static_cast<int>(sts));
return sts;
}
}
sts = iter->second->GenerateKeyRequest(
init_data, license_type, app_parameters, key_request, server_url,
key_set_id_out);
init_data, license_type, app_parameters, key_request, key_request_type,
server_url, key_set_id_out);
if (KEY_MESSAGE != sts) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
}
LOGE(
"CdmEngine::GenerateKeyRequest: key request generation failed, "
"sts = %d",
(int)sts);
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
"sts = %d", static_cast<int>(sts));
return sts;
}
@@ -221,18 +268,18 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
if (license_type_release) {
if (!key_set_id) {
LOGE("CdmEngine::AddKey: no key set id provided");
return KEY_ERROR;
return INVALID_PARAMETERS_ENG_3;
}
if (key_set_id->empty()) {
LOGE("CdmEngine::AddKey: invalid key set id");
return KEY_ERROR;
return EMPTY_KEYSET_ID_ENG_3;
}
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
return KEY_ERROR;
return KEYSET_ID_NOT_FOUND_3;
}
id = iter->second;
@@ -242,18 +289,18 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
if (iter == sessions_.end()) {
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_3;
}
if (key_data.empty()) {
LOGE("CdmEngine::AddKey: no key_data");
return KEY_ERROR;
return EMPTY_KEY_DATA_1;
}
CdmResponseType sts = iter->second->AddKey(key_data, key_set_id);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
LOGE("CdmEngine::AddKey: keys not added, result = %d", sts);
return sts;
}
@@ -266,14 +313,14 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
if (key_set_id.empty()) {
LOGI("CdmEngine::RestoreKey: invalid key set id");
return KEY_ERROR;
return EMPTY_KEYSET_ID_ENG_4;
}
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
session_id.c_str());
return UNKNOWN_ERROR;
return SESSION_NOT_FOUND_4;
}
CdmResponseType sts =
@@ -282,10 +329,10 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
}
if (sts != KEY_ADDED) {
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
}
return sts;
return sts; // TODO ewew
}
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
@@ -295,7 +342,7 @@ CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
if (iter == sessions_.end()) {
LOGE("CdmEngine::RemoveKeys: session_id not found = %s",
session_id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_5;
}
iter->second->ReleaseCrypto();
@@ -311,12 +358,12 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s",
session_id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_6;
}
if (!key_request) {
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination");
return KEY_ERROR;
return INVALID_PARAMETERS_ENG_4;
}
key_request->clear();
@@ -326,7 +373,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
if (KEY_MESSAGE != sts) {
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
(int)sts);
sts);
return sts;
}
@@ -340,26 +387,31 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_7;
}
if (key_data.empty()) {
LOGE("CdmEngine::RenewKey: no key_data");
return KEY_ERROR;
return EMPTY_KEY_DATA_2;
}
CdmResponseType sts = iter->second->RenewKey(key_data);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", (int)sts);
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast<int>(sts));
return sts;
}
return KEY_ADDED;
}
CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryStatus");
CryptoSession crypto_session;
if (security_level == kLevel3) {
CdmResponseType status = crypto_session.Open(kLevel3);
if (NO_ERROR != status) return INVALID_QUERY_STATUS;
}
switch (crypto_session.GetSecurityLevel()) {
case kSecurityLevelL1:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
@@ -376,7 +428,7 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
return KEY_ERROR;
return INVALID_QUERY_KEY;
}
std::string deviceId;
@@ -399,6 +451,39 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
(*key_info)[QUERY_KEY_PROVISIONING_ID] = provisioning_id;
}
CryptoSession::HdcpCapability current_hdcp;
CryptoSession::HdcpCapability max_hdcp;
success = crypto_session.GetHdcpCapabilities(&current_hdcp, &max_hdcp);
if (success) {
(*key_info)[QUERY_KEY_CURRENT_HDCP_LEVEL] = MapHdcpVersion(current_hdcp);
(*key_info)[QUERY_KEY_MAX_HDCP_LEVEL] = MapHdcpVersion(max_hdcp);
}
bool supports_usage_reporting;
success = crypto_session.UsageInformationSupport(&supports_usage_reporting);
if (success) {
(*key_info)[QUERY_KEY_USAGE_SUPPORT] =
supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
}
size_t number_of_open_sessions;
success = crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions);
if (success) {
std::ostringstream open_sessions_stream;
open_sessions_stream << number_of_open_sessions;
(*key_info)[QUERY_KEY_NUMBER_OF_OPEN_SESSIONS] =
open_sessions_stream.str();
}
size_t maximum_number_of_sessions;
success = crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions);
if (success) {
std::ostringstream max_sessions_stream;
max_sessions_stream << maximum_number_of_sessions;
(*key_info)[QUERY_KEY_MAX_NUMBER_OF_SESSIONS] =
max_sessions_stream.str();
}
return NO_ERROR;
}
@@ -409,11 +494,33 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
if (iter == sessions_.end()) {
LOGE("CdmEngine::QuerySessionStatus: session_id not found = %s",
session_id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_8;
}
return iter->second->QueryStatus(key_info);
}
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsReleaseSession");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::IsReleaseSession: session_id not found = %s",
session_id.c_str());
return false;
}
return iter->second->is_release();
}
bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsOfflineSession");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::IsOfflineSession: session_id not found = %s",
session_id.c_str());
return false;
}
return iter->second->is_offline();
}
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyStatus");
@@ -421,7 +528,7 @@ CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s",
session_id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_9;
}
return iter->second->QueryKeyStatus(key_info);
}
@@ -433,7 +540,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s",
session_id.c_str());
return KEY_ERROR;
return SESSION_NOT_FOUND_10;
}
return iter->second->QueryKeyControlInfo(key_info);
}
@@ -443,18 +550,30 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
* in *request. It also returns the default url for the provisioning server
* in *default_url.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
* Returns NO_ERROR for success and CdmResponseType error code if fails.
*/
CdmResponseType CdmEngine::GetProvisioningRequest(
CdmCertificateType cert_type, const std::string& cert_authority,
CdmProvisioningRequest* request, std::string* default_url) {
if (!request || !default_url) {
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
return UNKNOWN_ERROR;
const std::string& origin, CdmProvisioningRequest* request,
std::string* default_url) {
if (!request) {
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
return INVALID_PROVISIONING_REQUEST_PARAM_1;
}
return cert_provisioning_.GetProvisioningRequest(
if (!default_url) {
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
return INVALID_PROVISIONING_REQUEST_PARAM_2;
}
if (NULL == cert_provisioning_.get()) {
cert_provisioning_.reset(new CertificateProvisioning());
}
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_type, cert_authority,
request, default_url);
origin, request, default_url);
if (ret != NO_ERROR) {
cert_provisioning_.reset(NULL); // Release resources.
}
return ret;
}
/*
@@ -462,47 +581,183 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
* The device RSA key is stored in the T.E.E. The device certificate is stored
* in the device.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
* Returns NO_ERROR for success and CdmResponseType error code if fails.
*/
CdmResponseType CdmEngine::HandleProvisioningResponse(
CdmProvisioningResponse& response, std::string* cert,
std::string* wrapped_key) {
const std::string& origin, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
if (response.empty()) {
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
return UNKNOWN_ERROR;
cert_provisioning_.reset(NULL);
return EMPTY_PROVISIONING_RESPONSE;
}
if (NULL == cert) {
LOGE(
"CdmEngine::HandleProvisioningResponse: invalid certificate "
"destination");
return UNKNOWN_ERROR;
cert_provisioning_.reset(NULL);
return INVALID_PROVISIONING_PARAMETERS_1;
}
if (NULL == wrapped_key) {
LOGE(
"CdmEngine::HandleProvisioningResponse: invalid wrapped key "
"destination");
return UNKNOWN_ERROR;
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
"destination");
cert_provisioning_.reset(NULL);
return INVALID_PROVISIONING_PARAMETERS_2;
}
return cert_provisioning_.HandleProvisioningResponse(response, cert,
wrapped_key);
if (NULL == cert_provisioning_.get()) {
LOGE("CdmEngine::HandleProvisioningResponse: provisioning object missing.");
return EMPTY_PROVISIONING_CERTIFICATE;
}
CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
origin, response, cert, wrapped_key);
cert_provisioning_.reset(NULL); // Release resources.
return ret;
}
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level,
const std::string& origin) {
DeviceFiles handle;
if (!handle.Init(security_level)) {
LOGE("CdmEngine::IsProvisioned: unable to initialize device files");
return false;
}
return handle.HasCertificate(origin);
}
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level,
const std::string& origin) {
DeviceFiles handle;
if (!handle.Init(security_level)) {
LOGE("CdmEngine::Unprovision: unable to initialize device files");
return UNKNOWN_ERROR;
return UNPROVISION_ERROR_1;
}
if (!handle.DeleteAllFiles()) {
LOGE("CdmEngine::Unprovision: unable to delete files");
return UNKNOWN_ERROR;
if (origin != EMPTY_ORIGIN) {
if (!handle.RemoveCertificate(origin)) {
LOGE("CdmEngine::Unprovision: unable to delete certificate for origin %s",
origin.c_str());
return UNPROVISION_ERROR_2;
}
return NO_ERROR;
} else {
if (!handle.DeleteAllFiles()) {
LOGE("CdmEngine::Unprovision: unable to delete files");
return UNPROVISION_ERROR_3;
}
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (NO_ERROR != status) {
LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status);
return UNPROVISION_ERROR_4;
}
status = crypto_session->DeleteAllUsageReports();
if (status != NO_ERROR) {
LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status);
}
return status;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
usage_session_.reset(new CdmSession(NULL));
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info) {
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
}
usage_property_set_->set_security_level(kLevelDefault);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
CdmResponseType status = usage_session_->Init();
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
return status;
}
DeviceFiles handle;
if (!handle.Init(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: device file init error");
return GET_USAGE_INFO_ERROR_1;
}
CdmKeyMessage license_request;
CdmKeyResponse license_response;
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response)) {
usage_property_set_->set_security_level(kLevel3);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
status = usage_session_->Init();
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
return status;
}
if (!handle.Reset(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: device file init error");
return GET_USAGE_INFO_ERROR_2;
}
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response)) {
// No entry found for that ssid.
return USAGE_INFO_NOT_FOUND;
}
}
std::string server_url;
usage_info->resize(1);
status =
usage_session_->RestoreUsageSession(license_request, license_response);
if (KEY_ADDED != status) {
LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status);
usage_info->clear();
return status;
}
status =
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
if (KEY_MESSAGE != status) {
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d", status);
usage_info->clear();
return status;
}
return KEY_MESSAGE;
}
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
CdmUsageInfo* usage_info) {
// Return a random usage report from a random security level
SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3;
CdmResponseType status = UNKNOWN_ERROR;
do {
status = GetUsageInfo(app_id, security_level, usage_info);
if (KEY_MESSAGE == status && !usage_info->empty()) return status;
} while (KEY_CANCELED == status);
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
do {
status = GetUsageInfo(app_id, security_level, usage_info);
if (NEED_PROVISIONING == status)
return NO_ERROR; // Valid scenario that one of the security
// levels has not been provisioned
} while (KEY_CANCELED == status);
return status;
}
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
SecurityLevel requested_security_level,
CdmUsageInfo* usage_info) {
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
}
usage_property_set_->set_security_level(requested_security_level);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
CdmResponseType status = usage_session_->Init();
if (NO_ERROR != status) {
@@ -513,13 +768,13 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
DeviceFiles handle;
if (!handle.Init(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: unable to initialize device files");
return status;
return GET_USAGE_INFO_ERROR_3;
}
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
if (!handle.RetrieveUsageInfo(&license_info)) {
if (!handle.RetrieveUsageInfo(app_id, &license_info)) {
LOGE("CdmEngine::GetUsageInfo: unable to read usage information");
return UNKNOWN_ERROR;
return GET_USAGE_INFO_ERROR_4;
}
if (0 == license_info.size()) {
@@ -543,25 +798,59 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
status =
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
if (KEY_MESSAGE != status) {
LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld",
status);
usage_info->clear();
return status;
switch (status) {
case KEY_MESSAGE:
break;
case KEY_CANCELED: // usage information not present in
usage_session_->DeleteLicense(); // OEMCrypto, delete and try again
usage_info->clear();
break;
default:
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d",
status);
usage_info->clear();
break;
}
return KEY_MESSAGE;
return status;
}
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
CdmResponseType status = NO_ERROR;
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
DeviceFiles handle;
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure"
"stops", j);
status = RELEASE_ALL_USAGE_INFO_ERROR_1;
} else {
CdmResponseType status2 = usage_session_->
DeleteMultipleUsageInformation(provider_session_tokens);
if (status2 != NO_ERROR) {
status = status2;
}
}
} else {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device"
"files", j);
status = RELEASE_ALL_USAGE_INFO_ERROR_2;
}
}
return status;
}
CdmResponseType CdmEngine::ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message) {
if (NULL == usage_session_.get()) {
LOGE("CdmEngine::ReleaseUsageInfo: cdm session not initialized");
return UNKNOWN_ERROR;
return RELEASE_USAGE_INFO_ERROR;
}
CdmResponseType status = usage_session_->ReleaseKey(message);
usage_session_.reset(NULL);
if (NO_ERROR != status) {
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %ld", status);
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %d", status);
}
return status;
}
@@ -570,25 +859,26 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters) {
if (parameters.key_id == NULL) {
LOGE("CdmEngine::Decrypt: no key_id");
return KEY_ERROR;
return INVALID_DECRYPT_PARAMETERS_ENG_1;
}
if (parameters.encrypt_buffer == NULL) {
LOGE("CdmEngine::Decrypt: no src encrypt buffer");
return KEY_ERROR;
return INVALID_DECRYPT_PARAMETERS_ENG_2;
}
if (parameters.iv == NULL) {
LOGE("CdmEngine::Decrypt: no iv");
return KEY_ERROR;
return INVALID_DECRYPT_PARAMETERS_ENG_3;
}
if (parameters.decrypt_buffer == NULL) {
if (!parameters.is_secure &&
!Properties::Properties::oem_crypto_use_fifo()) {
LOGE("CdmEngine::Decrypt: no dest decrypt buffer");
return KEY_ERROR;
} // else we must be level 1 direct and we don't need to return a buffer.
return INVALID_DECRYPT_PARAMETERS_ENG_4;
}
// else we must be level 1 direct and we don't need to return a buffer.
}
CdmSessionMap::iterator iter;
@@ -605,7 +895,7 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
if (iter == sessions_.end()) {
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.c_str(), session_id.size());
return KEY_ERROR;
return SESSION_NOT_FOUND_FOR_DECRYPT;
}
return iter->second->Decrypt(parameters);
@@ -650,24 +940,12 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
return false;
}
bool CdmEngine::AttachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener) {
void CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
uint32_t height) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
if (iter != sessions_.end()) {
iter->second->NotifyResolution(width, height);
}
return iter->second->AttachEventListener(listener);
}
bool CdmEngine::DetachEventListener(const CdmSessionId& session_id,
WvCdmEventListener* listener) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
return iter->second->DetachEventListener(listener);
}
bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
@@ -677,37 +955,73 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
void CdmEngine::OnTimerEvent() {
Clock clock;
uint64_t current_time = clock.GetCurrentTime();
bool update_usage_information = false;
bool usage_update_period_expired = false;
if (current_time - last_usage_information_update_time >
if (current_time - last_usage_information_update_time_ >
kUpdateUsageInformationPeriod) {
update_usage_information = true;
last_usage_information_update_time = current_time;
usage_update_period_expired = true;
last_usage_information_update_time_ = current_time;
}
bool is_initial_usage_update = false;
bool is_usage_update_needed = false;
AutoLock lock(session_list_lock_);
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnTimerEvent();
is_initial_usage_update =
is_initial_usage_update || iter->second->is_initial_usage_update();
is_usage_update_needed =
is_usage_update_needed || iter->second->is_usage_update_needed();
if (update_usage_information && iter->second->is_usage_update_needed()) {
// usage is updated for all sessions so this needs to be
// called only once per update usage information period
CdmResponseType status = iter->second->UpdateUsageInformation();
if (NO_ERROR != status) {
LOGW("Update usage information failed: %u", status);
} else {
update_usage_information = false;
iter->second->OnTimerEvent(usage_update_period_expired);
}
if (is_usage_update_needed &&
(usage_update_period_expired || is_initial_usage_update)) {
bool has_usage_been_updated = false;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->reset_usage_flags();
if (!has_usage_been_updated) {
// usage is updated for all sessions so this needs to be
// called only once per update usage information period
CdmResponseType status = iter->second->UpdateUsageInformation();
if (NO_ERROR != status) {
LOGW("Update usage information failed: %d", status);
} else {
has_usage_been_updated = true;
}
}
}
iter->second->reset_is_usage_update_needed();
}
}
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
AutoLock lock(session_list_lock_);
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnKeyReleaseEvent(key_set_id);
}
}
std::string CdmEngine::MapHdcpVersion(
CryptoSession::HdcpCapability version) {
switch (version) {
case HDCP_NONE:
return QUERY_VALUE_UNPROTECTED;
case HDCP_V1:
return QUERY_VALUE_HDCP_V1;
case HDCP_V2:
return QUERY_VALUE_HDCP_V2_0;
case HDCP_V2_1:
return QUERY_VALUE_HDCP_V2_1;
case HDCP_V2_2:
return QUERY_VALUE_HDCP_V2_2;
case HDCP_NO_DIGITAL_OUTPUT:
return QUERY_VALUE_DISCONNECTED;
}
return "";
}
} // namespace wvcdm

View File

@@ -2,11 +2,14 @@
#include "cdm_session.h"
#include <iostream>
#include <sstream>
#include <assert.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include "cdm_engine.h"
#include "clock.h"
#include "crypto_session.h"
#include "device_files.h"
#include "file_store.h"
@@ -22,58 +25,41 @@ const size_t kKeySetIdLength = 14;
namespace wvcdm {
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) {
Create(new CdmLicense(), new CryptoSession(), new PolicyEngine(),
new DeviceFiles(), cdm_client_property_set);
}
CdmSession::CdmSession(CdmLicense* license_parser,
CryptoSession* crypto_session,
PolicyEngine* policy_engine, DeviceFiles* file_handle,
const CdmClientPropertySet* cdm_client_property_set) {
Create(license_parser, crypto_session, policy_engine, file_handle,
cdm_client_property_set);
}
void CdmSession::Create(CdmLicense* license_parser,
CryptoSession* crypto_session,
PolicyEngine* policy_engine, DeviceFiles* file_handle,
const CdmClientPropertySet* cdm_client_property_set) {
// Just return on failures. Failures will be signaled in Init.
if (NULL == license_parser) {
LOGE("CdmSession::Create: License parser not provided");
return;
CdmSession::CdmSession(CdmClientPropertySet* cdm_client_property_set,
const std::string& origin,
WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id)
: initialized_(false),
session_id_(GenerateSessionId()),
origin_(origin),
crypto_session_(new CryptoSession),
file_handle_(new DeviceFiles),
license_received_(false),
is_offline_(false),
is_release_(false),
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_decryption_(true),
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false) {
if (Properties::AlwaysUseKeySetIds()) {
if (forced_session_id) {
key_set_id_ = *forced_session_id;
} else {
bool ok = GenerateKeySetId(&key_set_id_);
assert(ok);
}
session_id_ = key_set_id_;
}
if (NULL == crypto_session) {
LOGE("CdmSession::Create: Crypto session not provided");
return;
}
if (NULL == policy_engine) {
LOGE("CdmSession::Create: Policy engine not provided");
return;
}
if (NULL == file_handle) {
LOGE("CdmSession::Create: Device files not provided");
return;
}
initialized_ = false;
session_id_ = GenerateSessionId();
license_parser_.reset(license_parser);
crypto_session_.reset(crypto_session);
file_handle_.reset(file_handle);
policy_engine_.reset(policy_engine);
license_received_ = false;
is_offline_ = false;
is_release_ = false;
is_usage_update_needed_ = false;
is_initial_decryption_ = true;
requested_security_level_ = kLevelDefault;
if (NULL != cdm_client_property_set) {
if (QUERY_VALUE_SECURITY_LEVEL_L3.compare(
cdm_client_property_set->security_level()) == 0) {
license_parser_.reset(new CdmLicense(session_id_));
policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get()));
if (cdm_client_property_set) {
if (cdm_client_property_set->security_level() ==
QUERY_VALUE_SECURITY_LEVEL_L3) {
requested_security_level_ = kLevel3;
security_level_ = kSecurityLevelL3;
}
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
}
@@ -84,11 +70,11 @@ CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); }
CdmResponseType CdmSession::Init() {
if (session_id_.empty()) {
LOGE("CdmSession::Init: Failed, session not properly constructed");
return UNKNOWN_ERROR;
return SESSION_INIT_ERROR_1;
}
if (initialized_) {
LOGE("CdmSession::Init: Failed due to previous initialization");
return UNKNOWN_ERROR;
return SESSION_INIT_ERROR_2;
}
CdmResponseType sts = crypto_session_->Open(requested_security_level_);
if (NO_ERROR != sts) return sts;
@@ -98,17 +84,18 @@ CdmResponseType CdmSession::Init() {
if (Properties::use_certificates_as_identification()) {
std::string wrapped_key;
if (!file_handle_->Init(security_level_) ||
!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
!file_handle_->RetrieveCertificate(origin_, &token, &wrapped_key) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING;
}
} else {
if (!crypto_session_->GetToken(&token)) return UNKNOWN_ERROR;
if (!crypto_session_->GetToken(&token))
return SESSION_INIT_GET_KEYBOX_ERROR;
}
if (!license_parser_->Init(token, crypto_session_.get(),
policy_engine_.get()))
return UNKNOWN_ERROR;
return LICENSE_PARSER_INIT_ERROR;
license_received_ = false;
is_initial_decryption_ = true;
@@ -121,27 +108,42 @@ CdmResponseType CdmSession::RestoreOfflineSession(
key_set_id_ = key_set_id;
// Retrieve license information from persistent store
if (!file_handle_->Reset(security_level_)) return UNKNOWN_ERROR;
if (!file_handle_->Reset(security_level_))
return RESTORE_OFFLINE_LICENSE_ERROR_1;
DeviceFiles::LicenseState license_state;
int64_t playback_start_time;
int64_t last_playback_time;
if (!file_handle_->RetrieveLicense(
key_set_id, &license_state, &offline_init_data_, &key_request_,
&key_response_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_release_server_url_)) {
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &app_parameters_)) {
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
key_set_id.c_str());
return UNKNOWN_ERROR;
return GET_LICENSE_ERROR;
}
if (license_state != DeviceFiles::kLicenseStateActive) {
LOGE("CdmSession::Init invalid offline license state = %s", license_state);
return UNKNOWN_ERROR;
// Do not restore a released offline license, unless a release retry
if (!(license_type == kLicenseTypeRelease ||
license_state == DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::Init invalid offline license state = %d, type = %d",
license_state, license_type);
return GET_RELEASED_LICENSE_ERROR;
}
if (!license_parser_->RestoreOfflineLicense(key_request_, key_response_,
offline_key_renewal_response_)) {
return UNKNOWN_ERROR;
if (license_type == kLicenseTypeRelease) {
if (!license_parser_->RestoreLicenseForRelease(key_request_,
key_response_)) {
return RELEASE_LICENSE_ERROR_1;
}
} else {
if (!license_parser_->RestoreOfflineLicense(
key_request_, key_response_, offline_key_renewal_response_,
playback_start_time, last_playback_time)) {
return RESTORE_OFFLINE_LICENSE_ERROR_2;
}
}
license_received_ = true;
@@ -155,8 +157,8 @@ CdmResponseType CdmSession::RestoreUsageSession(
key_request_ = key_request;
key_response_ = key_response;
if (!license_parser_->RestoreUsageLicense(key_request_, key_response_)) {
return UNKNOWN_ERROR;
if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) {
return RELEASE_LICENSE_ERROR_2;
}
license_received_ = true;
@@ -166,17 +168,18 @@ CdmResponseType CdmSession::RestoreUsageSession(
}
CdmResponseType CdmSession::GenerateKeyRequest(
const InitializationData& init_data, const CdmLicenseType license_type,
const InitializationData& init_data, CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url, CdmKeySetId* key_set_id) {
CdmKeyRequestType* key_request_type, std::string* server_url,
CdmKeySetId* key_set_id) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
return UNKNOWN_ERROR;
return INVALID_CRYPTO_SESSION_1;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::GenerateKeyRequest: Crypto session not open");
return UNKNOWN_ERROR;
return CRYPTO_SESSION_OPEN_ERROR_1;
}
switch (license_type) {
@@ -189,36 +192,61 @@ CdmResponseType CdmSession::GenerateKeyRequest(
case kLicenseTypeRelease:
is_release_ = true;
break;
case kLicenseTypeDeferred:
// If you're going to pass Deferred, you must have empty init data in
// this call and stored init data from the previous call.
if (!init_data.IsEmpty() || !license_parser_->HasInitData()) {
return INVALID_LICENSE_TYPE;
}
// The arguments check out.
// The is_release_ and is_offline_ flags were already set last time based
// on the original license type. Do not change them, and use them to
// re-derive the original license type.
if (is_release_) {
license_type = kLicenseTypeRelease;
} else if (is_offline_) {
license_type = kLicenseTypeOffline;
} else {
license_type = kLicenseTypeStreaming;
}
break;
default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type);
return UNKNOWN_ERROR;
return INVALID_LICENSE_TYPE;
}
if (is_release_) {
if (key_request_type) *key_request_type = kKeyRequestTypeRelease;
return GenerateReleaseRequest(key_request, server_url);
} else if (license_received_) { // renewal
if (key_request_type) *key_request_type = kKeyRequestTypeRenewal;
return GenerateRenewalRequest(key_request, server_url);
} else {
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return KEY_ERROR;
if (key_request_type) *key_request_type = kKeyRequestTypeInitial;
if (!license_parser_->HasInitData()) {
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA;
}
if (init_data.IsEmpty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return INIT_DATA_NOT_FOUND;
}
}
if (init_data.IsEmpty() && !license_parser_->HasInitData()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR;
}
if (is_offline_ && !GenerateKeySetId(&key_set_id_)) {
if (is_offline_ && key_set_id_.empty() &&
!GenerateKeySetId(&key_set_id_)) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
return UNKNOWN_ERROR;
return KEY_REQUEST_ERROR_1;
}
if (!license_parser_->PrepareKeyRequest(init_data, license_type,
app_parameters, session_id_,
key_request, server_url)) {
return KEY_ERROR;
}
app_parameters_ = app_parameters;
CdmResponseType status = license_parser_->PrepareKeyRequest(
init_data, license_type,
app_parameters, key_request, server_url);
if (KEY_MESSAGE != status) return status;
key_request_ = *key_request;
if (is_offline_) {
@@ -236,12 +264,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
CdmKeySetId* key_set_id) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::AddKey: Invalid crypto session");
return UNKNOWN_ERROR;
return INVALID_CRYPTO_SESSION_2;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::AddKey: Crypto session not open");
return UNKNOWN_ERROR;
return CRYPTO_SESSION_OPEN_ERROR_2;
}
if (is_release_) {
@@ -252,7 +280,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
} else {
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
if (sts != KEY_ADDED) return sts;
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? ADD_KEY_ERROR : sts;
license_received_ = true;
key_response_ = key_response;
@@ -270,12 +298,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
if (crypto_session_.get() == NULL) {
LOGE("CdmSession::QueryStatus: Invalid crypto session");
return UNKNOWN_ERROR;
return INVALID_CRYPTO_SESSION_3;
}
if (!crypto_session_->IsOpen()) {
LOGE("CdmSession::QueryStatus: Crypto session not open");
return UNKNOWN_ERROR;
return CRYPTO_SESSION_OPEN_ERROR_3;
}
switch (security_level_) {
@@ -294,7 +322,7 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
return KEY_ERROR;
return INVALID_QUERY_KEY;
}
return NO_ERROR;
}
@@ -306,12 +334,12 @@ CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::QueryKeyControlInfo: Invalid crypto session");
return UNKNOWN_ERROR;
return INVALID_CRYPTO_SESSION_4;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::QueryKeyControlInfo: Crypto session not open");
return UNKNOWN_ERROR;
return CRYPTO_SESSION_OPEN_ERROR_4;
}
std::stringstream ss;
@@ -322,20 +350,40 @@ CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
// Decrypt() - Accept encrypted buffer and return decrypted data.
CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
return UNKNOWN_ERROR;
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::Decrypt: Invalid crypto session");
return INVALID_CRYPTO_SESSION_5;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::Decrypt: Crypto session not open");
return CRYPTO_SESSION_OPEN_ERROR_5;
}
// Playback may not begin until either the start time passes or the license
// is updated, so we treat this Decrypt call as invalid.
if (params.is_encrypted && !policy_engine_->CanDecrypt(*params.key_id)) {
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
}
CdmResponseType status = crypto_session_->Decrypt(params);
if (NO_ERROR == status) {
if (status == NO_ERROR) {
if (is_initial_decryption_) {
policy_engine_->BeginDecryption();
is_initial_decryption_ = false;
}
has_decrypted_since_last_report_ = true;
if (!is_usage_update_needed_) {
is_usage_update_needed_ =
!license_parser_->provider_session_token().empty();
}
} else {
Clock clock;
int64_t current_time = clock.GetCurrentTime();
if (policy_engine_->IsLicenseOrPlaybackDurationExpired(current_time)) {
return NEED_KEY;
}
}
return status;
@@ -346,11 +394,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// session keys.
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
std::string* server_url) {
if (!license_parser_->PrepareKeyUpdateRequest(true, key_request,
server_url)) {
LOGE("CdmSession::GenerateRenewalRequest: ERROR on prepare");
return KEY_ERROR;
}
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
true, app_parameters_, key_request, server_url);
if (KEY_MESSAGE != status) return status;
if (is_offline_) {
offline_key_renewal_request_ = *key_request;
@@ -362,11 +409,12 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(true, key_response);
if (sts != KEY_ADDED) return sts;
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? RENEW_KEY_ERROR_1 : sts;
if (is_offline_) {
offline_key_renewal_response_ = key_response;
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) return UNKNOWN_ERROR;
if (!StoreLicense(DeviceFiles::kLicenseStateActive))
return RENEW_KEY_ERROR_2;
}
return KEY_ADDED;
}
@@ -374,12 +422,14 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
std::string* server_url) {
is_release_ = true;
if (!license_parser_->PrepareKeyUpdateRequest(false, key_request, server_url))
return UNKNOWN_ERROR;
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
false, app_parameters_, key_request, server_url);
if (KEY_MESSAGE != status) return status;
if (is_offline_) { // Mark license as being released
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))
return UNKNOWN_ERROR;
return RELEASE_KEY_REQUEST_ERROR;
}
return KEY_MESSAGE;
}
@@ -388,7 +438,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(false, key_response);
if (KEY_ADDED != sts) return sts;
if (KEY_ADDED != sts) return (KEY_ERROR == sts) ? RELEASE_KEY_ERROR : sts;
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
DeleteLicense();
@@ -427,6 +477,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
key_set_id->clear();
}
}
// Reserve the license ID to avoid collisions.
file_handle_->ReserveLicenseId(*key_set_id);
return true;
}
@@ -435,7 +486,7 @@ CdmResponseType CdmSession::StoreLicense() {
if (is_offline_) {
if (key_set_id_.empty()) {
LOGE("CdmSession::StoreLicense: No key set ID");
return UNKNOWN_ERROR;
return EMPTY_KEYSET_ID;
}
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
@@ -447,7 +498,7 @@ CdmResponseType CdmSession::StoreLicense() {
}
key_set_id_.clear();
return UNKNOWN_ERROR;
return STORE_LICENSE_ERROR_1;
}
return NO_ERROR;
}
@@ -456,18 +507,20 @@ CdmResponseType CdmSession::StoreLicense() {
license_parser_->provider_session_token();
if (provider_session_token.empty()) {
LOGE("CdmSession::StoreLicense: No provider session token and not offline");
return UNKNOWN_ERROR;
return STORE_LICENSE_ERROR_2;
}
if (!file_handle_->Reset(security_level_)) {
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
return UNKNOWN_ERROR;
return STORE_LICENSE_ERROR_3;
}
std::string app_id;
GetApplicationId(&app_id);
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
key_response_)) {
key_response_, app_id)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return UNKNOWN_ERROR;
return STORE_USAGE_INFO_ERROR;
}
return NO_ERROR;
}
@@ -478,57 +531,67 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
return file_handle_->StoreLicense(
key_set_id_, state, offline_init_data_, key_request_, key_response_,
offline_key_renewal_request_, offline_key_renewal_response_,
offline_release_server_url_);
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
policy_engine_->GetLastPlaybackTime(), app_parameters_);
}
bool CdmSession::DeleteLicense() {
if (!is_offline_ && license_parser_->provider_session_token().empty())
return false;
if (!license_parser_->provider_session_token().empty()) {
if (crypto_session_->DeleteUsageInformation(
license_parser_->provider_session_token()) != NO_ERROR) {
LOGE("CdmSession::DeleteLicense: error deleting usage info");
}
}
if (!file_handle_->Reset(security_level_)) {
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
return false;
}
if (is_offline_)
if (is_offline_) {
return file_handle_->DeleteLicense(key_set_id_);
else
} else {
std::string app_id;
GetApplicationId(&app_id);
return file_handle_->DeleteUsageInfo(
license_parser_->provider_session_token());
app_id, license_parser_->provider_session_token());
}
}
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
std::pair<CdmEventListenerIter, bool> result = listeners_.insert(listener);
return result.second;
void CdmSession::NotifyResolution(uint32_t width, uint32_t height) {
policy_engine_->NotifyResolution(width, height);
}
bool CdmSession::DetachEventListener(WvCdmEventListener* listener) {
return (listeners_.erase(listener) == 1);
}
void CdmSession::OnTimerEvent() {
bool event_occurred = false;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
if (event_occurred) {
for (CdmEventListenerIter iter = listeners_.begin();
iter != listeners_.end(); ++iter) {
(*iter)->OnEvent(session_id_, event);
void CdmSession::OnTimerEvent(bool update_usage) {
if (update_usage && has_decrypted_since_last_report_) {
policy_engine_->DecryptionEvent();
has_decrypted_since_last_report_ = false;
if (is_offline_ && !is_release_) {
StoreLicense(DeviceFiles::kLicenseStateActive);
}
}
policy_engine_->OnTimerEvent();
}
void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
if (key_set_id_ == key_set_id) {
for (CdmEventListenerIter iter = listeners_.begin();
iter != listeners_.end(); ++iter) {
(*iter)->OnEvent(session_id_, LICENSE_EXPIRED_EVENT);
}
policy_engine_->NotifySessionExpiration();
}
}
void CdmSession::GetApplicationId(std::string* app_id) {
if (app_id && !Properties::GetApplicationId(session_id_, app_id)) {
*app_id = "";
}
}
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
const std::vector<std::string>& provider_session_tokens) {
return crypto_session_->DeleteMultipleUsageInformation(
provider_session_tokens);
}
CdmResponseType CdmSession::UpdateUsageInformation() {
return crypto_session_->UpdateUsageInformation();
}
@@ -538,4 +601,20 @@ CdmResponseType CdmSession::ReleaseCrypto() {
return NO_ERROR;
}
void CdmSession::set_license_parser(CdmLicense* license_parser) {
license_parser_.reset(license_parser);
}
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
crypto_session_.reset(crypto_session);
}
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {
policy_engine_.reset(policy_engine);
}
void CdmSession::set_file_handle(DeviceFiles* file_handle) {
file_handle_.reset(file_handle);
}
} // namespace wvcdm

View File

@@ -53,15 +53,15 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
* in *request. It also returns the default url for the provisioning server
* in *default_url.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
*/
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SecurityLevel requested_security_level, CdmCertificateType cert_type,
const std::string& cert_authority, CdmProvisioningRequest* request,
std::string* default_url) {
const std::string& cert_authority, const std::string& origin,
CdmProvisioningRequest* request, std::string* default_url) {
if (!default_url) {
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_REQUEST_ERROR_1;
}
default_url->assign(kProvisioningServerUrl);
@@ -79,14 +79,14 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
std::string token;
if (!crypto_session_.GetToken(&token)) {
LOGE("GetProvisioningRequest: fails to get token");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_GET_KEYBOX_ERROR_1;
}
client_id->set_token(token);
uint32_t nonce;
if (!crypto_session_.GenerateNonce(&nonce)) {
LOGE("GetProvisioningRequest: fails to generate a nonce");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_REQUEST_ERROR_2;
}
// The provisioning server does not convert the nonce to uint32_t, it just
@@ -107,12 +107,21 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
break;
default:
LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type);
return UNKNOWN_ERROR;
return CERT_PROVISIONING_INVALID_CERT_TYPE;
}
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
if (origin != EMPTY_ORIGIN) {
std::string device_unique_id;
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
LOGE("GetProvisioningRequest: fails to get device unique ID");
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
}
provisioning_request.set_stable_id(device_unique_id + origin);
}
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
@@ -121,11 +130,11 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
if (!crypto_session_.PrepareRequest(serialized_message, true,
&request_signature)) {
LOGE("GetProvisioningRequest: fails to prepare request");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_REQUEST_ERROR_3;
}
if (request_signature.empty()) {
LOGE("GetProvisioningRequest: request signature is empty");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_REQUEST_ERROR_4;
}
SignedProvisioningMessage signed_provisioning_msg;
@@ -177,11 +186,11 @@ bool CertificateProvisioning::ParseJsonResponse(
* The device RSA key is stored in the T.E.E. The device certificate is stored
* in the device.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
* Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails.
*/
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
CdmProvisioningResponse& response, std::string* cert,
std::string* wrapped_key) {
const std::string& origin, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
@@ -189,7 +198,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
&serialized_signed_response)) {
LOGE("Fails to extract signed serialized response from JSON response");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_RESPONSE_ERROR_1;
}
// Authenticates provisioning response using D1s (server key derived from
@@ -198,7 +207,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(serialized_signed_response)) {
LOGE("HandleProvisioningResponse: fails to parse signed response");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_RESPONSE_ERROR_2;
}
bool error = false;
@@ -212,19 +221,19 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
error = true;
}
if (error) return UNKNOWN_ERROR;
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
const std::string& signed_message = signed_response.message();
ProvisioningResponse provisioning_response;
if (!provisioning_response.ParseFromString(signed_message)) {
LOGE("HandleProvisioningResponse: Fails to parse signed message");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_RESPONSE_ERROR_4;
}
if (!provisioning_response.has_device_rsa_key()) {
LOGE("HandleProvisioningResponse: key not found");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_RESPONSE_ERROR_5;
}
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
@@ -236,7 +245,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
enc_rsa_key, rsa_key_iv,
&wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_RESPONSE_ERROR_6;
}
crypto_session_.Close();
@@ -253,11 +262,11 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
DeviceFiles handle;
if (!handle.Init(crypto_session_.GetSecurityLevel())) {
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_RESPONSE_ERROR_7;
}
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
if (!handle.StoreCertificate(origin, device_certificate, wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
return UNKNOWN_ERROR;
return CERT_PROVISIONING_RESPONSE_ERROR_8;
}
handle.DeleteAllLicenses();

View File

@@ -6,6 +6,7 @@
#include "crypto_session.h"
#include <arpa/inet.h> // needed for ntoh()
#include <string.h>
#include <iostream>
#include "crypto_key.h"
@@ -24,6 +25,7 @@ std::string EncodeUint32(unsigned int u) {
s.append(1, (u >> 0) & 0xFF);
return s;
}
const uint32_t kRsaSignatureLength = 256;
}
namespace wvcdm {
@@ -35,6 +37,7 @@ uint64_t CryptoSession::request_id_index_ = 0;
CryptoSession::CryptoSession()
: open_(false),
update_usage_table_after_close_session_(false),
is_destination_buffer_type_valid_(false),
requested_security_level_(kLevelDefault),
request_id_base_(0) {
@@ -64,7 +67,11 @@ void CryptoSession::Init() {
void CryptoSession::Terminate() {
LOGV("CryptoSession::Terminate");
AutoLock auto_lock(crypto_lock_);
session_count_ -= 1;
if (session_count_ > 0) {
session_count_ -= 1;
} else {
LOGE("CryptoSession::Terminate error, session count: %d", session_count_);
}
if (session_count_ > 0 || !initialized_) return;
OEMCryptoResult sts = OEMCrypto_Terminate();
if (OEMCrypto_SUCCESS != sts) {
@@ -168,8 +175,6 @@ bool CryptoSession::GetApiVersion(uint32_t* version) {
if (!initialized_) {
return false;
}
LOGV("CryptoSession::GetApiVersion: Lock");
*version = OEMCrypto_APIVersion(requested_security_level_);
return true;
}
@@ -242,9 +247,15 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
open_ = true;
} else if (OEMCrypto_ERROR_TOO_MANY_SESSIONS == sts) {
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
session_count_, (int)initialized_);
return INSUFFICIENT_CRYPTO_RESOURCES;
}
if (!open_) return UNKNOWN_ERROR;
if (!open_) {
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
session_count_, (int)initialized_);
return UNKNOWN_ERROR;
}
OEMCrypto_GetRandom(reinterpret_cast<uint8_t*>(&request_id_base_),
sizeof(request_id_base_));
@@ -259,6 +270,11 @@ void CryptoSession::Close() {
if (!open_) return;
if (OEMCrypto_SUCCESS == OEMCrypto_CloseSession(oec_session_id_)) {
open_ = false;
if (update_usage_table_after_close_session_) {
OEMCryptoResult sts = OEMCrypto_UpdateUsageTable();
if (sts != OEMCrypto_SUCCESS)
LOGW("CryptoSession::Close: OEMCrypto_UpdateUsageTable error=%ld", sts);
}
}
}
@@ -410,13 +426,21 @@ CdmResponseType CryptoSession::LoadKeys(
provider_session_token.length());
if (OEMCrypto_SUCCESS == sts) {
if (!provider_session_token.empty()) {
update_usage_table_after_close_session_ = true;
sts = OEMCrypto_UpdateUsageTable();
if (sts != OEMCrypto_SUCCESS) {
LOGW("CryptoSession::LoadKeys: OEMCrypto_UpdateUsageTable error=%ld",
sts);
}
}
return KEY_ADDED;
} else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) {
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
return INSUFFICIENT_CRYPTO_RESOURCES;
} else {
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
return KEY_ERROR;
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
return LOAD_KEY_ERROR;
}
}
@@ -430,7 +454,7 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
wrapped_key.size());
if (OEMCrypto_SUCCESS != sts) {
LOGD("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts);
LOGE("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts);
return false;
}
@@ -500,7 +524,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message) {
enc_deriv_message.size());
if (OEMCrypto_SUCCESS != sts) {
LOGD("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
return false;
}
@@ -524,7 +548,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
enc_deriv_message.size());
if (OEMCrypto_SUCCESS != sts) {
LOGD("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
return false;
}
@@ -545,7 +569,7 @@ bool CryptoSession::GenerateSignature(const std::string& message,
if (OEMCrypto_SUCCESS != sts) {
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
}
@@ -558,7 +582,7 @@ bool CryptoSession::GenerateSignature(const std::string& message,
&length);
if (OEMCrypto_SUCCESS != sts) {
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
}
}
@@ -574,6 +598,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_);
if (!signature) return false;
signature->resize(kRsaSignatureLength);
size_t length = signature->size();
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
@@ -583,7 +608,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
if (OEMCrypto_SUCCESS != sts) {
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
@@ -596,7 +621,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
&length, kSign_RSASSA_PSS);
if (OEMCrypto_SUCCESS != sts) {
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
}
@@ -612,18 +637,6 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
if (!SetDestinationBufferType()) return UNKNOWN_ERROR;
}
AutoLock auto_lock(crypto_lock_);
// Check if key needs to be selected
if (params.is_encrypted) {
if (key_id_ != *params.key_id) {
if (SelectKey(*params.key_id)) {
key_id_ = *params.key_id;
} else {
return NEED_KEY;
}
}
}
OEMCrypto_DestBufferDesc buffer_descriptor;
buffer_descriptor.type =
params.is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear;
@@ -647,10 +660,29 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
break;
}
OEMCryptoResult sts = OEMCrypto_DecryptCTR(
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, params.subsample_flags);
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (!params.is_encrypted) {
sts = OEMCrypto_CopyBuffer(requested_security_level_,
params.encrypt_buffer, params.encrypt_length,
&buffer_descriptor, params.subsample_flags);
}
if (params.is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
AutoLock auto_lock(crypto_lock_);
// Check if key needs to be selected
if (params.is_encrypted) {
if (key_id_ != *params.key_id) {
if (SelectKey(*params.key_id)) {
key_id_ = *params.key_id;
} else {
return NEED_KEY;
}
}
}
sts = OEMCrypto_DecryptCTR(
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, params.subsample_flags);
}
switch (sts) {
case OEMCrypto_SUCCESS:
@@ -668,8 +700,7 @@ bool CryptoSession::UsageInformationSupport(bool* has_support) {
LOGV("UsageInformationSupport: id=%ld", (uint32_t)oec_session_id_);
if (!initialized_) return false;
*has_support = OEMCrypto_SupportsUsageTable(
kSecurityLevelL3 == GetSecurityLevel() ? kLevel3 : kLevelDefault);
*has_support = OEMCrypto_SupportsUsageTable(requested_security_level_);
return true;
}
@@ -686,14 +717,9 @@ CdmResponseType CryptoSession::UpdateUsageInformation() {
return NO_ERROR;
}
CdmResponseType CryptoSession::GenerateUsageReport(
const std::string& provider_session_token, std::string* usage_report) {
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
if (NULL == usage_report) {
LOGE("usage_report parameter is null");
return UNKNOWN_ERROR;
}
CdmResponseType CryptoSession::DeactivateUsageInformation(
const std::string& provider_session_token) {
LOGV("DeactivateUsageInformation: id=%ld", (uint32_t)oec_session_id_);
AutoLock auto_lock(crypto_lock_);
uint8_t* pst = reinterpret_cast<uint8_t*>(
@@ -702,20 +728,44 @@ CdmResponseType CryptoSession::GenerateUsageReport(
OEMCryptoResult status =
OEMCrypto_DeactivateUsageEntry(pst, provider_session_token.length());
if (OEMCrypto_SUCCESS != status) {
LOGE("CryptoSession::GenerateUsageReport: Deactivate Usage Entry error=%ld",
status);
switch (status) {
case OEMCrypto_SUCCESS:
return NO_ERROR;
case OEMCrypto_ERROR_INVALID_CONTEXT:
LOGE("CryptoSession::DeactivateUsageInformation: invalid context error");
return KEY_CANCELED;
default:
LOGE("CryptoSession::DeactivateUsageInformation: error=%ld", status);
return UNKNOWN_ERROR;
}
}
CdmResponseType CryptoSession::GenerateUsageReport(
const std::string& provider_session_token, std::string* usage_report,
UsageDurationStatus* usage_duration_status, int64_t* seconds_since_started,
int64_t* seconds_since_last_played) {
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
if (NULL == usage_report) {
LOGE("CryptoSession::GenerateUsageReport: usage_report parameter is null");
return UNKNOWN_ERROR;
}
size_t usage_length = 0;
status = OEMCrypto_ReportUsage(oec_session_id_, pst,
provider_session_token.length(), NULL,
&usage_length);
AutoLock auto_lock(crypto_lock_);
uint8_t* pst = reinterpret_cast<uint8_t*>(
const_cast<char*>(provider_session_token.data()));
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld", status);
return UNKNOWN_ERROR;
size_t usage_length = 0;
OEMCryptoResult status = OEMCrypto_ReportUsage(
oec_session_id_, pst, provider_session_token.length(), NULL,
&usage_length);
if (OEMCrypto_SUCCESS != status) {
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld",
status);
return UNKNOWN_ERROR;
}
}
usage_report->resize(usage_length);
@@ -730,10 +780,49 @@ CdmResponseType CryptoSession::GenerateUsageReport(
return UNKNOWN_ERROR;
}
if (usage_length < usage_report->length()) {
if (usage_length != usage_report->length()) {
usage_report->resize(usage_length);
}
OEMCrypto_PST_Report pst_report;
*usage_duration_status = kUsageDurationsInvalid;
if (usage_length < sizeof(pst_report)) {
LOGE("CryptoSession::GenerateUsageReport: usage report too small=%ld",
usage_length);
return NO_ERROR; // usage report available but no duration information
}
memcpy(&pst_report, usage_report->data(), sizeof(pst_report));
if (kUnused == pst_report.status) {
*usage_duration_status = kUsageDurationPlaybackNotBegun;
return NO_ERROR;
}
LOGV("OEMCrypto_PST_Report.status: %d\n", pst_report.status);
LOGV("OEMCrypto_PST_Report.clock_security_level: %d\n",
pst_report.clock_security_level);
LOGV("OEMCrypto_PST_Report.pst_length: %d\n", pst_report.pst_length);
LOGV("OEMCrypto_PST_Report.padding: %d\n", pst_report.padding);
LOGV("OEMCrypto_PST_Report.seconds_since_license_received: %lld\n",
ntohll64(pst_report.seconds_since_license_received));
LOGV("OEMCrypto_PST_Report.seconds_since_first_decrypt: %lld\n",
ntohll64(pst_report.seconds_since_first_decrypt));
LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %lld\n",
ntohll64(pst_report.seconds_since_last_decrypt));
LOGV("OEMCrypto_PST_Report: %s\n", b2a_hex(*usage_report).c_str());
// When usage report state is inactive, we have to deduce whether the
// license was ever used.
if (kInactive == pst_report.status &&
(0 > ntohll64(pst_report.seconds_since_first_decrypt) ||
ntohll64(pst_report.seconds_since_license_received) <
ntohll64(pst_report.seconds_since_first_decrypt))) {
*usage_duration_status = kUsageDurationPlaybackNotBegun;
return NO_ERROR;
}
*usage_duration_status = kUsageDurationsValid;
*seconds_since_started = ntohll64(pst_report.seconds_since_first_decrypt);
*seconds_since_last_played = ntohll64(pst_report.seconds_since_last_decrypt);
return NO_ERROR;
}
@@ -755,10 +844,81 @@ CdmResponseType CryptoSession::ReleaseUsageInformation(
status);
return UNKNOWN_ERROR;
}
status = OEMCrypto_UpdateUsageTable();
if (status != OEMCrypto_SUCCESS) {
LOGW("CryptoSession::ReleaseUsageInformation: update table error=%ld",
status);
}
return NO_ERROR;
}
CdmResponseType CryptoSession::DeleteUsageInformation(
const std::string& provider_session_token) {
CdmResponseType response = NO_ERROR;
LOGV("CryptoSession::DeleteUsageInformation");
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
reinterpret_cast<const uint8_t*>(provider_session_token.c_str()),
provider_session_token.length());
if (OEMCrypto_SUCCESS != status) {
LOGE("CryptoSession::DeleteUsageInformation: Delete Usage Table error =%ld",
status);
response = UNKNOWN_ERROR;
}
status = OEMCrypto_UpdateUsageTable();
if (status != OEMCrypto_SUCCESS) {
LOGE("CryptoSession::DeleteUsageInformation: update table error=%ld",
status);
response = UNKNOWN_ERROR;
}
return response;
}
CdmResponseType CryptoSession::DeleteMultipleUsageInformation(
const std::vector<std::string>& provider_session_tokens) {
LOGV("CryptoSession::DeleteMultipleUsageInformation");
CdmResponseType response = NO_ERROR;
for (size_t i=0; i < provider_session_tokens.size(); ++i) {
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
reinterpret_cast<const uint8_t*>(provider_session_tokens[i].c_str()),
provider_session_tokens[i].length());
if (OEMCrypto_SUCCESS != status) {
LOGW("CryptoSession::DeleteMultipleUsageInformation: "
"Delete Usage Table error =%ld", status);
response = UNKNOWN_ERROR;
}
}
OEMCryptoResult status = OEMCrypto_UpdateUsageTable();
if (status != OEMCrypto_SUCCESS) {
LOGE("CryptoSession::DeleteMultipleUsageInformation: update table error=%ld",
status);
response = UNKNOWN_ERROR;
}
return response;
}
CdmResponseType CryptoSession::DeleteAllUsageReports() {
LOGV("DeleteAllUsageReports");
OEMCryptoResult status = OEMCrypto_DeleteUsageTable();
if (OEMCrypto_SUCCESS != status) {
LOGE("CryptoSession::DeleteAllUsageReports: Delete Usage Table error =%ld",
status);
}
status = OEMCrypto_UpdateUsageTable();
if (status != OEMCrypto_SUCCESS) {
LOGE("CryptoSession::DeletaAllUsageReports: update table error=%ld",
status);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
bool CryptoSession::IsAntiRollbackHwPresent() {
return OEMCrypto_IsAntiRollbackHwPresent(requested_security_level_);
}
bool CryptoSession::GenerateNonce(uint32_t* nonce) {
if (!nonce) {
LOGE("input parameter is null");
@@ -796,7 +956,7 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
const std::string& enc_rsa_key,
const std::string& rsa_key_iv,
std::string* wrapped_rsa_key) {
LOGD("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
LOGV("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
static_cast<uint32_t>(oec_session_id_));
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
@@ -841,20 +1001,20 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
return true;
}
bool CryptoSession::GetHdcpCapabilities(OemCryptoHdcpVersion* current_version,
OemCryptoHdcpVersion* max_version) {
bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
HdcpCapability* max) {
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);
if (!initialized_) return UNKNOWN_ERROR;
OEMCrypto_HDCP_Capability current, max;
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(&current, &max);
if (!initialized_) return false;
if (current == NULL || max == NULL) {
LOGE("CryptoSession::GetHdcpCapabilities: |current|, |max| cannot be NULL");
return false;
}
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(
requested_security_level_, current, max);
if (OEMCrypto_SUCCESS != status) {
LOGW("OEMCrypto_GetHDCPCapability fails with %d", status);
return false;
}
*current_version = static_cast<OemCryptoHdcpVersion>(current);
*max_version = static_cast<OemCryptoHdcpVersion>(max);
return true;
}
@@ -873,4 +1033,42 @@ bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) {
return true;
}
}; // namespace wvcdm
bool CryptoSession::GetNumberOfOpenSessions(size_t* count) {
LOGV("GetNumberOfOpenSessions");
if (!initialized_) return false;
if (count == NULL) {
LOGE("CryptoSession::GetNumberOfOpenSessions: |count| cannot be NULL");
return false;
}
size_t sessions_count;
OEMCryptoResult status = OEMCrypto_GetNumberOfOpenSessions(
requested_security_level_, &sessions_count);
if (OEMCrypto_SUCCESS != status) {
LOGW("OEMCrypto_GetNumberOfOpenSessions fails with %d", status);
return false;
}
*count = sessions_count;
return true;
}
bool CryptoSession::GetMaxNumberOfSessions(size_t* max) {
LOGV("GetMaxNumberOfSessions");
if (!initialized_) return false;
if (max == NULL) {
LOGE("CryptoSession::GetMaxNumberOfSessions: |max| cannot be NULL");
return false;
}
size_t max_sessions;
OEMCryptoResult status = OEMCrypto_GetMaxNumberOfSessions(
requested_security_level_, &max_sessions);
if (OEMCrypto_SUCCESS != status) {
LOGW("OEMCrypto_GetMaxNumberOfSessions fails with %d", status);
return false;
}
*max = max_sessions;
return true;
}
} // namespace wvcdm

View File

@@ -2,15 +2,7 @@
#include "device_files.h"
#if defined(__APPLE__)
#include <CommonCrypto/CommonDigest.h>
#define SHA256 CC_SHA256
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
#else
#include <openssl/sha.h>
#endif
#include <cstring>
#include <string.h>
#include <string>
#include "device_files.pb.h"
@@ -18,6 +10,16 @@
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#if defined(__APPLE__)
#include <CommonCrypto/CommonDigest.h>
#define SHA256 CC_SHA256
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
#else
#include <openssl/sha.h>
#include <openssl/md5.h>
#endif
// Protobuf generated classes.
using video_widevine_client::sdk::DeviceCertificate;
@@ -25,22 +27,25 @@ using video_widevine_client::sdk::HashedFile;
using video_widevine_client::sdk::License;
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
using video_widevine_client::sdk::License_LicenseState_RELEASING;
using video_widevine_client::sdk::NameValue;
using video_widevine_client::sdk::UsageInfo;
using video_widevine_client::sdk::UsageInfo_ProviderSession;
namespace {
const char kCertificateFileName[] = "cert.bin";
const char kUsageInfoFileName[] = "usage.bin";
const char kCertificateFileNamePrefix[] = "cert";
const char kCertificateFileNameExt[] = ".bin";
const char kUsageInfoFileNamePrefix[] = "usage";
const char kUsageInfoFileNameExt[] = ".bin";
const char kLicenseFileNameExt[] = ".lic";
const char kEmptyFileName[] = "";
const char kWildcard[] = "*";
const char kDirectoryDelimiter = '/';
const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
const char* kSecurityLevelPathCompatibilityExclusionList[] = {
"ay64.dat", "ay64.dat2", "ay64.dat3"};
size_t kSecurityLevelPathCompatibilityExclusionListSize =
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
// Some platforms cannot store a truly blank file, so we use a W for Widevine.
const char kBlankFileData[] = "W";
bool Hash(const std::string& data, std::string* hash) {
if (!hash) return false;
@@ -53,10 +58,13 @@ bool Hash(const std::string& data, std::string* hash) {
return true;
}
} // unnamed namespace
} // namespace
namespace wvcdm {
// static
std::set<std::string> DeviceFiles::reserved_license_ids_;
DeviceFiles::DeviceFiles()
: file_(NULL),
security_level_(kSecurityLevelUninitialized),
@@ -68,14 +76,10 @@ DeviceFiles::~DeviceFiles() {
}
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
switch (security_level) {
case kSecurityLevelL1:
case kSecurityLevelL2:
case kSecurityLevelL3:
break;
default:
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
return false;
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level, &path)) {
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
return false;
}
if (!test_file_) file_.reset(new File());
security_level_ = security_level;
@@ -83,7 +87,8 @@ bool DeviceFiles::Init(CdmSecurityLevel security_level) {
return true;
}
bool DeviceFiles::StoreCertificate(const std::string& certificate,
bool DeviceFiles::StoreCertificate(const std::string& origin,
const std::string& certificate,
const std::string& wrapped_private_key) {
if (!initialized_) {
LOGW("DeviceFiles::StoreCertificate: not initialized");
@@ -103,10 +108,11 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(kCertificateFileName, serialized_file);
return StoreFileWithHash(GetCertificateFileName(origin), serialized_file);
}
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
bool DeviceFiles::RetrieveCertificate(const std::string& origin,
std::string* certificate,
std::string* wrapped_private_key) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
@@ -118,7 +124,9 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
}
std::string serialized_file;
if (!RetrieveHashedFile(kCertificateFileName, &serialized_file)) return false;
if (!RetrieveHashedFile(GetCertificateFileName(origin), &serialized_file)) {
return false;
}
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
@@ -148,14 +156,32 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
return true;
}
bool DeviceFiles::StoreLicense(const std::string& key_set_id,
const LicenseState state,
const CdmInitData& pssh_data,
const CdmKeyMessage& license_request,
const CdmKeyResponse& license_message,
const CdmKeyMessage& license_renewal_request,
const CdmKeyResponse& license_renewal,
const std::string& release_server_url) {
bool DeviceFiles::HasCertificate(const std::string& origin) {
if (!initialized_) {
LOGW("DeviceFiles::HasCertificate: not initialized");
return false;
}
return FileExists(GetCertificateFileName(origin));
}
bool DeviceFiles::RemoveCertificate(const std::string& origin) {
if (!initialized_) {
LOGW("DeviceFiles::RemoveCertificate: not initialized");
return false;
}
return RemoveFile(GetCertificateFileName(origin));
}
bool DeviceFiles::StoreLicense(
const std::string& key_set_id, const LicenseState state,
const CdmInitData& pssh_data, const CdmKeyMessage& license_request,
const CdmKeyResponse& license_message,
const CdmKeyMessage& license_renewal_request,
const CdmKeyResponse& license_renewal,
const std::string& release_server_url, int64_t playback_start_time,
int64_t last_playback_time, const CdmAppParameterMap& app_parameters) {
if (!initialized_) {
LOGW("DeviceFiles::StoreLicense: not initialized");
return false;
@@ -186,29 +212,38 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
license->set_renewal_request(license_renewal_request);
license->set_renewal(license_renewal);
license->set_release_server_url(release_server_url);
license->set_playback_start_time(playback_start_time);
license->set_last_playback_time(last_playback_time);
NameValue* app_params;
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
iter != app_parameters.end(); ++iter) {
app_params = license->add_app_parameters();
app_params->set_name(iter->first);
app_params->set_value(iter->second);
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
std::string file_name = key_set_id + kLicenseFileNameExt;
return StoreFileWithHash(file_name.c_str(), serialized_file);
reserved_license_ids_.erase(key_set_id);
return StoreFileWithHash(key_set_id + kLicenseFileNameExt, serialized_file);
}
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
LicenseState* state, CdmInitData* pssh_data,
CdmKeyMessage* license_request,
CdmKeyResponse* license_message,
CdmKeyMessage* license_renewal_request,
CdmKeyResponse* license_renewal,
std::string* release_server_url) {
bool DeviceFiles::RetrieveLicense(
const std::string& key_set_id, LicenseState* state, CdmInitData* pssh_data,
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
std::string* release_server_url, int64_t* playback_start_time,
int64_t* last_playback_time, CdmAppParameterMap* app_parameters) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveLicense: not initialized");
return false;
}
std::string serialized_file;
std::string file_name = key_set_id + kLicenseFileNameExt;
if (!RetrieveHashedFile(file_name.c_str(), &serialized_file)) return false;
if (!RetrieveHashedFile(key_set_id + kLicenseFileNameExt, &serialized_file)) {
return false;
}
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
@@ -252,6 +287,12 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
*license_renewal_request = license.renewal_request();
*license_renewal = license.renewal();
*release_server_url = license.release_server_url();
*playback_start_time = license.playback_start_time();
*last_playback_time = license.last_playback_time();
for (int i = 0; i < license.app_parameters_size(); ++i) {
(*app_parameters)[license.app_parameters(i).name()] =
license.app_parameters(i).value();
}
return true;
}
@@ -260,16 +301,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
LOGW("DeviceFiles::DeleteLicense: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::DeleteLicense: Unable to get base path");
return false;
}
path.append(key_set_id);
path.append(kLicenseFileNameExt);
return file_->Remove(path);
return RemoveFile(key_set_id + kLicenseFileNameExt);
}
bool DeviceFiles::DeleteAllLicenses() {
@@ -277,16 +309,7 @@ bool DeviceFiles::DeleteAllLicenses() {
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::DeleteAllLicenses: Unable to get base path");
return false;
}
path.append(kWildcard);
path.append(kLicenseFileNameExt);
return file_->Remove(path);
return RemoveFile(std::string(kWildcard) + kLicenseFileNameExt);
}
bool DeviceFiles::DeleteAllFiles() {
@@ -295,13 +318,9 @@ bool DeviceFiles::DeleteAllFiles() {
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::DeleteAllFiles: Unable to get base path");
return false;
}
return file_->Remove(path);
// We pass an empty string to RemoveFile to delete the device files base
// directory itself.
return RemoveFile(kEmptyFileName);
}
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
@@ -309,16 +328,8 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
LOGW("DeviceFiles::LicenseExists: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::LicenseExists: Unable to get base path");
return false;
}
path.append(key_set_id);
path.append(kLicenseFileNameExt);
return file_->Exists(path);
return reserved_license_ids_.count(key_set_id) ||
FileExists(key_set_id + kLicenseFileNameExt);
}
bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
@@ -326,14 +337,14 @@ bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
LOGW("DeviceFiles::ReserveLicenseId: not initialized");
return false;
}
std::string file_name = key_set_id + kLicenseFileNameExt;
return StoreFileRaw(file_name.c_str(), kBlankFileData);
reserved_license_ids_.insert(key_set_id);
return true;
}
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response) {
const CdmKeyResponse& key_response,
const std::string& app_id) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
@@ -341,7 +352,8 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
std::string serialized_file;
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveHashedFile(file_name, &serialized_file)) {
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
} else {
@@ -360,17 +372,18 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
provider_session->set_license(key_response.data(), key_response.size());
file.SerializeToString(&serialized_file);
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
return StoreFileWithHash(file_name, serialized_file);
}
bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
const std::string& provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
return false;
}
std::string serialized_file;
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) return false;
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveHashedFile(file_name, &serialized_file)) return false;
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
@@ -382,8 +395,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
int index = 0;
bool found = false;
for (; index < usage_info->sessions_size(); ++index) {
if (usage_info->sessions(index).token().compare(provider_session_token) ==
0) {
if (usage_info->sessions(index).token() == provider_session_token) {
found = true;
break;
}
@@ -405,26 +417,43 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
sessions->RemoveLast();
file.SerializeToString(&serialized_file);
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
return StoreFileWithHash(file_name, serialized_file);
}
bool DeviceFiles::DeleteUsageInfo() {
bool DeviceFiles::DeleteAllUsageInfoForApp(
const std::string& app_id,
std::vector<std::string>* provider_session_tokens) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::DeleteUsageInfo: Unable to get base path");
if (NULL == provider_session_tokens) {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: pst destination not provided");
return false;
}
path.append(kUsageInfoFileName);
provider_session_tokens->clear();
return file_->Remove(path);
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name)) return true;
std::string serialized_file;
if (RetrieveHashedFile(file_name, &serialized_file)) {
video_widevine_client::sdk::File file_proto;
if (!file_proto.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to parse file");
} else {
for (int i = 0; i < file_proto.usage_info().sessions_size(); ++i) {
provider_session_tokens->push_back(
file_proto.usage_info().sessions(i).token());
}
}
} else {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file");
}
return RemoveFile(file_name);
}
bool DeviceFiles::RetrieveUsageInfo(
const std::string& app_id,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
@@ -439,15 +468,9 @@ bool DeviceFiles::RetrieveUsageInfo(
}
std::string serialized_file;
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
return false;
}
path += kUsageInfoFileName;
if (!file_->Exists(path) || 0 == file_->FileSize(path)) {
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveHashedFile(file_name, &serialized_file)) {
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
usage_info->resize(0);
return true;
}
@@ -471,15 +494,45 @@ bool DeviceFiles::RetrieveUsageInfo(
return true;
}
bool DeviceFiles::StoreFileWithHash(const char* name,
const std::string& serialized_file) {
if (!file_.get()) {
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
std::string serialized_file;
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveHashedFile(file_name, &serialized_file)) return false;
video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) {
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
return false;
}
int index = 0;
bool found = false;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).token() == provider_session_token) {
found = true;
break;
}
}
if (!found) {
return false;
}
if (!name) {
LOGW("DeviceFiles::StoreFileWithHash: Unspecified file name parameter");
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
return true;
}
bool DeviceFiles::StoreFileWithHash(const std::string& name,
const std::string& serialized_file) {
if (!file_.get()) {
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
return false;
}
@@ -501,18 +554,13 @@ bool DeviceFiles::StoreFileWithHash(const char* name,
return StoreFileRaw(name, serialized_hash_file);
}
bool DeviceFiles::StoreFileRaw(const char* name,
bool DeviceFiles::StoreFileRaw(const std::string& name,
const std::string& serialized_file) {
if (!file_.get()) {
LOGW("DeviceFiles::StoreFileRaw: Invalid file handle");
return false;
}
if (!name) {
LOGW("DeviceFiles::StoreFileRaw: Unspecified file name parameter");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
@@ -546,18 +594,13 @@ bool DeviceFiles::StoreFileRaw(const char* name,
return true;
}
bool DeviceFiles::RetrieveHashedFile(const char* name,
bool DeviceFiles::RetrieveHashedFile(const std::string& name,
std::string* serialized_file) {
if (!file_.get()) {
LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle");
return false;
}
if (!name) {
LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file name parameter");
return false;
}
if (!serialized_file) {
LOGW(
"DeviceFiles::RetrieveHashedFile: Unspecified serialized_file "
@@ -582,6 +625,9 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
if (bytes <= 0) {
LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s",
path.c_str());
// Remove the corrupted file so the caller will not get the same error
// when trying to access the file repeatedly, causing the system to stall.
file_->Remove(path);
return false;
}
@@ -614,8 +660,11 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
return false;
}
if (hash.compare(hash_file.hash())) {
if (hash != hash_file.hash()) {
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
// Remove the corrupted file so the caller will not get the same error
// when trying to access the file repeatedly, causing the system to stall.
file_->Remove(path);
return false;
}
@@ -623,6 +672,54 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
return true;
}
bool DeviceFiles::FileExists(const std::string& name) {
if (!file_.get()) {
LOGW("DeviceFiles::FileExists: Invalid file handle");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::FileExists: Unable to get base path");
return false;
}
path += name;
return file_->Exists(path);
}
bool DeviceFiles::RemoveFile(const std::string& name) {
if (!file_.get()) {
LOGW("DeviceFiles::RemoveFile: Invalid file handle");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
return false;
}
path += name;
return file_->Remove(path);
}
ssize_t DeviceFiles::GetFileSize(const std::string& name) {
if (!file_.get()) {
LOGW("DeviceFiles::GetFileSize: Invalid file handle");
return -1;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::GetFileSize: Unable to get base path");
return -1;
}
path += name;
return file_->FileSize(path);
}
void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
@@ -687,17 +784,33 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
}
}
std::string DeviceFiles::GetCertificateFileName() {
return kCertificateFileName;
std::string DeviceFiles::GetCertificateFileName(const std::string& origin) {
std::string hash;
if (origin != EMPTY_ORIGIN) {
hash = GetFileNameSafeHash(origin);
}
return kCertificateFileNamePrefix + hash + kCertificateFileNameExt;
}
std::string DeviceFiles::GetLicenseFileNameExtension() {
return kLicenseFileNameExt;
}
std::string DeviceFiles::GetUsageInfoFileName() { return kUsageInfoFileName; }
std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
std::string hash;
if (app_id != "") {
hash = GetFileNameSafeHash(app_id);
}
return kUsageInfoFileNamePrefix + hash + kUsageInfoFileNameExt;
}
std::string DeviceFiles::GetBlankFileData() { return kBlankFileData; }
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
const unsigned char* input_ptr =
reinterpret_cast<const unsigned char*>(input.data());
MD5(input_ptr, input.size(), &hash[0]);
return wvcdm::Base64SafeEncode(hash);
}
void DeviceFiles::SetTestFile(File* file) {
file_.reset(file);

View File

@@ -13,6 +13,11 @@ package video_widevine_client.sdk;
// need this if we are using libprotobuf-cpp-2.3.0-lite
option optimize_for = LITE_RUNTIME;
message NameValue {
optional string name = 1;
optional string value = 2;
}
message DeviceCertificate {
optional bytes certificate = 1;
optional bytes wrapped_private_key = 2;
@@ -31,6 +36,9 @@ message License {
optional bytes renewal_request = 5;
optional bytes renewal = 6;
optional bytes release_server_url = 7;
optional int64 playback_start_time = 8 [default = 0];
optional int64 last_playback_time = 9 [default = 0];
repeated NameValue app_parameters = 10;
}
message UsageInfo {

View File

@@ -38,76 +38,122 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
BufferReader reader(reinterpret_cast<const uint8_t*>(init_data.data()),
init_data.length());
// TODO(kqyang): Extracted from an actual init_data;
// Need to find out where it comes from.
// Widevine's registered system ID.
static const uint8_t kWidevineSystemId[] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
};
// one PSSH blob consists of:
// 4 byte size of the PSSH atom, inclusive
// "pssh"
// 4 byte flags, value 0
// 16 byte system id
// 4 byte size of PSSH data, exclusive
// one PSSH box consists of:
// 4 byte size of the atom, inclusive. (0 means the rest of the buffer.)
// 4 byte atom type, "pssh".
// (optional, if size == 1) 8 byte size of the atom, inclusive.
// 1 byte version, value 0 or 1. (skip if larger.)
// 3 byte flags, value 0. (ignored.)
// 16 byte system id.
// (optional, if version == 1) 4 byte key ID count. (K)
// (optional, if version == 1) K * 16 byte key ID.
// 4 byte size of PSSH data, exclusive. (N)
// N byte PSSH data.
while (1) {
// size of PSSH atom, used for skipping
uint32_t size;
if (!reader.Read4(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
size_t start_pos = reader.pos();
// atom size, used for skipping.
uint64_t size;
if (!reader.Read4Into8(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
return false;
}
std::vector<uint8_t> atom_type;
if (!reader.ReadVec(&atom_type, 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
return false;
}
if (size == 1) {
if (!reader.Read8(&size)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
"size.");
return false;
}
} else if (size == 0) {
size = reader.size() - start_pos;
}
// "pssh"
std::vector<uint8_t> pssh;
if (!reader.ReadVec(&pssh, 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
return false;
if (memcmp(&atom_type[0], "pssh", 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present.");
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
"atom.");
return false;
}
continue;
}
if (memcmp(&pssh[0], "pssh", 4)) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
// version
uint8_t version;
if (!reader.Read1(&version)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
return false;
}
// flags
uint32_t flags;
if (!reader.Read4(&flags)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
return false;
if (version > 1) {
// unrecognized version - skip.
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
"atom.");
return false;
}
continue;
}
if (flags != 0) {
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
// flags
if (!reader.SkipBytes(3)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
return false;
}
// system id
std::vector<uint8_t> system_id;
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID.");
return false;
}
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
// skip the remaining contents of the atom,
// after size field, atom name, flags and system id
if (!reader.SkipBytes(size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
// skip non-Widevine PSSH boxes.
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
"atom.");
return false;
}
continue;
}
// size of PSSH box
uint32_t pssh_length;
if (!reader.Read4(&pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
if (version == 1) {
// v1 has additional fields for key IDs. We can skip them.
uint32_t num_key_ids;
if (!reader.Read4(&num_key_ids)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read num key IDs.");
return false;
}
if (!reader.SkipBytes(num_key_ids * 16)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip key IDs.");
return false;
}
}
// size of PSSH data
uint32_t data_length;
if (!reader.Read4(&data_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data size.");
return false;
}
output->clear();
if (!reader.ReadString(output, pssh_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
if (!reader.ReadString(output, data_length)) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -78,6 +78,10 @@ message License {
// Indicates that the license shall be sent for renewal when usage is
// started.
optional bool renew_with_usage = 11 [default = false];
// Indicates to client that license renewal and release requests ought to
// include ClientIdentification (client_id).
optional bool renew_with_client_id = 12 [default = false];
}
message KeyContainer {
@@ -167,6 +171,8 @@ message License {
optional KeyType type = 4;
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
optional OutputProtection required_protection = 6;
// NOTE: Use of requested_protection is not recommended as it is only
// supported on a small number of platforms.
optional OutputProtection requested_protection = 7;
optional KeyControl key_control = 8;
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
@@ -174,7 +180,13 @@ message License {
// content being decrypted/decoded falls within one of the specified ranges,
// the optional required_protections may be applied. Otherwise an error will
// be reported.
// NOTE: Use of this feature is not recommended, as it is only supported on
// a small number of platforms.
repeated VideoResolutionConstraint video_resolution_constraints = 10;
// Optional flag to indicate the key must only be used if the client
// supports anti rollback of the user table. Content provider can query the
// client capabilities to determine if the client support this feature.
optional bool anti_rollback_usage_table = 11 [default = false];
}
optional LicenseIdentification id = 1;
@@ -286,36 +298,6 @@ message SignedMessage {
optional RemoteAttestation remote_attestation = 5;
}
// This message is used to pass optional data on initial license issuance.
message SessionInit {
optional bytes session_id = 1;
optional bytes purchase_id = 2;
// master_signing_key should be 128 bits in length.
optional bytes master_signing_key = 3;
// signing_key should be 512 bits in length to be split into two
// (server || client) HMAC-SHA256 keys.
optional bytes signing_key = 4;
optional int64 license_start_time = 5;
// Client token for the session. This session is for use by the license
// provider, and is akin to a client cookie. It will be copied to
// License::provider_client_token, and sent back by the client in
// ClientIdentification::provider_client_token in all license requests
// thereafter.
optional bytes provider_client_token = 6;
// Session token for the session. This token is for use by the license
// provider, and is akin to a session cookie. It will be copied to
// LicenseIdentfication::provider_session_token, and sent back in all
// license renewal and release requests for the session thereafter.
optional bytes provider_session_token = 7;
}
// This message is used by the server to preserve and restore session state.
message SessionState {
optional LicenseIdentification license_id = 1;
optional bytes signing_key = 2;
optional uint32 keybox_system_id = 3;
}
// ----------------------------------------------------------------------------
// certificate_provisioning.proto
// ----------------------------------------------------------------------------
@@ -348,6 +330,9 @@ message ProvisioningRequest {
optional bytes nonce = 2;
// Options for type of certificate to generate. Optional.
optional ProvisioningOptions options = 3;
// Stable identifier, unique for each device + application (or origin).
// Required if doing per-origin provisioning.
optional bytes stable_id = 4;
}
// Provisioning response sent by the provisioning server to client devices.
@@ -410,6 +395,7 @@ message ClientIdentification {
optional bool video_resolution_constraints = 3 [default = false];
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
optional uint32 oem_crypto_api_version = 5;
optional bool anti_rollback_usage_table = 6 [default = false];
}
// Type of factory-provisioned device root of trust. Optional.

173
core/src/max_res_engine.cpp Normal file
View File

@@ -0,0 +1,173 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "max_res_engine.h"
#include "clock.h"
#include "log.h"
namespace {
const int64_t kHdcpCheckInterval = 10;
const uint32_t kNoResolution = 0;
} // namespace
namespace wvcdm {
MaxResEngine::MaxResEngine(CryptoSession* crypto_session) {
Init(crypto_session, new Clock());
}
MaxResEngine::MaxResEngine(CryptoSession* crypto_session, Clock* clock) {
Init(crypto_session, clock);
}
MaxResEngine::~MaxResEngine() {
AutoLock lock(status_lock_);
DeleteAllKeys();
}
bool MaxResEngine::CanDecrypt(const KeyId& key_id) {
AutoLock lock(status_lock_);
if (keys_.count(key_id) > 0) {
return keys_[key_id]->can_decrypt();
} else {
// If a Key ID is unknown to us, we don't know of any constraints for it,
// so never block decryption.
return true;
}
}
void MaxResEngine::Init(CryptoSession* crypto_session, Clock* clock) {
AutoLock lock(status_lock_);
current_resolution_ = kNoResolution;
clock_.reset(clock);
next_check_time_ = clock_->GetCurrentTime();
crypto_session_ = crypto_session;
}
void MaxResEngine::SetLicense(
const video_widevine_server::sdk::License& license) {
AutoLock lock(status_lock_);
DeleteAllKeys();
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
const KeyContainer& key = license.key(key_index);
if (key.type() == KeyContainer::CONTENT && key.has_id() &&
key.video_resolution_constraints_size() > 0) {
const ConstraintList& constraints = key.video_resolution_constraints();
const KeyId& key_id = key.id();
if (key.has_required_protection()) {
keys_[key_id] =
new KeyStatus(constraints, key.required_protection().hdcp());
} else {
keys_[key_id] = new KeyStatus(constraints);
}
}
}
}
void MaxResEngine::SetResolution(uint32_t width, uint32_t height) {
AutoLock lock(status_lock_);
current_resolution_ = width * height;
}
void MaxResEngine::OnTimerEvent() {
AutoLock lock(status_lock_);
int64_t current_time = clock_->GetCurrentTime();
if (!keys_.empty() && current_resolution_ != kNoResolution &&
current_time >= next_check_time_) {
CryptoSession::HdcpCapability current_hdcp_level;
CryptoSession::HdcpCapability ignored;
if (!crypto_session_->GetHdcpCapabilities(&current_hdcp_level, &ignored)) {
current_hdcp_level = HDCP_NONE;
}
for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) {
i->second->Update(current_resolution_, current_hdcp_level);
}
next_check_time_ = current_time + kHdcpCheckInterval;
}
}
void MaxResEngine::DeleteAllKeys() {
// This helper method assumes that status_lock_ is already held.
for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) delete i->second;
keys_.clear();
}
MaxResEngine::KeyStatus::KeyStatus(const ConstraintList& constraints)
: default_hdcp_level_(HDCP_NONE) {
Init(constraints);
}
MaxResEngine::KeyStatus::KeyStatus(
const ConstraintList& constraints,
const OutputProtection::HDCP& default_hdcp_level)
: default_hdcp_level_(ProtobufHdcpToOemCryptoHdcp(default_hdcp_level)) {
Init(constraints);
}
void MaxResEngine::KeyStatus::Init(const ConstraintList& constraints) {
constraints_.Clear();
constraints_.MergeFrom(constraints);
can_decrypt_ = true;
}
void MaxResEngine::KeyStatus::Update(
uint32_t res, CryptoSession::HdcpCapability current_hdcp_level) {
VideoResolutionConstraint* current_constraint = GetConstraintForRes(res);
if (current_constraint == NULL) {
can_decrypt_ = false;
return;
}
CryptoSession::HdcpCapability desired_hdcp_level;
if (current_constraint->has_required_protection()) {
desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp(
current_constraint->required_protection().hdcp());
} else {
desired_hdcp_level = default_hdcp_level_;
}
can_decrypt_ = (current_hdcp_level >= desired_hdcp_level);
}
MaxResEngine::VideoResolutionConstraint*
MaxResEngine::KeyStatus::GetConstraintForRes(uint32_t res) {
typedef ConstraintList::pointer_iterator Iterator;
for (Iterator i = constraints_.pointer_begin();
i != constraints_.pointer_end(); ++i) {
VideoResolutionConstraint* constraint = *i;
if (constraint->has_min_resolution_pixels() &&
constraint->has_max_resolution_pixels() &&
res >= constraint->min_resolution_pixels() &&
res <= constraint->max_resolution_pixels()) {
return constraint;
}
}
return NULL;
}
CryptoSession::HdcpCapability
MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp(
const OutputProtection::HDCP& input) {
switch (input) {
case OutputProtection::HDCP_NONE:
return HDCP_NONE;
case OutputProtection::HDCP_V1:
return HDCP_V1;
case OutputProtection::HDCP_V2:
return HDCP_V2;
case OutputProtection::HDCP_V2_1:
return HDCP_V2_1;
case OutputProtection::HDCP_V2_2:
return HDCP_V2_2;
case OutputProtection::HDCP_NO_DIGITAL_OUTPUT:
return HDCP_NO_DIGITAL_OUTPUT;
default:
LOGE("MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp: "
"Unknown HDCP Level");
return HDCP_NO_DIGITAL_OUTPUT;
}
}
} // wvcdm

View File

@@ -5,7 +5,6 @@
// compile time.
//
#include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h"
namespace wvcdm {
@@ -15,6 +14,12 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
return ::OEMCrypto_OpenSession(session);
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level) {
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
return ::OEMCrypto_IsKeyboxValid();
}
@@ -29,12 +34,6 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level) {
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
return ::OEMCrypto_APIVersion();
}
@@ -43,8 +42,34 @@ const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
return ::OEMCrypto_SecurityLevel();
}
OEMCryptoResult OEMCrypto_GetHDCPCapability(
SecurityLevel level, OEMCrypto_HDCP_Capability* current,
OEMCrypto_HDCP_Capability* maximum) {
return ::OEMCrypto_GetHDCPCapability(current, maximum);
}
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
return ::OEMCrypto_SupportsUsageTable();
}
}; // namespace wvcdm
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level) {
return ::OEMCrypto_IsAntiRollbackHwPresent();
}
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
size_t* count) {
return ::OEMCrypto_GetNumberOfOpenSessions(count);
}
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
size_t* maximum) {
return ::OEMCrypto_GetMaxNumberOfSessions(maximum);
}
OEMCryptoResult OEMCrypto_CopyBuffer(
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
return ::OEMCrypto_CopyBuffer(data_addr, data_length, out_buffer,
subsample_flags);
}
} // namespace wvcdm

View File

@@ -0,0 +1,62 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
// This should be used when liboemcrypto.so is linked with the CDM code at
// compile time.
//
// Defines APIs introduced in newer version (v10) which is not available in v9
// to allow an older oemcrypto implementation to be linked with CDM.
#include "OEMCryptoCENC.h"
extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability_V9(uint8_t* current,
uint8_t* maximum);
extern "C" OEMCryptoResult OEMCrypto_LoadTestKeybox() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_LoadTestRSAKey() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_QueryKeyControl(
OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length,
uint8_t* key_control_block, size_t* key_control_block_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_CopyBuffer(
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability(
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) {
if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
uint8_t current_byte, maximum_byte;
OEMCryptoResult sts = OEMCrypto_GetHDCPCapability_V9(&current_byte,
&maximum_byte);
*current = static_cast<OEMCrypto_HDCP_Capability>(current_byte);
*maximum = static_cast<OEMCrypto_HDCP_Capability>(maximum_byte);
return sts;
}
extern "C" bool OEMCrypto_IsAntiRollbackHwPresent() {
return false;
}
extern "C" OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(
const uint8_t* pst, size_t pst_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -1,19 +1,16 @@
/*******************************************************************************
*
* Copyright 2013 Google Inc. All Rights Reserved.
*
* Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
* This should be used when liboemcrypto.so is linked with the CDM code at
* compile time.
* An implementation should compile either oemcrypto_adapter_dynamic.cpp or
* oemcrypto_adapter_static.cpp, but not both.
* This version contains shim code to allow an older, version 8 API, oemcrypto,
* to be linked with CDM.
*
******************************************************************************/
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
// This should be used when liboemcrypto.so is linked with the CDM code at
// compile time.
//
// An implementation should compile either oemcrypto_adapter_dynamic.cpp or
// oemcrypto_adapter_static.cpp, but not both.
//
// Defines APIs introduced in newer version (v9) which is not available in v8
// to allow an older oemcrypto implementation to be linked with CDM.
#include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h"
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V8(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
@@ -25,45 +22,6 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
namespace wvcdm {
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
SecurityLevel level) {
return ::OEMCrypto_OpenSession(session);
}
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
return ::OEMCrypto_IsKeyboxValid();
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level) {
return ::OEMCrypto_GetDeviceID(deviceID, idLength);
}
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
SecurityLevel level) {
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level) {
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
return ::OEMCrypto_APIVersion();
}
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
return ::OEMCrypto_SecurityLevel();
}
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
return ::OEMCrypto_SupportsUsageTable();
}
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
@@ -113,4 +71,3 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
size_t signature_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
}; // namespace wvcdm

View File

@@ -13,48 +13,60 @@
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
using video_widevine_server::sdk::License;
namespace wvcdm {
PolicyEngine::PolicyEngine() { Init(new Clock()); }
PolicyEngine::PolicyEngine(CdmSessionId session_id,
WvCdmEventListener* event_listener,
CryptoSession* crypto_session)
: license_state_(kLicenseStateInitial),
license_start_time_(0),
playback_start_time_(0),
last_playback_time_(0),
last_expiry_time_(0),
last_expiry_time_set_(false),
next_renewal_time_(0),
policy_max_duration_seconds_(0),
session_id_(session_id),
event_listener_(event_listener),
max_res_engine_(new MaxResEngine(crypto_session)),
clock_(new Clock) {}
PolicyEngine::PolicyEngine(Clock* clock) { Init(clock); }
PolicyEngine::~PolicyEngine() {}
PolicyEngine::~PolicyEngine() {
if (clock_) delete clock_;
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
if (keys_status_.find(key_id) == keys_status_.end()) {
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
b2a_hex(key_id).c_str());
return false;
}
return keys_status_[key_id] == kKeyStatusUsable;
}
void PolicyEngine::Init(Clock* clock) {
license_state_ = kLicenseStateInitial;
can_decrypt_ = false;
license_start_time_ = 0;
playback_start_time_ = 0;
next_renewal_time_ = 0;
policy_max_duration_seconds_ = 0;
clock_ = clock;
}
void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
*event_occurred = false;
void PolicyEngine::OnTimerEvent() {
int64_t current_time = clock_->GetCurrentTime();
// License expiration trumps all.
if ((IsLicenseDurationExpired(current_time) ||
IsPlaybackDurationExpired(current_time)) &&
if (IsLicenseOrPlaybackDurationExpired(current_time) &&
license_state_ != kLicenseStateExpired) {
license_state_ = kLicenseStateExpired;
can_decrypt_ = false;
*event = LICENSE_EXPIRED_EVENT;
*event_occurred = true;
NotifyKeysChange(kKeyStatusExpired);
return;
}
max_res_engine_->OnTimerEvent();
bool renewal_needed = false;
// Test to determine if renewal should be attempted.
switch (license_state_) {
case kLicenseStateCanPlay: {
if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break;
}
@@ -71,7 +83,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
case kLicenseStatePending: {
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
NotifyKeysChange(kKeyStatusUsable);
}
break;
}
@@ -83,28 +95,46 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
default: {
license_state_ = kLicenseStateExpired;
can_decrypt_ = false;
NotifyKeysChange(kKeyStatusInternalError);
break;
}
}
if (renewal_needed) {
UpdateRenewalRequest(current_time);
*event = LICENSE_RENEWAL_NEEDED_EVENT;
*event_occurred = true;
if (event_listener_) event_listener_->OnSessionRenewalNeeded(session_id_);
}
}
void PolicyEngine::SetLicense(
const video_widevine_server::sdk::License& license) {
void PolicyEngine::SetLicense(const License& license) {
license_id_.Clear();
license_id_.CopyFrom(license.id());
policy_.Clear();
// Extract content key ids.
keys_status_.clear();
for (int key_index = 0; key_index < license.key_size(); ++key_index) {
const License::KeyContainer& key = license.key(key_index);
if (key.type() == License::KeyContainer::CONTENT && key.has_id())
keys_status_[key.id()] = kKeyStatusInternalError;
}
UpdateLicense(license);
max_res_engine_->SetLicense(license);
}
void PolicyEngine::SetLicenseForRelease(const License& license) {
license_id_.Clear();
license_id_.CopyFrom(license.id());
policy_.Clear();
// Expire any old keys.
NotifyKeysChange(kKeyStatusExpired);
UpdateLicense(license);
}
void PolicyEngine::UpdateLicense(
const video_widevine_server::sdk::License& license) {
void PolicyEngine::UpdateLicense(const License& license) {
if (!license.has_policy()) return;
if (kLicenseStateExpired == license_state_) {
@@ -144,23 +174,22 @@ void PolicyEngine::UpdateLicense(
policy_max_duration_seconds_ = policy_.license_duration_seconds();
}
if (!policy_.can_play()) {
int64_t current_time = clock_->GetCurrentTime();
if (!policy_.can_play() || IsLicenseOrPlaybackDurationExpired(current_time)) {
license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired);
return;
}
int64_t current_time = clock_->GetCurrentTime();
if (IsLicenseDurationExpired(current_time)) return;
if (IsPlaybackDurationExpired(current_time)) return;
// Update state
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
NotifyKeysChange(kKeyStatusUsable);
} else {
license_state_ = kLicenseStatePending;
can_decrypt_ = false;
NotifyKeysChange(kKeyStatusPending);
}
NotifyExpirationUpdate();
}
void PolicyEngine::BeginDecryption() {
@@ -170,10 +199,12 @@ void PolicyEngine::BeginDecryption() {
case kLicenseStateNeedRenewal:
case kLicenseStateWaitingLicenseUpdate:
playback_start_time_ = clock_->GetCurrentTime();
last_playback_time_ = playback_start_time_;
if (policy_.renew_with_usage()) {
license_state_ = kLicenseStateNeedRenewal;
}
NotifyExpirationUpdate();
break;
case kLicenseStateInitial:
case kLicenseStatePending:
@@ -184,11 +215,27 @@ void PolicyEngine::BeginDecryption() {
}
}
void PolicyEngine::DecryptionEvent() {
last_playback_time_ = clock_->GetCurrentTime();
}
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
max_res_engine_->SetResolution(width, height);
}
void PolicyEngine::NotifySessionExpiration() {
license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired);
}
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
std::stringstream ss;
int64_t current_time = clock_->GetCurrentTime();
if (license_state_ == kLicenseStateInitial) return UNKNOWN_ERROR;
if (license_state_ == kLicenseStateInitial) {
key_info->clear();
return NO_ERROR;
}
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
license_id_.type() == video_widevine_server::sdk::STREAMING
@@ -210,47 +257,73 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
return NO_ERROR;
}
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
if (playback_start_time_ == 0) return false;
*seconds_since_started = clock_->GetCurrentTime() - playback_start_time_;
return (*seconds_since_started >= 0) ? true : false;
}
bool PolicyEngine::GetSecondsSinceLastPlayed(
int64_t* seconds_since_last_played) {
if (last_playback_time_ == 0) return false;
*seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_;
return (*seconds_since_last_played >= 0) ? true : false;
}
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time) {
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0;
NotifyExpirationUpdate();
}
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
license_state_ = kLicenseStateWaitingLicenseUpdate;
next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds();
}
bool PolicyEngine::IsLicenseOrPlaybackDurationExpired(int64_t current_time) {
int64_t expiry_time =
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
return (expiry_time == NEVER_EXPIRES) ? false : (expiry_time <= current_time);
}
// For the policy time fields checked in the following methods, a value of 0
// indicates that there is no limit to the duration. These methods
// will always return false if the value is 0.
bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
return policy_max_duration_seconds_ &&
license_start_time_ + policy_max_duration_seconds_ <= current_time;
int64_t PolicyEngine::GetLicenseExpiryTime() {
return policy_max_duration_seconds_ > 0
? license_start_time_ + policy_max_duration_seconds_
: NEVER_EXPIRES;
}
int64_t PolicyEngine::GetPlaybackExpiryTime() {
return (playback_start_time_ > 0 && policy_.playback_duration_seconds() > 0)
? (playback_start_time_ + policy_.playback_duration_seconds())
: NEVER_EXPIRES;
}
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
if (0 == policy_max_duration_seconds_) return LLONG_MAX;
int64_t remaining_time =
policy_max_duration_seconds_ + license_start_time_ - current_time;
if (remaining_time < 0)
remaining_time = 0;
else if (remaining_time > policy_max_duration_seconds_)
remaining_time = policy_max_duration_seconds_;
return remaining_time;
}
bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
return (policy_.playback_duration_seconds() > 0) && playback_start_time_ &&
playback_start_time_ + policy_.playback_duration_seconds() <=
current_time;
int64_t license_expiry_time = GetLicenseExpiryTime();
if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX;
if (license_expiry_time < current_time) return 0;
return std::min(license_expiry_time - current_time,
policy_max_duration_seconds_);
}
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
if (0 == policy_.playback_duration_seconds()) return LLONG_MAX;
if (0 == playback_start_time_) return policy_.playback_duration_seconds();
int64_t playback_expiry_time = GetPlaybackExpiryTime();
if (playback_expiry_time == NEVER_EXPIRES) {
return (policy_.playback_duration_seconds() != 0)
? policy_.playback_duration_seconds()
: LLONG_MAX;
}
int64_t remaining_time =
policy_.playback_duration_seconds() + playback_start_time_ - current_time;
if (remaining_time < 0) remaining_time = 0;
return remaining_time;
if (playback_expiry_time < current_time) return 0;
return std::min(playback_expiry_time - current_time,
policy_.playback_duration_seconds());
}
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
@@ -271,4 +344,45 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
next_renewal_time_ <= current_time;
}
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
bool keys_changed = false;
bool has_new_usable_key = false;
for (std::map<KeyId, CdmKeyStatus>::iterator it = keys_status_.begin();
it != keys_status_.end(); ++it) {
const KeyId key_id = it->first;
CdmKeyStatus& key_status = it->second;
CdmKeyStatus updated_status = new_status;
if (updated_status == kKeyStatusUsable) {
if (!max_res_engine_->CanDecrypt(key_id))
updated_status = kKeyStatusOutputNotAllowed;
}
if (key_status != updated_status) {
key_status = updated_status;
if (updated_status == kKeyStatusUsable) has_new_usable_key = true;
keys_changed = true;
}
}
if (keys_changed && event_listener_) {
event_listener_->OnSessionKeysChange(session_id_, keys_status_,
has_new_usable_key);
}
}
void PolicyEngine::NotifyExpirationUpdate() {
int64_t expiry_time =
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) {
last_expiry_time_ = expiry_time;
if (event_listener_)
event_listener_->OnExpirationUpdate(session_id_, expiry_time);
}
last_expiry_time_set_ = true;
}
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
void PolicyEngine::set_max_res_engine(MaxResEngine* max_res_engine) {
max_res_engine_.reset(max_res_engine);
}
} // wvcdm

View File

@@ -108,8 +108,8 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
}
int padding = 0;
if (EVP_EncryptFinal(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
&padding) == 0) {
if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
&padding) == 0) {
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
ERR_error_string(ERR_get_error(), NULL));
return false;
@@ -180,6 +180,64 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
return true;
}
// LogOpenSSLError is a callback from OpenSSL which is called with each error
// in the thread's error queue.
static int LogOpenSSLError(const char *msg, size_t /* len */, void */* ctx */) {
LOGE(" %s", msg);
return 1;
}
static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
const std::string &signature) {
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
EVP_PKEY_CTX *pctx = NULL;
if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */,
pkey) != 1) {
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) {
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1) {
LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature");
goto err;
}
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, kPssSaltLength) != 1) {
LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) {
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyFinal(
&ctx, const_cast<uint8_t *>(
reinterpret_cast<const uint8_t *>(signature.data())),
signature.size()) != 1) {
LOGE(
"EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad "
"signature.)");
goto err;
}
EVP_MD_CTX_cleanup(&ctx);
return true;
err:
ERR_print_errors_cb(LogOpenSSLError, NULL);
EVP_MD_CTX_cleanup(&ctx);
return false;
}
bool RsaPublicKey::VerifySignature(const std::string& message,
const std::string& signature) {
if (serialized_key_.empty()) {
@@ -190,50 +248,25 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
LOGE("RsaPublicKey::VerifySignature: signed message is empty");
return false;
}
RSA* key = GetKey(serialized_key_);
if (key == NULL) {
RSA* rsa_key = GetKey(serialized_key_);
if (rsa_key == NULL) {
// Error already logged by GetKey.
return false;
}
int rsa_size = RSA_size(key);
if (static_cast<int>(signature.size()) != rsa_size) {
LOGE(
"RsaPublicKey::VerifySignature: message signature is of the wrong "
"size (expected %d, actual %d)",
rsa_size, signature.size());
FreeKey(key);
EVP_PKEY *pkey = EVP_PKEY_new();
if (pkey == NULL ||
EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
FreeKey(rsa_key);
LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY");
return false;
}
FreeKey(rsa_key);
// Decrypt the signature.
std::string padded_digest(signature.size(), 0);
if (RSA_public_decrypt(
signature.size(),
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(signature.data())),
reinterpret_cast<unsigned char*>(&padded_digest[0]), key,
RSA_NO_PADDING) != rsa_size) {
LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s",
ERR_error_string(ERR_get_error(), NULL));
FreeKey(key);
return false;
}
const bool ok = VerifyPSSSignature(pkey, message, signature);
EVP_PKEY_free(pkey);
// Hash the message using SHA1.
std::string message_digest(SHA_DIGEST_LENGTH, 0);
SHA1(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
reinterpret_cast<unsigned char*>(&message_digest[0]));
// Verify PSS padding.
if (RSA_verify_PKCS1_PSS(
key, reinterpret_cast<const unsigned char*>(message_digest.data()),
EVP_sha1(),
reinterpret_cast<const unsigned char*>(padded_digest.data()),
kPssSaltLength) == 0) {
LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s",
ERR_error_string(ERR_get_error(), NULL));
FreeKey(key);
if (!ok) {
LOGE("RsaPublicKey::VerifySignature: RSA verify failure");
return false;
}

View File

@@ -1,7 +1,7 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "log.h"
#include "properties_configuration.h"
#include "properties.h"
#include "wv_cdm_constants.h"
namespace {
@@ -12,31 +12,18 @@ namespace wvcdm {
bool Properties::oem_crypto_use_secure_buffers_;
bool Properties::oem_crypto_use_fifo_;
bool Properties::oem_crypto_use_userspace_buffers_;
bool Properties::oem_crypto_require_usage_tables_;
bool Properties::use_certificates_as_identification_;
bool Properties::security_level_path_backward_compatibility_support_;
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
void Properties::Init() {
oem_crypto_use_secure_buffers_ = kPropertyOemCryptoUseSecureBuffers;
oem_crypto_use_fifo_ = kPropertyOemCryptoUseFifo;
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
oem_crypto_require_usage_tables_ = kPropertyOemCryptoRequireUsageTable;
use_certificates_as_identification_ =
kPropertyUseCertificatesAsIdentification;
security_level_path_backward_compatibility_support_ =
kSecurityLevelPathBackwardCompatibilitySupport;
session_property_set_.reset(new CdmClientPropertySetMap());
}
bool Properties::AddSessionPropertySet(
const CdmSessionId& session_id, const CdmClientPropertySet* property_set) {
const CdmSessionId& session_id, CdmClientPropertySet* property_set) {
if (NULL == session_property_set_.get()) {
return false;
}
std::pair<CdmClientPropertySetMap::iterator, bool> result =
session_property_set_->insert(
std::pair<const CdmSessionId, const CdmClientPropertySet*>(
std::pair<const CdmSessionId, CdmClientPropertySet*>(
session_id, property_set));
return result.second;
}
@@ -48,10 +35,10 @@ bool Properties::RemoveSessionPropertySet(const CdmSessionId& session_id) {
return (1 == session_property_set_->erase(session_id));
}
const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
CdmClientPropertySet* Properties::GetCdmClientPropertySet(
const CdmSessionId& session_id) {
if (NULL != session_property_set_.get()) {
CdmClientPropertySetMap::const_iterator it =
CdmClientPropertySetMap::iterator it =
session_property_set_->find(session_id);
if (it != session_property_set_->end()) {
return it->second;
@@ -60,14 +47,14 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
return NULL;
}
bool Properties::GetSecurityLevel(const CdmSessionId& session_id,
std::string* security_level) {
bool Properties::GetApplicationId(const CdmSessionId& session_id,
std::string* app_id) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
return false;
}
*security_level = property_set->security_level();
*app_id = property_set->app_id();
return true;
}
@@ -82,6 +69,17 @@ bool Properties::GetServiceCertificate(const CdmSessionId& session_id,
return true;
}
bool Properties::SetServiceCertificate(const CdmSessionId& session_id,
const std::string& service_certificate) {
CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
return false;
}
property_set->set_service_certificate(service_certificate);
return true;
}
bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);

View File

@@ -4,14 +4,15 @@
#include <arpa/inet.h>
#include <ctype.h>
#include <iostream>
#include <stdio.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <modp_b64w.h>
#include "log.h"
#include "modp_b64w.h"
namespace wvcdm {
@@ -43,8 +44,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
unsigned char lsb = 0; // least significant 4 bits
if (!CharToDigit(byte[i * 2], &msb) ||
!CharToDigit(byte[i * 2 + 1], &lsb)) {
LOGE("Invalid hex value %c%c at index %d",
byte[i * 2], byte[i * 2 + 1], i);
LOGE("Invalid hex value %c%c at index %d", byte[i * 2], byte[i * 2 + 1],
i);
return array;
}
array.push_back((msb << 4) | lsb);
@@ -56,8 +57,9 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
// dump the string with the label.
std::vector<uint8_t> a2b_hex(const std::string& label,
const std::string& byte) {
std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]"
<< std::endl << std::endl;
std::cout << std::endl
<< "[[DUMP: " << label << " ]= \"" << byte << "\"]" << std::endl
<< std::endl;
return a2b_hex(byte);
}
@@ -173,15 +175,15 @@ int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order)
int64_t number;
} mixed;
mixed.number = 1;
if (mixed.array[0] == 1) {
mixed.number = x; // Little Endian.
if (mixed.array[0] == 1) { // Little Endian.
mixed.number = x;
uint32_t temp = mixed.array[0];
mixed.array[0] = htonl(mixed.array[1]);
mixed.array[1] = htonl(temp);
return mixed.number;
} else {
return x; // Big Endian.
} else { // Big Endian.
return x;
}
}
}; // namespace wvcdm
} // namespace wvcdm