Source release v3.5.0

This commit is contained in:
Gene Morgan
2017-11-28 17:42:16 -08:00
parent 501c22890d
commit 31381a1311
155 changed files with 16680 additions and 3816 deletions

View File

@@ -14,7 +14,6 @@
#include "clock.h"
#include "device_files.h"
#include "file_store.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
@@ -29,9 +28,6 @@ const size_t kUsageReportsPerRequest = 1;
namespace wvcdm {
using video_widevine::SignedMessage;
using video_widevine::LicenseError;
class UsagePropertySet : public CdmClientPropertySet {
public:
UsagePropertySet() {}
@@ -68,14 +64,27 @@ CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
usage_session_(NULL),
last_usage_information_update_time_(0) {
assert(file_system);
Properties::Init();
if (!seeded_) {
Properties::Init();
srand(clock_.GetCurrentTime());
seeded_ = true;
}
life_span_.Start();
metrics_.cdm_engine_creation_time_millis_.Record(clock_.GetCurrentTime());
std::string cdm_version;
if(Properties::GetWVCdmVersion(&cdm_version)) {
metrics_.cdm_engine_cdm_version_.Record(cdm_version);
} else {
// Set error "false", the return value of GetWVCdmVersion.
metrics_.cdm_engine_cdm_version_.SetError(false);
}
}
CdmEngine::~CdmEngine() {}
CdmEngine::~CdmEngine() {
M_RECORD(&metrics_, cdm_engine_life_span_, life_span_.AsMs());
}
CdmResponseType CdmEngine::SetServiceCertificate(
const std::string& certificate) {
@@ -86,66 +95,6 @@ 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) {
@@ -183,9 +132,10 @@ CdmResponseType CdmEngine::OpenSession(
CloseExpiredReleaseSessions();
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
CdmResponseType sts = new_session->Init(&service_certificate_, property_set,
forced_session_id, event_listener);
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_,
metrics_.AddSession()));
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
event_listener);
if (sts != NO_ERROR) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
@@ -240,7 +190,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(
}
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::CloseSession");
LOGV("CdmEngine::CloseSession: %s", session_id.c_str());
if (!session_map_.CloseSession(session_id)) {
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
return SESSION_NOT_FOUND_1;
@@ -312,8 +262,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
id = iter->second.first;
}
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(id, session)) {
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;
@@ -329,6 +279,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
if (license_type == kLicenseTypeRelease &&
!session->license_received()) {
sts = session->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
session->GetMetrics()->cdm_session_restore_offline_session_.Increment(sts);
if (sts != KEY_ADDED) {
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
"sts = %d", static_cast<int>(sts));
@@ -385,8 +336,8 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
id = iter->second.first;
}
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(id, session)) {
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;
}
@@ -398,7 +349,12 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
CdmResponseType sts = session->AddKey(key_data);
if (key_set_id) {
*key_set_id = session->key_set_id();
if ((session->is_offline() ||
session->has_provider_session_token()) && !license_type_release) {
*key_set_id = session->key_set_id();
} else {
key_set_id->clear();
}
}
switch (sts) {
@@ -424,15 +380,16 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
return EMPTY_KEYSET_ID_ENG_4;
}
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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 =
session->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
CdmResponseType sts;
sts = session->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
session->GetMetrics()->cdm_session_restore_offline_session_.Increment(sts);
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
session->GetRequestedSecurityLevel();
@@ -446,14 +403,15 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
LOGI("CdmEngine::RemoveKeys");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
}
session->ReleaseCrypto();
return NO_ERROR;
}
@@ -461,8 +419,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
const CdmSessionId& session_id, CdmKeyRequest* key_request) {
LOGI("CdmEngine::GenerateRenewalRequest");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -490,8 +448,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data) {
LOGI("CdmEngine::RenewKey");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
}
@@ -501,7 +459,14 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
return EMPTY_KEY_DATA_2;
}
CdmResponseType sts = session->RenewKey(key_data);
CdmResponseType sts;
M_TIME(
sts = session->RenewKey(
key_data),
session->GetMetrics(),
cdm_session_renew_key_,
sts);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast<int>(sts));
return sts;
@@ -514,9 +479,16 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
const std::string& query_token,
std::string* query_response) {
LOGI("CdmEngine::QueryStatus");
CryptoSession crypto_session;
CryptoSession crypto_session(metrics_.GetCryptoMetrics());
if (security_level == kLevel3) {
CdmResponseType status = crypto_session.Open(kLevel3);
CdmResponseType status;
M_TIME(
status = crypto_session.Open(
kLevel3),
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
kLevel3);
if (NO_ERROR != status) return INVALID_QUERY_STATUS;
}
@@ -526,8 +498,9 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
}
if (query_token == QUERY_KEY_SECURITY_LEVEL) {
CdmSecurityLevel level = crypto_session.GetSecurityLevel();
switch (level) {
CdmSecurityLevel found_security_level =
crypto_session.GetSecurityLevel();
switch (found_security_level) {
case kSecurityLevelL1:
*query_response = QUERY_VALUE_SECURITY_LEVEL_L1;
break;
@@ -542,12 +515,16 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
*query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
LOGW("CdmEngine::QueryStatus: Unknown security level: %d", level);
LOGW("CdmEngine::QueryStatus: Unknown security level: %d",
found_security_level);
return UNKNOWN_ERROR;
}
} else if (query_token == QUERY_KEY_DEVICE_ID) {
std::string deviceId;
if (!crypto_session.GetDeviceUniqueId(&deviceId)) {
bool got_id = crypto_session.GetExternalDeviceUniqueId(&deviceId);
metrics_.GetCryptoMetrics()->crypto_session_get_device_unique_id_
.Increment(got_id);
if (!got_id) {
LOGW("CdmEngine::QueryStatus: QUERY_KEY_DEVICE_ID unknown failure");
return UNKNOWN_ERROR;
}
@@ -555,7 +532,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
*query_response = deviceId;
} else if (query_token == QUERY_KEY_SYSTEM_ID) {
uint32_t system_id;
if (!crypto_session.GetSystemId(&system_id)) {
bool got_id = crypto_session.GetSystemId(&system_id);
if (!got_id) {
LOGW("CdmEngine::QueryStatus: QUERY_KEY_SYSTEM_ID unknown failure");
return UNKNOWN_ERROR;
}
@@ -584,10 +562,17 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
current_hdcp : max_hdcp);
} else if (query_token == QUERY_KEY_USAGE_SUPPORT) {
bool supports_usage_reporting;
if (!crypto_session.UsageInformationSupport(&supports_usage_reporting)) {
bool got_info = crypto_session.UsageInformationSupport(
&supports_usage_reporting);
if (!got_info) {
LOGW("CdmEngine::QueryStatus: UsageInformationSupport failed");
metrics_.GetCryptoMetrics()->crypto_session_usage_information_support_
.SetError(got_info);
return UNKNOWN_ERROR;
}
metrics_.GetCryptoMetrics()->crypto_session_usage_information_support_
.Record(supports_usage_reporting);
*query_response =
supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
@@ -613,7 +598,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
*query_response = max_sessions_stream.str();
} else if (query_token == QUERY_KEY_OEMCRYPTO_API_VERSION) {
uint32_t api_version;
if (!crypto_session.GetApiVersion(&api_version)) {
if (crypto_session.GetApiVersion(&api_version)) {
LOGW("CdmEngine::QueryStatus: GetApiVersion failed");
return UNKNOWN_ERROR;
}
@@ -621,6 +606,28 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
std::ostringstream api_version_stream;
api_version_stream << api_version;
*query_response = api_version_stream.str();
} else if (query_token == QUERY_KEY_CURRENT_SRM_VERSION) {
uint16_t current_srm_version;
if (!crypto_session.GetSrmVersion(&current_srm_version)) {
LOGW("CdmEngine::QueryStatus: GetCurrentSRMVersion failed");
return UNKNOWN_ERROR;
}
std::ostringstream current_srm_version_stream;
current_srm_version_stream << current_srm_version;
*query_response = current_srm_version_stream.str();
} else if (query_token == QUERY_KEY_SRM_UPDATE_SUPPORT) {
bool is_srm_update_supported = crypto_session.IsSrmUpdateSupported();
*query_response =
is_srm_update_supported ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
} else if (query_token == QUERY_KEY_WVCDM_VERSION) {
std::string cdm_version;
if (!Properties::GetWVCdmVersion(&cdm_version)) {
LOGW("CdmEngine::QueryStatus: GetWVCdmVersion failed");
return UNKNOWN_ERROR;
}
*query_response = cdm_version;
} else {
LOGW("CdmEngine::QueryStatus: Unknown status requested, token = %s",
query_token.c_str());
@@ -633,8 +640,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
CdmQueryMap* query_response) {
LOGI("CdmEngine::QuerySessionStatus");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -644,8 +651,8 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsReleaseSession");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -655,8 +662,8 @@ bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsOfflineSession");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -667,8 +674,8 @@ bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryKeyStatus");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -684,8 +691,8 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
return INVALID_PARAMETERS_ENG_12;
}
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -710,9 +717,6 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
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) {
@@ -738,8 +742,8 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
CdmResponseType CdmEngine::QueryOemCryptoSessionId(
const CdmSessionId& session_id, CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryOemCryptoSessionId");
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -770,7 +774,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
if (NULL == cert_provisioning_.get()) {
cert_provisioning_.reset(
new CertificateProvisioning(&service_certificate_));
new CertificateProvisioning(metrics_.GetCryptoMetrics(),
&service_certificate_));
}
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_type, cert_authority,
@@ -812,9 +817,15 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
if (NULL == cert_provisioning_.get()) {
// Certificate provisioning object has been released. Check if a concurrent
// provisioning attempt has succeeded before declaring failure.
CryptoSession crypto_session;
CdmResponseType status =
crypto_session.Open(cert_provisioning_requested_security_level_);
CryptoSession crypto_session(metrics_.GetCryptoMetrics());
CdmResponseType status;
M_TIME(
status = crypto_session.Open(
cert_provisioning_requested_security_level_),
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
cert_provisioning_requested_security_level_);
if (NO_ERROR != status) {
LOGE(
"CdmEngine::HandleProvisioningResponse: provisioning object "
@@ -873,17 +884,27 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
}
CdmResponseType CdmEngine::DeleteUsageTable(CdmSecurityLevel security_level) {
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
CryptoSession crypto_session(metrics_.GetCryptoMetrics());
CdmResponseType status;
M_TIME(
status = crypto_session.Open(
security_level == kSecurityLevelL3 ?
kLevel3 :
kLevelDefault),
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (NO_ERROR != status) {
LOGE("CdmEngine::DeleteUsageTable: error opening crypto session: %d",
status);
status);
return UNPROVISION_ERROR_4;
}
status = crypto_session->DeleteAllUsageReports();
status = crypto_session.DeleteAllUsageReports();
metrics_.GetCryptoMetrics()->crypto_session_delete_all_usage_reports_
.Increment(status);
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageTable: error deleting usage reports: %d",
LOGE("CdmEngine::DeleteUsageTable: error deleteing usage reports: %d",
status);
}
return status;
@@ -894,7 +915,7 @@ CdmResponseType CdmEngine::ListStoredLicenses(
DeviceFiles handle(file_system_);
if (!key_set_ids) {
LOGE("CdmEngine::ListStoredLicenses: no response destination");
return INVALID_PARAMETERS_ENG_17;
return INVALID_PARAMETERS_ENG_22;
}
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ListStoredLicenses: unable to initialize device files");
@@ -913,7 +934,7 @@ CdmResponseType CdmEngine::ListUsageRecords(const std::string& app_id,
DeviceFiles handle(file_system_);
if (!ksids) {
LOGE("CdmEngine::ListUsageRecords: no response destination");
return INVALID_PARAMETERS_ENG_18;
return INVALID_PARAMETERS_ENG_23;
}
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ListUsageRecords: unable to initialize device files");
@@ -936,13 +957,15 @@ CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
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");
if (!handle.GetProviderSessionToken(app_id, key_set_id,
&provider_session_token)) {
LOGE("CdmEngine::DeleteUsageRecord: GetProviderSessionToken failed");
return DELETE_USAGE_ERROR_2;
}
// Got provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
scoped_ptr<CryptoSession> crypto_session(
new CryptoSession(metrics_.GetCryptoMetrics()));
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
@@ -972,10 +995,10 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
}
usage_property_set_->set_security_level(kLevelDefault);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(file_system_));
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
LOGE("CdmEngine::GetUsageInfo: session init error: %d", status);
return status;
}
DeviceFiles handle(file_system_);
@@ -986,11 +1009,13 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
CdmKeyMessage license_request;
CdmKeyResponse license_response;
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response)) {
std::string usage_entry;
DeviceFiles::CdmUsageData usage_data;
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
ssid, &usage_data)) {
usage_property_set_->set_security_level(kLevel3);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(file_system_));
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
@@ -1000,15 +1025,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
LOGE("CdmEngine::GetUsageInfo: device file init error");
return GET_USAGE_INFO_ERROR_2;
}
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
&license_response)) {
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
ssid, &usage_data)) {
// No entry found for that ssid.
return USAGE_INFO_NOT_FOUND;
}
}
status =
usage_session_->RestoreUsageSession(license_request,license_response);
usage_session_->RestoreUsageSession(usage_data);
if (KEY_ADDED != status) {
LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status);
@@ -1067,7 +1092,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
usage_property_set_->set_security_level(requested_security_level);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(new CdmSession(file_system_));
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
@@ -1081,8 +1106,9 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
return GET_USAGE_INFO_ERROR_3;
}
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
if (!handle.RetrieveUsageInfo(app_id, &license_info)) {
std::vector<DeviceFiles::CdmUsageData> usage_data;
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
&usage_data)) {
LOGE("CdmEngine::GetUsageInfo: unable to read usage information");
return GET_USAGE_INFO_ERROR_4;
}
@@ -1091,16 +1117,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
LOGE("CdmEngine::GetUsageInfo: no usage info destination");
return INVALID_PARAMETERS_ENG_10;
}
if (0 == license_info.size()) {
if (0 == usage_data.size()) {
usage_info->resize(0);
return NO_ERROR;
}
usage_info->resize(kUsageReportsPerRequest);
uint32_t index = rand() % license_info.size();
status = usage_session_->RestoreUsageSession(license_info[index].first,
license_info[index].second);
uint32_t index = rand() % usage_data.size();
status = usage_session_->RestoreUsageSession(usage_data[index]);
if (KEY_ADDED != status) {
LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld", index,
status);
@@ -1135,12 +1160,12 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: unable to initialize device files");
return RELEASE_ALL_USAGE_INFO_ERROR_3;
return RELEASE_ALL_USAGE_INFO_ERROR_6;
}
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;
return RELEASE_ALL_USAGE_INFO_ERROR_7;
}
if (provider_session_tokens.size() == 0UL) {
@@ -1148,7 +1173,8 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(
}
// Got at least one provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
scoped_ptr<CryptoSession> crypto_session(
new CryptoSession(metrics_.GetCryptoMetrics()));
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
@@ -1156,7 +1182,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(
DeleteMultipleUsageInformation(provider_session_tokens);
}
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageRecord: CryptoSession failure");
LOGE("CdmEngine::ReleaseAllUsageInfo: CryptoSession failure");
}
return status;
}
@@ -1171,24 +1197,69 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
DeviceFiles handle(file_system_);
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure"
"stops", j);
status = RELEASE_ALL_USAGE_INFO_ERROR_1;
} else {
SecurityLevel security_level =
static_cast<CdmSecurityLevel>(j) == kSecurityLevelL3
? kLevel3
: kLevelDefault;
usage_property_set_->set_security_level(security_level);
usage_session_.reset(new CdmSession(file_system_));
usage_session_->Init(usage_property_set_.get());
CdmResponseType status2 = usage_session_->
DeleteMultipleUsageInformation(provider_session_tokens);
if (status2 != NO_ERROR) {
status = status2;
SecurityLevel security_level =
static_cast<CdmSecurityLevel>(j) == kSecurityLevelL3
? kLevel3
: kLevelDefault;
usage_property_set_->set_security_level(security_level);
usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession()));
usage_session_->Init(usage_property_set_.get());
switch (usage_session_->get_usage_support_type()) {
case kUsageEntrySupport: {
std::vector<DeviceFiles::CdmUsageData> usage_data;
// Retrieve all usage information but delete only one before
// refetching. This is because deleting the usage entry
// might cause other entries to be shifted and information updated.
do {
if (!handle.RetrieveUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
&usage_data)) {
status = RELEASE_ALL_USAGE_INFO_ERROR_4;
break;
}
if (usage_data.empty()) break;
status = usage_session_->DeleteUsageEntry(
usage_data[0].usage_entry_number);
if (status != NO_ERROR) break;
if (!handle.DeleteUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
usage_data[0].provider_session_token)) {
status = RELEASE_ALL_USAGE_INFO_ERROR_6;
break;
}
} while (status == NO_ERROR && !usage_data.empty());
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&provider_session_tokens)) {
status = RELEASE_ALL_USAGE_INFO_ERROR_5;
}
break;
}
case kUsageTableSupport: {
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&provider_session_tokens)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete %d secure"
"stops", j);
status = RELEASE_ALL_USAGE_INFO_ERROR_1;
} else {
CdmResponseType status2 = usage_session_->
DeleteMultipleUsageInformation(provider_session_tokens);
if (status2 != NO_ERROR) status = status2;
}
break;
}
default:
// Ignore
break;
}
} else {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device"
@@ -1227,8 +1298,8 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
return EMPTY_KEYSET_ID_ENG_5;
}
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(key_set_id, session)) {
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;
@@ -1248,16 +1319,19 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
std::string app_id;
session->GetApplicationId(&app_id);
CdmKeyMessage key_message;
CdmKeyResponse key_response;
if (!handle.RetrieveUsageInfoByKeySetId(app_id, key_set_id, &key_message,
&key_response)) {
DeviceFiles::CdmUsageData usage_data;
if (!handle.RetrieveUsageInfoByKeySetId(
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id,
&(usage_data.provider_session_token),
&(usage_data.license_request),
&(usage_data.license), &(usage_data.usage_entry),
&(usage_data.usage_entry_number))) {
LOGE("CdmEngine::LoadUsageSession: unable to find usage information");
return LOAD_USAGE_INFO_MISSING;
}
CdmResponseType status = session->RestoreUsageSession(key_message,
key_response);
CdmResponseType status = session->RestoreUsageSession(usage_data);
session->GetMetrics()->cdm_session_restore_usage_session_.Increment(status);
if (KEY_ADDED != status) {
LOGE("CdmEngine::LoadUsageSession: usage session error %ld", status);
return status;
@@ -1265,13 +1339,15 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
CdmKeyRequest request;
status = session->GenerateReleaseRequest(&request);
*release_message = request.message;
switch (status) {
case KEY_MESSAGE:
break;
case KEY_CANCELED: // usage information not present in
session->DeleteLicense(); // OEMCrypto, delete and try again
case KEY_CANCELED:
// usage information not present in OEMCrypto, delete and try again
session->DeleteLicense();
break;
default:
LOGE("CdmEngine::LoadUsageSession: generate release request error: %d",
@@ -1307,7 +1383,7 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
// else we must be level 1 direct and we don't need to return a buffer.
}
std::shared_ptr<CdmSession> session;
shared_ptr<CdmSession> session;
if (session_id.empty()) {
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
@@ -1317,9 +1393,6 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
int64_t seconds_remaining = 0;
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) {
@@ -1328,18 +1401,16 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
}
}
}
} else {
session_map_.FindSession(session_id, session);
}
if (session.get() == NULL) {
if (session_id.empty()) {
if (session.get() == NULL) {
LOGE("CdmEngine::Decrypt: session not found: Empty session ID");
} else {
return SESSION_NOT_FOUND_FOR_DECRYPT;
}
} else {
if (!session_map_.FindSession(session_id, &session)) {
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_NOT_FOUND_FOR_DECRYPT;
}
return session->Decrypt(parameters);
@@ -1349,8 +1420,8 @@ 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) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -1363,8 +1434,8 @@ CdmResponseType CdmEngine::GenericDecrypt(
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -1376,8 +1447,8 @@ CdmResponseType CdmEngine::GenericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
std::string* signature) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -1389,8 +1460,8 @@ CdmResponseType CdmEngine::GenericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
const std::string& signature) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
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;
@@ -1404,9 +1475,6 @@ bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
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;
}
@@ -1431,9 +1499,6 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
int64_t seconds_remaining = 0;
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)) {
@@ -1455,8 +1520,8 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
uint32_t height) {
std::shared_ptr<CdmSession> session;
if (session_map_.FindSession(session_id, session)) {
shared_ptr<CdmSession> session;
if (session_map_.FindSession(session_id, &session)) {
session->NotifyResolution(width, height);
return true;
}
@@ -1490,9 +1555,7 @@ void CdmEngine::OnTimerEvent() {
is_usage_update_needed =
is_usage_update_needed || sessions.front()->is_usage_update_needed();
if (!sessions.front()->IsClosed()) {
sessions.front()->OnTimerEvent(usage_update_period_expired);
}
sessions.front()->OnTimerEvent(usage_update_period_expired);
sessions.pop_front();
}
@@ -1505,19 +1568,26 @@ void CdmEngine::OnTimerEvent() {
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)->UpdateUsageInformation();
if (NO_ERROR != status) {
LOGW("Update usage information failed: %d", status);
} else {
has_usage_been_updated = true;
}
switch ((*iter)->get_usage_support_type()) {
case kUsageEntrySupport:
(*iter)->UpdateUsageEntryInformation();
break;
case kUsageTableSupport:
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)->UpdateUsageTableInformation();
if (NO_ERROR != status) {
LOGW("Update usage information failed: %d", status);
} else {
has_usage_been_updated = true;
}
}
break;
default:
// Ignore
break;
}
}
}
@@ -1529,9 +1599,7 @@ void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
session_map_.GetSessionList(sessions);
while (!sessions.empty()) {
if (!sessions.front()->IsClosed()) {
sessions.front()->OnKeyReleaseEvent(key_set_id);
}
sessions.front()->OnKeyReleaseEvent(key_set_id);
sessions.pop_front();
}
}
@@ -1588,11 +1656,20 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
if (!file_system_->Exists(device_base_path_level1) &&
!file_system_->Exists(device_base_path_level3)) {
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
scoped_ptr<CryptoSession> crypto_session(
new CryptoSession(metrics_.GetCryptoMetrics()));
CdmResponseType status;
M_TIME(
status = crypto_session->Open(
cert_provisioning_requested_security_level_),
metrics_.GetCryptoMetrics(),
crypto_session_open_,
status,
cert_provisioning_requested_security_level_);
if (NO_ERROR == status) {
status = crypto_session->DeleteAllUsageReports();
metrics_.GetCryptoMetrics()->crypto_session_delete_all_usage_reports_
.Increment(status);
if (NO_ERROR != status) {
LOGW(
"CdmEngine::DeleteAllUsageReportsUponFactoryReset: "

View File

@@ -16,6 +16,7 @@
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
#include "usage_table_header.h"
namespace {
const size_t kKeySetIdLength = 14;
@@ -23,10 +24,11 @@ const size_t kKeySetIdLength = 14;
namespace wvcdm {
CdmSession::CdmSession(FileSystem* file_system) :
CdmSession::CdmSession(FileSystem* file_system,
metrics::SessionMetrics* metrics) :
metrics_(metrics),
initialized_(false),
closed_(false),
crypto_session_(new CryptoSession),
closed_(true),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
is_offline_(false),
@@ -38,8 +40,16 @@ CdmSession::CdmSession(FileSystem* file_system) :
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
usage_support_type_(kNonSecureUsageSupport),
usage_table_header_(NULL),
usage_entry_number_(0),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {}
mock_policy_engine_in_use_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(new CryptoSession(crypto_metrics_));
life_span_.Start();
}
CdmSession::~CdmSession() {
if (!key_set_id_.empty()) {
@@ -47,15 +57,19 @@ CdmSession::~CdmSession() {
file_handle_->UnreserveLicenseId(key_set_id_);
}
Properties::RemoveSessionPropertySet(session_id_);
if (metrics_) {
M_RECORD(metrics_, cdm_session_life_span_, life_span_.AsMs());
metrics_->SetCompleted();
}
}
CdmResponseType CdmSession::Init(
CdmClientPropertySet* cdm_client_property_set) {
return Init(NULL, cdm_client_property_set, NULL, NULL);
return Init(cdm_client_property_set, NULL, NULL);
}
CdmResponseType CdmSession::Init(
ServiceCertificate* service_certificate,
CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener) {
if (initialized_) {
@@ -69,15 +83,30 @@ CdmResponseType CdmSession::Init(
requested_security_level_ = kLevel3;
security_level_ = kSecurityLevelL3;
}
CdmResponseType sts = crypto_session_->Open(requested_security_level_);
CdmResponseType sts;
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_,
crypto_session_open_,
sts,
requested_security_level_);
if (NO_ERROR != sts) return sts;
security_level_ = crypto_session_->GetSecurityLevel();
crypto_metrics_->crypto_session_security_level_.Record(security_level_);
if (!file_handle_->Init(security_level_)) {
LOGE("CdmSession::Init: Unable to initialize file handle");
return SESSION_FILE_HANDLE_INIT_ERROR;
}
if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) {
if (usage_support_type_ == kUsageEntrySupport)
usage_table_header_ = crypto_session_->GetUsageTableHeader();
} else {
usage_support_type_ = kNonSecureUsageSupport;
}
// Device Provisioning state is not yet known.
// If not using certificates, then Keybox is client token for license
// requests.
@@ -90,25 +119,25 @@ CdmResponseType CdmSession::Init(
std::string serial_number;
CdmClientTokenType client_token_type =
crypto_session_->GetPreProvisionTokenType();
if ((client_token_type == kClientTokenKeybox) &&
!Properties::use_certificates_as_identification()) {
// Keybox is client token.
LOGW("CdmSession::Init: Properties::use_certificates_as_identification() "
"is not set - using Keybox for license requests (not recommended).");
if (!crypto_session_->GetClientToken(&client_token)) {
return SESSION_INIT_ERROR_1;
}
} else {
// 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;
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
&serial_number, nullptr) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING;
}
client_token_type = kClientTokenDrmCert;
// 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;
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
&serial_number, NULL)) {
return NEED_PROVISIONING;
}
bool load_cert_sts;
M_TIME(
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(
wrapped_key),
crypto_metrics_,
crypto_session_load_certificate_private_key_,
load_cert_sts);
if(!load_cert_sts) {
return NEED_PROVISIONING;
}
client_token_type = kClientTokenDrmCert;
// Session is provisioned with certificate needed to construct
// license request (or with keybox).
@@ -123,6 +152,7 @@ CdmResponseType CdmSession::Init(
session_id_ =
Properties::AlwaysUseKeySetIds() ? key_set_id_ : GenerateSessionId();
metrics_->SetSessionId(session_id_);
if (session_id_.empty()) {
LOGE("CdmSession::Init: empty session ID");
@@ -137,19 +167,28 @@ CdmResponseType CdmSession::Init(
policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get()));
std::string service_certificate;
if (!Properties::GetServiceCertificate(session_id_, &service_certificate))
service_certificate.clear();
if (!license_parser_->Init(
service_certificate, client_token, client_token_type,
serial_number, crypto_session_.get(), policy_engine_.get()))
client_token, client_token_type, serial_number,
Properties::UsePrivacyMode(session_id_), service_certificate,
crypto_session_.get(), policy_engine_.get()))
return LICENSE_PARSER_INIT_ERROR;
license_received_ = false;
is_initial_decryption_ = true;
initialized_ = true;
closed_ = false;
return NO_ERROR;
}
CdmResponseType CdmSession::RestoreOfflineSession(
const CdmKeySetId& key_set_id, const CdmLicenseType license_type) {
if (!key_set_id_.empty()) {
file_handle_->UnreserveLicenseId(key_set_id_);
}
key_set_id_ = key_set_id;
DeviceFiles::LicenseState license_state;
@@ -162,20 +201,38 @@ CdmResponseType CdmSession::RestoreOfflineSession(
&key_response_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &grace_period_end_time,
&app_parameters_)) {
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
key_set_id.c_str());
&app_parameters_, &usage_entry_, &usage_entry_number_)) {
LOGE("CdmSession::RestoreOfflineSession: failed to retrieve license. "
"key set id = %s", key_set_id.c_str());
return GET_LICENSE_ERROR;
}
// Do not restore a released offline license, unless a release retry
if (!(license_type == kLicenseTypeRelease ||
license_state == DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::Init invalid offline license state = %d, type = %d",
license_state, license_type);
LOGE("CdmSession::RestoreOfflineSession: invalid offline license state = "
"%d, type = %d", license_state, license_type);
return GET_RELEASED_LICENSE_ERROR;
}
std::string provider_session_token;
if (usage_support_type_ == kUsageEntrySupport) {
if (!license_parser_->ExtractProviderSessionToken(
key_response_, &provider_session_token) ||
usage_table_header_ == NULL) {
provider_session_token.clear();
} else {
CdmResponseType sts =
usage_table_header_->LoadEntry(crypto_session_.get(), usage_entry_,
usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = "
"%d", sts);
return sts;
}
}
}
if (license_type == kLicenseTypeRelease) {
if (!license_parser_->RestoreLicenseForRelease(key_request_,
key_response_)) {
@@ -184,11 +241,27 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} else {
if (!license_parser_->RestoreOfflineLicense(
key_request_, key_response_, offline_key_renewal_response_,
playback_start_time, last_playback_time, grace_period_end_time)) {
playback_start_time, last_playback_time, grace_period_end_time,
this)) {
return RESTORE_OFFLINE_LICENSE_ERROR_2;
}
}
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty() && usage_table_header_ != NULL) {
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession failed to update usage entry = "
"%d", sts);
return sts;
}
if (!StoreLicense(license_state)) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
license_received_ = true;
is_offline_ = true;
is_release_ = license_type == kLicenseTypeRelease;
@@ -196,14 +269,47 @@ CdmResponseType CdmSession::RestoreOfflineSession(
}
CdmResponseType CdmSession::RestoreUsageSession(
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response) {
key_request_ = key_request;
key_response_ = key_response;
const DeviceFiles::CdmUsageData& usage_data) {
if (!key_set_id_.empty()) {
file_handle_->UnreserveLicenseId(key_set_id_);
}
key_set_id_ = usage_data.key_set_id;
key_request_ = usage_data.license_request;
key_response_ = usage_data.license;
usage_entry_ = usage_data.usage_entry;
usage_entry_number_ = usage_data.usage_entry_number;
usage_provider_session_token_ = usage_data.provider_session_token;
if (usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != NULL) {
CdmResponseType sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to load usage entry = %d",
sts);
return sts;
}
}
if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) {
return RELEASE_LICENSE_ERROR_2;
}
if (usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != NULL) {
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to update usage entry: %d",
sts);
return sts;
}
if (!UpdateUsageInfo()) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
license_received_ = true;
is_offline_ = false;
is_release_ = true;
@@ -274,9 +380,10 @@ CdmResponseType CdmSession::GenerateKeyRequest(
std::vector<video_widevine::SubLicense> embedded_key_data =
init_data.ExtractEmbeddedKeys();
for (int i = 0; i < embedded_key_data.size(); ++i) {
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
CdmResponseType sts = crypto_session_->AddSubSession(
embedded_key_data[i].sub_session_key_id());
embedded_key_data[i].sub_session_key_id(),
init_data.ExtractGroupMasterKeyId());
if (NO_ERROR != sts) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate sub session");
return sts;
@@ -318,7 +425,41 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
} else if (license_received_) { // renewal
return RenewKey(key_response);
} else {
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
// If usage table header+entries are supported, preprocess the license
// to see if it has a provider session token. If so a new entry needs
// to be created.
CdmResponseType sts;
std::string provider_session_token;
if (usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != NULL) {
if (license_parser_->ExtractProviderSessionToken(
key_response, &provider_session_token) &&
!provider_session_token.empty()) {
std::string app_id;
GetApplicationId(&app_id);
sts = usage_table_header_->AddEntry(
crypto_session_.get(), is_offline_, key_set_id_,
DeviceFiles::GetUsageInfoFileName(app_id), &usage_entry_number_);
if (sts != NO_ERROR) return sts;
}
}
sts = license_parser_->HandleKeyResponse(key_response);
// Update or delete entry if usage table header+entries are supported
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty() &&
usage_table_header_ != NULL) {
if (sts != KEY_ADDED) {
CdmResponseType delete_sts =
usage_table_header_->DeleteEntry(usage_entry_number_,
file_handle_.get(),
crypto_metrics_);
if (delete_sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d",
delete_sts);
}
}
}
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? ADD_KEY_ERROR : sts;
@@ -329,7 +470,17 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
license_parser_->provider_session_token().size(),
license_parser_->provider_session_token().c_str());
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
if (is_offline_ || has_provider_session_token()) {
if (has_provider_session_token() &&
usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != NULL) {
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
}
if (!is_offline_)
usage_provider_session_token_ =
license_parser_->provider_session_token();
sts = StoreLicense();
if (sts != NO_ERROR) return sts;
}
@@ -409,18 +560,18 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::Decrypt: Crypto session not open");
return CRYPTO_SESSION_OPEN_ERROR_5;
return DECRYPT_NOT_READY;
}
// Playback may not begin until either the start time passes or the license
// is updated, so we treat this Decrypt call as invalid.
if (params.is_encrypted &&
!policy_engine_->CanDecryptContent(*params.key_id)) {
if (policy_engine_->GetKeyStatus(*params.key_id) ==
kKeyStatusOutputNotAllowed) {
if (policy_engine_->IsLicenseForFuture())
return DECRYPT_NOT_READY;
if (!policy_engine_->IsSufficientOutputProtection(*params.key_id))
return INSUFFICIENT_OUTPUT_PROTECTION;
}
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
return NEED_KEY;
}
CdmResponseType status = crypto_session_->Decrypt(params);
@@ -432,8 +583,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
}
has_decrypted_since_last_report_ = true;
if (!is_usage_update_needed_) {
is_usage_update_needed_ =
!license_parser_->provider_session_token().empty();
is_usage_update_needed_ = has_provider_session_token();
}
} else {
Clock clock;
@@ -452,7 +602,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
CdmResponseType CdmSession::GenerateRenewalRequest(
CdmKeyRequest* key_request) {
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
true, app_parameters_, &key_request->message, &key_request->url);
true, app_parameters_, NULL, &key_request->message, &key_request->url);
key_request->type = kKeyRequestTypeRenewal;
@@ -482,16 +632,32 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
CdmKeyRequest* key_request) {
is_release_ = true;
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
false, app_parameters_, &key_request->message,
&key_request->url);
false, app_parameters_, usage_table_header_ == NULL ? NULL : this,
&key_request->message, &key_request->url);
key_request->type = kKeyRequestTypeRelease;
if (KEY_MESSAGE != status) return status;
if (has_provider_session_token() &&
usage_support_type_ == kUsageEntrySupport) {
status = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
if (status != NO_ERROR) {
LOGE("CdmSession::GenerateReleaseRequest: Update usage entry failed = "
"%d", status);
return status;
}
}
if (is_offline_) { // Mark license as being released
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))
return RELEASE_KEY_REQUEST_ERROR;
} else if (!usage_provider_session_token_.empty()) {
if (usage_support_type_ == kUsageEntrySupport) {
if (!UpdateUsageInfo())
return RELEASE_USAGE_INFO_FAILED;
}
}
return KEY_MESSAGE;
}
@@ -502,12 +668,56 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
license_parser_->HandleKeyUpdateResponse(false, key_response);
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RELEASE_KEY_ERROR : sts;
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
if (is_offline_ || has_provider_session_token()) {
DeleteLicense();
if (usage_support_type_ == kUsageEntrySupport &&
has_provider_session_token()) {
sts = DeleteUsageEntry(usage_entry_number_);
if (NO_ERROR != sts) return sts;
}
}
return NO_ERROR;
}
CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
if (usage_support_type_ != kUsageEntrySupport) {
LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d",
usage_support_type_);
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
// The usage entry cannot be deleted if it has a crypto session handling
// it, so close and reopen session.
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(new CryptoSession(crypto_metrics_));
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_,
crypto_session_open_,
sts,
requested_security_level_);
if (sts != NO_ERROR) return sts;
usage_table_header_ = NULL;
if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) {
if (usage_support_type_ == kUsageEntrySupport)
usage_table_header_ = crypto_session_->GetUsageTableHeader();
} else {
usage_support_type_ = kNonSecureUsageSupport;
}
if (usage_table_header_ == NULL) {
LOGE("CdmSession::DeleteUsageEntry: Usage table header unavailable");
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
return usage_table_header_->DeleteEntry(usage_entry_number,
file_handle_.get(),
crypto_metrics_);
}
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
return license_parser_->IsKeyLoaded(key_id);
}
@@ -532,8 +742,9 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
while (key_set_id->empty()) {
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0]))
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0])) {
return false;
}
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
@@ -581,7 +792,10 @@ CdmResponseType CdmSession::StoreLicense() {
std::string app_id;
GetApplicationId(&app_id);
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
key_response_, app_id, key_set_id_)) {
key_response_,
DeviceFiles::GetUsageInfoFileName(app_id),
key_set_id_, usage_entry_,
usage_entry_number_)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return STORE_USAGE_INFO_ERROR;
}
@@ -594,7 +808,8 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
offline_key_renewal_request_, offline_key_renewal_response_,
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
policy_engine_->GetLastPlaybackTime(),
policy_engine_->GetGracePeriodEndTime(), app_parameters_);
policy_engine_->GetGracePeriodEndTime(), app_parameters_, usage_entry_,
usage_entry_number_);
}
CdmResponseType CdmSession::ReleaseCrypto() {
@@ -603,7 +818,7 @@ CdmResponseType CdmSession::ReleaseCrypto() {
}
bool CdmSession::DeleteLicense() {
if (!is_offline_ && license_parser_->provider_session_token().empty())
if (!is_offline_ && !has_provider_session_token())
return false;
if (is_offline_) {
@@ -612,7 +827,8 @@ bool CdmSession::DeleteLicense() {
std::string app_id;
GetApplicationId(&app_id);
return file_handle_->DeleteUsageInfo(
app_id, license_parser_->provider_session_token());
DeviceFiles::GetUsageInfoFileName(app_id),
license_parser_->provider_session_token());
}
}
@@ -645,12 +861,55 @@ void CdmSession::GetApplicationId(std::string* app_id) {
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
const std::vector<std::string>& provider_session_tokens) {
return crypto_session_->DeleteMultipleUsageInformation(
CdmResponseType sts = crypto_session_->DeleteMultipleUsageInformation(
provider_session_tokens);
crypto_metrics_->crypto_session_delete_multiple_usage_information_
.Increment(sts);
return sts;
}
CdmResponseType CdmSession::UpdateUsageInformation() {
return crypto_session_->UpdateUsageInformation();
CdmResponseType CdmSession::UpdateUsageTableInformation() {
CdmUsageSupportType usage_support_type;
CdmResponseType sts =
crypto_session_->GetUsageSupportType(&usage_support_type);
if (sts == NO_ERROR && usage_support_type == kUsageTableSupport) {
M_TIME(
sts = crypto_session_->UpdateUsageInformation(),
crypto_metrics_,
crypto_session_update_usage_information_,
sts);
return sts;
}
return NO_ERROR; // Ignore
}
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (usage_support_type_ != kUsageEntrySupport ||
!has_provider_session_token() ||
usage_table_header_ == NULL) {
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected state, "
"usage support type: %d, PST present: %s, usage table header available"
": %s", usage_support_type_,
has_provider_session_token() ? "yes" : "no",
usage_table_header_ == NULL ? "no" : "yes");
return INCORRECT_USAGE_SUPPORT_TYPE_2;
}
CdmResponseType sts = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
if (sts != NO_ERROR) return sts;
if (is_offline_)
StoreLicense(is_release_
? DeviceFiles::kLicenseStateReleasing
: DeviceFiles::kLicenseStateActive);
else if (!usage_provider_session_token_.empty())
UpdateUsageInfo();
return NO_ERROR;
}
CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
@@ -662,8 +921,20 @@ CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
LOGE("CdmSession::GenericEncrypt: No output destination provided");
return INVALID_PARAMETERS_ENG_6;
}
return crypto_session_->GenericEncrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericEncrypt(
in_buffer,
key_id,
iv,
algorithm,
out_buffer),
crypto_metrics_,
crypto_session_generic_encrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
algorithm);
return sts;
}
CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
@@ -675,8 +946,20 @@ CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
LOGE("CdmSession::GenericDecrypt: No output destination provided");
return INVALID_PARAMETERS_ENG_7;
}
return crypto_session_->GenericDecrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericDecrypt(
in_buffer,
key_id,
iv,
algorithm,
out_buffer),
crypto_metrics_,
crypto_session_generic_decrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
algorithm);
return sts;
}
CdmResponseType CdmSession::GenericSign(const std::string& message,
@@ -687,14 +970,56 @@ CdmResponseType CdmSession::GenericSign(const std::string& message,
LOGE("CdmSession::GenericSign: No output destination provided");
return INVALID_PARAMETERS_ENG_8;
}
return crypto_session_->GenericSign(message, key_id, algorithm, signature);
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericSign(
message,
key_id,
algorithm,
signature),
crypto_metrics_,
crypto_session_generic_sign_,
sts,
metrics::Pow2Bucket(message.size()),
algorithm);
return sts;
}
CdmResponseType CdmSession::GenericVerify(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
const std::string& signature) {
return crypto_session_->GenericVerify(message, key_id, algorithm, signature);
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericVerify(
message,
key_id,
algorithm,
signature),
crypto_metrics_,
crypto_session_generic_verify_,
sts,
metrics::Pow2Bucket(message.size()),
algorithm);
return sts;
}
bool CdmSession::UpdateUsageInfo() {
std::string app_id;
GetApplicationId(&app_id);
DeviceFiles::CdmUsageData usage_data;
usage_data.provider_session_token = usage_provider_session_token_;
usage_data.license_request = key_request_;
usage_data.license = key_response_;
usage_data.key_set_id = key_set_id_;
usage_data.usage_entry = usage_entry_;
usage_data.usage_entry_number = usage_entry_number_;
return file_handle_->UpdateUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
usage_provider_session_token_,
usage_data);
}
// For testing only - takes ownership of pointers

View File

@@ -26,8 +26,8 @@ void CdmSessionMap::Add(const std::string& id, CdmSession* session) {
bool CdmSessionMap::CloseSession(const std::string& id) {
AutoLock lock(lock_);
std::shared_ptr<CdmSession> session;
if (!FindSessionNoLock(id, session)) {
shared_ptr<CdmSession> session;
if (!FindSessionNoLock(id, &session)) {
return false;
}
session->Close();
@@ -41,30 +41,30 @@ bool CdmSessionMap::Exists(const std::string& id) {
}
bool CdmSessionMap::FindSession(const CdmSessionId& id,
std::shared_ptr<CdmSession>& session) {
shared_ptr<CdmSession>* session) {
AutoLock lock(lock_);
return FindSessionNoLock(id, session);
}
bool CdmSessionMap::FindSessionNoLock(const CdmSessionId& session_id,
std::shared_ptr<CdmSession>& session) {
shared_ptr<CdmSession>* session) {
CdmIdToSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
session = iter->second;
assert(session.get() != NULL);
*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);
if (!(iter->second)->IsClosed()) {
sessions.push_back(iter->second);
}
}
}

View File

@@ -66,7 +66,7 @@ void ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
result->assign(decoded_message.begin(), decoded_message.end());
}
}
} // namespace
namespace wvcdm {
// Protobuf generated classes.
@@ -83,7 +83,8 @@ using video_widevine::SignedProvisioningMessage;
*/
bool CertificateProvisioning::GetProvisioningTokenType(
ClientIdentification::TokenType* token_type) {
switch (crypto_session_.GetPreProvisionTokenType()) {
CdmClientTokenType token = crypto_session_.GetPreProvisionTokenType();
switch (token) {
case kClientTokenKeybox:
*token_type = ClientIdentification::KEYBOX;
return true;
@@ -93,6 +94,8 @@ bool CertificateProvisioning::GetProvisioningTokenType(
case kClientTokenDrmCert:
default:
// shouldn't happen
LOGE("CertificateProvisioning::GetProvisioningTokenType: unexpected "
"provisioning type: %d", token);
return false;
}
}
@@ -107,7 +110,7 @@ bool CertificateProvisioning::SetSpoidParameter(
const std::string& origin, const std::string& spoid,
ProvisioningRequest* request) {
if (!request) {
LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer "
LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer "
"passed to method.");
return false;
}
@@ -125,7 +128,7 @@ bool CertificateProvisioning::SetSpoidParameter(
} else if (origin != EMPTY_ORIGIN) {
// Legacy behavior - Concatenate Unique ID with Origin
std::string device_unique_id;
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
if (!crypto_session_.GetInternalDeviceUniqueId(&device_unique_id)) {
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
"device unique ID");
return false;
@@ -208,7 +211,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
uint32_t nonce;
if (!crypto_session_.GenerateNonce(&nonce)) {
LOGE("GetProvisioningRequest: fails to generate a nonce");
return CERT_PROVISIONING_REQUEST_ERROR_2;
return CERT_PROVISIONING_NONCE_GENERATION_ERROR;
}
// The provisioning server does not convert the nonce to uint32_t, it just
@@ -280,29 +283,33 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
* Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails.
*/
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
FileSystem* file_system, const CdmProvisioningResponse& response,
FileSystem* file_system, const CdmProvisioningResponse& response_message,
std::string* cert, std::string* wrapped_key) {
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 (response_message.empty()) {
LOGE("HandleProvisioningResponse: response message is empty.");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
}
if (raw_string.empty()) {
LOGE("HandleProvisioningResponse: response message is empty or "
"an invalid JSON/base64 string.");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
std::string response;
if (wvcdm::Properties::provisioning_messages_are_binary()) {
response.assign(response_message);
} else {
// The response is base64 encoded in a JSON wrapper.
// Extract it and decode it. On error return an empty string.
ExtractAndDecodeSignedMessage(response_message, &response);
if (response.empty()) {
LOGE("HandleProvisioningResponse: response message is "
"an invalid JSON/base64 string.");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
}
}
// Authenticates provisioning response using D1s (server key derived from
// the provisioing request's input). Validate provisioning response and
// stores private device RSA key and certificate.
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(raw_string)) {
if (!signed_response.ParseFromString(response)) {
LOGE("HandleProvisioningResponse: fails to parse signed response");
return CERT_PROVISIONING_RESPONSE_ERROR_2;
}

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,14 @@ using video_widevine_client::sdk::License_LicenseState_RELEASING;
using video_widevine_client::sdk::NameValue;
using video_widevine_client::sdk::UsageInfo;
using video_widevine_client::sdk::UsageInfo_ProviderSession;
using video_widevine_client::sdk::UsageTableInfo;
using video_widevine_client::sdk::UsageTableInfo_UsageEntryInfo;
using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE;
using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO;
using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::DrmDeviceCertificate;
@@ -47,6 +55,7 @@ const char kUsageInfoFileNamePrefix[] = "usage";
const char kUsageInfoFileNameExt[] = ".bin";
const char kLicenseFileNameExt[] = ".lic";
const char kEmptyFileName[] = "";
const char kUsageTableFileName[] = "usgtable.bin";
const char kWildcard[] = "*";
bool Hash(const std::string& data, std::string* hash) {
@@ -154,7 +163,7 @@ bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
uint32_t* system_id) {
LOGI("ExtractDeviceInfo Entry");
if (!serial_number && !system_id) {
LOGE("Invalid paramters to DeviceFiles::ExtractDeviceInfo");
LOGE("DeviceFiles::ExtractDeviceInfo: invalid parameter.");
return false;
}
@@ -210,7 +219,9 @@ bool DeviceFiles::StoreLicense(
const CdmKeyResponse& license_renewal,
const std::string& release_server_url, int64_t playback_start_time,
int64_t last_playback_time, int64_t grace_period_end_time,
const CdmAppParameterMap& app_parameters) {
const CdmAppParameterMap& app_parameters,
const CdmUsageEntry& usage_entry,
const uint32_t usage_entry_number) {
if (!initialized_) {
LOGW("DeviceFiles::StoreLicense: not initialized");
return false;
@@ -251,6 +262,8 @@ bool DeviceFiles::StoreLicense(
app_params->set_name(iter->first);
app_params->set_value(iter->second);
}
license->set_usage_entry(usage_entry);
license->set_usage_entry_number(usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
@@ -265,7 +278,8 @@ bool DeviceFiles::RetrieveLicense(
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
std::string* release_server_url, int64_t* playback_start_time,
int64_t* last_playback_time, int64_t* grace_period_end_time,
CdmAppParameterMap* app_parameters) {
CdmAppParameterMap* app_parameters, CdmUsageEntry* usage_entry,
uint32_t* usage_entry_number) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveLicense: not initialized");
return false;
@@ -319,6 +333,8 @@ bool DeviceFiles::RetrieveLicense(
(*app_parameters)[license.app_parameters(i).name()] =
license.app_parameters(i).value();
}
*usage_entry = license.usage_entry();
*usage_entry_number = license.usage_entry_number();
return true;
}
@@ -336,6 +352,11 @@ bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
return false;
}
if (key_set_ids == NULL) {
LOGW("DeviceFiles::ListLicenses: key_set_ids parameter not provided");
return false;
}
// Get list of filenames
std::vector<std::string> filenames;
if (!ListFiles(&filenames)) {
@@ -407,20 +428,21 @@ bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) {
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const std::string& app_id,
const std::string& key_set_id) {
const std::string& usage_info_file_name,
const std::string& key_set_id,
const std::string& usage_entry,
uint32_t usage_entry_number) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name)) {
if (!FileExists(usage_info_file_name)) {
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
} else {
if (!RetrieveHashedFile(file_name, &file)) {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
LOGW("DeviceFiles::StoreUsageInfo: Unable to parse file");
return false;
}
@@ -434,10 +456,12 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
provider_session->set_license_request(key_request.data(), key_request.size());
provider_session->set_license(key_response.data(), key_response.size());
provider_session->set_key_set_id(key_set_id.data(), key_set_id.size());
provider_session->set_usage_entry(usage_entry);
provider_session->set_usage_entry_number(usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(file_name, serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
bool DeviceFiles::ListUsageRecords(const std::string& app_id,
@@ -476,28 +500,28 @@ bool DeviceFiles::ListUsageRecords(const std::string& app_id,
return true;
}
bool DeviceFiles::GetProviderToken(const std::string& app_id,
const std::string& key_set_id,
std::string* provider_session_token) {
bool DeviceFiles::GetProviderSessionToken(const std::string& app_id,
const std::string& key_set_id,
std::string* provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::GetProviderToken: not initialized");
LOGW("DeviceFiles::GetProviderSessionToken: not initialized");
return false;
}
if (provider_session_token == NULL) {
LOGW("DeviceFiles::GetProviderToken: NULL return argument pointer");
LOGW("DeviceFiles::GetProviderSessionToken: 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");
LOGW("DeviceFiles::GetProviderSessionToken: empty file");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::GetProviderToken: unable to parse file");
LOGW("DeviceFiles::GetProviderSessionToken: unable to parse file");
return false;
}
@@ -511,15 +535,14 @@ bool DeviceFiles::GetProviderToken(const std::string& app_id,
return false;
}
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveHashedFile(file_name, &file)) return false;
if (!RetrieveHashedFile(usage_info_file_name, &file)) return false;
UsageInfo* usage_info = file.mutable_usage_info();
int index = 0;
@@ -548,11 +571,11 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(file_name, serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
bool DeviceFiles::DeleteAllUsageInfoForApp(
const std::string& app_id,
const std::string& usage_info_file_name,
std::vector<std::string>* provider_session_tokens) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
@@ -564,22 +587,21 @@ bool DeviceFiles::DeleteAllUsageInfoForApp(
}
provider_session_tokens->clear();
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name)) return true;
if (!FileExists(usage_info_file_name)) return true;
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(file_name, &file)) {
if (RetrieveHashedFile(usage_info_file_name, &file)) {
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
provider_session_tokens->push_back(file.usage_info().sessions(i).token());
}
} else {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file");
}
return RemoveFile(file_name);
return RemoveFile(usage_info_file_name);
}
bool DeviceFiles::RetrieveUsageInfo(
const std::string& app_id,
const std::string& usage_info_file_name,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
@@ -593,14 +615,14 @@ bool DeviceFiles::RetrieveUsageInfo(
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
if (!FileExists(usage_info_file_name) ||
GetFileSize(usage_info_file_name) == 0) {
usage_info->resize(0);
return true;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
return false;
}
@@ -615,18 +637,19 @@ bool DeviceFiles::RetrieveUsageInfo(
return true;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response) {
CdmKeyResponse* license_response,
std::string* usage_entry,
uint32_t* usage_entry_number) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
@@ -635,6 +658,9 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
if (file.usage_info().sessions(index).token() == provider_session_token) {
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
@@ -643,26 +669,32 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
}
bool DeviceFiles::RetrieveUsageInfoByKeySetId(
const std::string& app_id,
const std::string& usage_info_file_name,
const std::string& key_set_id,
std::string* provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response) {
CdmKeyResponse* license_response,
std::string* usage_entry,
uint32_t* usage_entry_number) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: not initialized");
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
int index = 0;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).key_set_id() == key_set_id) {
*provider_session_token = file.usage_info().sessions(index).token();
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
@@ -670,6 +702,190 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId(
return false;
}
bool DeviceFiles::StoreUsageInfo(const std::string& usage_info_file_name,
const std::vector<CdmUsageData>& usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
UsageInfo* usage_info = file.mutable_usage_info();
for (size_t i = 0; i < usage_data.size(); ++i) {
UsageInfo_ProviderSession* provider_session = usage_info->add_sessions();
provider_session->set_token(usage_data[i].provider_session_token.data(),
usage_data[i].provider_session_token.size());
provider_session->set_license_request(usage_data[i].license_request.data(),
usage_data[i].license_request.size());
provider_session->set_license(usage_data[i].license.data(),
usage_data[i].license.size());
provider_session->set_key_set_id(usage_data[i].key_set_id.data(),
usage_data[i].key_set_id.size());
provider_session->set_usage_entry(usage_data[i].usage_entry);
provider_session->set_usage_entry_number(usage_data[i].usage_entry_number);
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
bool DeviceFiles::UpdateUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
const CdmUsageData& usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::UpdateUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
if (!FileExists(usage_info_file_name)) {
LOGW("DeviceFiles::UpdateUsageInfo: Usage file does not exist");
return false;
}
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
LOGW("DeviceFiles::UpdateUsageInfo: Unable to parse file");
return false;
}
int index = 0;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).token() == provider_session_token) {
UsageInfo* usage_info = file.mutable_usage_info();
UsageInfo_ProviderSession* provider_session =
usage_info->mutable_sessions(index);
provider_session->set_license_request(usage_data.license_request);
provider_session->set_license(usage_data.license);
provider_session->set_key_set_id(usage_data.key_set_id);
provider_session->set_usage_entry(usage_data.usage_entry);
provider_session->set_usage_entry_number(usage_data.usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
}
return false;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
std::vector<CdmUsageData>* usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
if (usage_data == NULL) {
LOGW("DeviceFiles::RetrieveUsageInfo: usage_data not provided");
return false;
}
if (!FileExists(usage_info_file_name) ||
GetFileSize(usage_info_file_name) == 0) {
usage_data->resize(0);
return true;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
usage_data->resize(file.usage_info().sessions_size());
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
(*usage_data)[i].provider_session_token =
file.usage_info().sessions(i).token();
(*usage_data)[i].license_request =
file.usage_info().sessions(i).license_request();
(*usage_data)[i].license = file.usage_info().sessions(i).license();
(*usage_data)[i].key_set_id = file.usage_info().sessions(i).key_set_id();
(*usage_data)[i].usage_entry = file.usage_info().sessions(i).usage_entry();
(*usage_data)[i].usage_entry_number =
file.usage_info().sessions(i).usage_entry_number();
}
return true;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
CdmUsageData* usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
if (usage_data == NULL) {
LOGW("DeviceFiles::RetrieveUsageInfo: usage_data not provided");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
int index = 0;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).token() == provider_session_token) {
usage_data->provider_session_token =
file.usage_info().sessions(index).token();
usage_data->license_request =
file.usage_info().sessions(index).license_request();
usage_data->license = file.usage_info().sessions(index).license();
usage_data->key_set_id = file.usage_info().sessions(index).key_set_id();
usage_data->usage_entry = file.usage_info().sessions(index).usage_entry();
usage_data->usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
return false;
}
bool DeviceFiles::ListUsageInfoFiles(
std::vector<std::string>* usage_info_file_names) {
if (!initialized_) {
LOGW("DeviceFiles::ListUsageInfoFiles: not initialized");
return false;
}
if (usage_info_file_names == NULL) {
LOGW("DeviceFiles::ListUsageInfoFiles: usage_info_file_names not provided");
return false;
}
// Get list of filenames
std::vector<std::string> filenames;
if (!ListFiles(&filenames)) {
return false;
}
// Scan list of all filenames and return only usage info filenames
usage_info_file_names->clear();
for (size_t i = 0; i < filenames.size(); i++) {
std::string* name = &filenames[i];
std::size_t pos_prefix = name->find(kUsageInfoFileNamePrefix);
std::size_t pos_suffix = name->find(kUsageInfoFileNameExt);
if (pos_prefix == std::string::npos ||
pos_suffix == std::string::npos) {
// Skip this file - extension does not match
continue;
}
usage_info_file_names->push_back(*name);
}
return true;
}
bool DeviceFiles::StoreHlsAttributes(
const std::string& key_set_id, const CdmHlsMethod method,
const std::vector<uint8_t>& media_segment_iv) {
@@ -767,6 +983,117 @@ bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) {
return RemoveFile(key_set_id + kHlsAttributesFileNameExt);
}
bool DeviceFiles::StoreUsageTableInfo(
const CdmUsageTableHeader& usage_table_header,
const std::vector<CdmUsageEntryInfo>& usage_entry_info) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageTableInfo: not initialized");
return false;
}
// Fill in file information
video_widevine_client::sdk::File file;
file.set_type(video_widevine_client::sdk::File::USAGE_TABLE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
UsageTableInfo* usage_table_info = file.mutable_usage_table_info();
usage_table_info->set_usage_table_header(usage_table_header);
for (size_t i = 0; i < usage_entry_info.size(); ++i) {
UsageTableInfo_UsageEntryInfo* info =
usage_table_info->add_usage_entry_info();
info->set_key_set_id(usage_entry_info[i].key_set_id);
switch (usage_entry_info[i].storage_type) {
case kStorageLicense:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE);
break;
case kStorageUsageInfo:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO);
info->set_usage_info_file_name(
usage_entry_info[i].usage_info_file_name);
break;
case kStorageTypeUnknown:
default:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN);
break;
}
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(GetUsageTableFileName(), serialized_file);
}
bool DeviceFiles::RetrieveUsageTableInfo(
CdmUsageTableHeader* usage_table_header,
std::vector<CdmUsageEntryInfo>* usage_entry_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageTableInfo: not initialized");
return false;
}
if (usage_table_header == NULL) {
LOGW(
"DeviceFiles::RetrieveUsageTableInfo: usage_table_header not provided");
return false;
}
if (usage_entry_info == NULL) {
LOGW("DeviceFiles::RetrieveUsageTableInfo: usage_entry_info not provided");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(GetUsageTableFileName(), &file)) {
return false;
}
if (file.type() != video_widevine_client::sdk::File::USAGE_TABLE_INFO) {
LOGW("DeviceFiles::RetrieveUsageTableInfo: Incorrect file type");
return false;
}
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
LOGW("DeviceFiles::RetrieveUsageTableInfo: Incorrect file version");
return false;
}
if (!file.has_usage_table_info()) {
LOGW("DeviceFiles::RetrieveUsageTableInfo: Usage table info not present");
return false;
}
const UsageTableInfo& usage_table_info = file.usage_table_info();
*usage_table_header = usage_table_info.usage_table_header();
usage_entry_info->resize(usage_table_info.usage_entry_info_size());
for (int i = 0; i < usage_table_info.usage_entry_info_size(); ++i) {
const UsageTableInfo_UsageEntryInfo& info =
usage_table_info.usage_entry_info(i);
(*usage_entry_info)[i].key_set_id = info.key_set_id();
switch (info.storage()) {
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE:
(*usage_entry_info)[i].storage_type = kStorageLicense;
break;
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO:
(*usage_entry_info)[i].storage_type = kStorageUsageInfo;
(*usage_entry_info)[i].usage_info_file_name =
info.usage_info_file_name();
break;
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN:
default:
(*usage_entry_info)[i].storage_type = kStorageTypeUnknown;
break;
}
}
return true;
}
bool DeviceFiles::StoreFileWithHash(const std::string& name,
const std::string& serialized_file) {
// calculate SHA hash
@@ -912,7 +1239,7 @@ bool DeviceFiles::FileExists(const std::string& name) {
bool DeviceFiles::ListFiles(std::vector<std::string>* names) {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
LOGW("DeviceFiles::ListFiles: Unable to get base path");
return false;
}
return file_system_->List(path, names);
@@ -944,6 +1271,10 @@ std::string DeviceFiles::GetCertificateFileName() {
return kCertificateFileName;
}
std::string DeviceFiles::GetUsageTableFileName() {
return kUsageTableFileName;
}
std::string DeviceFiles::GetHlsAttributesFileNameExtension() {
return kHlsAttributesFileNameExt;
}

View File

@@ -43,6 +43,8 @@ message License {
// contains the playback_start_time we should use as an override. This is
// ignored if there is no grace period.
optional int64 grace_period_end_time = 11 [default = 0];
optional bytes usage_entry = 12;
optional int64 usage_entry_number = 13;
}
message UsageInfo {
@@ -51,13 +53,15 @@ message UsageInfo {
optional bytes license_request = 2;
optional bytes license = 3;
optional bytes key_set_id = 4;
optional bytes usage_entry = 5;
optional int64 usage_entry_number = 6;
}
repeated ProviderSession sessions = 1;
}
message HlsAttributes {
enum Method {
enum Method {
AES_128 = 1;
SAMPLE_AES = 2;
}
@@ -65,12 +69,30 @@ message HlsAttributes {
optional bytes media_segment_iv = 2;
}
message UsageTableInfo {
message UsageEntryInfo {
enum UsageEntryStorage {
LICENSE = 1;
USAGE_INFO = 2;
UNKNOWN = 3;
}
optional UsageEntryStorage storage = 1;
optional bytes key_set_id = 2;
optional bytes usage_info_file_name = 3; // hash of the app_id
}
optional bytes usage_table_header = 1;
repeated UsageEntryInfo usage_entry_info = 2;
}
message File {
enum FileType {
DEVICE_CERTIFICATE = 1;
LICENSE = 2;
USAGE_INFO = 3;
HLS_ATTRIBUTES = 4;
USAGE_TABLE_INFO = 5;
}
enum FileVersion {
@@ -83,6 +105,7 @@ message File {
optional License license = 4;
optional UsageInfo usage_info = 5;
optional HlsAttributes hls_attributes = 6;
optional UsageTableInfo usage_table_info = 7;
}
message HashedFile {

View File

@@ -7,7 +7,6 @@
#include "buffer_reader.h"
#include "jsmn.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
@@ -15,10 +14,6 @@
namespace {
const char kKeyFormatVersionsSeparator = '/';
const char kColon = ':';
const char kDoubleQuote = '\"';
const char kLeftBracket = '[';
const char kRightBracket = ']';
const std::string kBase64String = "base64,";
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;
@@ -594,4 +589,19 @@ std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
return versions;
}
// Extract the key id of the group master key used to generate sublicense data.
// Returns an empty string if not defined.
const std::string InitializationData::ExtractGroupMasterKeyId() const {
if (!is_cenc_) {
return "";
}
WidevinePsshData cenc_header;
if (!cenc_header.ParseFromString(data_)) {
return "";
}
return cenc_header.group_master_key_id();
}
} // namespace wvcdm

View File

@@ -7,6 +7,7 @@
#include <vector>
#include "clock.h"
#include "cdm_session.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "device_files.h"
@@ -35,7 +36,6 @@ const uint32_t kFourCcCbcs = 0x63626373;
const uint32_t kFourCcLittleEndianCbc1 = 0x31636263;
const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
const uint32_t kFourCcCenc = 0x63656e63;
const uint32_t kFourCcCens = 0x63656e73;
} // namespace
@@ -123,7 +123,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
case kFourCcLittleEndianCbcs:
key.set_cipher_mode(kCipherModeCbc);
break;
default:
default:
key.set_cipher_mode(kCipherModeCtr);
break;
}
@@ -149,11 +149,11 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> sub_session_keys = ExtractSubSessionKeys(license);
// Match the track label from the key arrays and add sub_license_key_id to
// the content key array.
LOGI("Received %d subsession keys", sub_session_keys.size());
LOGV("Received %d subsession keys", sub_session_keys.size());
if (!sub_session_keys.empty()) {
for (int i = 0; i < key_array.size(); ++i) {
for (size_t i = 0; i < key_array.size(); ++i) {
if (key_array[i].track_label().empty()) continue;
for (int x = 0; x < sub_session_keys.size(); ++x) {
for (size_t x = 0; x < sub_session_keys.size(); ++x) {
if (sub_session_keys[x].track_label() == key_array[i].track_label()) {
key_array[i].set_sub_session_key_id(sub_session_keys[x].key_id());
key_array[i].set_sub_session_key(sub_session_keys[x].key_data());
@@ -172,6 +172,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
use_privacy_mode_(false),
clock_(new Clock()) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
@@ -180,16 +181,18 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false) {
is_offline_(false),
use_privacy_mode_(false) {
clock_.reset(clock);
}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(
ServiceCertificate* service_certificate, const std::string& client_token,
CdmClientTokenType client_token_type, const std::string& device_id,
CryptoSession* session, PolicyEngine* policy_engine) {
const std::string& client_token, CdmClientTokenType client_token_type,
const std::string& device_id, bool use_privacy_mode,
const std::string& signed_service_certificate, CryptoSession* session,
PolicyEngine* policy_engine) {
if (clock_.get() == NULL) {
LOGE("CdmLicense::Init: clock parameter not provided");
return false;
@@ -211,12 +214,24 @@ bool CdmLicense::Init(
return false;
}
service_certificate_ = service_certificate;
if (use_privacy_mode) {
if (!signed_service_certificate.empty()) {
if (service_certificate_.Init(signed_service_certificate) != NO_ERROR)
return false;
}
if (!service_certificate_.has_certificate() &&
!Properties::allow_service_certificate_requests()) {
LOGE("CdmLicense::Init: Required service certificate not provided");
return false;
}
}
client_token_ = client_token;
client_token_type_ = client_token_type;
device_id_ = device_id;
crypto_session_ = session;
policy_engine_ = policy_engine;
use_privacy_mode_ = use_privacy_mode;
initialized_ = true;
return true;
}
@@ -229,6 +244,12 @@ 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());
@@ -247,12 +268,25 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return INVALID_PARAMETERS_LIC_7;
}
// If privacy mode, must have service certificate
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->has_certificate()) {
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
// If privacy mode and no service certificate, depending on platform
// configuration, request service certificate or declare error
if (use_privacy_mode_ && !service_certificate_.has_certificate()) {
if (!Properties::allow_service_certificate_requests()) {
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_1;
return PRIVACY_MODE_ERROR_1;
}
stored_init_data_.reset(new InitializationData(init_data));
if (!ServiceCertificate::GetRequest(signed_request)) {
LOGE("CdmLicense::PrepareKeyRequest: failed to prepare a service "
"certificated request");
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
return KEY_MESSAGE;
}
std::string request_id;
@@ -284,30 +318,30 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// initialization data.
std::vector<video_widevine::SubLicense> embedded_key_data =
init_data.ExtractEmbeddedKeys();
for (int i = 0; i < embedded_key_data.size(); ++i) {
for (size_t i = 0; i < embedded_key_data.size(); ++i) {
bool exists = false;
if (!crypto_session_->GenerateSubSessionNonce(
embedded_key_data[i].sub_session_key_id(), &exists, &nonce)) {
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
if (exists) {
SignedMessage signed_sub_license;
License_KeyContainer keyc;
// Parse the sub license for this track to extract the label.
if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) ||
!keyc.ParseFromString(signed_sub_license.msg()) ||
keyc.track_label().empty()) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
if (exists) {
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
LicenseRequest::SubSessionData* sub_session_data =
license_request.add_sub_session_data();
sub_session_data->set_sub_session_key_id(
embedded_key_data[i].sub_session_key_id());
sub_session_data->set_nonce(nonce);
sub_session_data->set_track_label(keyc.track_label());
}
SignedMessage signed_sub_license;
License_KeyContainer keyc;
// Parse the sub license for this track to extract the label.
if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) ||
!keyc.ParseFromString(signed_sub_license.msg()) ||
keyc.track_label().empty()) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
}
LicenseRequest::SubSessionData* sub_session_data =
license_request.add_sub_session_data();
sub_session_data->set_sub_session_key_id(
embedded_key_data[i].sub_session_key_id());
sub_session_data->set_nonce(nonce);
sub_session_data->set_track_label(keyc.track_label());
}
license_request.set_protocol_version(video_widevine::VERSION_2_1);
@@ -316,8 +350,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
std::string serialized_license_req;
license_request.SerializeToString(&serialized_license_req);
if (Properties::use_certificates_as_identification())
key_request_ = serialized_license_req;
key_request_ = serialized_license_req;
// Derive signing and encryption keys and construct signature.
std::string license_request_signature;
@@ -347,7 +380,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
bool is_renewal, const CdmAppParameterMap& app_parameters,
CdmKeyMessage* signed_request, std::string* server_url) {
CdmSession* cdm_session, CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_1;
@@ -367,8 +401,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
}
if (renew_with_client_id_) {
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->has_certificate()) {
if (use_privacy_mode_ && !service_certificate_.has_certificate()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_2;
@@ -403,6 +436,13 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
if (NO_ERROR != status) return status;
}
// TODO(rfrias): Refactor to avoid needing to call CdmSession
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return status;
}
std::string usage_report;
CdmResponseType status = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
@@ -483,16 +523,26 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return INVALID_LICENSE_RESPONSE;
}
switch (signed_response.type()) {
case SignedMessage::LICENSE:
break;
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
LOGE(
"CdmLicense::HandleKeyResponse: unrecognized signed message type: %d",
signed_response.type());
return INVALID_LICENSE_TYPE;
if (use_privacy_mode_ &&
Properties::allow_service_certificate_requests() &&
signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) {
std::string signed_certificate;
CdmResponseType status =
ServiceCertificate::ParseResponse(license_response,
&signed_certificate);
if (status != NO_ERROR) return status;
status = service_certificate_.Init(signed_certificate);
return (status == NO_ERROR) ? NEED_KEY : status;
}
if (signed_response.type() == SignedMessage::ERROR_RESPONSE)
return HandleKeyErrorResponse(signed_response);
if (signed_response.type() != SignedMessage::LICENSE) {
LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d",
signed_response.type());
return INVALID_LICENSE_TYPE;
}
if (!signed_response.has_signature()) {
@@ -506,15 +556,13 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return LICENSE_RESPONSE_PARSE_ERROR_1;
}
if (Properties::use_certificates_as_identification()) {
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::HandleKeyResponse: no session keys present");
return SESSION_KEYS_NOT_FOUND;
}
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return GENERATE_DERIVED_KEYS_ERROR;
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::HandleKeyResponse: no session keys present");
return SESSION_KEYS_NOT_FOUND;
}
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return GENERATE_DERIVED_KEYS_ERROR;
// Extract mac key
std::string mac_key_iv;
@@ -544,17 +592,17 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return NO_CONTENT_KEY;
}
if (license.has_srm_update()) crypto_session_->LoadSrm(license.srm_update());
if (license.id().type() == video_widevine::OFFLINE &&
license.policy().can_persist())
is_offline_ = true;
LOGV("Get Provider_session_token:");
if (license.id().has_provider_session_token()) {
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
LOGV("Provider_session_token=%s", provider_session_token_.c_str());
} else {
LOGV("NO Provider_session_token");
}
LOGV("provider_session_token: %s", provider_session_token_.empty() ?
"N/A" : provider_session_token_.c_str());
if (license.policy().has_renewal_server_url()) {
server_url_ = license.policy().renewal_server_url();
@@ -564,10 +612,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
renew_with_client_id_ = license.policy().always_include_client_id();
}
CdmResponseType resp = NO_ERROR;
resp = crypto_session_->LoadKeys(
CdmResponseType resp = crypto_session_->LoadKeys(
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
key_array, provider_session_token_);
key_array, provider_session_token_, license.srm_requirement());
if (KEY_ADDED == resp) {
loaded_keys_.clear();
@@ -667,7 +714,7 @@ CdmResponseType CdmLicense::HandleSubLicense(
std::set<KeyId> loaded_keys;
// Build a license with the rotated keys.
License license;
for (int i = 0; i < subkeys.size(); ++i) {
for (size_t i = 0; i < subkeys.size(); ++i) {
SignedMessage sm;
if (!sm.ParseFromString(subkeys[i].key_msg())) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
@@ -676,18 +723,30 @@ CdmResponseType CdmLicense::HandleSubLicense(
if (!keyc.ParseFromString(sm.msg())) {
return LICENSE_REQUEST_INVALID_SUBLICENSE;
}
size_t length;
std::vector<CryptoKey> keys;
keys.resize(1);
keys[0].set_key_id(keyc.id());
keys[0].set_key_data(keyc.key());
// Strip PKCS#5 padding from sublicense content keys.
// TODO(jfore): Refactor this to use ExtractContentKeys.
if (keyc.key().size() > KEY_SIZE) {
length = keyc.key().size() - KEY_SIZE;
} else {
length = 0;
}
keys[0].set_key_data(keyc.key().substr(0, length));
keys[0].set_key_data_iv(keyc.iv());
keys[0].set_key_control(keyc.key_control().key_control_block());
keys[0].set_key_control_iv(keyc.key_control().iv());
keys[0].set_track_label(keyc.track_label());
//TODO: passing empty cipher_mode and srm_req params - OK?
CdmResponseType result = crypto_session_->LoadKeys(
sm.msg(), sm.signature(), std::string(), std::string(), keys,
std::string());
std::string(), std::string());
if (result != KEY_ADDED) {
LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d",
result);
return result;
}
loaded_keys.insert(keyc.id());
@@ -703,7 +762,8 @@ bool CdmLicense::RestoreOfflineLicense(
const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response, int64_t playback_start_time,
int64_t last_playback_time, int64_t grace_period_end_time) {
int64_t last_playback_time, int64_t grace_period_end_time,
CdmSession* cdm_session) {
if (license_request.empty() || license_response.empty()) {
LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
@@ -726,13 +786,7 @@ bool CdmLicense::RestoreOfflineLicense(
return false;
}
if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg();
} else {
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
}
key_request_ = signed_request.msg();
CdmResponseType sts = HandleKeyResponse(license_response);
@@ -745,9 +799,16 @@ bool CdmLicense::RestoreOfflineLicense(
}
if (!provider_session_token_.empty()) {
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return false;
}
std::string usage_report;
CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid;
int64_t seconds_since_started, seconds_since_last_played;
sts = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
@@ -803,13 +864,7 @@ bool CdmLicense::RestoreLicenseForRelease(
return false;
}
if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg();
} else {
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
}
key_request_ = signed_request.msg();
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
@@ -848,19 +903,15 @@ bool CdmLicense::RestoreLicenseForRelease(
if (license.policy().has_always_include_client_id())
renew_with_client_id_ = license.policy().always_include_client_id();
if (Properties::use_certificates_as_identification()) {
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present");
return false;
}
if (!signed_response.has_session_key()) {
LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present");
return false;
}
if (license.id().has_provider_session_token()) {
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return false;
} else {
return KEY_ADDED == HandleKeyResponse(license_response);
}
if (license.id().has_provider_session_token()) {
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return false;
}
if (license.policy().has_renewal_server_url())
@@ -876,6 +927,44 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}
bool CdmLicense::ExtractProviderSessionToken(
const CdmKeyResponse& license_response,
std::string* provider_session_token) {
if (license_response.empty()) {
LOGE("CdmLicense::ExtractProviderSessionToken: empty license response");
return false;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGE(
"CdmLicense::ExtractProviderSessionToken: unable to parse signed "
"license response");
return false;
}
if (signed_response.type() != SignedMessage::LICENSE) {
LOGE("CdmLicense::ExtractProviderSessionToken: unrecognized signed message "
"type: %d", signed_response.type());
return false;
}
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::ExtractProviderSessionToken: unable to parse license "
"response");
return false;
}
if (license.id().has_provider_session_token() &&
!license.id().provider_session_token().empty()) {
*provider_session_token = license.id().provider_session_token();
return true;
}
return false;
}
CdmResponseType CdmLicense::HandleKeyErrorResponse(
const SignedMessage& signed_message) {
LicenseError license_error;
@@ -969,7 +1058,7 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(b2a_hex(device_id_));
} else if (crypto_session_->GetDeviceUniqueId(&value)) {
} else if (crypto_session_->GetInternalDeviceUniqueId(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(value);
@@ -1041,16 +1130,36 @@ CdmResponseType CdmLicense::PrepareClientId(
}
}
CryptoSession::SupportedCertificateTypes supported_certs;
if (crypto_session_->GetSupportedCertificateTypes(&supported_certs)) {
if (supported_certs.rsa_2048_bit) {
client_capabilities->add_supported_certificate_key_type(
video_widevine::
ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_2048);
}
if (supported_certs.rsa_3072_bit) {
client_capabilities->add_supported_certificate_key_type(
video_widevine::
ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_3072);
}
}
client_capabilities->set_can_update_srm(
crypto_session_->IsSrmUpdateSupported());
uint16_t srm_version;
if (crypto_session_->GetSrmVersion(&srm_version))
client_capabilities->set_srm_version(srm_version);
if (Properties::UsePrivacyMode(session_id_)) {
if (service_certificate_->certificate().empty()) {
if (!service_certificate_.has_certificate()) {
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
return PRIVACY_MODE_ERROR_3;
}
EncryptedClientIdentification* encrypted_client_id =
license_request->mutable_encrypted_client_id();
CdmResponseType status;
status = service_certificate_->EncryptClientId(crypto_session_, client_id,
encrypted_client_id);
status = service_certificate_.EncryptClientId(crypto_session_, client_id,
encrypted_client_id);
if (NO_ERROR == status) {
license_request->clear_client_id();
} else {

View File

@@ -218,9 +218,13 @@ message License {
// 4cc code specifying the CENC protection scheme as defined in the CENC 3.0
// specification. Propagated from Widevine PSSH box. Optional.
optional uint32 protection_scheme = 7;
// Minimum HDCP SRM version needed for using this key on content sent to
// HDCP enabled outputs.
optional uint32 min_hdcp_srm_version = 8;
// 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum
// HDCP SRM version. Additional details can be found in
// Widevine Modular DRM Security Integration Guide for CENC.
optional bytes srm_requirement = 8;
// If present this contains a signed SRM file that should be installed
// on the client device.
optional bytes srm_update = 9;
}
enum ProtocolVersion {
@@ -527,6 +531,11 @@ message ClientIdentification {
HDCP_NO_DIGITAL_OUTPUT = 0xff;
}
enum CertificateKeyType {
RSA_2048 = 0;
RSA_3072 = 1;
}
optional bool client_token = 1 [default = false];
optional bool session_token = 2 [default = false];
optional bool video_resolution_constraints = 3 [default = false];
@@ -536,6 +545,12 @@ message ClientIdentification {
// storing the generation number in secure memory. For Details, see:
// Widevine Modular DRM Security Integration Guide for CENC
optional bool anti_rollback_usage_table = 6 [default = false];
// The client shall report |srm_version| if available.
optional uint32 srm_version = 7;
// A device may have SRM data, and report a version, but may not be capable
// of updating SRM data.
optional bool can_update_srm = 8 [default = false];
repeated CertificateKeyType supported_certificate_key_type = 9;
}
// Type of factory-provisioned device root of trust. Optional.
@@ -753,6 +768,10 @@ message WidevinePsshData {
// Required when using content keys that are embedded in content.
repeated SubLicense sub_licenses = 11;
// Key ID used to identify the group master key License Server is supposed
// to use to generate group license.
optional string group_master_key_id = 12;
}
// Signed device certificate definition.

View File

@@ -80,4 +80,49 @@ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(SecurityLevel) {
return ::OEMCrypto_GetProvisioningMethod();
}
uint32_t OEMCrypto_SupportedCertificates(SecurityLevel level) {
return ::OEMCrypto_SupportedCertificates();
}
OEMCryptoResult OEMCrypto_CreateUsageTableHeader(SecurityLevel level,
uint8_t* header_buffer,
size_t* header_buffer_length) {
return ::OEMCrypto_CreateUsageTableHeader(header_buffer,
header_buffer_length);
}
OEMCryptoResult OEMCrypto_LoadUsageTableHeader(SecurityLevel level,
const uint8_t* buffer,
size_t buffer_length) {
return ::OEMCrypto_LoadUsageTableHeader(buffer, buffer_length);
}
OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(SecurityLevel level,
uint32_t new_table_size,
uint8_t* header_buffer,
size_t* header_buffer_length) {
return ::OEMCrypto_ShrinkUsageTableHeader(new_table_size, header_buffer,
header_buffer_length);
}
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(SecurityLevel level,
uint64_t time_since_license_received,
uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt,
OEMCrypto_Usage_Entry_Status status,
uint8_t *server_mac_key,
uint8_t *client_mac_key,
const uint8_t* pst,
size_t pst_length) {
return ::OEMCrypto_CreateOldUsageEntry(time_since_license_received,
time_since_first_decrypt,
time_since_last_decrypt,
status,
server_mac_key,
client_mac_key,
pst,
pst_length);
}
} // namespace wvcdm

View File

@@ -8,6 +8,7 @@
// to allow an older oemcrypto implementation to be linked with CDM.
#include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h"
extern "C" OEMCryptoResult OEMCrypto_DecryptCTR_V10(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
@@ -43,7 +44,7 @@ extern "C" OEMCryptoResult OEMCrypto_DecryptCENC(
subsample_flags);
}
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V11_or_V12(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,

View File

@@ -0,0 +1,102 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
// This should be used when liboemcrypto.so is linked with the CDM code at
// compile time.
//
// Defines APIs introduced in newer version (v13) that are not available in v12.
// This allows an older oemcrypto implementation to be linked with CDM.
#include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h"
extern "C" {
// Assume only 2048 bit certificates are supported
uint32_t OEMCrypto_SupportedCertificates() {
return OEMCrypto_Supports_RSA_2048bit;
}
// SRM support is not available before v13.
bool OEMCrypto_IsSRMUpdateSupported() {
return false;
}
OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t*) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t*, size_t) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_RemoveSRM() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// Large Usage Table support is not available before v13.
OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t*, size_t*) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t*, size_t) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION, uint32_t*) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION, uint32_t,
const uint8_t*, size_t) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION, uint8_t*,
size_t*, uint8_t*, size_t*) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// Call V12 OEMCrypto_DeactivateUsageEntry - omit session param.
OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION,
const uint8_t* pst,
size_t pst_length) {
return OEMCrypto_DeactivateUsageEntry_V12(pst, pst_length);
}
// Call V12 OEMCrypto_LoadKeys - omit srm_requirement param.
OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
size_t pst_length, const uint8_t* /* srm_requirement */) {
return OEMCrypto_LoadKeys_V11_or_V12(
session, message, message_length, signature, signature_length,
enc_mac_keys_iv, enc_mac_keys, num_keys, key_array, pst, pst_length);
}
OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
uint64_t, uint64_t, uint64_t, OEMCrypto_Usage_Entry_Status,
uint8_t*, uint8_t*, const uint8_t*, size_t) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
// NOTE: OEMCrypto_DeleteOldUsageTable() uses OEMCrypto_DeleteUsageTable()
// entry point.
OEMCryptoResult OEMCrypto_CopyOldUsageEntry(
OEMCrypto_SESSION, const uint8_t*pst, size_t) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION, uint32_t) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t, uint8_t*, size_t*) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
} // extern "C"

View File

@@ -52,8 +52,8 @@ extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst,
size_t pst_length) {
extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry_V12(const uint8_t* pst,
size_t pst_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
@@ -72,6 +72,6 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_DeleteUsageTable(){
extern "C" OEMCryptoResult OEMCrypto_DeleteOldUsageTable(){
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -14,6 +14,13 @@
using video_widevine::License;
namespace {
const int kCdmPolicyTimerDurationSeconds = 1;
const int kClockSkewDelta = 5; // seconds
} // namespace
namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
@@ -28,6 +35,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
last_expiry_time_set_(false),
was_expired_on_load_(false),
next_renewal_time_(0),
last_recorded_current_time_(0),
session_id_(session_id),
event_listener_(event_listener),
license_keys_(new LicenseKeys),
@@ -81,7 +89,8 @@ void PolicyEngine::CheckDeviceHdcpStatus() {
}
void PolicyEngine::OnTimerEvent() {
int64_t current_time = clock_->GetCurrentTime();
last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds;
int64_t current_time = GetCurrentTime();
// If we have passed the grace period, the expiration will update.
if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) {
@@ -202,7 +211,7 @@ void PolicyEngine::UpdateLicense(const License& license) {
license_start_time_ = license.license_start_time();
next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds();
int64_t current_time = clock_->GetCurrentTime();
int64_t current_time = GetCurrentTime();
if (!policy_.can_play() ||
HasLicenseOrPlaybackDurationExpired(current_time)) {
license_state_ = kLicenseStateExpired;
@@ -227,7 +236,7 @@ void PolicyEngine::BeginDecryption() {
case kLicenseStateCanPlay:
case kLicenseStateNeedRenewal:
case kLicenseStateWaitingLicenseUpdate:
playback_start_time_ = clock_->GetCurrentTime();
playback_start_time_ = GetCurrentTime();
last_playback_time_ = playback_start_time_;
if (policy_.play_start_grace_period_seconds() == 0)
grace_period_end_time_ = playback_start_time_;
@@ -247,7 +256,7 @@ void PolicyEngine::BeginDecryption() {
}
void PolicyEngine::DecryptionEvent() {
last_playback_time_ = clock_->GetCurrentTime();
last_playback_time_ = GetCurrentTime();
}
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
@@ -261,7 +270,7 @@ void PolicyEngine::NotifySessionExpiration() {
CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
std::stringstream ss;
int64_t current_time = clock_->GetCurrentTime();
int64_t current_time = GetCurrentTime();
if (license_state_ == kLicenseStateInitial) {
query_response->clear();
@@ -302,7 +311,7 @@ CdmResponseType PolicyEngine::QueryKeyAllowedUsage(
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
if (playback_start_time_ == 0) return false;
*seconds_since_started = clock_->GetCurrentTime() - playback_start_time_;
*seconds_since_started = GetCurrentTime() - playback_start_time_;
return (*seconds_since_started >= 0) ? true : false;
}
@@ -310,13 +319,15 @@ bool PolicyEngine::GetSecondsSinceLastPlayed(
int64_t* seconds_since_last_played) {
if (last_playback_time_ == 0) return false;
*seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_;
*seconds_since_last_played = GetCurrentTime() - last_playback_time_;
return (*seconds_since_last_played >= 0) ? true : false;
}
int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() {
const int64_t current_time = clock_->GetCurrentTime();
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
const int64_t current_time = GetCurrentTime();
const int64_t expiry_time =
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ false);
if (expiry_time == NEVER_EXPIRES) return LLONG_MAX;
if (expiry_time < current_time) return 0;
return expiry_time - current_time;
@@ -337,8 +348,10 @@ void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
playback_start_time_ = grace_period_end_time;
}
const int64_t current_time = clock_->GetCurrentTime();
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ true);
const int64_t current_time = GetCurrentTime();
const int64_t expiry_time =
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ true);
was_expired_on_load_ =
expiry_time != NEVER_EXPIRES && expiry_time < current_time;
@@ -351,7 +364,9 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
}
bool PolicyEngine::HasLicenseOrPlaybackDurationExpired(int64_t current_time) {
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
const int64_t expiry_time =
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ false);
return expiry_time != NEVER_EXPIRES && expiry_time <= current_time;
}
@@ -374,13 +389,15 @@ int64_t PolicyEngine::GetRentalExpiryTime() {
return std::min(hard_limit, expiry_time);
}
int64_t PolicyEngine::GetExpiryTime(int64_t current_time, bool is_load) {
int64_t PolicyEngine::GetExpiryTime(
int64_t current_time,
bool ignore_soft_enforce_playback_duration) {
if (!HasPlaybackStarted(current_time))
return GetRentalExpiryTime();
const int64_t hard_limit = GetHardLicenseExpiryTime();
if (policy_.playback_duration_seconds() == 0) return hard_limit;
if (!is_load && !was_expired_on_load_ &&
if (!ignore_soft_enforce_playback_duration && !was_expired_on_load_ &&
policy_.soft_enforce_playback_duration()) {
return hard_limit;
}
@@ -401,8 +418,10 @@ int64_t PolicyEngine::GetLicenseOrRentalDurationRemaining(
const int64_t license_expiry_time = GetRentalExpiryTime();
if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX;
if (license_expiry_time < current_time) return 0;
return std::min(license_expiry_time - current_time,
policy_.license_duration_seconds());
const int64_t policy_license_duration = policy_.license_duration_seconds();
if (policy_license_duration == NEVER_EXPIRES)
return license_expiry_time - current_time;
return std::min(license_expiry_time - current_time, policy_license_duration);
}
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
@@ -414,8 +433,9 @@ int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
const int64_t playback_expiry_time = playback_duration + playback_start_time_;
if (playback_expiry_time < current_time) return 0;
const int64_t policy_playback_duration = policy_.playback_duration_seconds();
return std::min(playback_expiry_time - current_time,
policy_.playback_duration_seconds());
policy_playback_duration);
}
bool PolicyEngine::HasRenewalDelayExpired(int64_t current_time) {
@@ -457,7 +477,9 @@ void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
}
void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) {
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
const int64_t expiry_time =
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ false);
if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) {
last_expiry_time_ = expiry_time;
if (event_listener_)
@@ -466,6 +488,15 @@ void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) {
last_expiry_time_set_ = true;
}
int64_t PolicyEngine::GetCurrentTime() {
int64_t current_time = clock_->GetCurrentTime();
if (current_time + kClockSkewDelta < last_recorded_current_time_)
current_time = last_recorded_current_time_;
else
last_recorded_current_time_ = current_time;
return current_time;
}
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
} // namespace wvcdm

View File

@@ -56,8 +56,6 @@ SecKeyRef ImportPublicKey(const std::string& key) {
kSecAttrKeyTypeRSA);
CFDictionarySetValue(deleteAttributes.get(), kSecAttrApplicationTag,
peerData.get());
CFDictionarySetValue(deleteAttributes.get(), kSecAttrAccessible,
kSecAttrAccessibleAfterFirstUnlock);
SecItemDelete(deleteAttributes.get());
// Create attributes to add to the keystore.
@@ -72,10 +70,10 @@ SecKeyRef ImportPublicKey(const std::string& key) {
keyData.get());
CFDictionarySetValue(addAttributes.get(), kSecAttrKeyClass,
kSecAttrKeyClassPublic);
CFDictionarySetValue(addAttributes.get(), kSecAttrAccessible,
kSecAttrAccessibleAfterFirstUnlock);
CFDictionarySetValue(addAttributes.get(), kSecReturnPersistentRef,
kCFBooleanTrue);
CFDictionarySetValue(addAttributes.get(), kSecAttrAccessible,
kSecAttrAccessibleAfterFirstUnlock);
// Add the key to the keystore.
CFTypeRef temp = NULL;
@@ -96,9 +94,9 @@ SecKeyRef ImportPublicKey(const std::string& key) {
kSecAttrKeyTypeRSA);
CFDictionarySetValue(queryAttributes.get(), kSecAttrKeyClass,
kSecAttrKeyClassPublic);
CFDictionarySetValue(queryAttributes.get(), kSecReturnRef, kCFBooleanTrue);
CFDictionarySetValue(queryAttributes.get(), kSecAttrAccessible,
kSecAttrAccessibleAfterFirstUnlock);
CFDictionarySetValue(queryAttributes.get(), kSecReturnRef, kCFBooleanTrue);
// Query the keychain to get the public key ref.
CFTypeRef keyRef = NULL;

View File

@@ -29,14 +29,15 @@ RSA* GetKey(const std::string& serialized_key) {
return NULL;
}
RSA* key = d2i_RSAPublicKey_bio(bio, NULL);
BIO_free(bio);
if (key == NULL) {
LOGE("GetKey: RSA key deserialization failure: %s",
ERR_error_string(ERR_get_error(), NULL));
BIO_free(bio);
return NULL;
}
BIO_free(bio);
return key;
}
@@ -87,34 +88,60 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
return false;
}
EVP_CIPHER_CTX ctx;
if (EVP_EncryptInit(&ctx, EVP_aes_128_cbc(),
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX ctx_struct;
EVP_CIPHER_CTX* evp_cipher_ctx = &ctx_struct;
#else
EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new();
#endif
if (EVP_EncryptInit(evp_cipher_ctx, EVP_aes_128_cbc(),
reinterpret_cast<uint8_t*>(&key_[0]),
reinterpret_cast<uint8_t*>(&(*iv)[0])) == 0) {
LOGE("AesCbcKey::Encrypt: AES CBC setup failure: %s",
ERR_error_string(ERR_get_error(), NULL));
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
return false;
}
out->resize(in.size() + AES_BLOCK_SIZE);
int out_length = out->size();
if (EVP_EncryptUpdate(
&ctx, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
evp_cipher_ctx, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
reinterpret_cast<uint8_t*>(const_cast<char*>(in.data())),
in.size()) == 0) {
LOGE("AesCbcKey::Encrypt: encryption failure: %s",
ERR_error_string(ERR_get_error(), NULL));
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
return false;
}
int padding = 0;
if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
if (EVP_EncryptFinal_ex(evp_cipher_ctx,
reinterpret_cast<uint8_t*>(&(*out)[out_length]),
&padding) == 0) {
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
ERR_error_string(ERR_get_error(), NULL));
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
return false;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_cleanup(evp_cipher_ctx);
#else
EVP_CIPHER_CTX_free(evp_cipher_ctx);
#endif
out->resize(out_length + padding);
return true;
}
@@ -177,6 +204,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
return false;
}
FreeKey(key);
return true;
}
@@ -189,11 +217,16 @@ static int LogOpenSSLError(const char* msg, size_t /* len */, void* /* ctx */) {
static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
const std::string &signature) {
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_MD_CTX ctx_struct;
EVP_MD_CTX* evp_md_ctx = &ctx_struct;
EVP_MD_CTX_init(evp_md_ctx);
#else
EVP_MD_CTX* evp_md_ctx = EVP_MD_CTX_new();
#endif
EVP_PKEY_CTX *pctx = NULL;
if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */,
if (EVP_DigestVerifyInit(evp_md_ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */,
pkey) != 1) {
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
goto err;
@@ -215,13 +248,13 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
goto err;
}
if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) {
if (EVP_DigestVerifyUpdate(evp_md_ctx, message.data(), message.size()) != 1) {
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
goto err;
}
if (EVP_DigestVerifyFinal(
&ctx, const_cast<uint8_t *>(
evp_md_ctx, const_cast<uint8_t *>(
reinterpret_cast<const uint8_t *>(signature.data())),
signature.size()) != 1) {
LOGE(
@@ -230,12 +263,20 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
goto err;
}
EVP_MD_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_MD_CTX_cleanup(evp_md_ctx);
#else
EVP_MD_CTX_free(evp_md_ctx);
#endif
return true;
err:
ERR_print_errors_cb(LogOpenSSLError, NULL);
EVP_MD_CTX_cleanup(&ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_MD_CTX_cleanup(evp_md_ctx);
#else
EVP_MD_CTX_free(evp_md_ctx);
#endif
return false;
}
@@ -255,10 +296,15 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
return false;
}
EVP_PKEY *pkey = EVP_PKEY_new();
if (pkey == NULL ||
EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
if (pkey == NULL) {
LOGE("RsaPublicKey::VerifySignature: EVP_PKEY allocation failed");
FreeKey(rsa_key);
return false;
}
if (EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY");
FreeKey(rsa_key);
EVP_PKEY_free(pkey);
return false;
}
FreeKey(rsa_key);

View File

@@ -12,8 +12,8 @@ namespace wvcdm {
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::allow_service_certificate_requests_;
bool Properties::security_level_path_backward_compatibility_support_;
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;

View File

@@ -4,6 +4,7 @@
#include "crypto_key.h"
#include "crypto_session.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "privacy_crypto.h"
#include "properties.h"
@@ -123,6 +124,7 @@ namespace wvcdm {
using video_widevine::ClientIdentification;
using video_widevine::DrmDeviceCertificate;
using video_widevine::EncryptedClientIdentification;
using video_widevine::LicenseError;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::SignedMessage;
@@ -189,7 +191,7 @@ CdmResponseType ServiceCertificate::Init(const std::string& certificate) {
CdmResponseType ServiceCertificate::VerifySignedMessage(
const std::string& message, const std::string& signature) {
if (!public_key_) {
if (public_key_.get() == NULL) {
LOGE("Service certificate not set.");
return DEVICE_CERTIFICATE_ERROR_4;
}
@@ -200,14 +202,22 @@ CdmResponseType ServiceCertificate::VerifySignedMessage(
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
if (!public_key_) {
CdmResponseType ServiceCertificate::EncryptRsaOaep(const std::string& plaintext,
std::string* ciphertext) {
if (public_key_.get() == NULL) {
LOGE("Service certificate not set.");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (!public_key_->Encrypt(plaintext, ciphertext))
return CLIENT_ID_RSA_ENCRYPT_ERROR;
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
encrypted_client_id->set_provider_id(provider_id_);
encrypted_client_id->set_service_certificate_serial_number(serial_number_);
@@ -227,8 +237,9 @@ 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;
if (!public_key_->Encrypt(key, &enc_key))
return CLIENT_ID_RSA_ENCRYPT_ERROR;
CdmResponseType encrypt_result = EncryptRsaOaep(key, &enc_key);
if (encrypt_result != NO_ERROR)
return encrypt_result;
encrypted_client_id->set_encrypted_client_id_iv(iv);
encrypted_client_id->set_encrypted_privacy_key(enc_key);
@@ -236,4 +247,55 @@ CdmResponseType ServiceCertificate::EncryptClientId(
return NO_ERROR;
}
bool ServiceCertificate::GetRequest(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 ServiceCertificate::ParseResponse(
const std::string& response, std::string* certificate) {
if (response.empty()) {
LOGE("ServiceCertificate::ParseResponse: empty response");
return EMPTY_RESPONSE_ERROR_1;
}
if (!certificate) {
LOGE("ServiceCertificate::ParseResponse: null return parameter");
return INVALID_PARAMETERS_ENG_24;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(response)) {
LOGE("ServiceCertificate::ParseResponse: cannot parse response");
return PARSE_RESPONSE_ERROR_1;
}
if (signed_response.type() == SignedMessage::ERROR_RESPONSE) {
LicenseError license_error;
if (!license_error.ParseFromString(signed_response.msg())) {
LOGE("ServiceCertificate::ParseResponse: cannot parse license error");
return PARSE_RESPONSE_ERROR_2;
}
LOGE("ServiceCertificate::ParseResponse: server returned error = %d",
license_error.error_code());
return PARSE_RESPONSE_ERROR_3;
}
if (signed_response.type() != SignedMessage::SERVICE_CERTIFICATE) {
LOGE("ServiceCertificate::ParseResponse: response (%d) is wrong type",
signed_response.type());
return PARSE_RESPONSE_ERROR_4;
}
certificate->assign(signed_response.msg());
return NO_ERROR;
}
} // namespace wvcdm

View File

@@ -0,0 +1,631 @@
// Copyright 2017 Google Inc. All Rights Reserved.
#include "usage_table_header.h"
#include "crypto_session.h"
#include "license.h"
#include "log.h"
#include "wv_cdm_constants.h"
namespace {
std::string kEmptyString;
uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0;
uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0;
uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0;
std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0);
std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
std::string kOldUsageEntryPoviderSessionToken =
"nahZ6achSheiqua3TohQuei0ahwohv";
}
namespace wvcdm {
UsageTableHeader::UsageTableHeader()
: security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_inited_(false) {
file_handle_.reset(new DeviceFiles(file_system_.get()));
}
bool UsageTableHeader::Init(CdmSecurityLevel security_level,
CryptoSession* crypto_session) {
LOGV("UsageTableHeader::Init: security level: %d", security_level);
if (crypto_session == NULL) {
LOGE("UsageTableHeader::Init: no crypto session provided");
return false;
}
switch (security_level) {
case kSecurityLevelL1:
case kSecurityLevelL3:
break;
default:
LOGE("UsageTableHeader::Init: invalid security level provided: %d",
security_level);
return false;
}
security_level_ = security_level;
requested_security_level_ =
security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault;
if (!file_handle_->Init(security_level)) {
LOGE("UsageTableHeader::Init: device files initialization failed");
return false;
}
CdmResponseType status = USAGE_INFO_NOT_FOUND;
if (file_handle_->RetrieveUsageTableInfo(&usage_table_header_,
&usage_entry_info_)) {
status = crypto_session->LoadUsageTableHeader(usage_table_header_);
if (status != NO_ERROR) {
LOGE(
"UsageTableHeader::Init: load usage table failed, security level: %d",
security_level);
file_handle_->DeleteAllLicenses();
usage_entry_info_.clear();
usage_table_header_.clear();
status = crypto_session->CreateUsageTableHeader(&usage_table_header_);
if (status != NO_ERROR) return false;
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
}
} else {
status = crypto_session->CreateUsageTableHeader(&usage_table_header_);
if (status != NO_ERROR) return false;
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
metrics::CryptoMetrics alternate_metrics;
metrics::CryptoMetrics* metrics =
crypto_session->GetCryptoMetrics() != NULL ?
crypto_session->GetCryptoMetrics() : &alternate_metrics;
UpgradeFromUsageTable(file_handle_.get(), metrics);
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
}
is_inited_ = true;
return true;
}
CdmResponseType UsageTableHeader::AddEntry(
CryptoSession* crypto_session, bool persistent_license,
const CdmKeySetId& key_set_id, const std::string& usage_info_file_name,
uint32_t* usage_entry_number) {
LOGV("UsageTableHeader::AddEntry: Lock");
AutoLock auto_lock(usage_table_header_lock_);
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
if (status != NO_ERROR) return status;
if (*usage_entry_number < usage_entry_info_.size()) {
LOGE("UsageTableHeader::AddEntry: new entry %d smaller than table size: %d",
*usage_entry_number, usage_entry_info_.size());
return USAGE_INVALID_NEW_ENTRY;
}
if (*usage_entry_number > usage_entry_info_.size()) {
LOGW("UsageTableHeader::AddEntry: new entry %d larger than table size: %d",
*usage_entry_number, usage_entry_info_.size());
size_t number_of_entries = usage_entry_info_.size();
usage_entry_info_.resize(*usage_entry_number + 1);
for (size_t i = number_of_entries; i < usage_entry_info_.size() - 1; ++i) {
usage_entry_info_[i].storage_type = kStorageTypeUnknown;
usage_entry_info_[i].key_set_id.clear();
usage_entry_info_[i].usage_info_file_name.clear();
}
} else /* *usage_entry_number == usage_entry_info_.size() */ {
usage_entry_info_.resize(*usage_entry_number + 1);
}
usage_entry_info_[*usage_entry_number].storage_type =
persistent_license ? kStorageLicense : kStorageUsageInfo;
usage_entry_info_[*usage_entry_number].key_set_id = key_set_id;
if (!persistent_license)
usage_entry_info_[*usage_entry_number].usage_info_file_name =
usage_info_file_name;
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
return NO_ERROR;
}
CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number) {
LOGV("UsageTableHeader::LoadEntry: Lock");
AutoLock auto_lock(usage_table_header_lock_);
if (usage_entry_number >= usage_entry_info_.size()) {
LOGE(
"UsageTableHeader::LoadEntry: usage entry number %d larger than table "
"size: %d",
usage_entry_number, usage_entry_info_.size());
return USAGE_INVALID_LOAD_ENTRY;
}
return crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
}
CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session,
CdmUsageEntry* usage_entry) {
LOGV("UsageTableHeader::UpdateEntryL: Lock");
AutoLock auto_lock(usage_table_header_lock_);
CdmResponseType status =
crypto_session->UpdateUsageEntry(&usage_table_header_, usage_entry);
if (status != NO_ERROR) return status;
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
return NO_ERROR;
}
CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number,
DeviceFiles* handle,
metrics::CryptoMetrics* metrics) {
LOGV("UsageTableHeader::DeleteEntry: Lock");
AutoLock auto_lock(usage_table_header_lock_);
if (usage_entry_number >= usage_entry_info_.size()) {
LOGE("UsageTableHeader::DeleteEntry: usage entry number %d larger than "
"usage entry size %d", usage_entry_number, usage_entry_info_.size());
return USAGE_INVALID_PARAMETERS_1;
}
// Find the last valid entry number, in order to swap
size_t swap_entry_number = usage_entry_info_.size() - 1;
CdmUsageEntry swap_usage_entry;
bool swap_usage_entry_valid = false;
while (!swap_usage_entry_valid && swap_entry_number > usage_entry_number) {
switch (usage_entry_info_[swap_entry_number].storage_type) {
case kStorageLicense:
case kStorageUsageInfo: {
CdmResponseType status =
GetEntry(swap_entry_number, handle, &swap_usage_entry);
if (status == NO_ERROR) swap_usage_entry_valid = true;
break;
}
case kStorageTypeUnknown:
default:
break;
}
if (!swap_usage_entry_valid) --swap_entry_number;
}
uint32_t number_of_entries_to_be_deleted =
usage_entry_info_.size() - usage_entry_number;
if (swap_usage_entry_valid) {
CdmResponseType status = MoveEntry(swap_entry_number, swap_usage_entry,
usage_entry_number, handle, metrics);
// If unable to move entry, unset storage type of entry to be deleted and
// resize |usage_entry_info_| so that swap usage entry is the last entry.
if (status != NO_ERROR) {
usage_entry_info_[usage_entry_number].storage_type = kStorageTypeUnknown;
usage_entry_info_[usage_entry_number].key_set_id.clear();
if (usage_entry_info_.size() - 1 == swap_entry_number) {
file_handle_->StoreUsageTableInfo(usage_table_header_,
usage_entry_info_);
} else {
Shrink(metrics, usage_entry_info_.size() - swap_entry_number - 1);
}
return NO_ERROR;
}
number_of_entries_to_be_deleted =
usage_entry_info_.size() - swap_entry_number;
}
return Shrink(metrics, number_of_entries_to_be_deleted);
}
CdmResponseType UsageTableHeader::MoveEntry(
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
uint32_t to_usage_entry_number, DeviceFiles* handle,
metrics::CryptoMetrics* metrics) {
LOGV("UsageTableHeader::MoveEntry");
// crypto_session points to an object whose scope is this method or a test
// object whose scope is the lifetime of this class
scoped_ptr<CryptoSession> scoped_crypto_session;
CryptoSession* crypto_session = test_crypto_session_.get();
if (crypto_session == NULL) {
scoped_crypto_session.reset((new CryptoSession(metrics)));
crypto_session = scoped_crypto_session.get();
}
crypto_session->Open(requested_security_level_);
CdmResponseType status =
crypto_session->LoadUsageEntry(from_usage_entry_number, from_usage_entry);
if (status != NO_ERROR) {
LOGE("UsageTableHeader::MoveEntry: Failed to load usage entry: %d",
from_usage_entry_number);
return status;
}
status = crypto_session->MoveUsageEntry(to_usage_entry_number);
if (status != NO_ERROR) {
LOGE("UsageTableHeader::MoveEntry: Failed to move usage entry: %d->%d",
from_usage_entry_number, to_usage_entry_number);
return status;
}
usage_entry_info_[to_usage_entry_number] =
usage_entry_info_[from_usage_entry_number];
CdmUsageEntry usage_entry;
status = crypto_session->UpdateUsageEntry(&usage_table_header_, &usage_entry);
if (status != NO_ERROR) {
LOGE("UsageTableHeader::MoveEntry: Failed to update usage entry: %d",
to_usage_entry_number);
return status;
}
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
StoreEntry(to_usage_entry_number, handle, usage_entry);
return NO_ERROR;
}
CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number,
DeviceFiles* handle,
CdmUsageEntry* usage_entry) {
uint32_t entry_number;
switch (usage_entry_info_[usage_entry_number].storage_type) {
case kStorageLicense: {
DeviceFiles::LicenseState license_state;
std::string init_data, key_request, key_response, key_renewal_request;
std::string key_renewal_response, release_server_url;
int64_t playback_start_time, last_playback_time, grace_period_end_time;
CdmAppParameterMap app_parameters;
if (!handle->RetrieveLicense(
usage_entry_info_[usage_entry_number].key_set_id, &license_state,
&init_data, &key_request, &key_response, &key_renewal_request,
&key_renewal_response, &release_server_url, &playback_start_time,
&last_playback_time, &grace_period_end_time, &app_parameters,
usage_entry, &entry_number)) {
LOGE("UsageTableHeader::GetEntry: Failed to retrieve license");
return USAGE_GET_ENTRY_RETRIEVE_LICENSE_FAILED;
}
break;
}
case kStorageUsageInfo: {
std::string provider_session_token;
CdmKeyMessage license_request;
CdmKeyResponse license_response;
if (!handle->RetrieveUsageInfoByKeySetId(
usage_entry_info_[usage_entry_number].usage_info_file_name,
usage_entry_info_[usage_entry_number].key_set_id,
&provider_session_token, &license_request, &license_response,
usage_entry, &entry_number)) {
LOGE(
"UsageTableHeader::GetEntry: Failed to retrieve usage information");
return USAGE_GET_ENTRY_RETRIEVE_USAGE_INFO_FAILED;
}
break;
}
case kStorageTypeUnknown:
default:
LOGE(
"UsageTableHeader::GetEntry: Attempting to retrieve usage "
"information from unknown storage type: %d",
usage_entry_info_[usage_entry_number].storage_type);
return USAGE_GET_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE;
}
if (usage_entry_number != entry_number) {
LOGE("UsageTableHeader::GetEntry: entry number mismatch: (%d, %d)",
usage_entry_number, entry_number);
return USAGE_ENTRY_NUMBER_MISMATCH;
}
return NO_ERROR;
}
CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
DeviceFiles* handle,
const CdmUsageEntry& usage_entry) {
uint32_t entry_number;
switch (usage_entry_info_[usage_entry_number].storage_type) {
case kStorageLicense: {
DeviceFiles::LicenseState license_state;
std::string init_data, key_request, key_response, key_renewal_request;
std::string key_renewal_response, release_server_url;
int64_t playback_start_time, last_playback_time, grace_period_end_time;
CdmAppParameterMap app_parameters;
CdmUsageEntry entry;
if (!handle->RetrieveLicense(
usage_entry_info_[usage_entry_number].key_set_id, &license_state,
&init_data, &key_request, &key_response, &key_renewal_request,
&key_renewal_response, &release_server_url, &playback_start_time,
&last_playback_time, &grace_period_end_time, &app_parameters,
&entry, &entry_number)) {
LOGE("UsageTableHeader::StoreEntry: Failed to retrieve license");
return USAGE_STORE_ENTRY_RETRIEVE_LICENSE_FAILED;
}
if (!handle->StoreLicense(
usage_entry_info_[usage_entry_number].key_set_id, license_state,
init_data, key_request, key_response, key_renewal_request,
key_renewal_response, release_server_url, playback_start_time,
last_playback_time, grace_period_end_time, app_parameters,
usage_entry, usage_entry_number)) {
LOGE("UsageTableHeader::StoreEntry: Failed to store license");
return USAGE_STORE_LICENSE_FAILED;
}
break;
}
case kStorageUsageInfo: {
CdmUsageEntry entry;
std::string provider_session_token, init_data, key_request, key_response,
key_renewal_request;
if (!handle->RetrieveUsageInfoByKeySetId(
usage_entry_info_[usage_entry_number].usage_info_file_name,
usage_entry_info_[usage_entry_number].key_set_id,
&provider_session_token, &key_request, &key_response, &entry,
&entry_number)) {
LOGE(
"UsageTableHeader::StoreEntry: Failed to retrieve usage "
"information");
return USAGE_STORE_ENTRY_RETRIEVE_USAGE_INFO_FAILED;
}
handle->DeleteUsageInfo(
usage_entry_info_[usage_entry_number].usage_info_file_name,
provider_session_token);
if (!handle->StoreUsageInfo(
provider_session_token, key_request, key_response,
usage_entry_info_[usage_entry_number].usage_info_file_name,
usage_entry_info_[usage_entry_number].key_set_id, usage_entry,
usage_entry_number)) {
LOGE("UsageTableHeader::StoreEntry: Failed to store usage information");
return USAGE_STORE_USAGE_INFO_FAILED;
}
break;
}
case kStorageTypeUnknown:
default:
LOGE(
"UsageTableHeader::GetUsageEntry: Attempting to retrieve usage "
"information from unknown storage type: %d",
usage_entry_info_[usage_entry_number].storage_type);
return USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE;
}
return NO_ERROR;
}
CdmResponseType UsageTableHeader::Shrink(
metrics::CryptoMetrics* metrics,
uint32_t number_of_usage_entries_to_delete) {
if (usage_entry_info_.empty()) {
LOGE("UsageTableHeader::Shrink: usage entry info table unexpectedly empty");
return NO_USAGE_ENTRIES;
}
if (usage_entry_info_.size() < number_of_usage_entries_to_delete) {
LOGW(
"UsageTableHeader::Shrink: cannot delete %d entries when usage entry "
"table size is %d", number_of_usage_entries_to_delete,
usage_entry_info_.size());
return NO_ERROR;
}
if (number_of_usage_entries_to_delete == 0) return NO_ERROR;
usage_entry_info_.resize(usage_entry_info_.size() -
number_of_usage_entries_to_delete);
// crypto_session points to an object whose scope is this method or a test
// object whose scope is the lifetime of this class
scoped_ptr<CryptoSession> scoped_crypto_session;
CryptoSession* crypto_session = test_crypto_session_.get();
if (crypto_session == NULL) {
scoped_crypto_session.reset((new CryptoSession(metrics)));
crypto_session = scoped_crypto_session.get();
}
CdmResponseType status = crypto_session->Open(requested_security_level_);
if (status != NO_ERROR) return status;
status = crypto_session->ShrinkUsageTableHeader(usage_entry_info_.size(),
&usage_table_header_);
if (status != NO_ERROR) return status;
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
return NO_ERROR;
}
CdmResponseType UsageTableHeader::UpgradeFromUsageTable(
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
UpgradeLicensesFromUsageTable(handle, metrics);
UpgradeUsageInfoFromUsageTable(handle, metrics);
return NO_ERROR;
}
bool UsageTableHeader::UpgradeLicensesFromUsageTable(
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
// Fetch the key set IDs for each offline license. For each license
// * retrieve the provider session token,
// * create a new usage entry
// * copy over the entry from the usage table
// * update the usage header table and entry numbers
// * save the usage table header and store the usage entry number and
// usage entry along with the license to persistent memory
std::vector<std::string> key_set_ids;
if (!handle->ListLicenses(&key_set_ids)) {
LOGW(
"UpgradeUsageTableHeader::UpgradeLicensesFromUsageTable: unable to "
"retrieve list of licenses");
return false;
}
for (size_t i = 0; i < key_set_ids.size(); ++i) {
DeviceFiles::LicenseState license_state;
std::string init_data, key_request, key_response, key_renewal_request;
std::string key_renewal_response, release_server_url;
int64_t playback_start_time, last_playback_time, grace_period_end_time;
CdmAppParameterMap app_parameters;
CdmUsageEntry usage_entry;
uint32_t usage_entry_number;
if (!handle->RetrieveLicense(
key_set_ids[i], &license_state, &init_data, &key_request,
&key_response, &key_renewal_request, &key_renewal_response,
&release_server_url, &playback_start_time, &last_playback_time,
&grace_period_end_time, &app_parameters, &usage_entry,
&usage_entry_number)) {
LOGW(
"UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to "
"retrieve license");
continue;
}
std::string provider_session_token;
if (!CdmLicense::ExtractProviderSessionToken(key_response,
&provider_session_token)) {
LOGW(
"UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to "
"retrieve provider session token");
continue;
}
if (provider_session_token.empty()) continue;
CryptoSession crypto_session(metrics);
CdmResponseType status = crypto_session.Open(requested_security_level_);
if (status != NO_ERROR) continue;
// TODO(fredgc): remove when b/65730828 is addressed
if (!CreateDummyOldUsageEntry(&crypto_session)) continue;
status = AddEntry(&crypto_session, true /* persistent license */,
key_set_ids[i], kEmptyString, &usage_entry_number);
if (status != NO_ERROR) continue;
status = crypto_session.CopyOldUsageEntry(provider_session_token);
if (status != NO_ERROR) {
crypto_session.Close();
Shrink(metrics, 1);
continue;
}
status = UpdateEntry(&crypto_session, &usage_entry);
if (status != NO_ERROR) {
crypto_session.Close();
Shrink(metrics, 1);
continue;
}
if (!handle->StoreLicense(
key_set_ids[i], license_state, init_data, key_request, key_response,
key_renewal_request, key_renewal_response, release_server_url,
playback_start_time, last_playback_time, grace_period_end_time,
app_parameters, usage_entry, usage_entry_number)) {
LOGE(
"UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to store "
"license");
continue;
}
}
return NO_ERROR;
}
bool UsageTableHeader::UpgradeUsageInfoFromUsageTable(
DeviceFiles* handle, metrics::CryptoMetrics* metrics) {
// Fetch all usage files. For each file retrieve all the usage info records
// within the file. For each piece of usage information
// * create a new usage entry
// * copy over the entry from the usage table and
// * update the usage header table and entry numbers
// * save the usage table header
// * once done processing all the usage records from a file, save the usage
// information to persistent memory along with usage entry number and usage
// entry.
std::vector<std::string> usage_info_file_names;
if (!handle->ListUsageInfoFiles(&usage_info_file_names)) {
LOGW(
"UpgradeUsageTableHeader::UpgradeUsageInfoFromUsageTable: Unable to "
"retrieve list of usage info file names");
return false;
}
for (size_t i = 0; i < usage_info_file_names.size(); ++i) {
std::vector<DeviceFiles::CdmUsageData> usage_data;
if (!handle->RetrieveUsageInfo(usage_info_file_names[i], &usage_data)) {
LOGW(
"UsageTableHeader::UpgradeUsageInfoFromUsageTable: Failed to "
"retrieve usage records from %s",
usage_info_file_names[i].c_str());
continue;
}
for (size_t j = 0; j < usage_data.size(); ++j) {
if (usage_data[j].provider_session_token.empty()) {
LOGW(
"UsageTableHeader::UpgradeUsageInfoFromUsageTable: Provider "
"session id empty");
continue;
}
CryptoSession crypto_session(metrics);
CdmResponseType status = crypto_session.Open(requested_security_level_);
if (status != NO_ERROR) continue;
// TODO(fredgc): remove when b/65730828 is addressed
if (!CreateDummyOldUsageEntry(&crypto_session)) continue;
// TODO(rfrias): We need to fill in the app id, but it is hashed
// and we have no way to extract. Use the hased filename instead?
status = AddEntry(&crypto_session, false /* usage info */,
usage_data[j].key_set_id, usage_info_file_names[i],
&(usage_data[j].usage_entry_number));
if (status != NO_ERROR) continue;
status = crypto_session.CopyOldUsageEntry(
usage_data[j].provider_session_token);
if (status != NO_ERROR) {
crypto_session.Close();
Shrink(metrics, 1);
continue;
}
status = UpdateEntry(&crypto_session, &(usage_data[j].usage_entry));
if (status != NO_ERROR) {
crypto_session.Close();
Shrink(metrics, 1);
continue;
}
}
if (!handle->StoreUsageInfo(usage_info_file_names[i], usage_data)) {
LOGE(
"UsageTableHeader::StoreUsageInfo: Failed to store usage records to "
"%s",
usage_info_file_names[i].c_str());
continue;
}
}
return NO_ERROR;
}
// TODO(fredgc): remove when b/65730828 is addressed
bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) {
return crypto_session->CreateOldUsageEntry(
kOldUsageEntryTimeSinceLicenseReceived,
kOldUsageEntryTimeSinceFirstDecrypt,
kOldUsageEntryTimeSinceLastDecrypt,
CryptoSession::kUsageDurationsInvalid,
kOldUsageEntryServerMacKey,
kOldUsageEntryClientMacKey,
kOldUsageEntryPoviderSessionToken);
}
} // namespace wvcdm