Source release v3.3.0

This commit is contained in:
Gene Morgan
2017-05-04 14:01:27 -07:00
parent baa7b133d3
commit 8082775924
678 changed files with 51264 additions and 14200 deletions

View File

@@ -3,12 +3,14 @@
#include "cdm_engine.h"
#include <assert.h>
#include <list>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include "cdm_session.h"
#include "cdm_session_map.h"
#include "clock.h"
#include "device_files.h"
#include "file_store.h"
@@ -27,6 +29,9 @@ const size_t kUsageReportsPerRequest = 1;
namespace wvcdm {
using video_widevine::SignedMessage;
using video_widevine::LicenseError;
class UsagePropertySet : public CdmClientPropertySet {
public:
UsagePropertySet() {}
@@ -70,20 +75,77 @@ CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
}
}
CdmEngine::~CdmEngine() {
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator i(sessions_.begin());
for (; i != sessions_.end(); ++i) {
delete i->second;
}
sessions_.clear();
}
CdmEngine::~CdmEngine() {}
CdmResponseType CdmEngine::SetServiceCertificate(
const std::string& certificate) {
return service_certificate_.Init(certificate);
}
bool CdmEngine::HasServiceCertificate() {
return service_certificate_.has_certificate();
}
bool CdmEngine::GetServiceCertificateRequest(CdmKeyMessage* request) {
if (!request) {
LOGE("ServiceCertificate::PrepareRequest: no request parameter provided");
return false;
}
SignedMessage message;
message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
message.SerializeToString(request);
return true;
}
CdmResponseType CdmEngine::ParseServiceCertificateResponse(
const std::string& response, std::string* certificate) {
if (response.empty()) {
LOGE("CdmEngine::ParseServiceCertificateResponse: empty response");
return EMPTY_RESPONSE_ERROR_1;
}
if (!certificate) {
LOGE("CdmEngine::ParseServiceCertificateResponse: null return parameter");
return INVALID_PARAMETERS_ENG_19;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(response)) {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: cannot parse response");
return PARSE_RESPONSE_ERROR_1;
}
if (signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) {
CdmResponseType status;
status = service_certificate_.Init(signed_response.msg());
if (status != NO_ERROR) {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: certificate handling "
"failure, status=%d", status);
return PARSE_SERVICE_CERTIFICATE_ERROR;
}
certificate->assign(signed_response.msg());
} else if (signed_response.type() == SignedMessage::ERROR_RESPONSE) {
LicenseError license_error;
if (!license_error.ParseFromString(signed_response.msg())) {
LOGE("CdmEngine::ParseServiceCertificateResponse: cannot parse "
"license error");
return PARSE_RESPONSE_ERROR_2;
}
LOGE("CdmEngine::ParseServiceCertificateResponse: server returned error:"
"error code = %d", license_error.error_code());
return PARSE_RESPONSE_ERROR_3;
} else {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: response (%d) is "
"wrong type", signed_response.type());
return PARSE_RESPONSE_ERROR_4;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::OpenSession(
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) {
@@ -114,7 +176,7 @@ CdmResponseType CdmEngine::OpenSession(
}
if (forced_session_id) {
if (sessions_.find(*forced_session_id) != sessions_.end()) {
if (session_map_.Exists(*forced_session_id)) {
return DUPLICATE_SESSION_ID_SPECIFIED;
}
}
@@ -138,8 +200,7 @@ CdmResponseType CdmEngine::OpenSession(
}
CdmSessionId id = new_session->session_id();
AutoLock lock(session_list_lock_);
sessions_[id] = new_session.release();
session_map_.Add(id, new_session.release());
if (session_id) *session_id = id;
return NO_ERROR;
}
@@ -180,15 +241,10 @@ CdmResponseType CdmEngine::OpenKeySetSession(
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::CloseSession");
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
if (!session_map_.CloseSession(session_id)) {
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
return SESSION_NOT_FOUND_1;
}
CdmSession* session = iter->second;
sessions_.erase(session_id);
delete session;
return NO_ERROR;
}
@@ -218,9 +274,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
}
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator iter = sessions_.find(session_id);
return iter != sessions_.end();
return session_map_.Exists(session_id);
}
CdmResponseType CdmEngine::GenerateKeyRequest(
@@ -258,8 +312,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
id = iter->second.first;
}
CdmSessionMap::iterator iter = sessions_.find(id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(id, session)) {
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
id.c_str());
return SESSION_NOT_FOUND_2;
@@ -273,8 +327,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
key_request->message.clear();
if (license_type == kLicenseTypeRelease &&
!iter->second->license_received()) {
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
!session->license_received()) {
sts = session->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
if (sts != KEY_ADDED) {
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
"sts = %d", static_cast<int>(sts));
@@ -282,13 +336,13 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
}
}
sts = iter->second->GenerateKeyRequest(
init_data, license_type, app_parameters, key_request);
sts = session->GenerateKeyRequest(init_data, license_type, app_parameters,
key_request);
if (KEY_MESSAGE != sts) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
session->GetRequestedSecurityLevel();
}
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
"sts = %d", static_cast<int>(sts));
@@ -331,9 +385,8 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
id = iter->second.first;
}
CdmSessionMap::iterator iter = sessions_.find(id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(id, session)) {
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
return SESSION_NOT_FOUND_3;
}
@@ -343,9 +396,9 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
return EMPTY_KEY_DATA_1;
}
CdmResponseType sts = iter->second->AddKey(key_data);
CdmResponseType sts = session->AddKey(key_data);
if (key_set_id) {
*key_set_id = iter->second->key_set_id();
*key_set_id = session->key_set_id();
}
switch (sts) {
@@ -371,18 +424,18 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
return EMPTY_KEYSET_ID_ENG_4;
}
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_4;
}
CdmResponseType sts =
iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
session->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
session->GetRequestedSecurityLevel();
}
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
@@ -393,14 +446,14 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
LOGI("CdmEngine::RemoveKeys");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::RemoveKeys: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_5;
}
iter->second->ReleaseCrypto();
session->ReleaseCrypto();
return NO_ERROR;
}
@@ -408,8 +461,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
const CdmSessionId& session_id, CdmKeyRequest* key_request) {
LOGI("CdmEngine::GenerateRenewalRequest");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_6;
@@ -422,7 +475,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
key_request->message.clear();
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
CdmResponseType sts = session->GenerateRenewalRequest(key_request);
if (KEY_MESSAGE != sts) {
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
@@ -437,8 +490,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data) {
LOGI("CdmEngine::RenewKey");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
return SESSION_NOT_FOUND_7;
}
@@ -448,7 +501,7 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
return EMPTY_KEY_DATA_2;
}
CdmResponseType sts = iter->second->RenewKey(key_data);
CdmResponseType sts = session->RenewKey(key_data);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast<int>(sts));
return sts;
@@ -473,8 +526,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
}
if (query_token == QUERY_KEY_SECURITY_LEVEL) {
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
switch (security_level) {
CdmSecurityLevel level = crypto_session.GetSecurityLevel();
switch (level) {
case kSecurityLevelL1:
*query_response = QUERY_VALUE_SECURITY_LEVEL_L1;
break;
@@ -489,8 +542,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
*query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
LOGW("CdmEngine::QueryStatus: Unknown security level: %d",
security_level);
LOGW("CdmEngine::QueryStatus: Unknown security level: %d", level);
return UNKNOWN_ERROR;
}
} else if (query_token == QUERY_KEY_DEVICE_ID) {
@@ -581,47 +633,47 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
CdmQueryMap* query_response) {
LOGI("CdmEngine::QuerySessionStatus");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QuerySessionStatus: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_8;
}
return iter->second->QueryStatus(query_response);
return session->QueryStatus(query_response);
}
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsReleaseSession");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::IsReleaseSession: session_id not found = %s",
session_id.c_str());
return false;
}
return iter->second->is_release();
return session->is_release();
}
bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsOfflineSession");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::IsOfflineSession: session_id not found = %s",
session_id.c_str());
return false;
}
return iter->second->is_offline();
return session->is_offline();
}
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryKeyStatus");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_9;
}
return iter->second->QueryKeyStatus(query_response);
return session->QueryKeyStatus(query_response);
}
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
@@ -632,13 +684,13 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
return INVALID_PARAMETERS_ENG_12;
}
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QueryKeyAllowedUsage: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_12;
}
return iter->second->QueryKeyAllowedUsage(key_id, key_usage);
return session->QueryKeyAllowedUsage(key_id, key_usage);
}
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
@@ -652,10 +704,16 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
return INVALID_PARAMETERS_ENG_7;
}
key_usage->Clear();
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
session_sts = iter->second->QueryKeyAllowedUsage(key_id,
&found_in_this_session);
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
session_sts = (*iter)->QueryKeyAllowedUsage(key_id, &found_in_this_session);
if (session_sts == NO_ERROR) {
if (found) {
// Found another key. If usage settings do not match, fail.
@@ -680,13 +738,13 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
CdmResponseType CdmEngine::QueryOemCryptoSessionId(
const CdmSessionId& session_id, CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryOemCryptoSessionId");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QueryOemCryptoSessionId: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_10;
}
return iter->second->QueryOemCryptoSessionId(query_response);
return session->QueryOemCryptoSessionId(query_response);
}
/*
@@ -835,19 +893,73 @@ CdmResponseType CdmEngine::ListStoredLicenses(
CdmSecurityLevel security_level, std::vector<std::string>* key_set_ids) {
DeviceFiles handle(file_system_);
if (!key_set_ids) {
LOGE("CdmEngine::QueryStoredLicenses: no response destination");
LOGE("CdmEngine::ListStoredLicenses: no response destination");
return INVALID_PARAMETERS_ENG_17;
}
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ListStoredLicenses: unable to initialize device files");
return STORE_LICENSE_ERROR_3;
return LIST_LICENSE_ERROR_1;
}
if (!handle.ListLicenses(key_set_ids)) {
return UNKNOWN_ERROR;
LOGE("CdmEngine::ListStoredLicenses: ListLicenses call failed");
return LIST_LICENSE_ERROR_2;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::ListUsageRecords(const std::string& app_id,
CdmSecurityLevel security_level,
std::vector<std::string>* ksids) {
DeviceFiles handle(file_system_);
if (!ksids) {
LOGE("CdmEngine::ListUsageRecords: no response destination");
return INVALID_PARAMETERS_ENG_18;
}
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ListUsageRecords: unable to initialize device files");
return LIST_USAGE_ERROR_1;
}
if (!handle.ListUsageRecords(app_id, ksids)) {
LOGE("CdmEngine::ListUsageRecords: ListUsageRecords call failed");
return LIST_USAGE_ERROR_2;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
CdmSecurityLevel security_level,
const std::string& key_set_id) {
std::string provider_session_token;
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::DeleteUsageRecord: unable to initialize device files");
return DELETE_USAGE_ERROR_1;
}
if (!handle.GetProviderToken(app_id, key_set_id, &provider_session_token)) {
LOGE("CdmEngine::DeleteUsageRecord: GetProviderToken failed");
return DELETE_USAGE_ERROR_2;
}
// Got provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
status = crypto_session->DeleteUsageInformation(provider_session_token);
}
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageRecord: OEMCrypto failure");
}
// Remove from file system.
if (!handle.DeleteUsageInfo(app_id, provider_session_token)) {
LOGE("CdmEngine::DeleteUsageRecord: file system failure");
return DELETE_USAGE_ERROR_3;
}
return status;
}
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info) {
@@ -915,6 +1027,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
usage_info->clear();
return status;
}
return KEY_MESSAGE;
}
@@ -930,7 +1043,9 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
do {
status = GetUsageInfo(app_id, security_level, usage_info);
if (KEY_MESSAGE == status && !usage_info->empty()) return status;
if (KEY_MESSAGE == status && !usage_info->empty()) {
return status;
}
} while (KEY_CANCELED == status);
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
@@ -1015,6 +1130,37 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
return status;
}
CdmResponseType CdmEngine::ReleaseAllUsageInfo(
const std::string& app_id, CdmSecurityLevel security_level) {
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: unable to initialize device files");
return RELEASE_ALL_USAGE_INFO_ERROR_3;
}
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete usage records");
return RELEASE_ALL_USAGE_INFO_ERROR_4;
}
if (provider_session_tokens.size() == 0UL) {
return NO_ERROR;
}
// Got at least one provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
status = crypto_session->
DeleteMultipleUsageInformation(provider_session_tokens);
}
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageRecord: CryptoSession failure");
}
return status;
}
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
@@ -1081,8 +1227,8 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
return EMPTY_KEYSET_ID_ENG_5;
}
CdmSessionMap::iterator iter = sessions_.find(key_set_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(key_set_id, session)) {
LOGE("CdmEngine::LoadUsageSession: session_id not found = %s ",
key_set_id.c_str());
return SESSION_NOT_FOUND_11;
@@ -1094,13 +1240,13 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
}
DeviceFiles handle(file_system_);
if (!handle.Init(iter->second->GetSecurityLevel())) {
if (!handle.Init(session->GetSecurityLevel())) {
LOGE("CdmEngine::LoadUsageSession: unable to initialize device files");
return LOAD_USAGE_INFO_FILE_ERROR;
}
std::string app_id;
iter->second->GetApplicationId(&app_id);
session->GetApplicationId(&app_id);
CdmKeyMessage key_message;
CdmKeyResponse key_response;
@@ -1110,22 +1256,22 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
return LOAD_USAGE_INFO_MISSING;
}
CdmResponseType status =
iter->second->RestoreUsageSession(key_message, key_response);
CdmResponseType status = session->RestoreUsageSession(key_message,
key_response);
if (KEY_ADDED != status) {
LOGE("CdmEngine::LoadUsageSession: usage session error %ld", status);
return status;
}
CdmKeyRequest request;
status = iter->second->GenerateReleaseRequest(&request);
status = session->GenerateReleaseRequest(&request);
*release_message = request.message;
switch (status) {
case KEY_MESSAGE:
break;
case KEY_CANCELED: // usage information not present in
iter->second->DeleteLicense(); // OEMCrypto, delete and try again
session->DeleteLicense(); // OEMCrypto, delete and try again
break;
default:
LOGE("CdmEngine::LoadUsageSession: generate release request error: %d",
@@ -1161,45 +1307,55 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
// else we must be level 1 direct and we don't need to return a buffer.
}
CdmSessionMap::iterator session_iter = sessions_.end();
std::shared_ptr<CdmSession> session;
if (session_id.empty()) {
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
// Loop through the sessions to find the session containing the key_id
// with the longest remaining license validity.
int64_t seconds_remaining = 0;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyLoaded(*parameters.key_id)) {
int64_t duration = iter->second->GetDurationRemaining();
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
if ((*iter)->IsKeyLoaded(*parameters.key_id)) {
int64_t duration = (*iter)->GetDurationRemaining();
if (duration > seconds_remaining) {
session_iter = iter;
session = *iter;
seconds_remaining = duration;
}
}
}
} else {
session_iter = sessions_.find(session_id);
session_map_.FindSession(session_id, session);
}
if (session_iter == sessions_.end()) {
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.c_str(), session_id.size());
if (session.get() == NULL) {
if (session_id.empty()) {
LOGE("CdmEngine::Decrypt: session not found: Empty session ID");
} else {
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.c_str(), session_id.size());
}
return SESSION_NOT_FOUND_FOR_DECRYPT;
}
return session_iter->second->Decrypt(parameters);
return session->Decrypt(parameters);
}
CdmResponseType CdmEngine::GenericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm, std::string* out_buffer) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericEncrypt: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_13;
}
return iter->second->GenericEncrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
return session->GenericEncrypt(in_buffer, key_id, iv, algorithm, out_buffer);
}
CdmResponseType CdmEngine::GenericDecrypt(
@@ -1207,46 +1363,51 @@ CdmResponseType CdmEngine::GenericDecrypt(
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericDecrypt: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_14;
}
return iter->second->GenericDecrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
return session->GenericDecrypt(in_buffer, key_id, iv, algorithm, out_buffer);
}
CdmResponseType CdmEngine::GenericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
std::string* signature) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericSign: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_15;
}
return iter->second->GenericSign(message, key_id, algorithm, signature);
return session->GenericSign(message, key_id, algorithm, signature);
}
CdmResponseType CdmEngine::GenericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
const std::string& signature) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericVerify: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_16;
}
return iter->second->GenericVerify(message, key_id, algorithm, signature);
return session->GenericVerify(message, key_id, algorithm, signature);
}
// TODO(gmorgan) Used? Delete if unused.
bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyLoaded(key_id)) {
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
if ((*iter)->IsKeyLoaded(key_id)) {
return true;
}
}
@@ -1260,18 +1421,23 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
return false;
}
uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id);
CdmSessionMap::iterator session_iter = sessions_.end();
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
CdmSessionList::iterator session_iter = sessions.end();
int64_t seconds_remaining = 0;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
CdmSessionId local_session_id = iter->second->session_id();
if (Properties::GetSessionSharingId(local_session_id) ==
session_sharing_id) {
if (iter->second->IsKeyLoaded(key_id)) {
int64_t duration = iter->second->GetDurationRemaining();
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
CdmSessionId id = (*iter)->session_id();
if (Properties::GetSessionSharingId(id) == session_sharing_id) {
if ((*iter)->IsKeyLoaded(key_id)) {
int64_t duration = (*iter)->GetDurationRemaining();
if (duration > seconds_remaining) {
session_iter = iter;
seconds_remaining = duration;
@@ -1280,8 +1446,8 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
}
}
if (session_iter != sessions_.end()) {
*session_id = session_iter->second->session_id();
if (session_iter != sessions.end()) {
*session_id = (*session_iter)->session_id();
return true;
}
return false;
@@ -1289,9 +1455,9 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
uint32_t height) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter != sessions_.end()) {
iter->second->NotifyResolution(width, height);
std::shared_ptr<CdmSession> session;
if (session_map_.FindSession(session_id, session)) {
session->NotifyResolution(width, height);
return true;
}
return false;
@@ -1304,8 +1470,11 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
void CdmEngine::OnTimerEvent() {
Clock clock;
uint64_t current_time = clock.GetCurrentTime();
bool usage_update_period_expired = false;
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
bool usage_update_period_expired = false;
if (current_time - last_usage_information_update_time_ >
kUpdateUsageInformationPeriod) {
usage_update_period_expired = true;
@@ -1315,27 +1484,35 @@ void CdmEngine::OnTimerEvent() {
bool is_initial_usage_update = false;
bool is_usage_update_needed = false;
AutoLock lock(session_list_lock_);
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
while (!sessions.empty()) {
is_initial_usage_update =
is_initial_usage_update || iter->second->is_initial_usage_update();
is_initial_usage_update || sessions.front()->is_initial_usage_update();
is_usage_update_needed =
is_usage_update_needed || iter->second->is_usage_update_needed();
is_usage_update_needed || sessions.front()->is_usage_update_needed();
iter->second->OnTimerEvent(usage_update_period_expired);
if (!sessions.front()->IsClosed()) {
sessions.front()->OnTimerEvent(usage_update_period_expired);
}
sessions.pop_front();
}
if (is_usage_update_needed &&
(usage_update_period_expired || is_initial_usage_update)) {
bool has_usage_been_updated = false;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->reset_usage_flags();
// Session list may have changed. Rebuild.
session_map_.GetSessionList(sessions);
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
(*iter)->reset_usage_flags();
if (!has_usage_been_updated) {
// usage is updated for all sessions so this needs to be
// called only once per update usage information period
CdmResponseType status = iter->second->UpdateUsageInformation();
CdmResponseType status = (*iter)->UpdateUsageInformation();
if (NO_ERROR != status) {
LOGW("Update usage information failed: %d", status);
} else {
@@ -1344,15 +1521,18 @@ void CdmEngine::OnTimerEvent() {
}
}
}
CloseExpiredReleaseSessions();
}
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
AutoLock lock(session_list_lock_);
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnKeyReleaseEvent(key_set_id);
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
while (!sessions.empty()) {
if (!sessions.front()->IsClosed()) {
sessions.front()->OnKeyReleaseEvent(key_set_id);
}
sessions.pop_front();
}
}
@@ -1415,12 +1595,12 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
status = crypto_session->DeleteAllUsageReports();
if (NO_ERROR != status) {
LOGW(
"CdmEngine::GetProvisioningRequest: "
"CdmEngine::DeleteAllUsageReportsUponFactoryReset: "
"Fails to delete usage reports: %d", status);
}
} else {
LOGW(
"CdmEngine::GetProvisioningRequest: "
"CdmEngine::DeleteAllUsageReportsUponFactoryReset: "
"Fails to open crypto session: error=%d.\n"
"Usage reports are not removed after factory reset.", status);
}

View File

@@ -25,6 +25,7 @@ namespace wvcdm {
CdmSession::CdmSession(FileSystem* file_system) :
initialized_(false),
closed_(false),
crypto_session_(new CryptoSession),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
@@ -101,9 +102,8 @@ CdmResponseType CdmSession::Init(
// License server client ID token is a stored certificate. Stage it or
// indicate that provisioning is needed. Get token from stored certificate
std::string wrapped_key;
uint32_t system_id;
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
&serial_number, &system_id) ||
&serial_number, nullptr) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING;
}
@@ -243,26 +243,6 @@ CdmResponseType CdmSession::GenerateKeyRequest(
case kLicenseTypeRelease:
is_release_ = true;
break;
case kLicenseTypeDeferred:
// If you're going to pass Deferred, you must have empty init data in
// this call and stored init data from the previous call.
if (!init_data.IsEmpty() || !license_parser_->HasInitData()) {
return INVALID_LICENSE_TYPE;
}
// The arguments check out.
// The is_release_ and is_offline_ flags were already set last time based
// on the original license type. Do not change them, and use them to
// re-derive the original license type.
if (is_release_) {
license_type = kLicenseTypeRelease;
} else if (is_offline_) {
license_type = kLicenseTypeOffline;
} else if (is_temporary_) {
license_type = kLicenseTypeTemporary;
} else {
license_type = kLicenseTypeStreaming;
}
break;
default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type);
@@ -276,16 +256,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
} else {
key_request->type = kKeyRequestTypeInitial;
if (!license_parser_->HasInitData()) {
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA;
}
if (init_data.IsEmpty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return INIT_DATA_NOT_FOUND;
}
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA;
}
if (init_data.IsEmpty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return INIT_DATA_NOT_FOUND;
}
if (is_offline_ && key_set_id_.empty()) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
@@ -297,16 +275,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
init_data, license_type,
app_parameters, &key_request->message,
&key_request->url);
if (KEY_MESSAGE != status) return status;
if (status != KEY_MESSAGE)
return status;
key_request_ = key_request->message;
if (is_offline_) {
offline_init_data_ = init_data.data();
offline_release_server_url_ = key_request->url;
}
return KEY_MESSAGE;
}
}
@@ -336,6 +312,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
license_received_ = true;
key_response_ = key_response;
LOGV("AddKey: provider_session_token (size=%d) =%s",
license_parser_->provider_session_token().size(),
license_parser_->provider_session_token().c_str());
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
sts = StoreLicense();
if (sts != NO_ERROR) return sts;
@@ -423,6 +403,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// is updated, so we treat this Decrypt call as invalid.
if (params.is_encrypted &&
!policy_engine_->CanDecryptContent(*params.key_id)) {
if (policy_engine_->GetKeyStatus(*params.key_id) ==
kKeyStatusOutputNotAllowed) {
return INSUFFICIENT_OUTPUT_PROTECTION;
}
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
}

View File

@@ -0,0 +1,71 @@
// Copyright 2017 Google Inc. All Rights Reserved.
#include "cdm_session_map.h"
#include <assert.h>
#include "cdm_session.h"
#include "log.h"
namespace wvcdm {
CdmSessionMap::~CdmSessionMap() {
AutoLock lock(lock_);
for (CdmIdToSessionMap::iterator i = sessions_.begin();
i != sessions_.end(); ++i) {
i->second->Close();
i->second.reset();
}
sessions_.clear();
}
void CdmSessionMap::Add(const std::string& id, CdmSession* session) {
AutoLock lock(lock_);
sessions_[id].reset(session);
}
bool CdmSessionMap::CloseSession(const std::string& id) {
AutoLock lock(lock_);
std::shared_ptr<CdmSession> session;
if (!FindSessionNoLock(id, session)) {
return false;
}
session->Close();
sessions_.erase(id);
return true;
}
bool CdmSessionMap::Exists(const std::string& id) {
AutoLock lock(lock_);
return sessions_.find(id) != sessions_.end();
}
bool CdmSessionMap::FindSession(const CdmSessionId& id,
std::shared_ptr<CdmSession>& session) {
AutoLock lock(lock_);
return FindSessionNoLock(id, session);
}
bool CdmSessionMap::FindSessionNoLock(const CdmSessionId& session_id,
std::shared_ptr<CdmSession>& session) {
CdmIdToSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
session = iter->second;
assert(session.get() != NULL);
return true;
}
typedef std::list<std::shared_ptr<CdmSession> > CdmSessionList;
void CdmSessionMap::GetSessionList(CdmSessionList& sessions) {
sessions.clear();
AutoLock lock(lock_);
for (CdmIdToSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
sessions.push_back(iter->second);
}
}
} // namespace wvcdm

View File

@@ -19,37 +19,64 @@ const std::string kProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
/*
* Provisioning response is a base64-encoded protobuf, optionally within a
* JSON wrapper. If the JSON wrapper is present, extract the embedded response
* message. Then perform the base64 decode and return the result.
*
* If an error occurs during the parse or the decode, return an empty string.
*/
void ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
std::string* result) {
const std::string json_start_substr("\"signedResponse\": \"");
const std::string json_end_substr("\"");
std::string message_string;
size_t start = provisioning_response.find(json_start_substr);
if (start == provisioning_response.npos) {
// Message is not properly wrapped - reject it.
LOGE("ExtractAndDecodeSignedMessage: cannot locate start substring");
result->clear();
return;
} else {
// Appears to be JSON-wrapped protobuf - find end of protobuf portion.
size_t end = provisioning_response.find(json_end_substr,
start + json_start_substr.length());
if (end == provisioning_response.npos) {
LOGE("ExtractAndDecodeSignedMessage: cannot locate end substring");
result->clear();
return;
}
size_t b64_string_size = end - start - json_start_substr.length();
message_string.assign(provisioning_response,
start + json_start_substr.length(), b64_string_size);
}
if (message_string.empty()) {
LOGE("ExtractAndDecodeSignedMessage: CdmProvisioningResponse is empty");
result->clear();
return;
}
// Decode the base64-encoded message.
const std::vector<uint8_t> decoded_message =
wvcdm::Base64SafeDecode(message_string);
result->assign(decoded_message.begin(), decoded_message.end());
}
}
namespace wvcdm {
// Protobuf generated classes.
using video_widevine::ClientIdentification;
using video_widevine::EncryptedClientIdentification;
using video_widevine::ProvisioningOptions;
using video_widevine::ProvisioningRequest;
using video_widevine::ProvisioningResponse;
using video_widevine::SignedProvisioningMessage;
/*
* This function converts SignedProvisioningRequest into base64 string. It then
* wraps it in JSON format expected by the frontend. This server requires a
* "web-safe" base 64 encoding, where '+' becomes '-' and '/' becomes '_'.
*
* Returns the JSON formated string in *request. The JSON string will be
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
*
* The JSON formated request takes the following format:
*
* base64 encoded message
*/
void CertificateProvisioning::ComposeJsonRequestAsQueryString(
const std::string& message, CdmProvisioningRequest* request) {
// Performs base64 encoding for message
std::vector<uint8_t> message_vector(message.begin(), message.end());
std::string message_b64 = Base64SafeEncodeNoPad(message_vector);
request->assign(message_b64);
}
/*
* Return the ClientIdentification message token type for provisioning request.
* NOTE: a DRM Cert should never be presented to the provisioning server.
@@ -88,7 +115,7 @@ bool CertificateProvisioning::SetSpoidParameter(
// Use the SPOID that has been pre-provided
request->set_spoid(spoid);
} else if (Properties::UseProviderIdInProvisioningRequest()) {
if (service_certificate_->HasProviderId()) {
if (!service_certificate_->provider_id().empty()) {
request->set_provider_id(service_certificate_->provider_id());
} else {
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
@@ -121,9 +148,9 @@ SignedProvisioningMessage::ProtocolVersion
}
/*
* Composes a device provisioning request and output the request in JSON format
* in *request. It also returns the default url for the provisioning server
* in *default_url.
* Compose a device provisioning request and output *request in a
* JSON-compatible format (web-safe base64).
* Also return *default_url of the provisioning server.
*
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
*/
@@ -162,17 +189,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
client_id->set_token(token);
client_id->set_type(token_type);
#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification.
if (service_certificate_->HasCertificate()) {
#if 0 // TODO(gmorgan) Encrypt ClientIdentification. Pending Design.
if (service_certificate_->has_certificate()) {
EncryptedClientIdentification* encrypted_client_id =
provisioning_request->mutable_encrypted_client_id();
provisioning_request.mutable_encrypted_client_id();
CdmResponseType status;
status = service_certificate_->EncryptClientId(&crypto_session_, client_id,
encrypted_client_id);
if (status == NO_ERROR) {
provisioning_request->clear_client_id();
provisioning_request.clear_client_id();
} else {
provisioning_request->clear_encrypted_client_id();
provisioning_request.clear_encrypted_client_id();
}
return status;
}
@@ -234,43 +261,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
// Converts request into JSON string
ComposeJsonRequestAsQueryString(serialized_request, request);
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
// Return request as web-safe base64 string
std::vector<uint8_t> request_vector(serialized_request.begin(),
serialized_request.end());
request->assign(Base64SafeEncodeNoPad(request_vector));
} else {
request->swap(serialized_request);
}
return NO_ERROR;
}
/*
* Parses the input json_str and locates substring using start_substr and
* end_stubstr. The found base64 substring is then decoded and returns
* in *result.
*
* Returns true for success and false if fails.
*/
bool CertificateProvisioning::ParseJsonResponse(
const CdmProvisioningResponse& json_str, const std::string& start_substr,
const std::string& end_substr, std::string* result) {
std::string b64_string;
size_t start = json_str.find(start_substr);
if (start == json_str.npos) {
LOGE("ParseJsonResponse: cannot find start substring");
return false;
}
size_t end = json_str.find(end_substr, start + start_substr.length());
if (end == json_str.npos) {
LOGE("ParseJsonResponse cannot locate end substring");
return false;
}
size_t b64_string_size = end - start - start_substr.length();
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
// Decodes base64 substring and returns it in *result
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
result->assign(result_vector.begin(), result_vector.end());
return true;
}
/*
* The response message consists of a device certificate and the device RSA key.
* The device RSA key is stored in the T.E.E. The device certificate is stored
@@ -281,13 +282,19 @@ bool CertificateProvisioning::ParseJsonResponse(
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
FileSystem* file_system, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string serialized_signed_response;
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
&serialized_signed_response)) {
LOGE("Fails to extract signed serialized response from JSON response");
std::string raw_string;
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
// The response is base64 encoded in a JSON wrapper.
// Extract it and decode it. If errors, return an empty string.
ExtractAndDecodeSignedMessage(response, &raw_string);
} else {
raw_string.assign(response);
}
if (raw_string.empty()) {
LOGE("HandleProvisioningResponse: response message is empty or "
"an invalid JSON/base64 string.");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
}
@@ -295,7 +302,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
// the provisioing request's input). Validate provisioning response and
// stores private device RSA key and certificate.
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(serialized_signed_response)) {
if (!signed_response.ParseFromString(raw_string)) {
LOGE("HandleProvisioningResponse: fails to parse signed response");
return CERT_PROVISIONING_RESPONSE_ERROR_2;
}
@@ -314,6 +321,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
const std::string& signed_message = signed_response.message();
const std::string& signature = signed_response.signature();
ProvisioningResponse provisioning_response;
if (!provisioning_response.ParseFromString(signed_message)) {
@@ -326,15 +334,29 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
return CERT_PROVISIONING_RESPONSE_ERROR_5;
}
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
// If Provisioning 3.0 (OEM Cert provisioned), verify that the
// message is properly signed.
if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert) {
if (service_certificate_->VerifySignedMessage(signed_message, signature)
!= NO_ERROR) {
LOGE("HandleProvisioningResponse: message not properly signed");
return CERT_PROVISIONING_RESPONSE_ERROR_6;
}
}
const std::string& new_private_key = provisioning_response.device_rsa_key();
const std::string& nonce = provisioning_response.nonce();
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
const std::string& signature = signed_response.signature();
std::string wrapped_rsa_key;
if (!crypto_session_.RewrapDeviceRSAKey(signed_message, signature, nonce,
enc_rsa_key, rsa_key_iv,
&wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
const std::string& iv = provisioning_response.device_rsa_key_iv();
const std::string& wrapping_key = (provisioning_response.has_wrapping_key()) ?
provisioning_response.wrapping_key() : std::string();
std::string wrapped_private_key;
if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce,
new_private_key, iv, wrapping_key,
&wrapped_private_key)) {
LOGE("HandleProvisioningResponse: RewrapCertificate fails");
return CERT_PROVISIONING_RESPONSE_ERROR_6;
}
@@ -342,7 +364,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (cert_type_ == kCertificateX509) {
*cert = provisioning_response.device_certificate();
*wrapped_key = wrapped_rsa_key;
*wrapped_key = wrapped_private_key;
return NO_ERROR;
}
@@ -357,7 +379,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
return CERT_PROVISIONING_RESPONSE_ERROR_7;
}
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
if (!handle.StoreCertificate(device_certificate, wrapped_private_key)) {
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
return CERT_PROVISIONING_RESPONSE_ERROR_8;
}

View File

@@ -54,17 +54,17 @@ CryptoSession::~CryptoSession() {
Terminate();
}
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType& token_type) {
OEMCrypto_ProvisioningMethod method;
switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) {
case OEMCrypto_OEMCertificate:
*token_type = kClientTokenOemCert;
token_type = kClientTokenOemCert;
break;
case OEMCrypto_Keybox:
*token_type = kClientTokenKeybox;
token_type = kClientTokenKeybox;
break;
case OEMCrypto_DrmCertificate:
*token_type = kClientTokenDrmCert;
token_type = kClientTokenDrmCert;
break;
case OEMCrypto_ProvisioningError:
default:
@@ -86,7 +86,7 @@ void CryptoSession::Init() {
}
initialized_ = true;
}
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
if (!GetProvisioningMethod(pre_provision_token_type_)) {
initialized_ = false;
}
}
@@ -125,6 +125,10 @@ bool CryptoSession::GetTokenFromKeybox(std::string* token) {
bool CryptoSession::GetTokenFromOemCert(std::string* token) {
OEMCryptoResult status;
if (!oem_token_.empty()) {
token->assign(oem_token_);
return true;
}
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
// lock is held by caller
bool retrying = false;
@@ -133,6 +137,8 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) {
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
if (OEMCrypto_SUCCESS == status) {
temp_buffer.resize(buf_size);
oem_token_.assign(temp_buffer);
token->swap(temp_buffer);
return true;
}
@@ -387,7 +393,8 @@ bool CryptoSession::PrepareRequest(const std::string& message,
return false;
}
if (!Properties::use_certificates_as_identification() || is_provisioning) {
if (!Properties::use_certificates_as_identification() ||
(is_provisioning && (pre_provision_token_type_ == kClientTokenKeybox))) {
if (!GenerateDerivedKeys(message)) return false;
if (!GenerateSignature(message, signature)) return false;
@@ -535,8 +542,16 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
LOGV("CryptoSession::LoadCertificatePrivateKey: Lock");
AutoLock auto_lock(crypto_lock_);
// Call OEMCrypto_GetOEMPublicCertificate before OEMCrypto_LoadDeviceRSAKey
// so it caches the OEMCrypto Public Key and then throw away result
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
size_t buf_size = temp_buffer.size();
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
OEMCryptoResult sts =
OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts = OEMCrypto_LoadDeviceRSAKey(
sts = OEMCrypto_LoadDeviceRSAKey(
oec_session_id_, reinterpret_cast<const uint8_t*>(wrapped_key.data()),
wrapped_key.size());
@@ -1089,6 +1104,34 @@ bool CryptoSession::SetDestinationBufferType() {
return true;
}
bool CryptoSession::RewrapCertificate(const std::string& signed_message,
const std::string& signature,
const std::string& nonce,
const std::string& private_key,
const std::string& iv,
const std::string& wrapping_key,
std::string* wrapped_private_key) {
LOGV("CryptoSession::RewrapCertificate, session id=%ld",
static_cast<uint32_t>(oec_session_id_));
if (pre_provision_token_type_ == kClientTokenKeybox) {
return RewrapDeviceRSAKey(signed_message, signature, nonce, private_key,
iv, wrapped_private_key);
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
return RewrapDeviceRSAKey30(signed_message, nonce, private_key, iv,
wrapping_key, wrapped_private_key);
} else {
LOGE("CryptoSession::RewrapCertificate, Bad pre-provision type=%d: "
"session id=%ld", pre_provision_token_type_,
static_cast<uint32_t>(oec_session_id_));
return false;
}
}
bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
const std::string& signature,
const std::string& nonce,
@@ -1140,6 +1183,57 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
return true;
}
bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message,
const std::string& nonce,
const std::string& private_key,
const std::string& iv,
const std::string& wrapping_key,
std::string* wrapped_private_key) {
LOGV("CryptoSession::RewrapDeviceRSAKey30, session id=%ld",
static_cast<uint32_t>(oec_session_id_));
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* msg_private_key = NULL;
const uint8_t* msg_iv = NULL;
const uint32_t* msg_nonce = NULL;
const uint8_t* msg_wrapping_key = NULL;
if (private_key.size() >= MAC_KEY_SIZE && iv.size() >= KEY_IV_SIZE) {
msg_private_key = signed_msg + GetOffset(message, private_key);
msg_iv = signed_msg + GetOffset(message, iv);
msg_nonce = reinterpret_cast<const uint32_t*>(signed_msg +
GetOffset(message, nonce));
msg_wrapping_key = signed_msg + GetOffset(message, wrapping_key);
}
// Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key
// and 0 as wrapped_rsa_key_length.
size_t wrapped_private_key_length = 0;
OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv, NULL,
&wrapped_private_key_length);
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGE("OEMCrypto_RewrapDeviceRSAKey30 failed getting wrapped key length");
return false;
}
wrapped_private_key->resize(wrapped_private_key_length);
status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv,
reinterpret_cast<uint8_t*>(&(*wrapped_private_key)[0]),
&wrapped_private_key_length);
wrapped_private_key->resize(wrapped_private_key_length);
if (OEMCrypto_SUCCESS != status) {
LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status);
return false;
}
return true;
}
bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
HdcpCapability* max) {
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);

View File

@@ -143,52 +143,43 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
}
DeviceCertificate device_certificate = file.device_certificate();
ExtractDeviceInfo(device_certificate.certificate(), serial_number, system_id);
*certificate = device_certificate.certificate();
*wrapped_private_key = device_certificate.wrapped_private_key();
return true;
return ExtractDeviceInfo(device_certificate.certificate(), serial_number,
system_id);
}
bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
std::string* serial_number,
uint32_t* system_id) {
LOGI("[WEM] ExtractDeviceInfo");
LOGI("ExtractDeviceInfo Entry");
if (!serial_number && !system_id) {
LOGE("Invalid paramters to DeviceFiles::ExtractDeviceInfo");
return false;
}
// Get serial number and system ID from certificate
if ((serial_number != NULL) || (system_id != NULL)) {
SignedDrmDeviceCertificate signed_drm_device_certificate;
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
!signed_drm_device_certificate.has_drm_certificate()) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing signed drm device "
"certificate.");
return false;
}
DrmDeviceCertificate drm_device_certificate;
if (!drm_device_certificate.ParseFromString(
signed_drm_device_certificate.drm_certificate()) ||
(drm_device_certificate.type() !=
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing drm device "
"certificate message.");
return false;
}
if (serial_number != NULL) {
if (drm_device_certificate.has_serial_number()) {
LOGI("DeviceFiles::ExtractDeviceInfo: serial number: [%s]",
(b2a_hex(drm_device_certificate.serial_number())).c_str());
*serial_number = drm_device_certificate.serial_number();
}
}
if (system_id != NULL) {
if (drm_device_certificate.has_system_id()) {
LOGI("DeviceFiles::ExtractDeviceInfo: system id: [%d]",
drm_device_certificate.system_id());
*system_id = drm_device_certificate.system_id();
} else {
*system_id = 0;
}
}
SignedDrmDeviceCertificate signed_drm_device_certificate;
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
!signed_drm_device_certificate.has_drm_certificate()) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing signed drm device "
"certificate.");
return false;
}
DrmDeviceCertificate drm_device_certificate;
if (!drm_device_certificate.ParseFromString(
signed_drm_device_certificate.drm_certificate()) ||
(drm_device_certificate.type() !=
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing drm device "
"certificate message.");
return false;
}
if (serial_number != NULL) {
*serial_number = drm_device_certificate.serial_number();
}
if (system_id != NULL) {
*system_id = drm_device_certificate.system_id();
}
return true;
}
@@ -341,7 +332,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
LOGW("DeviceFiles::ListLicenses: not initialized");
return false;
}
@@ -354,7 +345,7 @@ bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
// Scan list of returned filenames, remove extension, and return
// as a list of key_set_ids.
key_set_ids->clear();
for (int i = 0; i < filenames.size(); i++) {
for (size_t i = 0; i < filenames.size(); i++) {
std::string* name = &filenames[i];
std::size_t pos = name->find(kLicenseFileNameExt);
if (pos == std::string::npos) {
@@ -449,6 +440,77 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
return StoreFileWithHash(file_name, serialized_file);
}
bool DeviceFiles::ListUsageRecords(const std::string& app_id,
std::vector<std::string>* ksids) {
if (!initialized_) {
LOGW("DeviceFiles::ListUsageRecords: not initialized");
return false;
}
if (ksids == NULL) {
LOGW("DeviceFiles::ListUsageRecords: return parameter not provided");
return false;
}
// Empty or non-existent file == no usage records.
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
ksids->clear();
return true;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::ListUsageRecords: Unable to parse file");
return false;
}
ksids->clear();
size_t num_records = file.usage_info().sessions_size();
for (size_t i = 0; i < num_records; ++i) {
if (!file.usage_info().sessions(i).key_set_id().empty()) {
ksids->push_back(file.usage_info().sessions(i).key_set_id());
}
}
return true;
}
bool DeviceFiles::GetProviderToken(const std::string& app_id,
const std::string& key_set_id,
std::string* provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::GetProviderToken: not initialized");
return false;
}
if (provider_session_token == NULL) {
LOGW("DeviceFiles::GetProviderToken: NULL return argument pointer");
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
LOGW("DeviceFiles::GetProviderToken: empty file");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::GetProviderToken: unable to parse file");
return false;
}
size_t num_records = file.usage_info().sessions_size();
for (size_t i = 0; i < num_records; ++i) {
if (file.usage_info().sessions(i).key_set_id() == key_set_id) {
*provider_session_token = file.usage_info().sessions(i).token();
return true;
}
}
return false;
}
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
const std::string& provider_session_token) {
if (!initialized_) {

View File

@@ -19,16 +19,16 @@
#include "wv_cdm_constants.h"
namespace {
std::string kCompanyNameKey = "company_name";
std::string kModelNameKey = "model_name";
std::string kArchitectureNameKey = "architecture_name";
std::string kDeviceNameKey = "device_name";
std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
std::string kWVCdmVersionKey = "widevine_cdm_version";
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
} // namespace
const std::string kCompanyNameKey = "company_name";
const std::string kModelNameKey = "model_name";
const std::string kArchitectureNameKey = "architecture_name";
const std::string kDeviceNameKey = "device_name";
const std::string kProductNameKey = "product_name";
const std::string kBuildInfoKey = "build_info";
const std::string kDeviceIdKey = "device_id";
const std::string kWVCdmVersionKey = "widevine_cdm_version";
const std::string kOemCryptoSecurityPatchLevelKey =
"oem_crypto_security_patch_level";
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;
@@ -37,6 +37,8 @@ const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
const uint32_t kFourCcCenc = 0x63656e63;
const uint32_t kFourCcCens = 0x63656e73;
} // namespace
namespace wvcdm {
// Protobuf generated classes.
@@ -144,7 +146,7 @@ CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(
ServiceCertificate* service_certificate, const std::string& client_token,
CdmClientTokenType client_token_type, const std::string& serial_number,
CdmClientTokenType client_token_type, const std::string& device_id,
CryptoSession* session, PolicyEngine* policy_engine) {
if (clock_.get() == NULL) {
LOGE("CdmLicense::Init: clock parameter not provided");
@@ -170,7 +172,7 @@ bool CdmLicense::Init(
service_certificate_ = service_certificate;
client_token_ = client_token;
client_token_type_ = client_token_type;
serial_number_ = serial_number;
device_id_ = device_id;
crypto_session_ = session;
policy_engine_ = policy_engine;
initialized_ = true;
@@ -185,12 +187,6 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_4;
}
if (init_data.IsEmpty() && stored_init_data_.get()) {
InitializationData restored_init_data = *stored_init_data_;
stored_init_data_.reset();
return PrepareKeyRequest(restored_init_data, license_type, app_parameters,
signed_request, server_url);
}
if (!init_data.is_supported()) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
@@ -209,16 +205,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return INVALID_PARAMETERS_LIC_7;
}
// If privacy mode and no service certificate, initiate a
// service certificate request.
// If privacy mode, must have service certificate
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->HasCertificate()) {
stored_init_data_.reset(new InitializationData(init_data));
*server_url = server_url_;
if (service_certificate_->PrepareRequest(signed_request)) {
return KEY_MESSAGE;
}
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
!service_certificate_->has_certificate()) {
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_1;
}
std::string request_id;
@@ -303,12 +295,10 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
if (renew_with_client_id_) {
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->HasCertificate()) {
*server_url = server_url_;
if (service_certificate_->PrepareRequest(signed_request)) {
return KEY_MESSAGE;
}
return LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
!service_certificate_->has_certificate()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_2;
}
}
@@ -423,14 +413,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
switch (signed_response.type()) {
case SignedMessage::LICENSE:
break;
case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status;
status = service_certificate_->HandleResponse(signed_response.msg());
if (status != NO_ERROR) {
return status;
}
return NEED_KEY;
}
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
@@ -494,8 +476,13 @@ CdmResponseType CdmLicense::HandleKeyResponse(
license.policy().can_persist())
is_offline_ = true;
if (license.id().has_provider_session_token())
LOGV("Get Provider_session_token:");
if (license.id().has_provider_session_token()) {
provider_session_token_ = license.id().provider_session_token();
LOGV("Provider_session_token=%s", provider_session_token_.c_str());
} else {
LOGV("NO Provider_session_token");
}
if (license.policy().has_renewal_server_url()) {
server_url_ = license.policy().renewal_server_url();
@@ -540,14 +527,6 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
switch (signed_response.type()) {
case SignedMessage::LICENSE:
break;
case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status;
status = service_certificate_->HandleResponse(signed_response.msg());
if (status != NO_ERROR) {
return status;
}
return NEED_KEY;
}
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
@@ -820,6 +799,7 @@ bool CdmLicense::GetClientTokenType(
case kClientTokenOemCert:
default:
// shouldn't happen
LOGE("GetClientTokenType: BAD TOKEN TYPE");
return false;
}
}
@@ -873,10 +853,10 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info->set_name(kBuildInfoKey);
client_info->set_value(value);
}
if (!serial_number_.empty()) {
if (!device_id_.empty()) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(b2a_hex(serial_number_));
client_info->set_value(b2a_hex(device_id_));
} else if (crypto_session_->GetDeviceUniqueId(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
@@ -950,9 +930,9 @@ CdmResponseType CdmLicense::PrepareClientId(
}
if (Properties::UsePrivacyMode(session_id_)) {
if (!service_certificate_->HasCertificate()) {
if (service_certificate_->certificate().empty()) {
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
return PRIVACY_MODE_ERROR_3;
}
EncryptedClientIdentification* encrypted_client_id =
license_request->mutable_encrypted_client_id();
@@ -983,7 +963,7 @@ CdmResponseType CdmLicense::PrepareContentId(
if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
LOGE("CdmLicense::PrepareContentId: ISO-CENC init data not available");
return CENC_INIT_DATA_UNAVAILABLE;
}
@@ -997,7 +977,7 @@ CdmResponseType CdmLicense::PrepareContentId(
if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
LOGE("CdmLicense::PrepareContentId: WebM init data not available");
return WEBM_INIT_DATA_UNAVAILABLE;
}
@@ -1005,7 +985,7 @@ CdmResponseType CdmLicense::PrepareContentId(
return PREPARE_WEBM_CONTENT_ID_FAILED;
}
} else {
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
LOGE("CdmLicense::PrepareContentId: no support for init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA_FORMAT;
}

View File

@@ -5,6 +5,7 @@
#include <string>
#include "log.h"
#include "wv_cdm_constants.h"
namespace {
// License protocol aliases
@@ -102,7 +103,15 @@ bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status,
return keys_changed;
}
CdmKeyStatus LicenseKeys::GetKeyStatus(const KeyId& key_id) {
if (keys_.count(key_id) == 0) {
return kKeyStatusKeyUnknown;
}
return keys_[key_id]->GetKeyStatus();
}
void LicenseKeys::ExtractKeyStatuses(CdmKeyStatusMap* content_keys) {
content_keys->clear();
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
if (it->second->IsContentKey()) {
const KeyId key_id = it->first;
@@ -251,11 +260,11 @@ bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
// requirement, use the key's default HDCP setting to check against the
// device's current HDCP level.
void LicenseKeyStatus::ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
uint32_t video_pixels, CryptoSession::HdcpCapability new_hdcp_level) {
VideoResolutionConstraint* current_constraint = NULL;
if (HasConstraints()) {
current_constraint = GetConstraintForRes(new_resolution, constraints_);
if (HasConstraints() && video_pixels != HDCP_UNSPECIFIED_VIDEO_RESOLUTION) {
current_constraint = GetConstraintForRes(video_pixels, constraints_);
if (NULL == current_constraint) {
meets_constraints_ = false;
return;

View File

@@ -10,75 +10,73 @@
namespace wvcdm {
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
SecurityLevel level) {
SecurityLevel) {
return ::OEMCrypto_OpenSession(session);
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level) {
size_t keyBoxLength, SecurityLevel) {
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel) {
return ::OEMCrypto_IsKeyboxValid();
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level) {
SecurityLevel) {
return ::OEMCrypto_GetDeviceID(deviceID, idLength);
}
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
SecurityLevel level) {
SecurityLevel) {
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
}
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
uint32_t OEMCrypto_APIVersion(SecurityLevel) {
return ::OEMCrypto_APIVersion();
}
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level) {
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel) {
return ::OEMCrypto_Security_Patch_Level();
}
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
const char* OEMCrypto_SecurityLevel(SecurityLevel) {
return ::OEMCrypto_SecurityLevel();
}
OEMCryptoResult OEMCrypto_GetHDCPCapability(
SecurityLevel level, OEMCrypto_HDCP_Capability* current,
SecurityLevel, OEMCrypto_HDCP_Capability* current,
OEMCrypto_HDCP_Capability* maximum) {
return ::OEMCrypto_GetHDCPCapability(current, maximum);
}
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
bool OEMCrypto_SupportsUsageTable(SecurityLevel) {
return ::OEMCrypto_SupportsUsageTable();
}
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level) {
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel) {
return ::OEMCrypto_IsAntiRollbackHwPresent();
}
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel,
size_t* count) {
return ::OEMCrypto_GetNumberOfOpenSessions(count);
}
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel,
size_t* maximum) {
return ::OEMCrypto_GetMaxNumberOfSessions(maximum);
}
OEMCryptoResult OEMCrypto_CopyBuffer(
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
SecurityLevel, const uint8_t* data_addr, size_t data_length,
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
return ::OEMCrypto_CopyBuffer(data_addr, data_length, out_buffer,
subsample_flags);
}
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
SecurityLevel level) {
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(SecurityLevel) {
return ::OEMCrypto_GetProvisioningMethod();
}

View File

@@ -14,13 +14,6 @@
using video_widevine::License;
namespace {
const int64_t kHdcpCheckInterval = 10;
const uint32_t kNoResolution = 0;
} // namespace
namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
@@ -54,25 +47,36 @@ bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
}
}
CdmKeyStatus PolicyEngine::GetKeyStatus(const KeyId& key_id) {
return license_keys_->GetKeyStatus(key_id);
}
void PolicyEngine::InitDevice(CryptoSession* crypto_session) {
current_resolution_ = kNoResolution;
current_resolution_ = HDCP_UNSPECIFIED_VIDEO_RESOLUTION;
next_device_check_ = 0;
crypto_session_ = crypto_session;
}
void PolicyEngine::CheckDevice(int64_t current_time) {
if (current_time < next_device_check_) {
return;
}
void PolicyEngine::SetDeviceResolution(uint32_t width, uint32_t height) {
current_resolution_ = width * height;
CheckDeviceHdcpStatus();
}
if (!license_keys_->Empty() && current_resolution_ != kNoResolution) {
void PolicyEngine::CheckDeviceHdcpStatusOnTimer(int64_t current_time) {
if (current_time >= next_device_check_) {
CheckDeviceHdcpStatus();
next_device_check_ = current_time + HDCP_DEVICE_CHECK_INTERVAL;
}
}
void PolicyEngine::CheckDeviceHdcpStatus() {
if (!license_keys_->Empty()) {
CryptoSession::HdcpCapability current_hdcp_level;
CryptoSession::HdcpCapability ignored;
if (!crypto_session_->GetHdcpCapabilities(&current_hdcp_level, &ignored)) {
current_hdcp_level = HDCP_NONE;
}
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
next_device_check_ = current_time + kHdcpCheckInterval;
}
}
@@ -94,14 +98,16 @@ void PolicyEngine::OnTimerEvent() {
}
// Check device conditions that affect playability (HDCP, resolution)
CheckDevice(current_time);
CheckDeviceHdcpStatusOnTimer(current_time);
bool renewal_needed = false;
// Test to determine if renewal should be attempted.
switch (license_state_) {
case kLicenseStateCanPlay: {
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
if (HasRenewalDelayExpired(current_time)) {
renewal_needed = true;
}
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break;
@@ -113,7 +119,9 @@ void PolicyEngine::OnTimerEvent() {
}
case kLicenseStateWaitingLicenseUpdate: {
if (HasRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
if (HasRenewalRetryIntervalExpired(current_time)) {
renewal_needed = true;
}
break;
}
@@ -420,9 +428,16 @@ bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
next_renewal_time_ <= current_time;
}
// Apply a key status to the current keys.
// If this represents a new key status, perform a notification callback.
// NOTE: if the new status is kKeyStatusUsable, the HDCP check may result in an
// override to kKeyStatusOutputNotAllowed.
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
bool keys_changed;
bool has_new_usable_key = false;
if (new_status == kKeyStatusUsable) {
CheckDeviceHdcpStatus();
}
keys_changed = license_keys_->ApplyStatusChange(new_status,
&has_new_usable_key);
if (event_listener_ && keys_changed) {

View File

@@ -182,7 +182,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
// LogOpenSSLError is a callback from OpenSSL which is called with each error
// in the thread's error queue.
static int LogOpenSSLError(const char *msg, size_t /* len */, void */* ctx */) {
static int LogOpenSSLError(const char* msg, size_t /* len */, void* /* ctx */) {
LOGE(" %s", msg);
return 1;
}
@@ -199,7 +199,8 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
goto err;
}
if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) {
if (EVP_PKEY_CTX_set_signature_md(pctx,
const_cast<EVP_MD *>(EVP_sha1())) != 1) {
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
goto err;
}

View File

@@ -13,6 +13,7 @@ bool Properties::oem_crypto_use_secure_buffers_;
bool Properties::oem_crypto_use_fifo_;
bool Properties::oem_crypto_use_userspace_buffers_;
bool Properties::use_certificates_as_identification_;
bool Properties::provisioning_messages_are_binary_;
bool Properties::security_level_path_backward_compatibility_support_;
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;

View File

@@ -11,41 +11,110 @@
namespace {
// Service certificate for Google/Widevine Provisioning and License servers.
const unsigned char kServiceCertificateCAPublicKey[] = {
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39,
0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd,
0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87,
0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6,
0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40,
0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98,
0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84,
0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab,
0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0x53, 0x3e, 0x47, 0x5f,
0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf,
0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, 0x45, 0xd6,
0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7,
0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50,
0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd,
0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0,
0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7,
0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b,
0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11,
0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8,
0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33,
0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0x60, 0x1a, 0x11, 0x3d,
0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b,
0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, 0xbe, 0x7f, 0xb0,
0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e, 0x69,
0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, 0x01,
0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24,
0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31,
0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5,
0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8,
0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d,
0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03,
0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f,
0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01,
0x00, 0x01};
static const unsigned char kRootCertForProd[] = {
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00,
0x18, 0xdd, 0x94, 0x88, 0x8b, 0x05, 0x22, 0x8e,
0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90,
0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68,
0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d,
0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc,
0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c,
0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3,
0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26,
0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21,
0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46,
0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb,
0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b,
0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48,
0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd,
0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c,
0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10,
0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19,
0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84,
0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26,
0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25,
0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1,
0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4,
0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17,
0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b,
0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba,
0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84,
0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c,
0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca,
0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6,
0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45,
0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba,
0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2,
0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77,
0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19,
0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc,
0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e,
0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed,
0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56,
0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3,
0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c,
0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c,
0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff,
0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4,
0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58,
0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41,
0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4,
0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12,
0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55,
0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33,
0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12,
0x80, 0x03, 0x58, 0xf1, 0xd6, 0x4d, 0x04, 0x09,
0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39,
0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67,
0x85, 0x57, 0x12, 0xa7, 0x14, 0xee, 0x35, 0x16,
0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00,
0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e,
0x44, 0xcd, 0x4c, 0xd4, 0xae, 0x09, 0x18, 0x53,
0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda,
0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90,
0x1e, 0x7e, 0x5b, 0xc6, 0xe9, 0x0f, 0x22, 0x9f,
0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b,
0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5,
0xf2, 0x08, 0xcd, 0x65, 0x41, 0x58, 0xe8, 0x7d,
0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69,
0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32,
0x87, 0x70, 0xb2, 0xb0, 0x76, 0x9c, 0xb2, 0xc3,
0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99,
0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a,
0x19, 0xc1, 0xd5, 0x65, 0x90, 0x76, 0x55, 0x74,
0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19,
0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92,
0x5f, 0x4f, 0xbc, 0x7a, 0x31, 0xf8, 0xae, 0x8e,
0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a,
0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2,
0xfe, 0x1c, 0x88, 0xac, 0x2a, 0x66, 0xc5, 0xff,
0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a,
0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a,
0x24, 0x2f, 0x6c, 0xa5, 0xe3, 0x1c, 0x88, 0x14,
0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85,
0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c,
0xfb, 0x92, 0x6c, 0x66, 0x75, 0x8a, 0xd6, 0x0d,
0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03,
0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01,
0x20, 0xa8, 0xa9, 0x84, 0x2e, 0x17, 0xca, 0xae,
0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0,
0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86,
0x30, 0x14, 0x78, 0x21, 0x11, 0x22, 0x88, 0xac,
0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5,
0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08,
0x7b, 0x7a, 0xfb, 0x95, 0x89, 0xc3, 0xee, 0x98,
0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12,
0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b,
0x46, 0x08, 0x7b, 0x3d, 0x32, 0x6a, 0x68, 0xca,
0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9,
0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd,
0xc4, 0x46, 0xf5, 0xa3, 0x62, 0x13, 0x74, 0x02,
0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf,
0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5,
0x02, 0x0c
};
} // namespace
namespace wvcdm {
@@ -57,41 +126,90 @@ using video_widevine::EncryptedClientIdentification;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::SignedMessage;
void ServiceCertificate::Clear() {
fetch_in_progress_ = false;
certificate_.clear();
provider_id_.clear();
CdmResponseType ServiceCertificate::Init(const std::string& certificate) {
std::string root_cert_str(reinterpret_cast<const char*>(&kRootCertForProd[0]),
sizeof(kRootCertForProd));
// Load root cert public key. Don't bother verifying it.
SignedDrmDeviceCertificate signed_root_cert;
if (!signed_root_cert.ParseFromString(root_cert_str)) {
LOGE("Failed to deserialize signed root certificate.");
return DEVICE_CERTIFICATE_ERROR_1;
}
DrmDeviceCertificate root_cert;
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
LOGE("Failed to deserialize signed root certificate.");
return DEVICE_CERTIFICATE_ERROR_1;
}
RsaPublicKey root_key;
if (!root_key.Init(root_cert.public_key())) {
LOGE("Failed to load root certificate public key.");
return DEVICE_CERTIFICATE_ERROR_1;
}
// Load the provided service certificate.
// First, parse it and verify its signature.
SignedDrmDeviceCertificate signed_service_cert;
if (!signed_service_cert.ParseFromString(certificate)) {
LOGE("Failed to parse signed service certificate.");
return DEVICE_CERTIFICATE_ERROR_2;
}
if (!root_key.VerifySignature(signed_service_cert.drm_certificate(),
signed_service_cert.signature())) {
LOGE("Service certificate signature verification failed.");
return DEVICE_CERTIFICATE_ERROR_3;
}
DrmDeviceCertificate service_cert;
if (!service_cert.ParseFromString(signed_service_cert.drm_certificate())) {
LOGE("Failed to parse service certificate.");
return DEVICE_CERTIFICATE_ERROR_2;
}
if (service_cert.type() !=
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
LOGE("Not a service certificate.");
return DEVICE_CERTIFICATE_ERROR_3;
}
// Service certificate passes all checks - set up its RSA public key.
public_key_.reset(new RsaPublicKey);
if (!public_key_->Init(service_cert.public_key())) {
public_key_.reset();
LOGE("Failed to load service certificate public key.");
return DEVICE_CERTIFICATE_ERROR_2;
}
// Have service certificate and its public key - keep relevant fields.
certificate_ = certificate;
serial_number_ = service_cert.serial_number();
provider_id_ = service_cert.provider_id();
has_certificate_ = true;
return NO_ERROR;
}
CdmResponseType ServiceCertificate::Init(const std::string& raw_certificate) {
return VerifyAndExtract(raw_certificate);
CdmResponseType ServiceCertificate::VerifySignedMessage(
const std::string& message, const std::string& signature) {
if (!public_key_) {
LOGE("Service certificate not set.");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (!public_key_->VerifySignature(message, signature))
return CLIENT_ID_RSA_ENCRYPT_ERROR; // TODO(tinskip): Need new error code.
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
DrmDeviceCertificate service_certificate;
if (certificate_.empty()) {
LOGE("ServiceCertificate::EncryptClientId: "
"service certificate is not properly initialized");
return UNKNOWN_ERROR;
}
if (!service_certificate.ParseFromString(certificate_)) {
LOGE("ServiceCertificate::EncryptClientId: unable to parse retrieved "
"service certificate");
return PARSE_SERVICE_CERTIFICATE_ERROR;
if (!public_key_) {
LOGE("Service certificate not set.");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (service_certificate.type() !=
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
LOGE("ServiceCertificate::EncryptClientId: retrieved certificate not of "
"type service, %d", service_certificate.type());
return SERVICE_CERTIFICATE_TYPE_ERROR;
}
encrypted_client_id->set_provider_id(service_certificate.provider_id());
encrypted_client_id->set_service_certificate_serial_number(
service_certificate.serial_number());
encrypted_client_id->set_provider_id(provider_id_);
encrypted_client_id->set_service_certificate_serial_number(serial_number_);
std::string iv(KEY_IV_SIZE, 0);
std::string key(KEY_SIZE, 0);
@@ -109,10 +227,8 @@ CdmResponseType ServiceCertificate::EncryptClientId(
if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR;
if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR;
RsaPublicKey rsa;
if (!rsa.Init(service_certificate.public_key()))
return CLIENT_ID_RSA_INIT_ERROR;
if (!rsa.Encrypt(key, &enc_key)) return CLIENT_ID_RSA_ENCRYPT_ERROR;
if (!public_key_->Encrypt(key, &enc_key))
return CLIENT_ID_RSA_ENCRYPT_ERROR;
encrypted_client_id->set_encrypted_client_id_iv(iv);
encrypted_client_id->set_encrypted_privacy_key(enc_key);
@@ -120,97 +236,4 @@ CdmResponseType ServiceCertificate::EncryptClientId(
return NO_ERROR;
}
bool ServiceCertificate::PrepareRequest(CdmKeyMessage* signed_request) {
if (!signed_request) {
LOGE("ServiceCertificate::PrepareRequest: no signed request provided");
return false;
}
SignedMessage signed_message;
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
signed_message.SerializeToString(signed_request);
fetch_in_progress_ = true;
return true;
}
CdmResponseType ServiceCertificate::HandleResponse(
const std::string& signed_response) {
if (!fetch_in_progress_) {
LOGE("ServiceCertificate::HandleResponse: unexpected service "
"certificate response.");
return UNKNOWN_ERROR;
}
fetch_in_progress_ = false;
CdmResponseType status = VerifyAndExtract(signed_response);
if (status != NO_ERROR) {
return status;
}
return NO_ERROR;
}
CdmResponseType ServiceCertificate::VerifyAndExtract(
const std::string& raw_certificate) {
if (raw_certificate.empty()) {
Clear();
return NO_ERROR;
}
// Deserialize and parse raw certificate.
SignedDrmDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(raw_certificate)) {
LOGE(
"ServiceCertificate::VerifyAndExtract: unable to parse signed "
"service certificate");
return DEVICE_CERTIFICATE_ERROR_1;
}
// Set up root key (for verifying signature).
RsaPublicKey root_ca_key;
std::string ca_public_key(
reinterpret_cast<const char*>(&kServiceCertificateCAPublicKey[0]),
sizeof(kServiceCertificateCAPublicKey));
if (!root_ca_key.Init(ca_public_key)) {
LOGE(
"ServiceCertificate::VerifyAndExtract: public key initialization "
"failed");
return DEVICE_CERTIFICATE_ERROR_2;
}
// Verify the signature.
if (!root_ca_key.VerifySignature(
signed_service_certificate.drm_certificate(),
signed_service_certificate.signature())) {
LOGE(
"ServiceCertificate::VerifyAndExtract: service certificate "
"verification failed");
return DEVICE_CERTIFICATE_ERROR_3;
}
// Deserialize and parse actual certificate.
DrmDeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(
signed_service_certificate.drm_certificate())) {
LOGE(
"ServiceCertificate::VerifyAndExtract: unable to parse retrieved "
"service certificate");
return DEVICE_CERTIFICATE_ERROR_4;
}
// Verify, extract needed fields.
if (service_certificate.type() !=
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
LOGE(
"ServiceCertificate::VerifyAndExtract: certificate not of type "
"service, %d", service_certificate.type());
return INVALID_DEVICE_CERTIFICATE_TYPE;
}
if (service_certificate.has_provider_id()) {
provider_id_.assign(service_certificate.provider_id());
} else {
provider_id_.clear();
}
certificate_.assign(signed_service_certificate.drm_certificate());
return NO_ERROR;
}
} // namespace wvcdm

View File

@@ -152,16 +152,21 @@ std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
}
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
// as Base64WebSafeDecode.
// as Base64WebSafeDecode. Add padding if needed.
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
if (b64_input.empty()) {
return std::vector<uint8_t>();
}
int in_size = b64_input.size();
std::string b64_padded(b64_input);
while (b64_padded.size() % 4 != 0) {
b64_padded = b64_padded + "=";
}
int in_size = b64_padded.size();
std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0);
int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]),
b64_input.data(), in_size);
b64_padded.data(), in_size);
if (out_size == -1) {
LOGE("Base64SafeDecode failed");
return std::vector<uint8_t>(0);