Source release v3.3.0
This commit is contained in:
@@ -3,12 +3,14 @@
|
||||
#include "cdm_engine.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <list>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "cdm_session.h"
|
||||
#include "cdm_session_map.h"
|
||||
#include "clock.h"
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
@@ -27,6 +29,9 @@ const size_t kUsageReportsPerRequest = 1;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
using video_widevine::SignedMessage;
|
||||
using video_widevine::LicenseError;
|
||||
|
||||
class UsagePropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
UsagePropertySet() {}
|
||||
@@ -70,20 +75,77 @@ CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
|
||||
}
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator i(sessions_.begin());
|
||||
for (; i != sessions_.end(); ++i) {
|
||||
delete i->second;
|
||||
}
|
||||
sessions_.clear();
|
||||
}
|
||||
CdmEngine::~CdmEngine() {}
|
||||
|
||||
CdmResponseType CdmEngine::SetServiceCertificate(
|
||||
const std::string& certificate) {
|
||||
return service_certificate_.Init(certificate);
|
||||
}
|
||||
|
||||
bool CdmEngine::HasServiceCertificate() {
|
||||
return service_certificate_.has_certificate();
|
||||
}
|
||||
|
||||
bool CdmEngine::GetServiceCertificateRequest(CdmKeyMessage* request) {
|
||||
if (!request) {
|
||||
LOGE("ServiceCertificate::PrepareRequest: no request parameter provided");
|
||||
return false;
|
||||
}
|
||||
SignedMessage message;
|
||||
message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||
message.SerializeToString(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ParseServiceCertificateResponse(
|
||||
const std::string& response, std::string* certificate) {
|
||||
if (response.empty()) {
|
||||
LOGE("CdmEngine::ParseServiceCertificateResponse: empty response");
|
||||
return EMPTY_RESPONSE_ERROR_1;
|
||||
}
|
||||
if (!certificate) {
|
||||
LOGE("CdmEngine::ParseServiceCertificateResponse: null return parameter");
|
||||
return INVALID_PARAMETERS_ENG_19;
|
||||
}
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(response)) {
|
||||
LOGE(
|
||||
"CdmEngine::ParseServiceCertificateResponse: cannot parse response");
|
||||
return PARSE_RESPONSE_ERROR_1;
|
||||
}
|
||||
if (signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) {
|
||||
|
||||
CdmResponseType status;
|
||||
status = service_certificate_.Init(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
LOGE(
|
||||
"CdmEngine::ParseServiceCertificateResponse: certificate handling "
|
||||
"failure, status=%d", status);
|
||||
return PARSE_SERVICE_CERTIFICATE_ERROR;
|
||||
}
|
||||
certificate->assign(signed_response.msg());
|
||||
|
||||
} else if (signed_response.type() == SignedMessage::ERROR_RESPONSE) {
|
||||
|
||||
LicenseError license_error;
|
||||
if (!license_error.ParseFromString(signed_response.msg())) {
|
||||
LOGE("CdmEngine::ParseServiceCertificateResponse: cannot parse "
|
||||
"license error");
|
||||
return PARSE_RESPONSE_ERROR_2;
|
||||
}
|
||||
LOGE("CdmEngine::ParseServiceCertificateResponse: server returned error:"
|
||||
"error code = %d", license_error.error_code());
|
||||
return PARSE_RESPONSE_ERROR_3;
|
||||
} else {
|
||||
LOGE(
|
||||
"CdmEngine::ParseServiceCertificateResponse: response (%d) is "
|
||||
"wrong type", signed_response.type());
|
||||
return PARSE_RESPONSE_ERROR_4;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) {
|
||||
@@ -114,7 +176,7 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
}
|
||||
|
||||
if (forced_session_id) {
|
||||
if (sessions_.find(*forced_session_id) != sessions_.end()) {
|
||||
if (session_map_.Exists(*forced_session_id)) {
|
||||
return DUPLICATE_SESSION_ID_SPECIFIED;
|
||||
}
|
||||
}
|
||||
@@ -138,8 +200,7 @@ CdmResponseType CdmEngine::OpenSession(
|
||||
}
|
||||
CdmSessionId id = new_session->session_id();
|
||||
|
||||
AutoLock lock(session_list_lock_);
|
||||
sessions_[id] = new_session.release();
|
||||
session_map_.Add(id, new_session.release());
|
||||
if (session_id) *session_id = id;
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -180,15 +241,10 @@ CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
|
||||
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()) {
|
||||
if (!session_map_.CloseSession(session_id)) {
|
||||
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
|
||||
return SESSION_NOT_FOUND_1;
|
||||
}
|
||||
CdmSession* session = iter->second;
|
||||
sessions_.erase(session_id);
|
||||
delete session;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -218,9 +274,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
return iter != sessions_.end();
|
||||
return session_map_.Exists(session_id);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
@@ -258,8 +312,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
id = iter->second.first;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(id, session)) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
|
||||
id.c_str());
|
||||
return SESSION_NOT_FOUND_2;
|
||||
@@ -273,8 +327,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
key_request->message.clear();
|
||||
|
||||
if (license_type == kLicenseTypeRelease &&
|
||||
!iter->second->license_received()) {
|
||||
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
|
||||
!session->license_received()) {
|
||||
sts = session->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
|
||||
if (sts != KEY_ADDED) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d", static_cast<int>(sts));
|
||||
@@ -282,13 +336,13 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
}
|
||||
}
|
||||
|
||||
sts = iter->second->GenerateKeyRequest(
|
||||
init_data, license_type, app_parameters, key_request);
|
||||
sts = session->GenerateKeyRequest(init_data, license_type, app_parameters,
|
||||
key_request);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
session->GetRequestedSecurityLevel();
|
||||
}
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d", static_cast<int>(sts));
|
||||
@@ -331,9 +385,8 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
id = iter->second.first;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(id, session)) {
|
||||
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
|
||||
return SESSION_NOT_FOUND_3;
|
||||
}
|
||||
@@ -343,9 +396,9 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
return EMPTY_KEY_DATA_1;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->AddKey(key_data);
|
||||
CdmResponseType sts = session->AddKey(key_data);
|
||||
if (key_set_id) {
|
||||
*key_set_id = iter->second->key_set_id();
|
||||
*key_set_id = session->key_set_id();
|
||||
}
|
||||
|
||||
switch (sts) {
|
||||
@@ -371,18 +424,18 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
|
||||
return EMPTY_KEYSET_ID_ENG_4;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_4;
|
||||
}
|
||||
|
||||
CdmResponseType sts =
|
||||
iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
|
||||
session->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
session->GetRequestedSecurityLevel();
|
||||
}
|
||||
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
|
||||
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
|
||||
@@ -393,14 +446,14 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
|
||||
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::RemoveKeys");
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::RemoveKeys: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_5;
|
||||
}
|
||||
|
||||
iter->second->ReleaseCrypto();
|
||||
session->ReleaseCrypto();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -408,8 +461,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
const CdmSessionId& session_id, CdmKeyRequest* key_request) {
|
||||
LOGI("CdmEngine::GenerateRenewalRequest");
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_6;
|
||||
@@ -422,7 +475,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
|
||||
key_request->message.clear();
|
||||
|
||||
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
|
||||
CdmResponseType sts = session->GenerateRenewalRequest(key_request);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
|
||||
@@ -437,8 +490,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data) {
|
||||
LOGI("CdmEngine::RenewKey");
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
|
||||
return SESSION_NOT_FOUND_7;
|
||||
}
|
||||
@@ -448,7 +501,7 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
|
||||
return EMPTY_KEY_DATA_2;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->RenewKey(key_data);
|
||||
CdmResponseType sts = session->RenewKey(key_data);
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast<int>(sts));
|
||||
return sts;
|
||||
@@ -473,8 +526,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
}
|
||||
|
||||
if (query_token == QUERY_KEY_SECURITY_LEVEL) {
|
||||
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
|
||||
switch (security_level) {
|
||||
CdmSecurityLevel level = crypto_session.GetSecurityLevel();
|
||||
switch (level) {
|
||||
case kSecurityLevelL1:
|
||||
*query_response = QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
break;
|
||||
@@ -489,8 +542,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
*query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
LOGW("CdmEngine::QueryStatus: Unknown security level: %d",
|
||||
security_level);
|
||||
LOGW("CdmEngine::QueryStatus: Unknown security level: %d", level);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
} else if (query_token == QUERY_KEY_DEVICE_ID) {
|
||||
@@ -581,47 +633,47 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* query_response) {
|
||||
LOGI("CdmEngine::QuerySessionStatus");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::QuerySessionStatus: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_8;
|
||||
}
|
||||
return iter->second->QueryStatus(query_response);
|
||||
return session->QueryStatus(query_response);
|
||||
}
|
||||
|
||||
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::IsReleaseSession");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::IsReleaseSession: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return false;
|
||||
}
|
||||
return iter->second->is_release();
|
||||
return session->is_release();
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::IsOfflineSession");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::IsOfflineSession: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return false;
|
||||
}
|
||||
return iter->second->is_offline();
|
||||
return session->is_offline();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* query_response) {
|
||||
LOGI("CdmEngine::QueryKeyStatus");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_9;
|
||||
}
|
||||
return iter->second->QueryKeyStatus(query_response);
|
||||
return session->QueryKeyStatus(query_response);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
|
||||
@@ -632,13 +684,13 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
|
||||
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
|
||||
return INVALID_PARAMETERS_ENG_12;
|
||||
}
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::QueryKeyAllowedUsage: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_12;
|
||||
}
|
||||
return iter->second->QueryKeyAllowedUsage(key_id, key_usage);
|
||||
return session->QueryKeyAllowedUsage(key_id, key_usage);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
|
||||
@@ -652,10 +704,16 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
|
||||
return INVALID_PARAMETERS_ENG_7;
|
||||
}
|
||||
key_usage->Clear();
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
session_sts = iter->second->QueryKeyAllowedUsage(key_id,
|
||||
&found_in_this_session);
|
||||
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
for (CdmSessionList::iterator iter = sessions.begin();
|
||||
iter != sessions.end(); ++iter) {
|
||||
if ((*iter)->IsClosed()) {
|
||||
continue;
|
||||
}
|
||||
session_sts = (*iter)->QueryKeyAllowedUsage(key_id, &found_in_this_session);
|
||||
if (session_sts == NO_ERROR) {
|
||||
if (found) {
|
||||
// Found another key. If usage settings do not match, fail.
|
||||
@@ -680,13 +738,13 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
|
||||
CdmResponseType CdmEngine::QueryOemCryptoSessionId(
|
||||
const CdmSessionId& session_id, CdmQueryMap* query_response) {
|
||||
LOGI("CdmEngine::QueryOemCryptoSessionId");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::QueryOemCryptoSessionId: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_10;
|
||||
}
|
||||
return iter->second->QueryOemCryptoSessionId(query_response);
|
||||
return session->QueryOemCryptoSessionId(query_response);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -835,19 +893,73 @@ CdmResponseType CdmEngine::ListStoredLicenses(
|
||||
CdmSecurityLevel security_level, std::vector<std::string>* key_set_ids) {
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!key_set_ids) {
|
||||
LOGE("CdmEngine::QueryStoredLicenses: no response destination");
|
||||
LOGE("CdmEngine::ListStoredLicenses: no response destination");
|
||||
return INVALID_PARAMETERS_ENG_17;
|
||||
}
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::ListStoredLicenses: unable to initialize device files");
|
||||
return STORE_LICENSE_ERROR_3;
|
||||
return LIST_LICENSE_ERROR_1;
|
||||
}
|
||||
if (!handle.ListLicenses(key_set_ids)) {
|
||||
return UNKNOWN_ERROR;
|
||||
LOGE("CdmEngine::ListStoredLicenses: ListLicenses call failed");
|
||||
return LIST_LICENSE_ERROR_2;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ListUsageRecords(const std::string& app_id,
|
||||
CdmSecurityLevel security_level,
|
||||
std::vector<std::string>* ksids) {
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!ksids) {
|
||||
LOGE("CdmEngine::ListUsageRecords: no response destination");
|
||||
return INVALID_PARAMETERS_ENG_18;
|
||||
}
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::ListUsageRecords: unable to initialize device files");
|
||||
return LIST_USAGE_ERROR_1;
|
||||
}
|
||||
if (!handle.ListUsageRecords(app_id, ksids)) {
|
||||
LOGE("CdmEngine::ListUsageRecords: ListUsageRecords call failed");
|
||||
return LIST_USAGE_ERROR_2;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
|
||||
CdmSecurityLevel security_level,
|
||||
const std::string& key_set_id) {
|
||||
std::string provider_session_token;
|
||||
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::DeleteUsageRecord: unable to initialize device files");
|
||||
return DELETE_USAGE_ERROR_1;
|
||||
}
|
||||
if (!handle.GetProviderToken(app_id, key_set_id, &provider_session_token)) {
|
||||
LOGE("CdmEngine::DeleteUsageRecord: GetProviderToken failed");
|
||||
return DELETE_USAGE_ERROR_2;
|
||||
}
|
||||
|
||||
// Got provider token. Remove from OEMCrypto.
|
||||
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
|
||||
CdmResponseType status = crypto_session->Open(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
if (status == NO_ERROR) {
|
||||
status = crypto_session->DeleteUsageInformation(provider_session_token);
|
||||
}
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("CdmEngine::DeleteUsageRecord: OEMCrypto failure");
|
||||
}
|
||||
|
||||
// Remove from file system.
|
||||
if (!handle.DeleteUsageInfo(app_id, provider_session_token)) {
|
||||
LOGE("CdmEngine::DeleteUsageRecord: file system failure");
|
||||
return DELETE_USAGE_ERROR_3;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
CdmUsageInfo* usage_info) {
|
||||
@@ -915,6 +1027,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
@@ -930,7 +1043,9 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, usage_info);
|
||||
|
||||
if (KEY_MESSAGE == status && !usage_info->empty()) return status;
|
||||
if (KEY_MESSAGE == status && !usage_info->empty()) {
|
||||
return status;
|
||||
}
|
||||
} while (KEY_CANCELED == status);
|
||||
|
||||
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
|
||||
@@ -1015,6 +1130,37 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseAllUsageInfo(
|
||||
const std::string& app_id, CdmSecurityLevel security_level) {
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: unable to initialize device files");
|
||||
return RELEASE_ALL_USAGE_INFO_ERROR_3;
|
||||
}
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete usage records");
|
||||
return RELEASE_ALL_USAGE_INFO_ERROR_4;
|
||||
}
|
||||
|
||||
if (provider_session_tokens.size() == 0UL) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// Got at least one provider token. Remove from OEMCrypto.
|
||||
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
|
||||
CdmResponseType status = crypto_session->Open(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
if (status == NO_ERROR) {
|
||||
status = crypto_session->
|
||||
DeleteMultipleUsageInformation(provider_session_tokens);
|
||||
}
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("CdmEngine::DeleteUsageRecord: CryptoSession failure");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
|
||||
if (NULL == usage_property_set_.get()) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
@@ -1081,8 +1227,8 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
|
||||
return EMPTY_KEYSET_ID_ENG_5;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(key_set_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(key_set_id, session)) {
|
||||
LOGE("CdmEngine::LoadUsageSession: session_id not found = %s ",
|
||||
key_set_id.c_str());
|
||||
return SESSION_NOT_FOUND_11;
|
||||
@@ -1094,13 +1240,13 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
|
||||
}
|
||||
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(iter->second->GetSecurityLevel())) {
|
||||
if (!handle.Init(session->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::LoadUsageSession: unable to initialize device files");
|
||||
return LOAD_USAGE_INFO_FILE_ERROR;
|
||||
}
|
||||
|
||||
std::string app_id;
|
||||
iter->second->GetApplicationId(&app_id);
|
||||
session->GetApplicationId(&app_id);
|
||||
|
||||
CdmKeyMessage key_message;
|
||||
CdmKeyResponse key_response;
|
||||
@@ -1110,22 +1256,22 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
|
||||
return LOAD_USAGE_INFO_MISSING;
|
||||
}
|
||||
|
||||
CdmResponseType status =
|
||||
iter->second->RestoreUsageSession(key_message, key_response);
|
||||
CdmResponseType status = session->RestoreUsageSession(key_message,
|
||||
key_response);
|
||||
if (KEY_ADDED != status) {
|
||||
LOGE("CdmEngine::LoadUsageSession: usage session error %ld", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmKeyRequest request;
|
||||
status = iter->second->GenerateReleaseRequest(&request);
|
||||
status = session->GenerateReleaseRequest(&request);
|
||||
*release_message = request.message;
|
||||
|
||||
switch (status) {
|
||||
case KEY_MESSAGE:
|
||||
break;
|
||||
case KEY_CANCELED: // usage information not present in
|
||||
iter->second->DeleteLicense(); // OEMCrypto, delete and try again
|
||||
session->DeleteLicense(); // OEMCrypto, delete and try again
|
||||
break;
|
||||
default:
|
||||
LOGE("CdmEngine::LoadUsageSession: generate release request error: %d",
|
||||
@@ -1161,45 +1307,55 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
|
||||
// else we must be level 1 direct and we don't need to return a buffer.
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator session_iter = sessions_.end();
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (session_id.empty()) {
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
// Loop through the sessions to find the session containing the key_id
|
||||
// with the longest remaining license validity.
|
||||
int64_t seconds_remaining = 0;
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
if (iter->second->IsKeyLoaded(*parameters.key_id)) {
|
||||
int64_t duration = iter->second->GetDurationRemaining();
|
||||
for (CdmSessionList::iterator iter = sessions.begin();
|
||||
iter != sessions.end(); ++iter) {
|
||||
if ((*iter)->IsClosed()) {
|
||||
continue;
|
||||
}
|
||||
if ((*iter)->IsKeyLoaded(*parameters.key_id)) {
|
||||
int64_t duration = (*iter)->GetDurationRemaining();
|
||||
if (duration > seconds_remaining) {
|
||||
session_iter = iter;
|
||||
session = *iter;
|
||||
seconds_remaining = duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
session_iter = sessions_.find(session_id);
|
||||
session_map_.FindSession(session_id, session);
|
||||
}
|
||||
if (session_iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
|
||||
session_id.c_str(), session_id.size());
|
||||
|
||||
if (session.get() == NULL) {
|
||||
if (session_id.empty()) {
|
||||
LOGE("CdmEngine::Decrypt: session not found: Empty session ID");
|
||||
} else {
|
||||
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
|
||||
session_id.c_str(), session_id.size());
|
||||
}
|
||||
return SESSION_NOT_FOUND_FOR_DECRYPT;
|
||||
}
|
||||
|
||||
return session_iter->second->Decrypt(parameters);
|
||||
return session->Decrypt(parameters);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericEncrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm, std::string* out_buffer) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::GenericEncrypt: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_13;
|
||||
}
|
||||
return iter->second->GenericEncrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer);
|
||||
return session->GenericEncrypt(in_buffer, key_id, iv, algorithm, out_buffer);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericDecrypt(
|
||||
@@ -1207,46 +1363,51 @@ CdmResponseType CdmEngine::GenericDecrypt(
|
||||
const std::string& key_id, const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::GenericDecrypt: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_14;
|
||||
}
|
||||
return iter->second->GenericDecrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer);
|
||||
return session->GenericDecrypt(in_buffer, key_id, iv, algorithm, out_buffer);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericSign(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, CdmSigningAlgorithm algorithm,
|
||||
std::string* signature) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::GenericSign: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_15;
|
||||
}
|
||||
return iter->second->GenericSign(message, key_id, algorithm, signature);
|
||||
return session->GenericSign(message, key_id, algorithm, signature);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericVerify(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(session_id, session)) {
|
||||
LOGE("CdmEngine::GenericVerify: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_16;
|
||||
}
|
||||
return iter->second->GenericVerify(message, key_id, algorithm, signature);
|
||||
return session->GenericVerify(message, key_id, algorithm, signature);
|
||||
}
|
||||
|
||||
// TODO(gmorgan) Used? Delete if unused.
|
||||
bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
if (iter->second->IsKeyLoaded(key_id)) {
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
for (CdmSessionList::iterator iter = sessions.begin();
|
||||
iter != sessions.end(); ++iter) {
|
||||
if ((*iter)->IsClosed()) {
|
||||
continue;
|
||||
}
|
||||
if ((*iter)->IsKeyLoaded(key_id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1260,18 +1421,23 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id);
|
||||
|
||||
CdmSessionMap::iterator session_iter = sessions_.end();
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
CdmSessionList::iterator session_iter = sessions.end();
|
||||
|
||||
int64_t seconds_remaining = 0;
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
CdmSessionId local_session_id = iter->second->session_id();
|
||||
if (Properties::GetSessionSharingId(local_session_id) ==
|
||||
session_sharing_id) {
|
||||
if (iter->second->IsKeyLoaded(key_id)) {
|
||||
int64_t duration = iter->second->GetDurationRemaining();
|
||||
for (CdmSessionList::iterator iter = sessions.begin();
|
||||
iter != sessions.end(); ++iter) {
|
||||
if ((*iter)->IsClosed()) {
|
||||
continue;
|
||||
}
|
||||
CdmSessionId id = (*iter)->session_id();
|
||||
if (Properties::GetSessionSharingId(id) == session_sharing_id) {
|
||||
if ((*iter)->IsKeyLoaded(key_id)) {
|
||||
int64_t duration = (*iter)->GetDurationRemaining();
|
||||
if (duration > seconds_remaining) {
|
||||
session_iter = iter;
|
||||
seconds_remaining = duration;
|
||||
@@ -1280,8 +1446,8 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
}
|
||||
}
|
||||
|
||||
if (session_iter != sessions_.end()) {
|
||||
*session_id = session_iter->second->session_id();
|
||||
if (session_iter != sessions.end()) {
|
||||
*session_id = (*session_iter)->session_id();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1289,9 +1455,9 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
|
||||
bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
uint32_t height) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter != sessions_.end()) {
|
||||
iter->second->NotifyResolution(width, height);
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (session_map_.FindSession(session_id, session)) {
|
||||
session->NotifyResolution(width, height);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1304,8 +1470,11 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
|
||||
void CdmEngine::OnTimerEvent() {
|
||||
Clock clock;
|
||||
uint64_t current_time = clock.GetCurrentTime();
|
||||
bool usage_update_period_expired = false;
|
||||
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
bool usage_update_period_expired = false;
|
||||
if (current_time - last_usage_information_update_time_ >
|
||||
kUpdateUsageInformationPeriod) {
|
||||
usage_update_period_expired = true;
|
||||
@@ -1315,27 +1484,35 @@ void CdmEngine::OnTimerEvent() {
|
||||
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) {
|
||||
while (!sessions.empty()) {
|
||||
is_initial_usage_update =
|
||||
is_initial_usage_update || iter->second->is_initial_usage_update();
|
||||
is_initial_usage_update || sessions.front()->is_initial_usage_update();
|
||||
is_usage_update_needed =
|
||||
is_usage_update_needed || iter->second->is_usage_update_needed();
|
||||
is_usage_update_needed || sessions.front()->is_usage_update_needed();
|
||||
|
||||
iter->second->OnTimerEvent(usage_update_period_expired);
|
||||
if (!sessions.front()->IsClosed()) {
|
||||
sessions.front()->OnTimerEvent(usage_update_period_expired);
|
||||
}
|
||||
sessions.pop_front();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Session list may have changed. Rebuild.
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
for (CdmSessionList::iterator iter = sessions.begin();
|
||||
iter != sessions.end(); ++iter) {
|
||||
if ((*iter)->IsClosed()) {
|
||||
continue;
|
||||
}
|
||||
(*iter)->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();
|
||||
CdmResponseType status = (*iter)->UpdateUsageInformation();
|
||||
if (NO_ERROR != status) {
|
||||
LOGW("Update usage information failed: %d", status);
|
||||
} else {
|
||||
@@ -1344,15 +1521,18 @@ void CdmEngine::OnTimerEvent() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseExpiredReleaseSessions();
|
||||
}
|
||||
|
||||
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);
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
while (!sessions.empty()) {
|
||||
if (!sessions.front()->IsClosed()) {
|
||||
sessions.front()->OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
sessions.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1415,12 +1595,12 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
|
||||
status = crypto_session->DeleteAllUsageReports();
|
||||
if (NO_ERROR != status) {
|
||||
LOGW(
|
||||
"CdmEngine::GetProvisioningRequest: "
|
||||
"CdmEngine::DeleteAllUsageReportsUponFactoryReset: "
|
||||
"Fails to delete usage reports: %d", status);
|
||||
}
|
||||
} else {
|
||||
LOGW(
|
||||
"CdmEngine::GetProvisioningRequest: "
|
||||
"CdmEngine::DeleteAllUsageReportsUponFactoryReset: "
|
||||
"Fails to open crypto session: error=%d.\n"
|
||||
"Usage reports are not removed after factory reset.", status);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace wvcdm {
|
||||
|
||||
CdmSession::CdmSession(FileSystem* file_system) :
|
||||
initialized_(false),
|
||||
closed_(false),
|
||||
crypto_session_(new CryptoSession),
|
||||
file_handle_(new DeviceFiles(file_system)),
|
||||
license_received_(false),
|
||||
@@ -101,9 +102,8 @@ CdmResponseType CdmSession::Init(
|
||||
// License server client ID token is a stored certificate. Stage it or
|
||||
// indicate that provisioning is needed. Get token from stored certificate
|
||||
std::string wrapped_key;
|
||||
uint32_t system_id;
|
||||
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
|
||||
&serial_number, &system_id) ||
|
||||
&serial_number, nullptr) ||
|
||||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
@@ -243,26 +243,6 @@ 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 if (is_temporary_) {
|
||||
license_type = kLicenseTypeTemporary;
|
||||
} else {
|
||||
license_type = kLicenseTypeStreaming;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
|
||||
license_type);
|
||||
@@ -276,16 +256,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
} else {
|
||||
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.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 (is_offline_ && key_set_id_.empty()) {
|
||||
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
|
||||
@@ -297,16 +275,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
init_data, license_type,
|
||||
app_parameters, &key_request->message,
|
||||
&key_request->url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
if (status != KEY_MESSAGE)
|
||||
return status;
|
||||
|
||||
key_request_ = key_request->message;
|
||||
if (is_offline_) {
|
||||
offline_init_data_ = init_data.data();
|
||||
offline_release_server_url_ = key_request->url;
|
||||
|
||||
}
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
}
|
||||
@@ -336,6 +312,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
license_received_ = true;
|
||||
key_response_ = key_response;
|
||||
|
||||
LOGV("AddKey: provider_session_token (size=%d) =%s",
|
||||
license_parser_->provider_session_token().size(),
|
||||
license_parser_->provider_session_token().c_str());
|
||||
|
||||
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
|
||||
sts = StoreLicense();
|
||||
if (sts != NO_ERROR) return sts;
|
||||
@@ -423,6 +403,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
// is updated, so we treat this Decrypt call as invalid.
|
||||
if (params.is_encrypted &&
|
||||
!policy_engine_->CanDecryptContent(*params.key_id)) {
|
||||
if (policy_engine_->GetKeyStatus(*params.key_id) ==
|
||||
kKeyStatusOutputNotAllowed) {
|
||||
return INSUFFICIENT_OUTPUT_PROTECTION;
|
||||
}
|
||||
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
|
||||
}
|
||||
|
||||
|
||||
71
core/src/cdm_session_map.cpp
Normal file
71
core/src/cdm_session_map.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "cdm_session_map.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "cdm_session.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
CdmSessionMap::~CdmSessionMap() {
|
||||
AutoLock lock(lock_);
|
||||
for (CdmIdToSessionMap::iterator i = sessions_.begin();
|
||||
i != sessions_.end(); ++i) {
|
||||
i->second->Close();
|
||||
i->second.reset();
|
||||
}
|
||||
sessions_.clear();
|
||||
}
|
||||
|
||||
void CdmSessionMap::Add(const std::string& id, CdmSession* session) {
|
||||
AutoLock lock(lock_);
|
||||
sessions_[id].reset(session);
|
||||
}
|
||||
|
||||
bool CdmSessionMap::CloseSession(const std::string& id) {
|
||||
AutoLock lock(lock_);
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!FindSessionNoLock(id, session)) {
|
||||
return false;
|
||||
}
|
||||
session->Close();
|
||||
sessions_.erase(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdmSessionMap::Exists(const std::string& id) {
|
||||
AutoLock lock(lock_);
|
||||
return sessions_.find(id) != sessions_.end();
|
||||
}
|
||||
|
||||
bool CdmSessionMap::FindSession(const CdmSessionId& id,
|
||||
std::shared_ptr<CdmSession>& session) {
|
||||
AutoLock lock(lock_);
|
||||
return FindSessionNoLock(id, session);
|
||||
}
|
||||
|
||||
bool CdmSessionMap::FindSessionNoLock(const CdmSessionId& session_id,
|
||||
std::shared_ptr<CdmSession>& session) {
|
||||
CdmIdToSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
}
|
||||
session = iter->second;
|
||||
assert(session.get() != NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef std::list<std::shared_ptr<CdmSession> > CdmSessionList;
|
||||
|
||||
void CdmSessionMap::GetSessionList(CdmSessionList& sessions) {
|
||||
sessions.clear();
|
||||
AutoLock lock(lock_);
|
||||
for (CdmIdToSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
sessions.push_back(iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -19,37 +19,64 @@ const std::string kProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
/*
|
||||
* Provisioning response is a base64-encoded protobuf, optionally within a
|
||||
* JSON wrapper. If the JSON wrapper is present, extract the embedded response
|
||||
* message. Then perform the base64 decode and return the result.
|
||||
*
|
||||
* If an error occurs during the parse or the decode, return an empty string.
|
||||
*/
|
||||
void ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
|
||||
std::string* result) {
|
||||
const std::string json_start_substr("\"signedResponse\": \"");
|
||||
const std::string json_end_substr("\"");
|
||||
std::string message_string;
|
||||
|
||||
size_t start = provisioning_response.find(json_start_substr);
|
||||
|
||||
if (start == provisioning_response.npos) {
|
||||
// Message is not properly wrapped - reject it.
|
||||
LOGE("ExtractAndDecodeSignedMessage: cannot locate start substring");
|
||||
result->clear();
|
||||
return;
|
||||
} else {
|
||||
// Appears to be JSON-wrapped protobuf - find end of protobuf portion.
|
||||
size_t end = provisioning_response.find(json_end_substr,
|
||||
start + json_start_substr.length());
|
||||
if (end == provisioning_response.npos) {
|
||||
LOGE("ExtractAndDecodeSignedMessage: cannot locate end substring");
|
||||
result->clear();
|
||||
return;
|
||||
}
|
||||
size_t b64_string_size = end - start - json_start_substr.length();
|
||||
message_string.assign(provisioning_response,
|
||||
start + json_start_substr.length(), b64_string_size);
|
||||
}
|
||||
|
||||
if (message_string.empty()) {
|
||||
LOGE("ExtractAndDecodeSignedMessage: CdmProvisioningResponse is empty");
|
||||
result->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode the base64-encoded message.
|
||||
const std::vector<uint8_t> decoded_message =
|
||||
wvcdm::Base64SafeDecode(message_string);
|
||||
result->assign(decoded_message.begin(), decoded_message.end());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
// Protobuf generated classes.
|
||||
using video_widevine::ClientIdentification;
|
||||
using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::ProvisioningOptions;
|
||||
using video_widevine::ProvisioningRequest;
|
||||
using video_widevine::ProvisioningResponse;
|
||||
using video_widevine::SignedProvisioningMessage;
|
||||
|
||||
/*
|
||||
* This function converts SignedProvisioningRequest into base64 string. It then
|
||||
* wraps it in JSON format expected by the frontend. This server requires a
|
||||
* "web-safe" base 64 encoding, where '+' becomes '-' and '/' becomes '_'.
|
||||
*
|
||||
* Returns the JSON formated string in *request. The JSON string will be
|
||||
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
|
||||
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
|
||||
*
|
||||
* The JSON formated request takes the following format:
|
||||
*
|
||||
* base64 encoded message
|
||||
*/
|
||||
void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
const std::string& message, CdmProvisioningRequest* request) {
|
||||
// Performs base64 encoding for message
|
||||
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
||||
std::string message_b64 = Base64SafeEncodeNoPad(message_vector);
|
||||
request->assign(message_b64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the ClientIdentification message token type for provisioning request.
|
||||
* NOTE: a DRM Cert should never be presented to the provisioning server.
|
||||
@@ -88,7 +115,7 @@ bool CertificateProvisioning::SetSpoidParameter(
|
||||
// Use the SPOID that has been pre-provided
|
||||
request->set_spoid(spoid);
|
||||
} else if (Properties::UseProviderIdInProvisioningRequest()) {
|
||||
if (service_certificate_->HasProviderId()) {
|
||||
if (!service_certificate_->provider_id().empty()) {
|
||||
request->set_provider_id(service_certificate_->provider_id());
|
||||
} else {
|
||||
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
|
||||
@@ -121,9 +148,9 @@ SignedProvisioningMessage::ProtocolVersion
|
||||
}
|
||||
|
||||
/*
|
||||
* Composes a device provisioning request and output the request in JSON format
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
* in *default_url.
|
||||
* Compose a device provisioning request and output *request in a
|
||||
* JSON-compatible format (web-safe base64).
|
||||
* Also return *default_url of the provisioning server.
|
||||
*
|
||||
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
|
||||
*/
|
||||
@@ -162,17 +189,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
client_id->set_token(token);
|
||||
client_id->set_type(token_type);
|
||||
|
||||
#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification.
|
||||
if (service_certificate_->HasCertificate()) {
|
||||
#if 0 // TODO(gmorgan) Encrypt ClientIdentification. Pending Design.
|
||||
if (service_certificate_->has_certificate()) {
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
provisioning_request->mutable_encrypted_client_id();
|
||||
provisioning_request.mutable_encrypted_client_id();
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->EncryptClientId(&crypto_session_, client_id,
|
||||
encrypted_client_id);
|
||||
if (status == NO_ERROR) {
|
||||
provisioning_request->clear_client_id();
|
||||
provisioning_request.clear_client_id();
|
||||
} else {
|
||||
provisioning_request->clear_encrypted_client_id();
|
||||
provisioning_request.clear_encrypted_client_id();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -234,43 +261,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
|
||||
// Converts request into JSON string
|
||||
ComposeJsonRequestAsQueryString(serialized_request, request);
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
// Return request as web-safe base64 string
|
||||
std::vector<uint8_t> request_vector(serialized_request.begin(),
|
||||
serialized_request.end());
|
||||
request->assign(Base64SafeEncodeNoPad(request_vector));
|
||||
} else {
|
||||
request->swap(serialized_request);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses the input json_str and locates substring using start_substr and
|
||||
* end_stubstr. The found base64 substring is then decoded and returns
|
||||
* in *result.
|
||||
*
|
||||
* Returns true for success and false if fails.
|
||||
*/
|
||||
bool CertificateProvisioning::ParseJsonResponse(
|
||||
const CdmProvisioningResponse& json_str, const std::string& start_substr,
|
||||
const std::string& end_substr, std::string* result) {
|
||||
std::string b64_string;
|
||||
size_t start = json_str.find(start_substr);
|
||||
if (start == json_str.npos) {
|
||||
LOGE("ParseJsonResponse: cannot find start substring");
|
||||
return false;
|
||||
}
|
||||
size_t end = json_str.find(end_substr, start + start_substr.length());
|
||||
if (end == json_str.npos) {
|
||||
LOGE("ParseJsonResponse cannot locate end substring");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t b64_string_size = end - start - start_substr.length();
|
||||
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
|
||||
|
||||
// Decodes base64 substring and returns it in *result
|
||||
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
|
||||
result->assign(result_vector.begin(), result_vector.end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The response message consists of a device certificate and the device RSA key.
|
||||
* The device RSA key is stored in the T.E.E. The device certificate is stored
|
||||
@@ -281,13 +282,19 @@ bool CertificateProvisioning::ParseJsonResponse(
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
FileSystem* file_system, 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 = "\"";
|
||||
std::string serialized_signed_response;
|
||||
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
|
||||
&serialized_signed_response)) {
|
||||
LOGE("Fails to extract signed serialized response from JSON response");
|
||||
|
||||
std::string raw_string;
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
// The response is base64 encoded in a JSON wrapper.
|
||||
// Extract it and decode it. If errors, return an empty string.
|
||||
ExtractAndDecodeSignedMessage(response, &raw_string);
|
||||
} else {
|
||||
raw_string.assign(response);
|
||||
}
|
||||
|
||||
if (raw_string.empty()) {
|
||||
LOGE("HandleProvisioningResponse: response message is empty or "
|
||||
"an invalid JSON/base64 string.");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_1;
|
||||
}
|
||||
|
||||
@@ -295,7 +302,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
// the provisioing request's input). Validate provisioning response and
|
||||
// stores private device RSA key and certificate.
|
||||
SignedProvisioningMessage signed_response;
|
||||
if (!signed_response.ParseFromString(serialized_signed_response)) {
|
||||
if (!signed_response.ParseFromString(raw_string)) {
|
||||
LOGE("HandleProvisioningResponse: fails to parse signed response");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_2;
|
||||
}
|
||||
@@ -314,6 +321,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
const std::string& signature = signed_response.signature();
|
||||
ProvisioningResponse provisioning_response;
|
||||
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
@@ -326,15 +334,29 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_5;
|
||||
}
|
||||
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
// If Provisioning 3.0 (OEM Cert provisioned), verify that the
|
||||
// message is properly signed.
|
||||
if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert) {
|
||||
if (service_certificate_->VerifySignedMessage(signed_message, signature)
|
||||
!= NO_ERROR) {
|
||||
LOGE("HandleProvisioningResponse: message not properly signed");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_6;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& new_private_key = provisioning_response.device_rsa_key();
|
||||
const std::string& nonce = provisioning_response.nonce();
|
||||
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
||||
const std::string& signature = signed_response.signature();
|
||||
std::string wrapped_rsa_key;
|
||||
if (!crypto_session_.RewrapDeviceRSAKey(signed_message, signature, nonce,
|
||||
enc_rsa_key, rsa_key_iv,
|
||||
&wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
const std::string& iv = provisioning_response.device_rsa_key_iv();
|
||||
|
||||
const std::string& wrapping_key = (provisioning_response.has_wrapping_key()) ?
|
||||
provisioning_response.wrapping_key() : std::string();
|
||||
|
||||
std::string wrapped_private_key;
|
||||
|
||||
if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce,
|
||||
new_private_key, iv, wrapping_key,
|
||||
&wrapped_private_key)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapCertificate fails");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_6;
|
||||
}
|
||||
|
||||
@@ -342,7 +364,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
|
||||
if (cert_type_ == kCertificateX509) {
|
||||
*cert = provisioning_response.device_certificate();
|
||||
*wrapped_key = wrapped_rsa_key;
|
||||
*wrapped_key = wrapped_private_key;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -357,7 +379,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_7;
|
||||
}
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_private_key)) {
|
||||
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_8;
|
||||
}
|
||||
|
||||
@@ -54,17 +54,17 @@ CryptoSession::~CryptoSession() {
|
||||
Terminate();
|
||||
}
|
||||
|
||||
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
|
||||
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType& token_type) {
|
||||
OEMCrypto_ProvisioningMethod method;
|
||||
switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) {
|
||||
case OEMCrypto_OEMCertificate:
|
||||
*token_type = kClientTokenOemCert;
|
||||
token_type = kClientTokenOemCert;
|
||||
break;
|
||||
case OEMCrypto_Keybox:
|
||||
*token_type = kClientTokenKeybox;
|
||||
token_type = kClientTokenKeybox;
|
||||
break;
|
||||
case OEMCrypto_DrmCertificate:
|
||||
*token_type = kClientTokenDrmCert;
|
||||
token_type = kClientTokenDrmCert;
|
||||
break;
|
||||
case OEMCrypto_ProvisioningError:
|
||||
default:
|
||||
@@ -86,7 +86,7 @@ void CryptoSession::Init() {
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
|
||||
if (!GetProvisioningMethod(pre_provision_token_type_)) {
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,10 @@ bool CryptoSession::GetTokenFromKeybox(std::string* token) {
|
||||
|
||||
bool CryptoSession::GetTokenFromOemCert(std::string* token) {
|
||||
OEMCryptoResult status;
|
||||
if (!oem_token_.empty()) {
|
||||
token->assign(oem_token_);
|
||||
return true;
|
||||
}
|
||||
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
|
||||
// lock is held by caller
|
||||
bool retrying = false;
|
||||
@@ -133,6 +137,8 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) {
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
|
||||
if (OEMCrypto_SUCCESS == status) {
|
||||
temp_buffer.resize(buf_size);
|
||||
oem_token_.assign(temp_buffer);
|
||||
token->swap(temp_buffer);
|
||||
return true;
|
||||
}
|
||||
@@ -387,7 +393,8 @@ bool CryptoSession::PrepareRequest(const std::string& message,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Properties::use_certificates_as_identification() || is_provisioning) {
|
||||
if (!Properties::use_certificates_as_identification() ||
|
||||
(is_provisioning && (pre_provision_token_type_ == kClientTokenKeybox))) {
|
||||
if (!GenerateDerivedKeys(message)) return false;
|
||||
|
||||
if (!GenerateSignature(message, signature)) return false;
|
||||
@@ -535,8 +542,16 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
|
||||
LOGV("CryptoSession::LoadCertificatePrivateKey: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
// Call OEMCrypto_GetOEMPublicCertificate before OEMCrypto_LoadDeviceRSAKey
|
||||
// so it caches the OEMCrypto Public Key and then throw away result
|
||||
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
|
||||
size_t buf_size = temp_buffer.size();
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
|
||||
|
||||
LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_);
|
||||
OEMCryptoResult sts = OEMCrypto_LoadDeviceRSAKey(
|
||||
sts = OEMCrypto_LoadDeviceRSAKey(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(wrapped_key.data()),
|
||||
wrapped_key.size());
|
||||
|
||||
@@ -1089,6 +1104,34 @@ bool CryptoSession::SetDestinationBufferType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::RewrapCertificate(const std::string& signed_message,
|
||||
const std::string& signature,
|
||||
const std::string& nonce,
|
||||
const std::string& private_key,
|
||||
const std::string& iv,
|
||||
const std::string& wrapping_key,
|
||||
std::string* wrapped_private_key) {
|
||||
LOGV("CryptoSession::RewrapCertificate, session id=%ld",
|
||||
static_cast<uint32_t>(oec_session_id_));
|
||||
|
||||
if (pre_provision_token_type_ == kClientTokenKeybox) {
|
||||
|
||||
return RewrapDeviceRSAKey(signed_message, signature, nonce, private_key,
|
||||
iv, wrapped_private_key);
|
||||
|
||||
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
|
||||
return RewrapDeviceRSAKey30(signed_message, nonce, private_key, iv,
|
||||
wrapping_key, wrapped_private_key);
|
||||
|
||||
} else {
|
||||
LOGE("CryptoSession::RewrapCertificate, Bad pre-provision type=%d: "
|
||||
"session id=%ld", pre_provision_token_type_,
|
||||
static_cast<uint32_t>(oec_session_id_));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
const std::string& signature,
|
||||
const std::string& nonce,
|
||||
@@ -1140,6 +1183,57 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message,
|
||||
const std::string& nonce,
|
||||
const std::string& private_key,
|
||||
const std::string& iv,
|
||||
const std::string& wrapping_key,
|
||||
std::string* wrapped_private_key) {
|
||||
LOGV("CryptoSession::RewrapDeviceRSAKey30, session id=%ld",
|
||||
static_cast<uint32_t>(oec_session_id_));
|
||||
|
||||
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
const uint8_t* msg_private_key = NULL;
|
||||
const uint8_t* msg_iv = NULL;
|
||||
const uint32_t* msg_nonce = NULL;
|
||||
const uint8_t* msg_wrapping_key = NULL;
|
||||
if (private_key.size() >= MAC_KEY_SIZE && iv.size() >= KEY_IV_SIZE) {
|
||||
msg_private_key = signed_msg + GetOffset(message, private_key);
|
||||
msg_iv = signed_msg + GetOffset(message, iv);
|
||||
msg_nonce = reinterpret_cast<const uint32_t*>(signed_msg +
|
||||
GetOffset(message, nonce));
|
||||
msg_wrapping_key = signed_msg + GetOffset(message, wrapping_key);
|
||||
}
|
||||
|
||||
// Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key
|
||||
// and 0 as wrapped_rsa_key_length.
|
||||
size_t wrapped_private_key_length = 0;
|
||||
OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey30(
|
||||
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
|
||||
msg_private_key, private_key.size(), msg_iv, NULL,
|
||||
&wrapped_private_key_length);
|
||||
|
||||
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
|
||||
LOGE("OEMCrypto_RewrapDeviceRSAKey30 failed getting wrapped key length");
|
||||
return false;
|
||||
}
|
||||
|
||||
wrapped_private_key->resize(wrapped_private_key_length);
|
||||
status = OEMCrypto_RewrapDeviceRSAKey30(
|
||||
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
|
||||
msg_private_key, private_key.size(), msg_iv,
|
||||
reinterpret_cast<uint8_t*>(&(*wrapped_private_key)[0]),
|
||||
&wrapped_private_key_length);
|
||||
|
||||
wrapped_private_key->resize(wrapped_private_key_length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
|
||||
HdcpCapability* max) {
|
||||
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
@@ -143,52 +143,43 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
}
|
||||
|
||||
DeviceCertificate device_certificate = file.device_certificate();
|
||||
|
||||
ExtractDeviceInfo(device_certificate.certificate(), serial_number, system_id);
|
||||
|
||||
*certificate = device_certificate.certificate();
|
||||
*wrapped_private_key = device_certificate.wrapped_private_key();
|
||||
return true;
|
||||
return ExtractDeviceInfo(device_certificate.certificate(), serial_number,
|
||||
system_id);
|
||||
}
|
||||
|
||||
bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id) {
|
||||
LOGI("[WEM] ExtractDeviceInfo");
|
||||
LOGI("ExtractDeviceInfo Entry");
|
||||
if (!serial_number && !system_id) {
|
||||
LOGE("Invalid paramters to DeviceFiles::ExtractDeviceInfo");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get serial number and system ID from certificate
|
||||
if ((serial_number != NULL) || (system_id != NULL)) {
|
||||
SignedDrmDeviceCertificate signed_drm_device_certificate;
|
||||
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
|
||||
!signed_drm_device_certificate.has_drm_certificate()) {
|
||||
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing signed drm device "
|
||||
"certificate.");
|
||||
return false;
|
||||
}
|
||||
DrmDeviceCertificate drm_device_certificate;
|
||||
if (!drm_device_certificate.ParseFromString(
|
||||
signed_drm_device_certificate.drm_certificate()) ||
|
||||
(drm_device_certificate.type() !=
|
||||
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
|
||||
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing drm device "
|
||||
"certificate message.");
|
||||
return false;
|
||||
}
|
||||
if (serial_number != NULL) {
|
||||
if (drm_device_certificate.has_serial_number()) {
|
||||
LOGI("DeviceFiles::ExtractDeviceInfo: serial number: [%s]",
|
||||
(b2a_hex(drm_device_certificate.serial_number())).c_str());
|
||||
*serial_number = drm_device_certificate.serial_number();
|
||||
}
|
||||
}
|
||||
if (system_id != NULL) {
|
||||
if (drm_device_certificate.has_system_id()) {
|
||||
LOGI("DeviceFiles::ExtractDeviceInfo: system id: [%d]",
|
||||
drm_device_certificate.system_id());
|
||||
*system_id = drm_device_certificate.system_id();
|
||||
} else {
|
||||
*system_id = 0;
|
||||
}
|
||||
}
|
||||
SignedDrmDeviceCertificate signed_drm_device_certificate;
|
||||
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
|
||||
!signed_drm_device_certificate.has_drm_certificate()) {
|
||||
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing signed drm device "
|
||||
"certificate.");
|
||||
return false;
|
||||
}
|
||||
DrmDeviceCertificate drm_device_certificate;
|
||||
if (!drm_device_certificate.ParseFromString(
|
||||
signed_drm_device_certificate.drm_certificate()) ||
|
||||
(drm_device_certificate.type() !=
|
||||
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
|
||||
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing drm device "
|
||||
"certificate message.");
|
||||
return false;
|
||||
}
|
||||
if (serial_number != NULL) {
|
||||
*serial_number = drm_device_certificate.serial_number();
|
||||
}
|
||||
if (system_id != NULL) {
|
||||
*system_id = drm_device_certificate.system_id();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -341,7 +332,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||
|
||||
bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
|
||||
LOGW("DeviceFiles::ListLicenses: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -354,7 +345,7 @@ bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
|
||||
// Scan list of returned filenames, remove extension, and return
|
||||
// as a list of key_set_ids.
|
||||
key_set_ids->clear();
|
||||
for (int i = 0; i < filenames.size(); i++) {
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
std::string* name = &filenames[i];
|
||||
std::size_t pos = name->find(kLicenseFileNameExt);
|
||||
if (pos == std::string::npos) {
|
||||
@@ -449,6 +440,77 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
return StoreFileWithHash(file_name, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::ListUsageRecords(const std::string& app_id,
|
||||
std::vector<std::string>* ksids) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::ListUsageRecords: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ksids == NULL) {
|
||||
LOGW("DeviceFiles::ListUsageRecords: return parameter not provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Empty or non-existent file == no usage records.
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
|
||||
ksids->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveHashedFile(file_name, &file)) {
|
||||
LOGW("DeviceFiles::ListUsageRecords: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
|
||||
ksids->clear();
|
||||
|
||||
size_t num_records = file.usage_info().sessions_size();
|
||||
for (size_t i = 0; i < num_records; ++i) {
|
||||
if (!file.usage_info().sessions(i).key_set_id().empty()) {
|
||||
ksids->push_back(file.usage_info().sessions(i).key_set_id());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::GetProviderToken(const std::string& app_id,
|
||||
const std::string& key_set_id,
|
||||
std::string* provider_session_token) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::GetProviderToken: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (provider_session_token == NULL) {
|
||||
LOGW("DeviceFiles::GetProviderToken: NULL return argument pointer");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
|
||||
LOGW("DeviceFiles::GetProviderToken: empty file");
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveHashedFile(file_name, &file)) {
|
||||
LOGW("DeviceFiles::GetProviderToken: unable to parse file");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_records = file.usage_info().sessions_size();
|
||||
for (size_t i = 0; i < num_records; ++i) {
|
||||
if (file.usage_info().sessions(i).key_set_id() == key_set_id) {
|
||||
*provider_session_token = file.usage_info().sessions(i).token();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token) {
|
||||
if (!initialized_) {
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
std::string kCompanyNameKey = "company_name";
|
||||
std::string kModelNameKey = "model_name";
|
||||
std::string kArchitectureNameKey = "architecture_name";
|
||||
std::string kDeviceNameKey = "device_name";
|
||||
std::string kProductNameKey = "product_name";
|
||||
std::string kBuildInfoKey = "build_info";
|
||||
std::string kDeviceIdKey = "device_id";
|
||||
std::string kWVCdmVersionKey = "widevine_cdm_version";
|
||||
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
|
||||
} // namespace
|
||||
const std::string kCompanyNameKey = "company_name";
|
||||
const std::string kModelNameKey = "model_name";
|
||||
const std::string kArchitectureNameKey = "architecture_name";
|
||||
const std::string kDeviceNameKey = "device_name";
|
||||
const std::string kProductNameKey = "product_name";
|
||||
const std::string kBuildInfoKey = "build_info";
|
||||
const std::string kDeviceIdKey = "device_id";
|
||||
const std::string kWVCdmVersionKey = "widevine_cdm_version";
|
||||
const std::string kOemCryptoSecurityPatchLevelKey =
|
||||
"oem_crypto_security_patch_level";
|
||||
|
||||
const uint32_t kFourCcCbc1 = 0x63626331;
|
||||
const uint32_t kFourCcCbcs = 0x63626373;
|
||||
@@ -37,6 +37,8 @@ const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
|
||||
const uint32_t kFourCcCenc = 0x63656e63;
|
||||
const uint32_t kFourCcCens = 0x63656e73;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
@@ -144,7 +146,7 @@ CdmLicense::~CdmLicense() {}
|
||||
|
||||
bool CdmLicense::Init(
|
||||
ServiceCertificate* service_certificate, const std::string& client_token,
|
||||
CdmClientTokenType client_token_type, const std::string& serial_number,
|
||||
CdmClientTokenType client_token_type, const std::string& device_id,
|
||||
CryptoSession* session, PolicyEngine* policy_engine) {
|
||||
if (clock_.get() == NULL) {
|
||||
LOGE("CdmLicense::Init: clock parameter not provided");
|
||||
@@ -170,7 +172,7 @@ bool CdmLicense::Init(
|
||||
service_certificate_ = service_certificate;
|
||||
client_token_ = client_token;
|
||||
client_token_type_ = client_token_type;
|
||||
serial_number_ = serial_number;
|
||||
device_id_ = device_id;
|
||||
crypto_session_ = session;
|
||||
policy_engine_ = policy_engine;
|
||||
initialized_ = true;
|
||||
@@ -185,12 +187,6 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
|
||||
return LICENSE_PARSER_NOT_INITIALIZED_4;
|
||||
}
|
||||
if (init_data.IsEmpty() && stored_init_data_.get()) {
|
||||
InitializationData restored_init_data = *stored_init_data_;
|
||||
stored_init_data_.reset();
|
||||
return PrepareKeyRequest(restored_init_data, license_type, app_parameters,
|
||||
signed_request, server_url);
|
||||
}
|
||||
if (!init_data.is_supported()) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
@@ -209,16 +205,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
return INVALID_PARAMETERS_LIC_7;
|
||||
}
|
||||
|
||||
// If privacy mode and no service certificate, initiate a
|
||||
// service certificate request.
|
||||
// If privacy mode, must have service certificate
|
||||
if (Properties::UsePrivacyMode(session_id_) &&
|
||||
!service_certificate_->HasCertificate()) {
|
||||
stored_init_data_.reset(new InitializationData(init_data));
|
||||
*server_url = server_url_;
|
||||
if (service_certificate_->PrepareRequest(signed_request)) {
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
!service_certificate_->has_certificate()) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
|
||||
"no service certificate.");
|
||||
return PRIVACY_MODE_ERROR_1;
|
||||
}
|
||||
|
||||
std::string request_id;
|
||||
@@ -303,12 +295,10 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
|
||||
if (renew_with_client_id_) {
|
||||
if (Properties::UsePrivacyMode(session_id_) &&
|
||||
!service_certificate_->HasCertificate()) {
|
||||
*server_url = server_url_;
|
||||
if (service_certificate_->PrepareRequest(signed_request)) {
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
return LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
!service_certificate_->has_certificate()) {
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - "
|
||||
"no service certificate.");
|
||||
return PRIVACY_MODE_ERROR_2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,14 +413,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
switch (signed_response.type()) {
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->HandleResponse(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
return NEED_KEY;
|
||||
}
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
default:
|
||||
@@ -494,8 +476,13 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
license.policy().can_persist())
|
||||
is_offline_ = true;
|
||||
|
||||
if (license.id().has_provider_session_token())
|
||||
LOGV("Get Provider_session_token:");
|
||||
if (license.id().has_provider_session_token()) {
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
LOGV("Provider_session_token=%s", provider_session_token_.c_str());
|
||||
} else {
|
||||
LOGV("NO Provider_session_token");
|
||||
}
|
||||
|
||||
if (license.policy().has_renewal_server_url()) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
@@ -540,14 +527,6 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
switch (signed_response.type()) {
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->HandleResponse(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
return NEED_KEY;
|
||||
}
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
default:
|
||||
@@ -820,6 +799,7 @@ bool CdmLicense::GetClientTokenType(
|
||||
case kClientTokenOemCert:
|
||||
default:
|
||||
// shouldn't happen
|
||||
LOGE("GetClientTokenType: BAD TOKEN TYPE");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -873,10 +853,10 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
client_info->set_name(kBuildInfoKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (!serial_number_.empty()) {
|
||||
if (!device_id_.empty()) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(b2a_hex(serial_number_));
|
||||
client_info->set_value(b2a_hex(device_id_));
|
||||
} else if (crypto_session_->GetDeviceUniqueId(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
@@ -950,9 +930,9 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
}
|
||||
|
||||
if (Properties::UsePrivacyMode(session_id_)) {
|
||||
if (!service_certificate_->HasCertificate()) {
|
||||
if (service_certificate_->certificate().empty()) {
|
||||
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
|
||||
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
return PRIVACY_MODE_ERROR_3;
|
||||
}
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
license_request->mutable_encrypted_client_id();
|
||||
@@ -983,7 +963,7 @@ CdmResponseType CdmLicense::PrepareContentId(
|
||||
if (!init_data.IsEmpty()) {
|
||||
cenc_content_id->add_pssh(init_data.data());
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
|
||||
LOGE("CdmLicense::PrepareContentId: ISO-CENC init data not available");
|
||||
return CENC_INIT_DATA_UNAVAILABLE;
|
||||
}
|
||||
|
||||
@@ -997,7 +977,7 @@ CdmResponseType CdmLicense::PrepareContentId(
|
||||
if (!init_data.IsEmpty()) {
|
||||
webm_content_id->set_header(init_data.data());
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
|
||||
LOGE("CdmLicense::PrepareContentId: WebM init data not available");
|
||||
return WEBM_INIT_DATA_UNAVAILABLE;
|
||||
}
|
||||
|
||||
@@ -1005,7 +985,7 @@ CdmResponseType CdmLicense::PrepareContentId(
|
||||
return PREPARE_WEBM_CONTENT_ID_FAILED;
|
||||
}
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
|
||||
LOGE("CdmLicense::PrepareContentId: no support for init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
return UNSUPPORTED_INIT_DATA_FORMAT;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "log.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
// License protocol aliases
|
||||
@@ -102,7 +103,15 @@ bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status,
|
||||
return keys_changed;
|
||||
}
|
||||
|
||||
CdmKeyStatus LicenseKeys::GetKeyStatus(const KeyId& key_id) {
|
||||
if (keys_.count(key_id) == 0) {
|
||||
return kKeyStatusKeyUnknown;
|
||||
}
|
||||
return keys_[key_id]->GetKeyStatus();
|
||||
}
|
||||
|
||||
void LicenseKeys::ExtractKeyStatuses(CdmKeyStatusMap* content_keys) {
|
||||
content_keys->clear();
|
||||
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
|
||||
if (it->second->IsContentKey()) {
|
||||
const KeyId key_id = it->first;
|
||||
@@ -251,11 +260,11 @@ bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
|
||||
// requirement, use the key's default HDCP setting to check against the
|
||||
// device's current HDCP level.
|
||||
void LicenseKeyStatus::ApplyConstraints(
|
||||
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
|
||||
uint32_t video_pixels, CryptoSession::HdcpCapability new_hdcp_level) {
|
||||
|
||||
VideoResolutionConstraint* current_constraint = NULL;
|
||||
if (HasConstraints()) {
|
||||
current_constraint = GetConstraintForRes(new_resolution, constraints_);
|
||||
if (HasConstraints() && video_pixels != HDCP_UNSPECIFIED_VIDEO_RESOLUTION) {
|
||||
current_constraint = GetConstraintForRes(video_pixels, constraints_);
|
||||
if (NULL == current_constraint) {
|
||||
meets_constraints_ = false;
|
||||
return;
|
||||
|
||||
@@ -10,75 +10,73 @@
|
||||
namespace wvcdm {
|
||||
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
|
||||
SecurityLevel level) {
|
||||
SecurityLevel) {
|
||||
return ::OEMCrypto_OpenSession(session);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level) {
|
||||
size_t keyBoxLength, SecurityLevel) {
|
||||
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel) {
|
||||
return ::OEMCrypto_IsKeyboxValid();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
|
||||
SecurityLevel level) {
|
||||
SecurityLevel) {
|
||||
return ::OEMCrypto_GetDeviceID(deviceID, idLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
|
||||
SecurityLevel level) {
|
||||
SecurityLevel) {
|
||||
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
|
||||
}
|
||||
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel) {
|
||||
return ::OEMCrypto_APIVersion();
|
||||
}
|
||||
|
||||
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level) {
|
||||
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel) {
|
||||
return ::OEMCrypto_Security_Patch_Level();
|
||||
}
|
||||
|
||||
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
const char* OEMCrypto_SecurityLevel(SecurityLevel) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
SecurityLevel level, OEMCrypto_HDCP_Capability* current,
|
||||
SecurityLevel, OEMCrypto_HDCP_Capability* current,
|
||||
OEMCrypto_HDCP_Capability* maximum) {
|
||||
return ::OEMCrypto_GetHDCPCapability(current, maximum);
|
||||
}
|
||||
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel) {
|
||||
return ::OEMCrypto_SupportsUsageTable();
|
||||
}
|
||||
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level) {
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel) {
|
||||
return ::OEMCrypto_IsAntiRollbackHwPresent();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
|
||||
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel,
|
||||
size_t* count) {
|
||||
return ::OEMCrypto_GetNumberOfOpenSessions(count);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel,
|
||||
size_t* maximum) {
|
||||
return ::OEMCrypto_GetMaxNumberOfSessions(maximum);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
|
||||
SecurityLevel, 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);
|
||||
}
|
||||
|
||||
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
|
||||
SecurityLevel level) {
|
||||
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(SecurityLevel) {
|
||||
return ::OEMCrypto_GetProvisioningMethod();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,6 @@
|
||||
|
||||
using video_widevine::License;
|
||||
|
||||
namespace {
|
||||
|
||||
const int64_t kHdcpCheckInterval = 10;
|
||||
const uint32_t kNoResolution = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
@@ -54,25 +47,36 @@ bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
|
||||
}
|
||||
}
|
||||
|
||||
CdmKeyStatus PolicyEngine::GetKeyStatus(const KeyId& key_id) {
|
||||
return license_keys_->GetKeyStatus(key_id);
|
||||
}
|
||||
|
||||
void PolicyEngine::InitDevice(CryptoSession* crypto_session) {
|
||||
current_resolution_ = kNoResolution;
|
||||
current_resolution_ = HDCP_UNSPECIFIED_VIDEO_RESOLUTION;
|
||||
next_device_check_ = 0;
|
||||
crypto_session_ = crypto_session;
|
||||
}
|
||||
|
||||
void PolicyEngine::CheckDevice(int64_t current_time) {
|
||||
if (current_time < next_device_check_) {
|
||||
return;
|
||||
}
|
||||
void PolicyEngine::SetDeviceResolution(uint32_t width, uint32_t height) {
|
||||
current_resolution_ = width * height;
|
||||
CheckDeviceHdcpStatus();
|
||||
}
|
||||
|
||||
if (!license_keys_->Empty() && current_resolution_ != kNoResolution) {
|
||||
void PolicyEngine::CheckDeviceHdcpStatusOnTimer(int64_t current_time) {
|
||||
if (current_time >= next_device_check_) {
|
||||
CheckDeviceHdcpStatus();
|
||||
next_device_check_ = current_time + HDCP_DEVICE_CHECK_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::CheckDeviceHdcpStatus() {
|
||||
if (!license_keys_->Empty()) {
|
||||
CryptoSession::HdcpCapability current_hdcp_level;
|
||||
CryptoSession::HdcpCapability ignored;
|
||||
if (!crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored)) {
|
||||
current_hdcp_level = HDCP_NONE;
|
||||
}
|
||||
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
|
||||
next_device_check_ = current_time + kHdcpCheckInterval;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,14 +98,16 @@ void PolicyEngine::OnTimerEvent() {
|
||||
}
|
||||
|
||||
// Check device conditions that affect playability (HDCP, resolution)
|
||||
CheckDevice(current_time);
|
||||
CheckDeviceHdcpStatusOnTimer(current_time);
|
||||
|
||||
bool renewal_needed = false;
|
||||
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateCanPlay: {
|
||||
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
|
||||
if (HasRenewalDelayExpired(current_time)) {
|
||||
renewal_needed = true;
|
||||
}
|
||||
// HDCP may change, so force a check.
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
break;
|
||||
@@ -113,7 +119,9 @@ void PolicyEngine::OnTimerEvent() {
|
||||
}
|
||||
|
||||
case kLicenseStateWaitingLicenseUpdate: {
|
||||
if (HasRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
|
||||
if (HasRenewalRetryIntervalExpired(current_time)) {
|
||||
renewal_needed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -420,9 +428,16 @@ bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
next_renewal_time_ <= current_time;
|
||||
}
|
||||
|
||||
// Apply a key status to the current keys.
|
||||
// If this represents a new key status, perform a notification callback.
|
||||
// NOTE: if the new status is kKeyStatusUsable, the HDCP check may result in an
|
||||
// override to kKeyStatusOutputNotAllowed.
|
||||
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
|
||||
bool keys_changed;
|
||||
bool has_new_usable_key = false;
|
||||
if (new_status == kKeyStatusUsable) {
|
||||
CheckDeviceHdcpStatus();
|
||||
}
|
||||
keys_changed = license_keys_->ApplyStatusChange(new_status,
|
||||
&has_new_usable_key);
|
||||
if (event_listener_ && keys_changed) {
|
||||
|
||||
@@ -182,7 +182,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
|
||||
// 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 */) {
|
||||
static int LogOpenSSLError(const char* msg, size_t /* len */, void* /* ctx */) {
|
||||
LOGE(" %s", msg);
|
||||
return 1;
|
||||
}
|
||||
@@ -199,7 +199,8 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) {
|
||||
if (EVP_PKEY_CTX_set_signature_md(pctx,
|
||||
const_cast<EVP_MD *>(EVP_sha1())) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ bool Properties::oem_crypto_use_secure_buffers_;
|
||||
bool Properties::oem_crypto_use_fifo_;
|
||||
bool Properties::oem_crypto_use_userspace_buffers_;
|
||||
bool Properties::use_certificates_as_identification_;
|
||||
bool Properties::provisioning_messages_are_binary_;
|
||||
bool Properties::security_level_path_backward_compatibility_support_;
|
||||
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
||||
|
||||
|
||||
@@ -11,41 +11,110 @@
|
||||
|
||||
namespace {
|
||||
// Service certificate for Google/Widevine Provisioning and License servers.
|
||||
const unsigned char kServiceCertificateCAPublicKey[] = {
|
||||
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39,
|
||||
0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd,
|
||||
0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87,
|
||||
0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6,
|
||||
0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40,
|
||||
0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98,
|
||||
0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84,
|
||||
0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab,
|
||||
0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0x53, 0x3e, 0x47, 0x5f,
|
||||
0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf,
|
||||
0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, 0x45, 0xd6,
|
||||
0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7,
|
||||
0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50,
|
||||
0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd,
|
||||
0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0,
|
||||
0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7,
|
||||
0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b,
|
||||
0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11,
|
||||
0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8,
|
||||
0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33,
|
||||
0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0x60, 0x1a, 0x11, 0x3d,
|
||||
0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b,
|
||||
0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, 0xbe, 0x7f, 0xb0,
|
||||
0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e, 0x69,
|
||||
0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, 0x01,
|
||||
0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24,
|
||||
0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31,
|
||||
0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5,
|
||||
0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8,
|
||||
0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d,
|
||||
0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03,
|
||||
0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f,
|
||||
0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01,
|
||||
0x00, 0x01};
|
||||
static const unsigned char kRootCertForProd[] = {
|
||||
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00,
|
||||
0x18, 0xdd, 0x94, 0x88, 0x8b, 0x05, 0x22, 0x8e,
|
||||
0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
|
||||
0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90,
|
||||
0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68,
|
||||
0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d,
|
||||
0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc,
|
||||
0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c,
|
||||
0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3,
|
||||
0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26,
|
||||
0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21,
|
||||
0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46,
|
||||
0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb,
|
||||
0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b,
|
||||
0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48,
|
||||
0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd,
|
||||
0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c,
|
||||
0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10,
|
||||
0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19,
|
||||
0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84,
|
||||
0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26,
|
||||
0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25,
|
||||
0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1,
|
||||
0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4,
|
||||
0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17,
|
||||
0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b,
|
||||
0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba,
|
||||
0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84,
|
||||
0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c,
|
||||
0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca,
|
||||
0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6,
|
||||
0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45,
|
||||
0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba,
|
||||
0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2,
|
||||
0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77,
|
||||
0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19,
|
||||
0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc,
|
||||
0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e,
|
||||
0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed,
|
||||
0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56,
|
||||
0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3,
|
||||
0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c,
|
||||
0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c,
|
||||
0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff,
|
||||
0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4,
|
||||
0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58,
|
||||
0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41,
|
||||
0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4,
|
||||
0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12,
|
||||
0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55,
|
||||
0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33,
|
||||
0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12,
|
||||
0x80, 0x03, 0x58, 0xf1, 0xd6, 0x4d, 0x04, 0x09,
|
||||
0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39,
|
||||
0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67,
|
||||
0x85, 0x57, 0x12, 0xa7, 0x14, 0xee, 0x35, 0x16,
|
||||
0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00,
|
||||
0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e,
|
||||
0x44, 0xcd, 0x4c, 0xd4, 0xae, 0x09, 0x18, 0x53,
|
||||
0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda,
|
||||
0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90,
|
||||
0x1e, 0x7e, 0x5b, 0xc6, 0xe9, 0x0f, 0x22, 0x9f,
|
||||
0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b,
|
||||
0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5,
|
||||
0xf2, 0x08, 0xcd, 0x65, 0x41, 0x58, 0xe8, 0x7d,
|
||||
0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69,
|
||||
0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32,
|
||||
0x87, 0x70, 0xb2, 0xb0, 0x76, 0x9c, 0xb2, 0xc3,
|
||||
0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99,
|
||||
0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a,
|
||||
0x19, 0xc1, 0xd5, 0x65, 0x90, 0x76, 0x55, 0x74,
|
||||
0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19,
|
||||
0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92,
|
||||
0x5f, 0x4f, 0xbc, 0x7a, 0x31, 0xf8, 0xae, 0x8e,
|
||||
0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a,
|
||||
0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2,
|
||||
0xfe, 0x1c, 0x88, 0xac, 0x2a, 0x66, 0xc5, 0xff,
|
||||
0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a,
|
||||
0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a,
|
||||
0x24, 0x2f, 0x6c, 0xa5, 0xe3, 0x1c, 0x88, 0x14,
|
||||
0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85,
|
||||
0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c,
|
||||
0xfb, 0x92, 0x6c, 0x66, 0x75, 0x8a, 0xd6, 0x0d,
|
||||
0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03,
|
||||
0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01,
|
||||
0x20, 0xa8, 0xa9, 0x84, 0x2e, 0x17, 0xca, 0xae,
|
||||
0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0,
|
||||
0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86,
|
||||
0x30, 0x14, 0x78, 0x21, 0x11, 0x22, 0x88, 0xac,
|
||||
0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5,
|
||||
0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08,
|
||||
0x7b, 0x7a, 0xfb, 0x95, 0x89, 0xc3, 0xee, 0x98,
|
||||
0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12,
|
||||
0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b,
|
||||
0x46, 0x08, 0x7b, 0x3d, 0x32, 0x6a, 0x68, 0xca,
|
||||
0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9,
|
||||
0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd,
|
||||
0xc4, 0x46, 0xf5, 0xa3, 0x62, 0x13, 0x74, 0x02,
|
||||
0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf,
|
||||
0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5,
|
||||
0x02, 0x0c
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -57,41 +126,90 @@ using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::SignedDrmDeviceCertificate;
|
||||
using video_widevine::SignedMessage;
|
||||
|
||||
void ServiceCertificate::Clear() {
|
||||
fetch_in_progress_ = false;
|
||||
certificate_.clear();
|
||||
provider_id_.clear();
|
||||
CdmResponseType ServiceCertificate::Init(const std::string& certificate) {
|
||||
std::string root_cert_str(reinterpret_cast<const char*>(&kRootCertForProd[0]),
|
||||
sizeof(kRootCertForProd));
|
||||
|
||||
// Load root cert public key. Don't bother verifying it.
|
||||
SignedDrmDeviceCertificate signed_root_cert;
|
||||
if (!signed_root_cert.ParseFromString(root_cert_str)) {
|
||||
LOGE("Failed to deserialize signed root certificate.");
|
||||
return DEVICE_CERTIFICATE_ERROR_1;
|
||||
}
|
||||
DrmDeviceCertificate root_cert;
|
||||
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
|
||||
LOGE("Failed to deserialize signed root certificate.");
|
||||
return DEVICE_CERTIFICATE_ERROR_1;
|
||||
}
|
||||
RsaPublicKey root_key;
|
||||
if (!root_key.Init(root_cert.public_key())) {
|
||||
LOGE("Failed to load root certificate public key.");
|
||||
return DEVICE_CERTIFICATE_ERROR_1;
|
||||
}
|
||||
|
||||
// Load the provided service certificate.
|
||||
// First, parse it and verify its signature.
|
||||
SignedDrmDeviceCertificate signed_service_cert;
|
||||
if (!signed_service_cert.ParseFromString(certificate)) {
|
||||
LOGE("Failed to parse signed service certificate.");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
if (!root_key.VerifySignature(signed_service_cert.drm_certificate(),
|
||||
signed_service_cert.signature())) {
|
||||
LOGE("Service certificate signature verification failed.");
|
||||
return DEVICE_CERTIFICATE_ERROR_3;
|
||||
}
|
||||
DrmDeviceCertificate service_cert;
|
||||
if (!service_cert.ParseFromString(signed_service_cert.drm_certificate())) {
|
||||
LOGE("Failed to parse service certificate.");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
if (service_cert.type() !=
|
||||
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE("Not a service certificate.");
|
||||
return DEVICE_CERTIFICATE_ERROR_3;
|
||||
}
|
||||
|
||||
// Service certificate passes all checks - set up its RSA public key.
|
||||
public_key_.reset(new RsaPublicKey);
|
||||
if (!public_key_->Init(service_cert.public_key())) {
|
||||
public_key_.reset();
|
||||
LOGE("Failed to load service certificate public key.");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
|
||||
// Have service certificate and its public key - keep relevant fields.
|
||||
certificate_ = certificate;
|
||||
serial_number_ = service_cert.serial_number();
|
||||
provider_id_ = service_cert.provider_id();
|
||||
has_certificate_ = true;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::Init(const std::string& raw_certificate) {
|
||||
return VerifyAndExtract(raw_certificate);
|
||||
CdmResponseType ServiceCertificate::VerifySignedMessage(
|
||||
const std::string& message, const std::string& signature) {
|
||||
if (!public_key_) {
|
||||
LOGE("Service certificate not set.");
|
||||
return DEVICE_CERTIFICATE_ERROR_4;
|
||||
}
|
||||
|
||||
if (!public_key_->VerifySignature(message, signature))
|
||||
return CLIENT_ID_RSA_ENCRYPT_ERROR; // TODO(tinskip): Need new error code.
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::EncryptClientId(
|
||||
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
|
||||
EncryptedClientIdentification* encrypted_client_id) {
|
||||
DrmDeviceCertificate service_certificate;
|
||||
|
||||
if (certificate_.empty()) {
|
||||
LOGE("ServiceCertificate::EncryptClientId: "
|
||||
"service certificate is not properly initialized");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (!service_certificate.ParseFromString(certificate_)) {
|
||||
LOGE("ServiceCertificate::EncryptClientId: unable to parse retrieved "
|
||||
"service certificate");
|
||||
return PARSE_SERVICE_CERTIFICATE_ERROR;
|
||||
if (!public_key_) {
|
||||
LOGE("Service certificate not set.");
|
||||
return DEVICE_CERTIFICATE_ERROR_4;
|
||||
}
|
||||
|
||||
if (service_certificate.type() !=
|
||||
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE("ServiceCertificate::EncryptClientId: retrieved certificate not of "
|
||||
"type service, %d", service_certificate.type());
|
||||
return SERVICE_CERTIFICATE_TYPE_ERROR;
|
||||
}
|
||||
encrypted_client_id->set_provider_id(service_certificate.provider_id());
|
||||
encrypted_client_id->set_service_certificate_serial_number(
|
||||
service_certificate.serial_number());
|
||||
encrypted_client_id->set_provider_id(provider_id_);
|
||||
encrypted_client_id->set_service_certificate_serial_number(serial_number_);
|
||||
|
||||
std::string iv(KEY_IV_SIZE, 0);
|
||||
std::string key(KEY_SIZE, 0);
|
||||
@@ -109,10 +227,8 @@ CdmResponseType ServiceCertificate::EncryptClientId(
|
||||
if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR;
|
||||
if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR;
|
||||
|
||||
RsaPublicKey rsa;
|
||||
if (!rsa.Init(service_certificate.public_key()))
|
||||
return CLIENT_ID_RSA_INIT_ERROR;
|
||||
if (!rsa.Encrypt(key, &enc_key)) return CLIENT_ID_RSA_ENCRYPT_ERROR;
|
||||
if (!public_key_->Encrypt(key, &enc_key))
|
||||
return CLIENT_ID_RSA_ENCRYPT_ERROR;
|
||||
|
||||
encrypted_client_id->set_encrypted_client_id_iv(iv);
|
||||
encrypted_client_id->set_encrypted_privacy_key(enc_key);
|
||||
@@ -120,97 +236,4 @@ CdmResponseType ServiceCertificate::EncryptClientId(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool ServiceCertificate::PrepareRequest(CdmKeyMessage* signed_request) {
|
||||
if (!signed_request) {
|
||||
LOGE("ServiceCertificate::PrepareRequest: no signed request provided");
|
||||
return false;
|
||||
}
|
||||
SignedMessage signed_message;
|
||||
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||
signed_message.SerializeToString(signed_request);
|
||||
|
||||
fetch_in_progress_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::HandleResponse(
|
||||
const std::string& signed_response) {
|
||||
if (!fetch_in_progress_) {
|
||||
LOGE("ServiceCertificate::HandleResponse: unexpected service "
|
||||
"certificate response.");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
fetch_in_progress_ = false;
|
||||
CdmResponseType status = VerifyAndExtract(signed_response);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::VerifyAndExtract(
|
||||
const std::string& raw_certificate) {
|
||||
if (raw_certificate.empty()) {
|
||||
Clear();
|
||||
return NO_ERROR;
|
||||
}
|
||||
// Deserialize and parse raw certificate.
|
||||
SignedDrmDeviceCertificate signed_service_certificate;
|
||||
if (!signed_service_certificate.ParseFromString(raw_certificate)) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: unable to parse signed "
|
||||
"service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_1;
|
||||
}
|
||||
|
||||
// Set up root key (for verifying signature).
|
||||
RsaPublicKey root_ca_key;
|
||||
std::string ca_public_key(
|
||||
reinterpret_cast<const char*>(&kServiceCertificateCAPublicKey[0]),
|
||||
sizeof(kServiceCertificateCAPublicKey));
|
||||
if (!root_ca_key.Init(ca_public_key)) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: public key initialization "
|
||||
"failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
|
||||
// Verify the signature.
|
||||
if (!root_ca_key.VerifySignature(
|
||||
signed_service_certificate.drm_certificate(),
|
||||
signed_service_certificate.signature())) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: service certificate "
|
||||
"verification failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_3;
|
||||
}
|
||||
|
||||
// Deserialize and parse actual certificate.
|
||||
DrmDeviceCertificate service_certificate;
|
||||
if (!service_certificate.ParseFromString(
|
||||
signed_service_certificate.drm_certificate())) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: unable to parse retrieved "
|
||||
"service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_4;
|
||||
}
|
||||
|
||||
// Verify, extract needed fields.
|
||||
if (service_certificate.type() !=
|
||||
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: certificate not of type "
|
||||
"service, %d", service_certificate.type());
|
||||
return INVALID_DEVICE_CERTIFICATE_TYPE;
|
||||
}
|
||||
if (service_certificate.has_provider_id()) {
|
||||
provider_id_.assign(service_certificate.provider_id());
|
||||
} else {
|
||||
provider_id_.clear();
|
||||
}
|
||||
certificate_.assign(signed_service_certificate.drm_certificate());
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -152,16 +152,21 @@ std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
|
||||
}
|
||||
|
||||
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
|
||||
// as Base64WebSafeDecode.
|
||||
// as Base64WebSafeDecode. Add padding if needed.
|
||||
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
||||
if (b64_input.empty()) {
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
int in_size = b64_input.size();
|
||||
std::string b64_padded(b64_input);
|
||||
while (b64_padded.size() % 4 != 0) {
|
||||
b64_padded = b64_padded + "=";
|
||||
}
|
||||
|
||||
int in_size = b64_padded.size();
|
||||
std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0);
|
||||
int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]),
|
||||
b64_input.data(), in_size);
|
||||
b64_padded.data(), in_size);
|
||||
if (out_size == -1) {
|
||||
LOGE("Base64SafeDecode failed");
|
||||
return std::vector<uint8_t>(0);
|
||||
|
||||
Reference in New Issue
Block a user