Source release v3.5.0
This commit is contained in:
@@ -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(¤t_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: "
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
102
core/src/oemcrypto_adapter_static_v13.cpp
Normal file
102
core/src/oemcrypto_adapter_static_v13.cpp
Normal 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"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
631
core/src/usage_table_header.cpp
Normal file
631
core/src/usage_table_header.cpp
Normal 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
|
||||
Reference in New Issue
Block a user