Source release v3.2.0

This commit is contained in:
Gene Morgan
2017-02-01 16:36:41 -08:00
parent 643b91b616
commit 2fde891c01
370 changed files with 40622 additions and 276133 deletions

View File

@@ -20,6 +20,7 @@
#include "wv_cdm_event_listener.h"
namespace {
const uint64_t kReleaseSessionTimeToLive = 60; // seconds
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
const size_t kUsageReportsPerRequest = 1;
} // namespace
@@ -54,17 +55,17 @@ class UsagePropertySet : public CdmClientPropertySet {
bool CdmEngine::seeded_ = false;
CdmEngine::CdmEngine(FileSystem* file_system)
CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
: cert_provisioning_(NULL),
cert_provisioning_requested_security_level_(kLevelDefault),
file_system_(file_system),
spoid_(spoid),
usage_session_(NULL),
last_usage_information_update_time_(0) {
assert(file_system);
Properties::Init();
if (!seeded_) {
Clock clock;
srand(clock.GetCurrentTime());
srand(clock_.GetCurrentTime());
seeded_ = true;
}
}
@@ -78,27 +79,28 @@ CdmEngine::~CdmEngine() {
sessions_.clear();
}
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
const CdmSessionId& forced_session_id,
WvCdmEventListener* event_listener) {
CdmResponseType CdmEngine::SetServiceCertificate(
const std::string& certificate) {
return service_certificate_.Init(certificate);
}
CdmResponseType CdmEngine::OpenSession(
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) {
return OpenSession(key_system, property_set, event_listener,
&forced_session_id, NULL);
}
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener,
CdmSessionId* session_id) {
return OpenSession(key_system, property_set, event_listener, NULL,
session_id);
CdmResponseType CdmEngine::OpenSession(
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener, CdmSessionId* session_id) {
return OpenSession(key_system, property_set, event_listener, NULL, session_id);
}
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id,
CdmSessionId* session_id) {
CdmResponseType CdmEngine::OpenSession(
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener, const CdmSessionId* forced_session_id,
CdmSessionId* session_id) {
LOGI("CdmEngine::OpenSession");
if (!ValidateKeySystem(key_system)) {
@@ -117,10 +119,11 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
}
}
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
CloseExpiredReleaseSessions();
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
event_listener);
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
CdmResponseType sts = new_session->Init(&service_certificate_, property_set,
forced_session_id, event_listener);
if (sts != NO_ERROR) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
@@ -151,13 +154,27 @@ CdmResponseType CdmEngine::OpenKeySetSession(
return EMPTY_KEYSET_ID_ENG_1;
}
// If in-use, release key set before re-opening, to avoid leaking
// resources (CryptoSession etc).
bool key_set_in_use = false;
{
AutoLock lock(release_key_sets_lock_);
key_set_in_use =
release_key_sets_.find(key_set_id) != release_key_sets_.end();
}
if (key_set_in_use)
CloseKeySetSession(key_set_id);
CdmSessionId session_id;
CdmResponseType sts = OpenSession(KEY_SYSTEM, property_set, event_listener,
NULL /* forced_session_id */, &session_id);
if (sts != NO_ERROR) return sts;
release_key_sets_[key_set_id] = session_id;
AutoLock lock(release_key_sets_lock_);
release_key_sets_[key_set_id] = std::make_pair(session_id,
clock_.GetCurrentTime() + kReleaseSessionTimeToLive);
return NO_ERROR;
}
@@ -178,19 +195,30 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
LOGI("CdmEngine::CloseKeySetSession");
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
key_set_id.c_str());
return KEYSET_ID_NOT_FOUND_1;
CdmSessionId session_id;
{
AutoLock lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
key_set_id.c_str());
return KEYSET_ID_NOT_FOUND_1;
}
session_id = iter->second.first;
}
CdmResponseType sts = CloseSession(iter->second);
release_key_sets_.erase(iter);
CdmResponseType sts = CloseSession(session_id);
AutoLock lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter != release_key_sets_.end()) {
release_key_sets_.erase(iter);
}
return sts;
}
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator iter = sessions_.find(session_id);
return iter != sessions_.end();
}
@@ -219,6 +247,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
return INVALID_SESSION_ID;
}
AutoLock lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
@@ -226,7 +255,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
return KEYSET_ID_NOT_FOUND_2;
}
id = iter->second;
id = iter->second.first;
}
CdmSessionMap::iterator iter = sessions_.find(id);
@@ -292,13 +321,14 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
return EMPTY_KEYSET_ID_ENG_3;
}
AutoLock lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
return KEYSET_ID_NOT_FOUND_3;
}
id = iter->second;
id = iter->second.first;
}
CdmSessionMap::iterator iter = sessions_.find(id);
@@ -466,7 +496,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
} else if (query_token == QUERY_KEY_DEVICE_ID) {
std::string deviceId;
if (!crypto_session.GetDeviceUniqueId(&deviceId)) {
LOGW("CdmEngine::QueryStatus: GetDeviceUniqueId failed");
LOGW("CdmEngine::QueryStatus: QUERY_KEY_DEVICE_ID unknown failure");
return UNKNOWN_ERROR;
}
@@ -474,7 +504,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
} else if (query_token == QUERY_KEY_SYSTEM_ID) {
uint32_t system_id;
if (!crypto_session.GetSystemId(&system_id)) {
LOGW("CdmEngine::QueryStatus: GetSystemId failed");
LOGW("CdmEngine::QueryStatus: QUERY_KEY_SYSTEM_ID unknown failure");
return UNKNOWN_ERROR;
}
@@ -681,11 +711,12 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
DeleteAllUsageReportsUponFactoryReset();
if (NULL == cert_provisioning_.get()) {
cert_provisioning_.reset(new CertificateProvisioning());
cert_provisioning_.reset(
new CertificateProvisioning(&service_certificate_));
}
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_type, cert_authority,
file_system_->origin(), request, default_url);
file_system_->origin(), spoid_, request, default_url);
if (ret != NO_ERROR) {
cert_provisioning_.reset(NULL); // Release resources.
}
@@ -707,14 +738,14 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
cert_provisioning_.reset(NULL);
return EMPTY_PROVISIONING_RESPONSE;
}
if (NULL == cert) {
if (cert == NULL) {
LOGE(
"CdmEngine::HandleProvisioningResponse: invalid certificate "
"destination");
cert_provisioning_.reset(NULL);
return INVALID_PROVISIONING_PARAMETERS_1;
}
if (NULL == wrapped_key) {
if (wrapped_key == NULL) {
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
"destination");
cert_provisioning_.reset(NULL);
@@ -768,7 +799,7 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
return UNPROVISION_ERROR_1;
}
if (!file_system_->origin().empty()) {
if (!file_system_->IsGlobal()) {
if (!handle.RemoveCertificate()) {
LOGE("CdmEngine::Unprovision: unable to delete certificate");
return UNPROVISION_ERROR_2;
@@ -779,22 +810,44 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
LOGE("CdmEngine::Unprovision: unable to delete files");
return UNPROVISION_ERROR_3;
}
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (NO_ERROR != status) {
LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status);
return UNPROVISION_ERROR_4;
}
status = crypto_session->DeleteAllUsageReports();
if (status != NO_ERROR) {
LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status);
}
return status;
return DeleteUsageTable(security_level);
}
}
CdmResponseType CdmEngine::DeleteUsageTable(CdmSecurityLevel security_level) {
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (NO_ERROR != status) {
LOGE("CdmEngine::DeleteUsageTable: error opening crypto session: %d",
status);
return UNPROVISION_ERROR_4;
}
status = crypto_session->DeleteAllUsageReports();
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageTable: error deleting usage reports: %d",
status);
}
return status;
}
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");
return INVALID_PARAMETERS_ENG_17;
}
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ListStoredLicenses: unable to initialize device files");
return STORE_LICENSE_ERROR_3;
}
if (!handle.ListLicenses(key_set_ids)) {
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info) {
@@ -1234,12 +1287,14 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
return false;
}
void CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
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);
return true;
}
return false;
}
bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
@@ -1289,6 +1344,8 @@ void CdmEngine::OnTimerEvent() {
}
}
}
CloseExpiredReleaseSessions();
}
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
@@ -1318,6 +1375,29 @@ std::string CdmEngine::MapHdcpVersion(
return "";
}
void CdmEngine::CloseExpiredReleaseSessions() {
int64_t current_time = clock_.GetCurrentTime();
std::set<CdmSessionId> close_session_set;
{
AutoLock lock(release_key_sets_lock_);
for (CdmReleaseKeySetMap::iterator iter = release_key_sets_.begin();
iter != release_key_sets_.end();) {
if (iter->second.second < current_time) {
close_session_set.insert(iter->second.first);
release_key_sets_.erase(iter++);
} else {
++iter;
}
}
}
for (std::set<CdmSessionId>::iterator iter = close_session_set.begin();
iter != close_session_set.end(); ++iter) {
CloseSession(*iter);
}
}
void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
std::string device_base_path_level1 = "";
std::string device_base_path_level3 = "";

View File

@@ -50,12 +50,13 @@ CdmSession::~CdmSession() {
CdmResponseType CdmSession::Init(
CdmClientPropertySet* cdm_client_property_set) {
return Init(cdm_client_property_set, NULL, NULL);
return Init(NULL, cdm_client_property_set, NULL, NULL);
}
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id,
WvCdmEventListener* event_listener) {
CdmResponseType CdmSession::Init(
ServiceCertificate* service_certificate,
CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener) {
if (initialized_) {
LOGE("CdmSession::Init: Failed due to previous initialization");
return SESSION_INIT_ERROR_2;
@@ -76,18 +77,42 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
return SESSION_FILE_HANDLE_INIT_ERROR;
}
std::string token;
if (Properties::use_certificates_as_identification()) {
// Device Provisioning state is not yet known.
// If not using certificates, then Keybox is client token for license
// requests.
// Otherwise, try to fetch device certificate. If not successful and
// provisioning is supported, return NEED_PROVISIONING. Otherwise, return
// an error.
// client_token and client_token_type are determined here; they are needed
// to initialize the license parser.
std::string client_token;
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(&token, &wrapped_key) ||
uint32_t system_id;
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
&serial_number, &system_id) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING;
}
} else {
if (!crypto_session_->GetToken(&token))
return SESSION_INIT_GET_KEYBOX_ERROR;
client_token_type = kClientTokenDrmCert;
}
// Session is provisioned with certificate needed to construct
// license request (or with keybox).
if (forced_session_id) {
key_set_id_ = *forced_session_id;
} else {
@@ -112,8 +137,9 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get()));
if (!license_parser_->Init(token, crypto_session_.get(),
policy_engine_.get()))
if (!license_parser_->Init(
service_certificate, client_token, client_token_type,
serial_number, crypto_session_.get(), policy_engine_.get()))
return LICENSE_PARSER_INIT_ERROR;
license_received_ = false;
@@ -129,12 +155,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
DeviceFiles::LicenseState license_state;
int64_t playback_start_time;
int64_t last_playback_time;
int64_t grace_period_end_time;
if (!file_handle_->RetrieveLicense(
key_set_id, &license_state, &offline_init_data_, &key_request_,
&key_response_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &app_parameters_)) {
&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());
return GET_LICENSE_ERROR;
@@ -156,7 +184,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} else {
if (!license_parser_->RestoreOfflineLicense(
key_request_, key_response_, offline_key_renewal_response_,
playback_start_time, last_playback_time)) {
playback_start_time, last_playback_time, grace_period_end_time)) {
return RESTORE_OFFLINE_LICENSE_ERROR_2;
}
}
@@ -297,13 +325,13 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
if (is_release_) {
CdmResponseType sts = ReleaseKey(key_response);
return (NO_ERROR == sts) ? KEY_ADDED : sts;
return (sts == NO_ERROR) ? KEY_ADDED : sts;
} else if (license_received_) { // renewal
return RenewKey(key_response);
} else {
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? ADD_KEY_ERROR : sts;
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? ADD_KEY_ERROR : sts;
license_received_ = true;
key_response_ = key_response;
@@ -393,7 +421,8 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// Playback may not begin until either the start time passes or the license
// is updated, so we treat this Decrypt call as invalid.
if (params.is_encrypted && !policy_engine_->CanDecrypt(*params.key_id)) {
if (params.is_encrypted &&
!policy_engine_->CanDecryptContent(*params.key_id)) {
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
}
@@ -412,7 +441,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
} else {
Clock clock;
int64_t current_time = clock.GetCurrentTime();
if (policy_engine_->IsLicenseOrPlaybackDurationExpired(current_time)) {
if (policy_engine_->HasLicenseOrPlaybackDurationExpired(current_time)) {
return NEED_KEY;
}
}
@@ -442,7 +471,7 @@ CdmResponseType CdmSession::GenerateRenewalRequest(
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(true, key_response);
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? RENEW_KEY_ERROR_1 : sts;
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RENEW_KEY_ERROR_1 : sts;
if (is_offline_) {
offline_key_renewal_response_ = key_response;
@@ -474,7 +503,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(false, key_response);
if (KEY_ADDED != sts) return (KEY_ERROR == sts) ? RELEASE_KEY_ERROR : sts;
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RELEASE_KEY_ERROR : sts;
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
DeleteLicense();
@@ -567,7 +596,8 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
key_set_id_, state, offline_init_data_, key_request_, key_response_,
offline_key_renewal_request_, offline_key_renewal_response_,
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
policy_engine_->GetLastPlaybackTime(), app_parameters_);
policy_engine_->GetLastPlaybackTime(),
policy_engine_->GetGracePeriodEndTime(), app_parameters_);
}
CdmResponseType CdmSession::ReleaseCrypto() {

View File

@@ -5,6 +5,8 @@
#include "file_store.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "service_certificate.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
@@ -21,11 +23,11 @@ const std::string kProvisioningServerUrl =
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ProvisioningOptions;
using video_widevine_server::sdk::ProvisioningRequest;
using video_widevine_server::sdk::ProvisioningResponse;
using video_widevine_server::sdk::SignedProvisioningMessage;
using video_widevine::ClientIdentification;
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
@@ -48,6 +50,76 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
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.
*/
bool CertificateProvisioning::GetProvisioningTokenType(
ClientIdentification::TokenType* token_type) {
switch (crypto_session_.GetPreProvisionTokenType()) {
case kClientTokenKeybox:
*token_type = ClientIdentification::KEYBOX;
return true;
case kClientTokenOemCert:
*token_type = ClientIdentification::OEM_DEVICE_CERTIFICATE;
return true;
case kClientTokenDrmCert:
default:
// shouldn't happen
return false;
}
}
/*
* Fill in the appropriate SPOID (Stable Per-Origin IDentifier) option.
* One of spoid, provider_id or stable_id will be passed to the provisioning
* server for determining a unique per origin ID for the device.
* It is also valid (though deprecated) to leave the settings unset.
*/
bool CertificateProvisioning::SetSpoidParameter(
const std::string& origin, const std::string& spoid,
ProvisioningRequest* request) {
if (!request) {
LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer "
"passed to method.");
return false;
}
if (!spoid.empty()) {
// Use the SPOID that has been pre-provided
request->set_spoid(spoid);
} else if (Properties::UseProviderIdInProvisioningRequest()) {
if (service_certificate_->HasProviderId()) {
request->set_provider_id(service_certificate_->provider_id());
} else {
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
"provider ID");
return false;
}
} else if (origin != EMPTY_ORIGIN) {
// Legacy behavior - Concatenate Unique ID with Origin
std::string device_unique_id;
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
"device unique ID");
return false;
}
request->set_stable_id(device_unique_id + origin);
} // No else clause, by design. It is valid to do nothing.
return true;
}
/*
* Return the provisioning protocol version - dictated by OEMCrypto
* support for OEM certificates.
*/
SignedProvisioningMessage::ProtocolVersion
CertificateProvisioning::GetProtocolVersion() {
if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert)
return SignedProvisioningMessage::VERSION_3;
else
return SignedProvisioningMessage::VERSION_2;
}
/*
* Composes a device provisioning request and output the request in JSON format
* in *request. It also returns the default url for the provisioning server
@@ -58,7 +130,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SecurityLevel requested_security_level, CdmCertificateType cert_type,
const std::string& cert_authority, const std::string& origin,
CdmProvisioningRequest* request, std::string* default_url) {
const std::string& spoid, CdmProvisioningRequest* request,
std::string* default_url) {
if (!default_url) {
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
return CERT_PROVISIONING_REQUEST_ERROR_1;
@@ -66,22 +139,44 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
default_url->assign(kProvisioningServerUrl);
CdmResponseType sts = crypto_session_.Open(requested_security_level);
if (NO_ERROR != sts) {
CdmResponseType status = crypto_session_.Open(requested_security_level);
if (NO_ERROR != status) {
LOGE("GetProvisioningRequest: fails to create a crypto session");
return sts;
return status;
}
// Prepares device provisioning request.
// Prepare device provisioning request.
ProvisioningRequest provisioning_request;
ClientIdentification* client_id = provisioning_request.mutable_client_id();
client_id->set_type(ClientIdentification::KEYBOX);
std::string token;
if (!crypto_session_.GetToken(&token)) {
LOGE("GetProvisioningRequest: fails to get token");
return CERT_PROVISIONING_GET_KEYBOX_ERROR_1;
ClientIdentification* client_id = provisioning_request.mutable_client_id();
ClientIdentification::TokenType token_type;
if (!GetProvisioningTokenType(&token_type)) {
LOGE("GetProvisioningRequest: bad token type");
return CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1;
}
if (!crypto_session_.GetProvisioningToken(&token)) {
LOGE("GetProvisioningRequest: failure getting provisioning token");
return CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2;
}
client_id->set_token(token);
client_id->set_type(token_type);
#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification.
if (service_certificate_->HasCertificate()) {
EncryptedClientIdentification* 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();
} else {
provisioning_request->clear_encrypted_client_id();
}
return status;
}
#endif
uint32_t nonce;
if (!crypto_session_.GenerateNonce(&nonce)) {
@@ -98,12 +193,11 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
switch (cert_type) {
case kCertificateWidevine:
options->set_certificate_type(
video_widevine_server::sdk::
ProvisioningOptions_CertificateType_WIDEVINE_DRM);
video_widevine::ProvisioningOptions_CertificateType_WIDEVINE_DRM);
break;
case kCertificateX509:
options->set_certificate_type(
video_widevine_server::sdk::ProvisioningOptions_CertificateType_X509);
video_widevine::ProvisioningOptions_CertificateType_X509);
break;
default:
LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type);
@@ -113,13 +207,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
if (origin != EMPTY_ORIGIN) {
std::string device_unique_id;
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
LOGE("GetProvisioningRequest: fails to get device unique ID");
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
}
provisioning_request.set_stable_id(device_unique_id + origin);
if (!SetSpoidParameter(origin, spoid, &provisioning_request)) {
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
}
std::string serialized_message;
@@ -140,6 +229,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message);
signed_provisioning_msg.set_signature(request_signature);
signed_provisioning_msg.set_protocol_version(GetProtocolVersion());
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
@@ -256,6 +346,9 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
return NO_ERROR;
}
// This is the entire certificate (SignedDrmDeviceCertificate).
// This will be stored to the device as the final step in the device
// provisioning process.
const std::string& device_certificate =
provisioning_response.device_certificate();

View File

@@ -8,6 +8,7 @@
#include <arpa/inet.h> // needed for ntoh()
#include <string.h>
#include <iostream>
#include <algorithm>
#include "crypto_key.h"
#include "log.h"
@@ -26,6 +27,7 @@ std::string EncodeUint32(unsigned int u) {
return s;
}
const uint32_t kRsaSignatureLength = 256;
const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB
}
namespace wvcdm {
@@ -52,6 +54,26 @@ CryptoSession::~CryptoSession() {
Terminate();
}
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
OEMCrypto_ProvisioningMethod method;
switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) {
case OEMCrypto_OEMCertificate:
*token_type = kClientTokenOemCert;
break;
case OEMCrypto_Keybox:
*token_type = kClientTokenKeybox;
break;
case OEMCrypto_DrmCertificate:
*token_type = kClientTokenDrmCert;
break;
case OEMCrypto_ProvisioningError:
default:
LOGE("OEMCrypto_GetProvisioningMethod failed", method);
return false;
}
return true;
}
void CryptoSession::Init() {
LOGV("CryptoSession::Init");
AutoLock auto_lock(crypto_lock_);
@@ -64,6 +86,9 @@ void CryptoSession::Init() {
}
initialized_ = true;
}
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
initialized_ = false;
}
}
void CryptoSession::Terminate() {
@@ -83,35 +108,81 @@ void CryptoSession::Terminate() {
initialized_ = false;
}
bool CryptoSession::ValidateKeybox() {
LOGV("CryptoSession::ValidateKeybox: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
bool CryptoSession::GetTokenFromKeybox(std::string* token) {
OEMCryptoResult status;
std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0');
// lock is held by caller
size_t buf_size = temp_buffer.size();
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
status = OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_);
if (status == OEMCrypto_SUCCESS) {
token->swap(temp_buffer);
return true;
}
OEMCryptoResult result = OEMCrypto_IsKeyboxValid(requested_security_level_);
return (OEMCrypto_SUCCESS == result);
LOGE("CryptoSession::GetTokenFromKeybox : error %d.", status);
return false;
}
bool CryptoSession::GetToken(std::string* token) {
if (!token) {
LOGE("CryptoSession::GetToken : No token passed to method.");
bool CryptoSession::GetTokenFromOemCert(std::string* token) {
OEMCryptoResult status;
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
// lock is held by caller
bool retrying = false;
while (true) {
size_t buf_size = temp_buffer.size();
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
if (OEMCrypto_SUCCESS == status) {
token->swap(temp_buffer);
return true;
}
if (OEMCrypto_ERROR_SHORT_BUFFER && !retrying) {
temp_buffer.resize(buf_size);
retrying = true;
continue;
}
LOGE("CryptoSession::GetTokenFromOemCert : error %d.", status);
return false;
}
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
size_t bufSize = sizeof(buf);
LOGV("CryptoSession::GetToken: Lock");
}
bool CryptoSession::GetClientToken(std::string* token) {
if (!token) {
LOGE("CryptoSession::GetClientToken : No token passed to method.");
return false;
}
LOGV("CryptoSession::GetClientToken: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
OEMCryptoResult sts =
OEMCrypto_GetKeyData(buf, &bufSize, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
// Only keybox is used for client token. All other cases use DRM Cert.
if (pre_provision_token_type_ != kClientTokenKeybox) {
return false;
}
return GetTokenFromKeybox(token);
}
bool CryptoSession::GetProvisioningToken(std::string* token) {
if (!token) {
LOGE("CryptoSession::GetProvisioningToken : No token passed to method.");
return false;
}
LOGV("CryptoSession::GetProvisioningToken: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
if (pre_provision_token_type_ == kClientTokenKeybox) {
return GetTokenFromKeybox(token);
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
return GetTokenFromOemCert(token);
} else {
return false;
}
token->assign((const char*)buf, (size_t)bufSize);
return true;
}
CdmSecurityLevel CryptoSession::GetSecurityLevel() {
@@ -148,25 +219,31 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
return false;
}
std::vector<uint8_t> id;
size_t id_length = 32;
id.resize(id_length);
LOGV("CryptoSession::GetDeviceUniqueId: Lock");
AutoLock auto_lock(crypto_lock_);
if (!initialized_) {
return false;
}
OEMCryptoResult sts =
if (pre_provision_token_type_ == kClientTokenOemCert) {
return GetTokenFromOemCert(device_id);
} else {
// Device's authentication root is a keybox.
// Or not. If no keybox, let the OEMCrypto call fail.
std::vector<uint8_t> id;
size_t id_length = 32;
id.resize(id_length);
OEMCryptoResult sts =
OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) {
return false;
}
if (OEMCrypto_SUCCESS != sts) {
return false;
}
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
return true;
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
return true;
}
}
bool CryptoSession::GetApiVersion(uint32_t* version) {
@@ -690,6 +767,13 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
sts = OEMCrypto_CopyBuffer(requested_security_level_,
params.encrypt_buffer, params.encrypt_length,
&buffer_descriptor, params.subsample_flags);
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE &&
params.encrypt_length > kMaximumChunkSize) {
// OEMCrypto_CopyBuffer rejected the buffer as too large, so chunk it up
// into 100 KiB sections.
sts = CopyBufferInChunks(params, buffer_descriptor);
}
}
if (params.is_encrypted && params.cipher_mode != cipher_mode_) {
return INCORRECT_CRYPTO_MODE;
@@ -698,7 +782,7 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
OEMCrypto_CENCEncryptPatternDesc pattern_descriptor;
pattern_descriptor.encrypt = params.pattern_descriptor.encrypt_blocks;
pattern_descriptor.skip = params.pattern_descriptor.skip_blocks;
pattern_descriptor.offset = params.pattern_descriptor.offset_blocks;
pattern_descriptor.offset = 0; // Deprecated field
AutoLock auto_lock(crypto_lock_);
// Check if key needs to be selected
if (params.is_encrypted) {
@@ -710,6 +794,26 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, &pattern_descriptor, params.subsample_flags);
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
// OEMCrypto_DecryptCENC rejected the buffer as too large, so chunk it up
// into sections no more than 100 KiB. The exact chunk size needs to be
// an even number of pattern repetitions long or else the pattern will get
// out of sync.
const size_t pattern_length =
(pattern_descriptor.encrypt + pattern_descriptor.skip) *
kAes128BlockSize;
const size_t chunk_size =
pattern_length > 0 ?
kMaximumChunkSize - (kMaximumChunkSize % pattern_length) :
kMaximumChunkSize;
if (params.encrypt_length > chunk_size) {
sts = DecryptInChunks(params, buffer_descriptor, pattern_descriptor,
chunk_size);
}
}
}
switch (sts) {
@@ -1302,4 +1406,182 @@ size_t CryptoSession::GenericEncryptionBlockSize(
}
}
OEMCryptoResult CryptoSession::CopyBufferInChunks(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc buffer_descriptor) {
size_t remaining_encrypt_length = params.encrypt_length;
uint8_t subsample_flags = OEMCrypto_FirstSubsample;
while (remaining_encrypt_length > 0) {
// Calculate the size of the next chunk and its offset into the original
// buffer.
const size_t chunk_size = std::min(remaining_encrypt_length,
kMaximumChunkSize);
const size_t additional_offset =
params.encrypt_length - remaining_encrypt_length;
// Update the remaining length of the original buffer only after calculating
// the new values.
remaining_encrypt_length -= chunk_size;
// Update the destination buffer with the new offset.
switch (buffer_descriptor.type) {
case OEMCrypto_BufferType_Clear:
buffer_descriptor.buffer.clear.address =
static_cast<uint8_t*>(params.decrypt_buffer) +
params.decrypt_buffer_offset + additional_offset;
buffer_descriptor.buffer.clear.max_length =
params.decrypt_buffer_length -
(params.decrypt_buffer_offset + additional_offset);
break;
case OEMCrypto_BufferType_Secure:
buffer_descriptor.buffer.secure.offset =
params.decrypt_buffer_offset + additional_offset;
break;
case OEMCrypto_BufferType_Direct:
// OEMCrypto_BufferType_Direct does not need modification.
break;
}
// Re-add "last subsample" flag if this is the last subsample.
if (remaining_encrypt_length == 0) {
subsample_flags |= OEMCrypto_LastSubsample;
}
OEMCryptoResult sts = OEMCrypto_CopyBuffer(
requested_security_level_, params.encrypt_buffer + additional_offset,
chunk_size, &buffer_descriptor, subsample_flags);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
// Clear any subsample flags before the next loop iteration.
subsample_flags = 0;
}
return OEMCrypto_SUCCESS;
}
OEMCryptoResult CryptoSession::DecryptInChunks(
const CdmDecryptionParameters& params,
const OEMCrypto_DestBufferDesc& full_buffer_descriptor,
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor,
size_t max_chunk_size) {
size_t remaining_encrypt_length = params.encrypt_length;
uint8_t subsample_flags =
(params.subsample_flags & OEMCrypto_FirstSubsample) ?
OEMCrypto_FirstSubsample : 0;
std::vector<uint8_t> iv = *params.iv;
const size_t pattern_length_in_bytes =
(pattern_descriptor.encrypt + pattern_descriptor.skip) *
kAes128BlockSize;
while (remaining_encrypt_length > 0) {
// Calculate the size of the next chunk and its offset into the
// original buffer.
const size_t chunk_size = std::min(remaining_encrypt_length,
max_chunk_size);
const size_t additional_offset =
params.encrypt_length - remaining_encrypt_length;
// Update the remaining length of the original buffer only after
// calculating the new values.
remaining_encrypt_length -= chunk_size;
// Update the destination buffer with the new offset. Because OEMCrypto can
// modify the OEMCrypto_DestBufferDesc during the call to
// OEMCrypto_DecryptCENC, (and is known to do so on some platforms) a new
// OEMCrypto_DestBufferDesc must be allocated for each call.
OEMCrypto_DestBufferDesc buffer_descriptor = full_buffer_descriptor;
switch (buffer_descriptor.type) {
case OEMCrypto_BufferType_Clear:
buffer_descriptor.buffer.clear.address += additional_offset;
buffer_descriptor.buffer.clear.max_length -= additional_offset;
break;
case OEMCrypto_BufferType_Secure:
buffer_descriptor.buffer.secure.offset += additional_offset;
break;
case OEMCrypto_BufferType_Direct:
// OEMCrypto_BufferType_Direct does not need modification.
break;
}
// Re-add "last subsample" flag if this is the last subsample.
if (remaining_encrypt_length == 0 &&
params.subsample_flags & OEMCrypto_LastSubsample) {
subsample_flags |= OEMCrypto_LastSubsample;
}
// block_offset and pattern_descriptor do not need to change because
// max_chunk_size is guaranteed to be an even multiple of the
// pattern length long, which is also guaranteed to be an exact number
// of AES blocks long.
OEMCryptoResult sts = OEMCrypto_DecryptCENC(
oec_session_id_, params.encrypt_buffer + additional_offset,
chunk_size, params.is_encrypted, &iv.front(), params.block_offset,
&buffer_descriptor, &pattern_descriptor, subsample_flags);
if (sts != OEMCrypto_SUCCESS) {
return sts;
}
// If we are not yet done, update the IV so that it is valid for the next
// iteration.
if (remaining_encrypt_length != 0) {
if (cipher_mode_ == kCipherModeCtr) {
// For CTR modes, update the IV depending on how many encrypted blocks
// we passed. Since we calculated the chunk size to be an even number
// of crypto blocks and pattern repetitions in size, we can do a
// simplified calculation for this.
uint64_t encrypted_blocks_passed = 0;
if (pattern_length_in_bytes == 0) {
encrypted_blocks_passed = chunk_size / kAes128BlockSize;
} else {
const size_t pattern_repetitions_passed =
chunk_size / pattern_length_in_bytes;
encrypted_blocks_passed =
pattern_repetitions_passed * pattern_descriptor.encrypt;
}
IncrementIV(encrypted_blocks_passed, &iv);
} else if (cipher_mode_ == kCipherModeCbc) {
// For CBC modes, use the previous ciphertext block.
// Stash the last crypto block in the IV. We don't have to handle
// partial crypto blocks here because we know we broke the buffer into
// chunks along even crypto block boundaries.
const uint8_t* const buffer_end =
params.encrypt_buffer + additional_offset + chunk_size;
const uint8_t* block_end = NULL;
if (pattern_length_in_bytes == 0) {
// For cbc1, the last encrypted block is the last block of the
// subsample.
block_end = buffer_end;
} else {
// For cbcs, we must look for the last encrypted block, which is
// probably not the last block of the subsample. Luckily, since the
// buffer size is guaranteed to be an even number of pattern
// repetitions long, we can use the pattern to know how many blocks to
// look back.
block_end = buffer_end - kAes128BlockSize * pattern_descriptor.skip;
}
iv.assign(block_end - kAes128BlockSize, block_end);
}
}
// Clear any subsample flags before the next loop iteration.
subsample_flags = 0;
}
return OEMCrypto_SUCCESS;
}
void CryptoSession::IncrementIV(uint64_t increase_by,
std::vector<uint8_t>* iv_out) {
std::vector<uint8_t>& iv = *iv_out;
uint64_t* counter_buffer = reinterpret_cast<uint64_t*>(&iv[8]);
(*counter_buffer) = htonll64(ntohll64(*counter_buffer) + increase_by);
}
} // namespace wvcdm

View File

@@ -6,6 +6,7 @@
#include <string>
#include "file_store.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
@@ -35,6 +36,9 @@ using video_widevine_client::sdk::NameValue;
using video_widevine_client::sdk::UsageInfo;
using video_widevine_client::sdk::UsageInfo_ProviderSession;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::DrmDeviceCertificate;
namespace {
const char kCertificateFileName[] = "cert.bin";
@@ -110,7 +114,9 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
}
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
std::string* wrapped_private_key) {
std::string* wrapped_private_key,
std::string* serial_number,
uint32_t* system_id) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
return false;
@@ -138,11 +144,55 @@ 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;
}
bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
std::string* serial_number,
uint32_t* system_id) {
LOGI("[WEM] ExtractDeviceInfo");
// 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;
}
}
}
return true;
}
bool DeviceFiles::HasCertificate() {
if (!initialized_) {
LOGW("DeviceFiles::HasCertificate: not initialized");
@@ -168,7 +218,8 @@ bool DeviceFiles::StoreLicense(
const CdmKeyMessage& license_renewal_request,
const CdmKeyResponse& license_renewal,
const std::string& release_server_url, int64_t playback_start_time,
int64_t last_playback_time, const CdmAppParameterMap& app_parameters) {
int64_t last_playback_time, int64_t grace_period_end_time,
const CdmAppParameterMap& app_parameters) {
if (!initialized_) {
LOGW("DeviceFiles::StoreLicense: not initialized");
return false;
@@ -201,6 +252,7 @@ bool DeviceFiles::StoreLicense(
license->set_release_server_url(release_server_url);
license->set_playback_start_time(playback_start_time);
license->set_last_playback_time(last_playback_time);
license->set_grace_period_end_time(grace_period_end_time);
NameValue* app_params;
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
iter != app_parameters.end(); ++iter) {
@@ -221,7 +273,8 @@ bool DeviceFiles::RetrieveLicense(
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
std::string* release_server_url, int64_t* playback_start_time,
int64_t* last_playback_time, CdmAppParameterMap* app_parameters) {
int64_t* last_playback_time, int64_t* grace_period_end_time,
CdmAppParameterMap* app_parameters) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveLicense: not initialized");
return false;
@@ -270,6 +323,7 @@ bool DeviceFiles::RetrieveLicense(
*release_server_url = license.release_server_url();
*playback_start_time = license.playback_start_time();
*last_playback_time = license.last_playback_time();
*grace_period_end_time = license.grace_period_end_time();
for (int i = 0; i < license.app_parameters_size(); ++i) {
(*app_parameters)[license.app_parameters(i).name()] =
license.app_parameters(i).value();
@@ -285,6 +339,34 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
return RemoveFile(key_set_id + kLicenseFileNameExt);
}
bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
return false;
}
// Get list of filenames
std::vector<std::string> filenames;
if (!ListFiles(&filenames)) {
return false;
}
// 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++) {
std::string* name = &filenames[i];
std::size_t pos = name->find(kLicenseFileNameExt);
if (pos == std::string::npos) {
// Skip this file - extension does not match
continue;
}
// Store filename (minus extension). This should be a key set ID.
key_set_ids->push_back(name->substr(0, pos));
}
return true;
}
bool DeviceFiles::DeleteAllLicenses() {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
@@ -688,7 +770,7 @@ bool DeviceFiles::RetrieveHashedFile(
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFileWithHash: Unable to get base path");
LOGW("DeviceFiles::RetrieveHashedFile: Unable to get base path");
return false;
}
@@ -765,6 +847,15 @@ bool DeviceFiles::FileExists(const std::string& name) {
return file_system_->Exists(path);
}
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");
return false;
}
return file_system_->List(path, names);
}
bool DeviceFiles::RemoveFile(const std::string& name) {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {

View File

@@ -39,6 +39,10 @@ message License {
optional int64 playback_start_time = 8 [default = 0];
optional int64 last_playback_time = 9 [default = 0];
repeated NameValue app_parameters = 10;
// This will be 0/missing if the grace period has not expired; otherwise it
// 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];
}
message UsageInfo {

View File

@@ -35,9 +35,9 @@ const int kDefaultNumJsonTokens = 128;
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::WidevineCencHeader;
using video_widevine_server::sdk::WidevineCencHeader_Algorithm;
using video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR;
using video_widevine::WidevineCencHeader;
using video_widevine::WidevineCencHeader_Algorithm;
using video_widevine::WidevineCencHeader_Algorithm_AESCTR;
InitializationData::InitializationData(const std::string& type,
const CdmInitData& data)
@@ -476,9 +476,9 @@ bool InitializationData::ConstructWidevineInitData(
cenc_header.set_provider(provider);
cenc_header.set_content_id(content_id);
if (method == kHlsMethodAes128)
cenc_header.set_protection_scheme(htonl(kFourCcCbc1));
cenc_header.set_protection_scheme(kFourCcCbc1);
else
cenc_header.set_protection_scheme(htonl(kFourCcCbcs));
cenc_header.set_protection_scheme(kFourCcCbcs);
cenc_header.SerializeToString(init_data_proto);
return true;
}

View File

@@ -14,6 +14,7 @@
#include "policy_engine.h"
#include "privacy_crypto.h"
#include "properties.h"
#include "service_certificate.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
@@ -27,67 +28,34 @@ std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
std::string kWVCdmVersionKey = "widevine_cdm_version";
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
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};
}
} // namespace
const uint32_t kFourCcCbc1 = 0x63626331;
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 wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ClientIdentification_ClientCapabilities;
using video_widevine_server::sdk::ClientIdentification_NameValue;
using video_widevine_server::sdk::DeviceCertificate;
using video_widevine_server::sdk::EncryptedClientIdentification;
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_KeyContainer;
using video_widevine_server::sdk::LicenseError;
using video_widevine_server::sdk::LicenseIdentification;
using video_widevine_server::sdk::LicenseRequest;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_WebM;
using video_widevine_server::sdk::
LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine_server::sdk::SignedDeviceCertificate;
using video_widevine_server::sdk::SignedMessage;
using video_widevine::ClientIdentification;
using video_widevine::ClientIdentification_ClientCapabilities;
using video_widevine::ClientIdentification_NameValue;
using video_widevine::DrmDeviceCertificate;
using video_widevine::EncryptedClientIdentification;
using video_widevine::License;
using video_widevine::License_KeyContainer;
using video_widevine::LicenseError;
using video_widevine::LicenseIdentification;
using video_widevine::LicenseRequest;
using video_widevine::LicenseRequest_ContentIdentification;
using video_widevine::LicenseRequest_ContentIdentification_CencDeprecated;
using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated;
using video_widevine::LicenseRequest_ContentIdentification_ExistingLicense;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::SignedMessage;
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
std::vector<CryptoKey> key_array;
@@ -115,12 +83,23 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
}
uint32_t four_cc = kFourCcCenc;
if (license.has_protection_scheme()) {
four_cc = ntohl(license.protection_scheme());
four_cc = license.protection_scheme();
}
switch (four_cc) {
// b/30713238: Android N assumed that the "protection scheme" Four
// CC code, after being extracted from the protobuf, was host byte
// order dependent. Later versions do not assume this, and thus,
// for backwards compatibility, must support both byte orders.
case kFourCcCbc1:
case kFourCcCbcs:
case kFourCcLittleEndianCbc1:
case kFourCcLittleEndianCbcs:
key.set_cipher_mode(kCipherModeCbc);
break;
default:
key.set_cipher_mode(kCipherModeCtr);
break;
}
if (four_cc == kFourCcCbc1 || four_cc == kFourCcCbcs)
key.set_cipher_mode(kCipherModeCbc);
else
key.set_cipher_mode(kCipherModeCtr);
key_array.push_back(key);
break;
}
@@ -143,7 +122,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
}
CdmLicense::CdmLicense(const CdmSessionId& session_id)
: session_(NULL),
: crypto_session_(NULL),
policy_engine_(NULL),
session_id_(session_id),
initialized_(false),
@@ -152,7 +131,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
clock_(new Clock()) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
: session_(NULL),
: crypto_session_(NULL),
policy_engine_(NULL),
session_id_(session_id),
initialized_(false),
@@ -163,8 +142,10 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine) {
bool CdmLicense::Init(
ServiceCertificate* service_certificate, const std::string& client_token,
CdmClientTokenType client_token_type, const std::string& serial_number,
CryptoSession* session, PolicyEngine* policy_engine) {
if (clock_.get() == NULL) {
LOGE("CdmLicense::Init: clock parameter not provided");
return false;
@@ -173,8 +154,8 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
LOGE("CdmLicense::Init: empty session id provided");
return false;
}
if (token.size() == 0) {
LOGE("CdmLicense::Init: empty token provided");
if (client_token.size() == 0) {
LOGE("CdmLicense::Init: empty client token provided");
return false;
}
if (session == NULL || !session->IsOpen()) {
@@ -185,15 +166,19 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
LOGE("CdmLicense::Init: no policy engine provided");
return false;
}
token_ = token;
session_ = session;
service_certificate_ = service_certificate;
client_token_ = client_token;
client_token_type_ = client_token_type;
serial_number_ = serial_number;
crypto_session_ = session;
policy_engine_ = policy_engine;
initialized_ = true;
return true;
}
CdmResponseType CdmLicense::PrepareKeyRequest(
const InitializationData& init_data, const CdmLicenseType license_type,
const InitializationData& init_data, CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
@@ -224,63 +209,29 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return INVALID_PARAMETERS_LIC_7;
}
std::string service_certificate;
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id_);
if (privacy_mode_enabled) {
if (!GetServiceCertificate(&service_certificate)) {
stored_init_data_.reset(new InitializationData(init_data));
return PrepareServiceCertificateRequest(signed_request, server_url)
? KEY_MESSAGE
: LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
// If privacy mode and no service certificate, initiate a
// service certificate request.
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;
}
std::string request_id;
session_->GenerateRequestId(&request_id);
crypto_session_->GenerateRequestId(&request_id);
LicenseRequest license_request;
CdmResponseType status =
PrepareClientId(privacy_mode_enabled, service_certificate, app_parameters,
&license_request);
CdmResponseType status;
status = PrepareClientId(app_parameters, &license_request);
if (NO_ERROR != status) return status;
// Content Identification may be a cenc_id, a webm_id or a license_id
LicenseRequest_ContentIdentification* content_id =
license_request.mutable_content_id();
if (init_data.is_cenc() || init_data.is_hls()) {
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id();
if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
return CENC_INIT_DATA_UNAVAILABLE;
}
if (!PrepareContentId(license_type, request_id, cenc_content_id)) {
return PREPARE_CENC_CONTENT_ID_FAILED;
}
} else if (init_data.is_webm()) {
LicenseRequest_ContentIdentification_WebM* webm_content_id =
content_id->mutable_webm_id();
if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
return WEBM_INIT_DATA_UNAVAILABLE;
}
if (!PrepareContentId(license_type, request_id, webm_content_id)) {
return PREPARE_WEBM_CONTENT_ID_FAILED;
}
} else {
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA_FORMAT;
}
status = PrepareContentId(init_data, license_type, request_id,
&license_request);
if (NO_ERROR != status) return status;
license_request.set_type(LicenseRequest::NEW);
@@ -289,12 +240,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
uint32_t nonce;
if (!session_->GenerateNonce(&nonce)) {
if (!crypto_session_->GenerateNonce(&nonce)) {
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
}
license_request.set_key_control_nonce(nonce);
LOGD("PrepareKeyRequest: nonce=%u", nonce);
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
std::string serialized_license_req;
@@ -305,8 +256,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// Derive signing and encryption keys and construct signature.
std::string license_request_signature;
if (!session_->PrepareRequest(serialized_license_req, false,
&license_request_signature)) {
if (!crypto_session_->PrepareRequest(serialized_license_req, false,
&license_request_signature)) {
signed_request->clear();
return LICENSE_REQUEST_SIGNING_ERROR;
}
@@ -317,7 +268,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return EMPTY_LICENSE_REQUEST;
}
// Put serialize license request and signature together
// Put serialized license request and signature together
SignedMessage signed_message;
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature);
@@ -350,6 +301,17 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
return LICENSE_RENEWAL_PROHIBITED;
}
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;
}
}
LicenseRequest license_request;
if (is_renewal)
license_request.set_type(LicenseRequest::RENEWAL);
@@ -359,23 +321,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
license_request.set_request_time(clock_->GetCurrentTime());
if (renew_with_client_id_) {
std::string service_certificate;
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id_);
if (privacy_mode_enabled) {
if (!GetServiceCertificate(&service_certificate)) {
return PrepareServiceCertificateRequest(signed_request, server_url)
? KEY_MESSAGE
: LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
}
CdmResponseType status =
PrepareClientId(privacy_mode_enabled, service_certificate,
app_parameters, &license_request);
CdmResponseType status = PrepareClientId(app_parameters, &license_request);
if (NO_ERROR != status) return status;
}
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
license_request.mutable_content_id()->mutable_license();
license_request.mutable_content_id()->mutable_existing_license();
LicenseIdentification license_id = policy_engine_->license_id();
current_license->mutable_license_id()->CopyFrom(license_id);
@@ -385,12 +336,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
if (!provider_session_token_.empty()) {
if (!is_renewal) {
CdmResponseType status =
session_->DeactivateUsageInformation(provider_session_token_);
crypto_session_->DeactivateUsageInformation(provider_session_token_);
if (NO_ERROR != status) return status;
}
std::string usage_report;
CdmResponseType status = session_->GenerateUsageReport(
CdmResponseType status = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
&seconds_since_started, &seconds_since_last_played);
if (!is_renewal) {
@@ -415,12 +366,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
uint32_t nonce;
if (!session_->GenerateNonce(&nonce)) {
if (!crypto_session_->GenerateNonce(&nonce)) {
return LICENSE_RENEWAL_NONCE_GENERATION_ERROR;
}
license_request.set_key_control_nonce(nonce);
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
license_request.set_protocol_version(video_widevine::VERSION_2_1);
// License request is complete. Serialize it.
std::string serialized_license_req;
@@ -428,8 +379,8 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
// Construct signature.
std::string license_request_signature;
if (!session_->PrepareRenewalRequest(serialized_license_req,
&license_request_signature))
if (!crypto_session_->PrepareRenewalRequest(serialized_license_req,
&license_request_signature))
return LICENSE_RENEWAL_SIGNING_ERROR;
if (license_request_signature.empty()) {
@@ -473,12 +424,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
case SignedMessage::LICENSE:
break;
case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status =
VerifySignedServiceCertificate(signed_response.msg());
CdmResponseType status;
status = service_certificate_->HandleResponse(signed_response.msg());
if (status != NO_ERROR) {
return status;
}
Properties::SetServiceCertificate(session_id_, signed_response.msg());
return NEED_KEY;
}
case SignedMessage::ERROR_RESPONSE:
@@ -507,8 +457,8 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return SESSION_KEYS_NOT_FOUND;
}
if (!session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return GENERATE_DERIVED_KEYS_ERROR;
}
@@ -540,7 +490,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return NO_CONTENT_KEY;
}
if (license.id().type() == video_widevine_server::sdk::OFFLINE &&
if (license.id().type() == video_widevine::OFFLINE &&
license.policy().can_persist())
is_offline_ = true;
@@ -551,11 +501,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
server_url_ = license.policy().renewal_server_url();
}
if (license.policy().has_renew_with_client_id()) {
renew_with_client_id_ = license.policy().renew_with_client_id();
if (license.policy().has_always_include_client_id()) {
renew_with_client_id_ = license.policy().always_include_client_id();
}
CdmResponseType resp = session_->LoadKeys(
CdmResponseType resp = crypto_session_->LoadKeys(
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
key_array, provider_session_token_);
@@ -591,12 +541,11 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
case SignedMessage::LICENSE:
break;
case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status =
VerifySignedServiceCertificate(signed_response.msg());
CdmResponseType status;
status = service_certificate_->HandleResponse(signed_response.msg());
if (status != NO_ERROR) {
return status;
}
Properties::SetServiceCertificate(session_id_, signed_response.msg());
return NEED_KEY;
}
case SignedMessage::ERROR_RESPONSE:
@@ -627,15 +576,15 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
return LICENSE_ID_NOT_FOUND;
}
if (license.policy().has_renew_with_client_id()) {
renew_with_client_id_ = license.policy().renew_with_client_id();
if (license.policy().has_always_include_client_id()) {
renew_with_client_id_ = license.policy().always_include_client_id();
}
if (!is_renewal) {
if (!license.id().has_provider_session_token()) return KEY_ADDED;
provider_session_token_ = license.id().provider_session_token();
CdmResponseType status = session_->ReleaseUsageInformation(
CdmResponseType status = crypto_session_->ReleaseUsageInformation(
signed_response.msg(), signed_response.signature(),
provider_session_token_);
return (NO_ERROR == status) ? KEY_ADDED : status;
@@ -648,8 +597,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(),
key_array.size(), &key_array[0])) {
if (crypto_session_->RefreshKeys(signed_response.msg(),
signed_response.signature(),
key_array.size(), &key_array[0])) {
policy_engine_->UpdateLicense(license);
return KEY_ADDED;
@@ -662,7 +612,7 @@ 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 last_playback_time, int64_t grace_period_end_time) {
if (license_request.empty() || license_response.empty()) {
LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
@@ -688,7 +638,9 @@ bool CdmLicense::RestoreOfflineLicense(
if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg();
} else {
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false;
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
}
CdmResponseType sts = HandleKeyResponse(license_response);
@@ -706,7 +658,7 @@ bool CdmLicense::RestoreOfflineLicense(
CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid;
int64_t seconds_since_started, seconds_since_last_played;
sts = session_->GenerateUsageReport(
sts = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
&seconds_since_started, &seconds_since_last_played);
@@ -730,7 +682,8 @@ bool CdmLicense::RestoreOfflineLicense(
}
}
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time);
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time,
grace_period_end_time);
return true;
}
@@ -762,7 +715,9 @@ bool CdmLicense::RestoreLicenseForRelease(
if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg();
} else {
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false;
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
}
SignedMessage signed_response;
@@ -799,8 +754,8 @@ bool CdmLicense::RestoreLicenseForRelease(
if (license.id().has_provider_session_token())
provider_session_token_ = license.id().provider_session_token();
if (license.policy().has_renew_with_client_id())
renew_with_client_id_ = license.policy().renew_with_client_id();
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()) {
@@ -809,8 +764,8 @@ bool CdmLicense::RestoreLicenseForRelease(
}
if (license.id().has_provider_session_token()) {
if (!session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key()))
return false;
} else {
return KEY_ADDED == HandleKeyResponse(license_response);
@@ -830,92 +785,6 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}
CdmResponseType CdmLicense::VerifySignedServiceCertificate(
const std::string& signed_service_certificate) {
return VerifyAndExtractSignedServiceCertificate(signed_service_certificate,
NULL);
}
bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense::PrepareServiceCertificateRequest: not initialized");
return false;
}
if (!signed_request) {
LOGE(
"CdmLicense::PrepareServiceCertificateRequest: no signed request"
" provided");
return false;
}
if (!server_url) {
LOGE(
"CdmLicense::PrepareServiceCertificateRequest: no server url"
" provided");
return false;
}
SignedMessage signed_message;
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
signed_message.SerializeToString(signed_request);
*server_url = server_url_;
return true;
}
CdmResponseType CdmLicense::VerifyAndExtractSignedServiceCertificate(
const std::string& signed_certificate, std::string* certificate) {
SignedDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(signed_certificate)) {
LOGE(
"CdmLicense::VerifyAndExtractSignedServiceCertificate: unable to parse "
"signed device certificate");
return DEVICE_CERTIFICATE_ERROR_1;
}
RsaPublicKey root_ca_key;
std::string ca_public_key(
&kServiceCertificateCAPublicKey[0],
&kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]);
if (!root_ca_key.Init(ca_public_key)) {
LOGE(
"CdmLicense::VerifyAndExtractSignedServiceCertificate: public key "
"initialization failed");
return DEVICE_CERTIFICATE_ERROR_2;
}
if (!root_ca_key.VerifySignature(
signed_service_certificate.device_certificate(),
signed_service_certificate.signature())) {
LOGE(
"CdmLicense::VerifyAndExtractSignedServiceCertificate: service "
"certificate verification failed");
return DEVICE_CERTIFICATE_ERROR_3;
}
DeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(
signed_service_certificate.device_certificate())) {
LOGE(
"CdmLicense::VerifyAndExtractSignedServiceCertificate: unable to parse "
"retrieved service certificate");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (service_certificate.type() !=
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
LOGE(
"CdmLicense::VerifyAndExtractSignedServiceCertificate: certificate not "
"of type service, %d",
service_certificate.type());
return INVALID_DEVICE_CERTIFICATE_TYPE;
}
if (certificate != NULL) {
*certificate = signed_service_certificate.device_certificate();
}
return NO_ERROR;
}
CdmResponseType CdmLicense::HandleKeyErrorResponse(
const SignedMessage& signed_message) {
LicenseError license_error;
@@ -925,28 +794,46 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
}
switch (license_error.error_code()) {
case LicenseError::INVALID_DEVICE_CERTIFICATE:
case LicenseError::INVALID_DRM_DEVICE_CERTIFICATE:
return NEED_PROVISIONING;
case LicenseError::REVOKED_DEVICE_CERTIFICATE:
case LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE:
return DEVICE_REVOKED;
case LicenseError::SERVICE_UNAVAILABLE:
default:
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d",
LOGW("CdmLicense::HandleKeyErrorResponse: Unknown error type = %d",
license_error.error_code());
return KEY_ERROR;
}
}
// Return the ClientIdentification message token type for license request.
// NOTE: an OEM Cert should never be presented to the provisioning server.
bool CdmLicense::GetClientTokenType(
ClientIdentification::TokenType* token_type) {
switch (client_token_type_) {
case kClientTokenKeybox:
*token_type = ClientIdentification::KEYBOX;
return true;
case kClientTokenDrmCert:
*token_type = ClientIdentification::DRM_DEVICE_CERTIFICATE;
return true;
case kClientTokenOemCert:
default:
// shouldn't happen
return false;
}
}
CdmResponseType CdmLicense::PrepareClientId(
bool encrypt, const std::string& certificate,
const CdmAppParameterMap& app_parameters, LicenseRequest* license_request) {
ClientIdentification* client_id = license_request->mutable_client_id();
if (Properties::use_certificates_as_identification())
client_id->set_type(ClientIdentification::DEVICE_CERTIFICATE);
else
client_id->set_type(ClientIdentification::KEYBOX);
client_id->set_token(token_);
ClientIdentification::TokenType token_type;
if (!GetClientTokenType(&token_type)) {
return LICENSING_CLIENT_TOKEN_ERROR_1;
}
client_id->set_type(token_type);
client_id->set_token(client_token_);
ClientIdentification_NameValue* client_info;
CdmAppParameterMap::const_iterator iter;
@@ -986,7 +873,11 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info->set_name(kBuildInfoKey);
client_info->set_value(value);
}
if (session_->GetDeviceUniqueId(&value)) {
if (!serial_number_.empty()) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(b2a_hex(serial_number_));
} else if (crypto_session_->GetDeviceUniqueId(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(value);
@@ -999,55 +890,55 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info = client_id->add_client_info();
client_info->set_name(kOemCryptoSecurityPatchLevelKey);
std::stringstream ss;
ss << (uint32_t)session_->GetSecurityPatchLevel();
ss << (uint32_t)crypto_session_->GetSecurityPatchLevel();
client_info->set_value(ss.str());
ClientIdentification_ClientCapabilities* client_capabilities =
client_id->mutable_client_capabilities();
bool supports_usage_information;
if (session_->UsageInformationSupport(&supports_usage_information)) {
if (crypto_session_->UsageInformationSupport(&supports_usage_information)) {
client_capabilities->set_session_token(supports_usage_information);
}
client_capabilities->set_anti_rollback_usage_table(
session_->IsAntiRollbackHwPresent());
crypto_session_->IsAntiRollbackHwPresent());
uint32_t api_version = 0;
if (session_->GetApiVersion(&api_version)) {
if (crypto_session_->GetApiVersion(&api_version)) {
client_capabilities->set_oem_crypto_api_version(api_version);
}
CryptoSession::HdcpCapability current_version, max_version;
if (session_->GetHdcpCapabilities(&current_version, &max_version)) {
if (crypto_session_->GetHdcpCapabilities(&current_version, &max_version)) {
switch (max_version) {
case HDCP_NONE:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
break;
case HDCP_V1:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
break;
case HDCP_V2:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
break;
case HDCP_V2_1:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
break;
case HDCP_V2_2:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
break;
case HDCP_NO_DIGITAL_OUTPUT:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
video_widevine::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NO_DIGITAL_OUTPUT);
break;
default:
@@ -1058,78 +949,80 @@ CdmResponseType CdmLicense::PrepareClientId(
}
}
if (encrypt) {
if (Properties::UsePrivacyMode(session_id_)) {
if (!service_certificate_->HasCertificate()) {
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
EncryptedClientIdentification* encrypted_client_id =
license_request->mutable_encrypted_client_id();
DeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(certificate)) {
LOGE(
"CdmLicense::PrepareClientId: unable to parse retrieved "
"service certificate");
return PARSE_SERVICE_CERTIFICATE_ERROR;
CdmResponseType status;
status = service_certificate_->EncryptClientId(crypto_session_, client_id,
encrypted_client_id);
if (NO_ERROR == status) {
license_request->clear_client_id();
} else {
license_request->clear_encrypted_client_id();
}
if (service_certificate.type() !=
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
LOGE(
"CdmLicense::PrepareClientId: retrieved certificate not of type"
" service, %d",
service_certificate.type());
return SERVICE_CERTIFICATE_TYPE_ERROR;
}
encrypted_client_id->set_service_id(service_certificate.service_id());
encrypted_client_id->set_service_certificate_serial_number(
service_certificate.serial_number());
std::string iv(KEY_IV_SIZE, 0);
std::string key(KEY_SIZE, 0);
if (!session_->GetRandom(key.size(), reinterpret_cast<uint8_t*>(&key[0])))
return CLIENT_ID_GENERATE_RANDOM_ERROR;
if (!session_->GetRandom(iv.size(), reinterpret_cast<uint8_t*>(&iv[0])))
return CLIENT_ID_GENERATE_RANDOM_ERROR;
std::string id, enc_id, enc_key;
client_id->SerializeToString(&id);
AesCbcKey aes;
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;
encrypted_client_id->set_encrypted_client_id_iv(iv);
encrypted_client_id->set_encrypted_privacy_key(enc_key);
encrypted_client_id->set_encrypted_client_id(enc_id);
license_request->clear_client_id();
return status;
}
return NO_ERROR;
}
bool CdmLicense::GetServiceCertificate(std::string* service_certificate) {
std::string signed_service_certificate;
return Properties::GetServiceCertificate(session_id_,
&signed_service_certificate) &&
!signed_service_certificate.empty() &&
NO_ERROR == VerifyAndExtractSignedServiceCertificate(
signed_service_certificate, service_certificate) &&
!service_certificate->empty();
CdmResponseType CdmLicense::PrepareContentId(
const InitializationData& init_data, CdmLicenseType license_type,
const std::string& request_id, LicenseRequest* license_request) {
// Content Identification may be a cenc_id, a webm_id or a license_id
LicenseRequest_ContentIdentification* content_id =
license_request->mutable_content_id();
if (init_data.is_cenc() || init_data.is_hls()) {
LicenseRequest_ContentIdentification_CencDeprecated* cenc_content_id =
content_id->mutable_cenc_id_deprecated();
if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
return CENC_INIT_DATA_UNAVAILABLE;
}
if (!SetTypeAndId(license_type, request_id, cenc_content_id)) {
return PREPARE_CENC_CONTENT_ID_FAILED;
}
} else if (init_data.is_webm()) {
LicenseRequest_ContentIdentification_WebmDeprecated* webm_content_id =
content_id->mutable_webm_id_deprecated();
if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
return WEBM_INIT_DATA_UNAVAILABLE;
}
if (!SetTypeAndId(license_type, request_id, webm_content_id)) {
return PREPARE_WEBM_CONTENT_ID_FAILED;
}
} else {
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA_FORMAT;
}
return NO_ERROR;
}
template <typename T>
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
const std::string& request_id,
T* content_id) {
bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
const std::string& request_id,
T* content_id) {
switch (license_type) {
case kLicenseTypeOffline:
content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
content_id->set_license_type(video_widevine::OFFLINE);
break;
case kLicenseTypeStreaming:
case kLicenseTypeTemporary:
content_id->set_license_type(video_widevine_server::sdk::STREAMING);
content_id->set_license_type(video_widevine::STREAMING);
break;
default:
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",

View File

@@ -8,7 +8,7 @@
namespace {
// License protocol aliases
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef ::video_widevine::License::KeyContainer KeyContainer;
typedef KeyContainer::OutputProtection OutputProtection;
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
@@ -130,7 +130,7 @@ void LicenseKeys::ApplyConstraints(
}
void LicenseKeys::SetFromLicense(
const video_widevine_server::sdk::License& license) {
const video_widevine::License& license) {
this->Clear();
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
const KeyContainer& key = license.key(key_index);

View File

@@ -9,11 +9,13 @@
syntax = "proto2";
package video_widevine_server.sdk;
package video_widevine;
// need this if we are using libprotobuf-cpp-2.3.0-lite
option optimize_for = LITE_RUNTIME;
option java_package = "com.google.video.widevine.protos";
enum LicenseType {
STREAMING = 1;
OFFLINE = 2;
@@ -81,7 +83,15 @@ message License {
// Indicates to client that license renewal and release requests ought to
// include ClientIdentification (client_id).
optional bool renew_with_client_id = 12 [default = false];
optional bool always_include_client_id = 12 [default = false];
// Duration of grace period before playback_duration_seconds (short window)
// goes into effect. Optional.
optional int64 play_start_grace_period_seconds = 13 [default = 0];
// Enables "soft enforcement" of playback_duration_seconds, letting the user
// finish playback even if short window expires. Optional.
optional bool soft_enforce_playback_duration = 14 [default = false];
}
message KeyContainer {
@@ -117,6 +127,8 @@ message License {
}
message KeyControl {
// |key_control| is documented in:
// Widevine Modular DRM Security Integration Guide for CENC
// If present, the key control must be communicated to the secure
// environment prior to any usage. This message is automatically generated
// by the Widevine License Server SDK.
@@ -192,14 +204,19 @@ message License {
optional LicenseIdentification id = 1;
optional Policy policy = 2;
repeated KeyContainer key = 3;
// Time of the request in seconds (UTC) as set in
// LicenseRequest.request_time. If this time is not set in the request,
// the local time at the license service is used in this field.
optional int64 license_start_time = 4;
optional bool remote_attestation_verified = 5 [default = false];
// Client token generated by the content provider. Optional.
optional bytes provider_client_token = 6;
// Protection scheme identifying the encryption algorithm. Represented as one
// of the following 4CC values: 'cenc' (AES-CTR), 'cbc1' (AES-CBC),
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
// 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;
}
enum ProtocolVersion {
@@ -209,13 +226,13 @@ enum ProtocolVersion {
message LicenseRequest {
message ContentIdentification {
message CENC {
message CencDeprecated {
repeated bytes pssh = 1;
optional LicenseType license_type = 2;
optional bytes request_id = 3; // Opaque, client-specified.
}
message WebM {
message WebmDeprecated {
optional bytes header = 1;
optional LicenseType license_type = 2;
optional bytes request_id = 3; // Opaque, client-specified.
@@ -228,10 +245,25 @@ message LicenseRequest {
optional bytes session_usage_table_entry = 4;
}
// Exactly one of these must be present.
optional CENC cenc_id = 1;
optional WebM webm_id = 2;
optional ExistingLicense license = 3;
message InitData {
enum InitDataType {
CENC = 1;
WEBM = 2;
}
optional InitDataType init_data_type = 1 [default = CENC];
optional bytes init_data = 2;
optional LicenseType license_type = 3;
optional bytes request_id = 4;
}
//oneof content_id_variant {
// Exactly one of these must be present.
optional CencDeprecated cenc_id_deprecated = 1;
optional WebmDeprecated webm_id_deprecated = 2;
optional ExistingLicense existing_license = 3;
optional InitData init_data = 4;
//}
}
enum RequestType {
@@ -247,6 +279,7 @@ message LicenseRequest {
optional ClientIdentification client_id = 1;
optional ContentIdentification content_id = 2;
optional RequestType type = 3;
// Time of the request in seconds (UTC) as set by the client.
optional int64 request_time = 4;
// Old-style decimal-encoded string key control nonce.
optional bytes key_control_nonce_deprecated = 5;
@@ -261,10 +294,10 @@ message LicenseRequest {
message LicenseError {
enum Error {
// The device credentials are invalid. The device must re-provision.
INVALID_DEVICE_CERTIFICATE = 1;
INVALID_DRM_DEVICE_CERTIFICATE = 1;
// The device credentials have been revoked. Re-provisioning is not
// possible.
REVOKED_DEVICE_CERTIFICATE = 2;
REVOKED_DRM_DEVICE_CERTIFICATE = 2;
// The service is currently unavailable due to the backend being down
// or similar circumstances.
SERVICE_UNAVAILABLE = 3;
@@ -326,17 +359,38 @@ message SignedMessage {
}
message GroupKeys {
repeated License.KeyContainer key = 1;
enum GroupLicenseVersion {
GROUP_LICENSE_VERSION_1 = 0;
GROUP_LICENSE_VERSION_2 = 1;
}
message GroupKeyData {
// Required track type. This indicates the track type to which this key
// belongs.
optional string track_type = 1;
// A required signed message. The message body contains a serialized group
// msg.
optional bytes key = 2;
}
// Optional key container array used in group licensing V1. This is not used
// in V2.
repeated License.KeyContainer key = 1 [deprecated = true];
// Byte string that identifies the group to which this license material
// belongs.
optional bytes group_id = 2;
// Required version id beginning with version 2. If not present version 1
// should be assumed.
optional GroupLicenseVersion version = 3 [default = GROUP_LICENSE_VERSION_1];
// Optional key container array for group licensing V2.
repeated GroupKeyData key_data = 4;
}
// ----------------------------------------------------------------------------
// certificate_provisioning.proto
// ----------------------------------------------------------------------------
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Description:
// Public protocol buffer definitions for Widevine Device Certificate
// Provisioning protocol.
@@ -349,53 +403,80 @@ message ProvisioningOptions {
X509 = 1; // X.509 certificate.
}
optional CertificateType certificate_type = 1;
optional CertificateType certificate_type = 1 [default = WIDEVINE_DRM];
// It is recommended that the certificate_authority specify the X.509
// Subject of the signing certificate.
// Contains the application-specific name used to identify the certificate
// authority for signing the generated certificate. This is required iff the
// certificate type is X509.
optional string certificate_authority = 2;
}
// Provisioning request sent by client devices to provisioning service.
message ProvisioningRequest {
// Device root of trust and other client identification. Required.
optional ClientIdentification client_id = 1;
//oneof clear_or_encrypted_client_id {
// Device root of trust and other client identification. Required.
optional ClientIdentification client_id = 1;
optional EncryptedClientIdentification encrypted_client_id = 5;
//}
// Nonce value used to prevent replay attacks. Required.
optional bytes nonce = 2;
// Options for type of certificate to generate. Optional.
optional ProvisioningOptions options = 3;
// Stable identifier, unique for each device + application (or origin).
// Required if doing per-origin provisioning.
optional bytes stable_id = 4;
//oneof spoid_param {
// Stable identifier, unique for each device + application (or origin).
// To be deprecated.
optional bytes stable_id = 4;
// Service provider ID from the service certificate's provider_id field.
// Preferred parameter.
optional bytes provider_id = 6;
// Client-generated stable per-origin identifier to be copied directly
// to the client certificater serial number.
optional bytes spoid = 7;
//}
}
// Provisioning response sent by the provisioning server to client devices.
// This message is used for both regular Widevine DRM certificates and for
// application-specific X.509 certificates.
message ProvisioningResponse {
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
// Required.
// Required. For X.509 certificates, the private RSA key may also include
// a prefix as specified by private_key_prefix in the X509CertificateMetadata
// proto message.
optional bytes device_rsa_key = 1;
// Initialization vector used to encrypt device_rsa_key. Required.
optional bytes device_rsa_key_iv = 2;
// Serialized SignedDeviceCertificate. Required.
// For Widevine DRM certificates, this contains the serialized
// SignedDrmDeviceCertificate. For X.509 certificates, this contains the PEM
// encoded X.509 certificate. Required.
optional bytes device_certificate = 3;
// Nonce value matching nonce in ProvisioningRequest. Required.
optional bytes nonce = 4;
// Key used to wrap device_rsa_key when DRM provisioning an OEM factory
// provisioned device. Encrypted with the device OEM public key using
// RSA-OAEP.
optional bytes wrapping_key = 5;
}
// Serialized ProvisioningRequest or ProvisioningResponse signed with
// The message authentication key.
message SignedProvisioningMessage {
enum ProtocolVersion {
VERSION_2 = 2; // Keybox factory-provisioned devices.
VERSION_3 = 3; // OEM certificate factory-provisioned devices.
}
// Serialized ProvisioningRequest or ProvisioningResponse. Required.
optional bytes message = 1;
// HMAC-SHA256 signature of message. Required.
// HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required.
optional bytes signature = 2;
// Version number of provisioning protocol.
optional ProtocolVersion protocol_version = 3 [default = VERSION_2];
}
// ----------------------------------------------------------------------------
// client_identification.proto
// ----------------------------------------------------------------------------
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Description:
// ClientIdentification messages used by provisioning and license protocols.
@@ -403,8 +484,9 @@ message SignedProvisioningMessage {
message ClientIdentification {
enum TokenType {
KEYBOX = 0;
DEVICE_CERTIFICATE = 1;
DRM_DEVICE_CERTIFICATE = 1;
REMOTE_ATTESTATION_CERTIFICATE = 2;
OEM_DEVICE_CERTIFICATE = 3;
}
message NameValue {
@@ -429,6 +511,9 @@ message ClientIdentification {
optional bool video_resolution_constraints = 3 [default = false];
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
optional uint32 oem_crypto_api_version = 5;
// Client has hardware support for protecting the usage table, such as
// 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];
}
@@ -445,43 +530,43 @@ message ClientIdentification {
optional uint32 license_counter = 5;
// List of non-baseline client capabilities.
optional ClientCapabilities client_capabilities = 6;
// Serialized VmpData message. Optional.
optional bytes vmp_data = 7;
}
// EncryptedClientIdentification message used to hold ClientIdentification
// messages encrypted for privacy purposes.
message EncryptedClientIdentification {
// Service ID for which the ClientIdentifcation is encrypted (owner of service
// certificate).
optional string service_id = 1;
// Provider ID for which the ClientIdentifcation is encrypted (owner of
// service certificate).
optional string provider_id = 1;
// Serial number for the service certificate for which ClientIdentification is
// encrypted.
optional bytes service_certificate_serial_number = 2;
// Serialized ClientIdentification message, encrypted with the privacy key using
// AES-128-CBC with PKCS#5 padding.
// Serialized ClientIdentification message, encrypted with the privacy key
// using AES-128-CBC with PKCS#5 padding.
optional bytes encrypted_client_id = 3;
// Initialization vector needed to decrypt encrypted_client_id.
optional bytes encrypted_client_id_iv = 4;
// AES-128 privacy key, encrypted with the service public public key using
// RSA-OAEP.
// AES-128 privacy key, encrypted with the service public key using RSA-OAEP.
optional bytes encrypted_privacy_key = 5;
}
// ----------------------------------------------------------------------------
// device_certificate.proto
// ----------------------------------------------------------------------------
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Description:
// Device certificate and certificate status list format definitions.
// Certificate definition for user devices, intermediate, service, and root
// DRM certificate definition for user devices, intermediate, service, and root
// certificates.
message DeviceCertificate {
message DrmDeviceCertificate {
enum CertificateType {
ROOT = 0;
INTERMEDIATE = 1;
USER_DEVICE = 2;
DRM_INTERMEDIATE = 1;
DRM_USER_DEVICE = 2;
SERVICE = 3;
PROVISIONER = 4;
}
// Type of certificate. Required.
@@ -500,28 +585,60 @@ message DeviceCertificate {
// (non-production) device. The test_device field in ProvisionedDeviceInfo
// below should be observed instead.
optional bool test_device_deprecated = 6 [deprecated = true];
// Service identifier (web origin) for the service which owns the certificate.
// Required for service certificates.
optional string service_id = 7;
// Service identifier (web origin) for the provider which owns the
// certificate. Required for service and provisioner certificates.
optional string provider_id = 7;
}
// DeviceCertificate signed with intermediate or root certificate private key.
message SignedDeviceCertificate {
// Serialized DeviceCertificate. Required.
optional bytes device_certificate = 1;
// Signature of device_certificate. Signed with root or intermediate
// certificate private key using RSASSA-PSS. Required.
optional bytes signature = 2;
// Intermediate signing certificate. Present only for user device
// certificates. All others signed with root certificate private key.
optional SignedDeviceCertificate signer = 3;
// Contains DRM and OEM certificate status and device information for a
// specific system ID.
message DeviceCertificateStatus {
enum Status {
VALID = 0;
REVOKED = 1;
};
// Serial number of the intermediate DrmDeviceCertificate to which this
// message refers. Required.
optional bytes drm_serial_number = 1;
// Status of the certificate. Optional.
optional Status status = 2 [default = VALID];
// Device model information about the device to which the intermediate
// certificate(s) correspond.
optional ProvisionedDeviceInfo device_info = 4;
// Serial number of the OEM X.509 intermediate certificate for this type
// of device. Present only if the device is OEM-provisioned.
optional bytes oem_serial_number = 5;
}
// List of DeviceCertificateStatus. Used to propagate certificate revocation
// status and device information.
message DeviceCertificateStatusList {
// POSIX time, in seconds, when the list was created. Required.
optional uint32 creation_time_seconds = 1;
// DeviceCertificateStatus for each system ID.
repeated DeviceCertificateStatus certificate_status = 2;
}
// Signed CertificateStatusList
message SignedCertificateStatusList {
// Serialized DeviceCertificateStatusList. Required.
optional bytes certificate_status_list = 1;
// Signature of certificate_status_list. Signed with root certificate private
// key using RSASSA-PSS. Required.
optional bytes signature = 2;
}
// ----------------------------------------------------------------------------
// provisioned_device_info.proto
// ----------------------------------------------------------------------------
// Description:
// Provisioned device info format definitions.
// Contains device model information for a provisioned device.
message ProvisionedDeviceInfo {
enum WvSecurityLevel {
// Defined in "WV Modular DRM Security Integration Guide for
// Common Encryption (CENC)"
// Defined in "Widevine Security Integration Guide for DASH on Android"
LEVEL_UNSPECIFIED = 0;
LEVEL_1 = 1;
LEVEL_2 = 2;
@@ -547,49 +664,13 @@ message ProvisionedDeviceInfo {
optional bool test_device = 8 [default = false];
}
// Contains the status of the root or an intermediate DeviceCertificate.
message DeviceCertificateStatus {
enum CertificateStatus {
VALID = 0;
REVOKED = 1;
};
// Serial number of the DeviceCertificate to which this message refers.
// Required.
optional bytes serial_number = 1;
// Status of the certificate. Optional.
optional CertificateStatus status = 2 [default = VALID];
// Device model information about the device to which the certificate
// corresponds. Required.
optional ProvisionedDeviceInfo device_info = 4;
}
// List of DeviceCertificateStatus. Used to propagate certificate revocation and
// update list.
message DeviceCertificateStatusList {
// POSIX time, in seconds, when the list was created. Required.
optional uint32 creation_time_seconds = 1;
// DeviceCertificateStatus for each certifificate.
repeated DeviceCertificateStatus certificate_status = 2;
}
// Signed CertificateStatusList
message SignedCertificateStatusList {
// Serialized DeviceCertificateStatusList. Required.
optional bytes certificate_status_list = 1;
// Signature of certificate_status_list. Signed with root certificate private
// key using RSASSA-PSS. Required.
optional bytes signature = 2;
}
// ----------------------------------------------------------------------------
// widevine_header.proto
// ----------------------------------------------------------------------------
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Description:
// Public protocol buffer definitions for Widevine Cenc Header
// protocol.
message WidevineCencHeader {
enum Algorithm {
UNENCRYPTED = 0;
@@ -626,3 +707,15 @@ message WidevineCencHeader {
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
optional uint32 protection_scheme = 9;
}
// Signed device certificate definition.
// DrmDeviceCertificate signed by a higher (CA) DRM certificate.
message SignedDrmDeviceCertificate {
// Serialized certificate. Required.
optional bytes drm_certificate = 1;
// Signature of certificate. Signed with root or intermediate
// certificate specified below. Required.
optional bytes signature = 2;
// SignedDrmDeviceCertificate used to sign this certificate.
optional SignedDrmDeviceCertificate signer = 3;
}

View File

@@ -76,4 +76,10 @@ OEMCryptoResult OEMCrypto_CopyBuffer(
return ::OEMCrypto_CopyBuffer(data_addr, data_length, out_buffer,
subsample_flags);
}
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
SecurityLevel level) {
return ::OEMCrypto_GetProvisioningMethod();
}
} // namespace wvcdm

View File

@@ -0,0 +1,33 @@
// Copyright 2016 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 (v12) which is not available in v11
// to allow an older oemcrypto implementation to be linked with CDM.
#include "OEMCryptoCENC.h"
extern "C" OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() {
return OEMCrypto_Keybox;
}
extern "C" OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
OEMCrypto_SESSION session, uint8_t* public_cert,
size_t* public_cert_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
OEMCrypto_SESSION session,
const uint32_t *nonce,
const uint8_t* encrypted_message_key,
size_t encrypted_message_key_length,
const uint8_t* enc_rsa_key,
size_t enc_rsa_key_length,
const uint8_t* enc_rsa_key_iv,
uint8_t* wrapped_rsa_key,
size_t* wrapped_rsa_key_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -22,7 +22,7 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V9_or_V10(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys,

View File

@@ -12,7 +12,7 @@
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
using video_widevine_server::sdk::License;
using video_widevine::License;
namespace {
@@ -31,9 +31,10 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
playback_start_time_(0),
last_playback_time_(0),
last_expiry_time_(0),
grace_period_end_time_(0),
last_expiry_time_set_(false),
was_expired_on_load_(false),
next_renewal_time_(0),
policy_max_duration_seconds_(0),
session_id_(session_id),
event_listener_(event_listener),
license_keys_(new LicenseKeys),
@@ -43,11 +44,11 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
PolicyEngine::~PolicyEngine() {}
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
if (license_keys_->IsContentKey(key_id)) {
return license_keys_->CanDecryptContent(key_id);
} else {
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
LOGE("PolicyEngine::CanDecryptContent Key '%s' not in license.",
b2a_hex(key_id).c_str());
return false;
}
@@ -78,8 +79,14 @@ void PolicyEngine::CheckDevice(int64_t current_time) {
void PolicyEngine::OnTimerEvent() {
int64_t current_time = clock_->GetCurrentTime();
// If we have passed the grace period, the expiration will update.
if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) {
grace_period_end_time_ = playback_start_time_;
NotifyExpirationUpdate(current_time);
}
// License expiration trumps all.
if (IsLicenseOrPlaybackDurationExpired(current_time) &&
if (HasLicenseOrPlaybackDurationExpired(current_time) &&
license_state_ != kLicenseStateExpired) {
license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired);
@@ -94,7 +101,7 @@ void PolicyEngine::OnTimerEvent() {
// Test to determine if renewal should be attempted.
switch (license_state_) {
case kLicenseStateCanPlay: {
if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break;
@@ -106,7 +113,7 @@ void PolicyEngine::OnTimerEvent() {
}
case kLicenseStateWaitingLicenseUpdate: {
if (IsRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
if (HasRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
break;
}
@@ -179,23 +186,9 @@ void PolicyEngine::UpdateLicense(const License& license) {
license_start_time_ = license.license_start_time();
next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds();
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
// will be set to the minimum of the following policies :
// rental_duration_seconds and license_duration_seconds.
// The value is used to determine when the license expires.
policy_max_duration_seconds_ = 0;
if (policy_.has_rental_duration_seconds())
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
if ((policy_.license_duration_seconds() > 0) &&
((policy_.license_duration_seconds() < policy_max_duration_seconds_) ||
policy_max_duration_seconds_ == 0)) {
policy_max_duration_seconds_ = policy_.license_duration_seconds();
}
int64_t current_time = clock_->GetCurrentTime();
if (!policy_.can_play() || IsLicenseOrPlaybackDurationExpired(current_time)) {
if (!policy_.can_play() ||
HasLicenseOrPlaybackDurationExpired(current_time)) {
license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired);
return;
@@ -209,7 +202,7 @@ void PolicyEngine::UpdateLicense(const License& license) {
license_state_ = kLicenseStatePending;
NotifyKeysChange(kKeyStatusPending);
}
NotifyExpirationUpdate();
NotifyExpirationUpdate(current_time);
}
void PolicyEngine::BeginDecryption() {
@@ -220,11 +213,13 @@ void PolicyEngine::BeginDecryption() {
case kLicenseStateWaitingLicenseUpdate:
playback_start_time_ = clock_->GetCurrentTime();
last_playback_time_ = playback_start_time_;
if (policy_.play_start_grace_period_seconds() == 0)
grace_period_end_time_ = playback_start_time_;
if (policy_.renew_with_usage()) {
license_state_ = kLicenseStateNeedRenewal;
}
NotifyExpirationUpdate();
NotifyExpirationUpdate(playback_start_time_);
break;
case kLicenseStateInitial:
case kLicenseStatePending:
@@ -258,7 +253,7 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
}
(*query_response)[QUERY_KEY_LICENSE_TYPE] =
license_id_.type() == video_widevine_server::sdk::STREAMING
license_id_.type() == video_widevine::STREAMING
? QUERY_VALUE_STREAMING
: QUERY_VALUE_OFFLINE;
(*query_response)[QUERY_KEY_PLAY_ALLOWED] =
@@ -267,7 +262,7 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
(*query_response)[QUERY_KEY_RENEW_ALLOWED] =
policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
ss << GetLicenseDurationRemaining(current_time);
ss << GetLicenseOrRentalDurationRemaining(current_time);
(*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
ss.str("");
ss << GetPlaybackDurationRemaining(current_time);
@@ -304,16 +299,34 @@ bool PolicyEngine::GetSecondsSinceLastPlayed(
}
int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() {
int64_t current_time = clock_->GetCurrentTime();
return std::min(GetLicenseDurationRemaining(current_time),
GetPlaybackDurationRemaining(current_time));
const int64_t current_time = clock_->GetCurrentTime();
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
if (expiry_time == NEVER_EXPIRES) return LLONG_MAX;
if (expiry_time < current_time) return 0;
return expiry_time - current_time;
}
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time) {
int64_t last_playback_time,
int64_t grace_period_end_time) {
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0;
NotifyExpirationUpdate();
grace_period_end_time_ = grace_period_end_time;
if (policy_.play_start_grace_period_seconds() != 0) {
// If we are using grace period, we may need to override some of the values
// given to us by OEMCrypto. |grace_period_end_time| will be 0 if the grace
// period has not expired (effectively playback has not begun). Otherwise,
// |grace_period_end_time| contains the playback start time we should use.
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);
was_expired_on_load_ =
expiry_time != NEVER_EXPIRES && expiry_time < current_time;
NotifyExpirationUpdate(current_time);
}
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
@@ -321,61 +334,87 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds();
}
bool PolicyEngine::IsLicenseOrPlaybackDurationExpired(int64_t current_time) {
int64_t expiry_time =
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
return (expiry_time == NEVER_EXPIRES) ? false : (expiry_time <= current_time);
bool PolicyEngine::HasLicenseOrPlaybackDurationExpired(int64_t current_time) {
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
return expiry_time != NEVER_EXPIRES && expiry_time <= current_time;
}
// For the policy time fields checked in the following methods, a value of 0
// indicates that there is no limit to the duration. These methods
// will always return false if the value is 0.
int64_t PolicyEngine::GetLicenseExpiryTime() {
return policy_max_duration_seconds_ > 0
? license_start_time_ + policy_max_duration_seconds_
// indicates that there is no limit to the duration. If the fields are zero
// (including the hard limit) then these methods will return NEVER_EXPIRES.
int64_t PolicyEngine::GetHardLicenseExpiryTime() {
return policy_.license_duration_seconds() > 0
? license_start_time_ + policy_.license_duration_seconds()
: NEVER_EXPIRES;
}
int64_t PolicyEngine::GetPlaybackExpiryTime() {
return (playback_start_time_ > 0 && policy_.playback_duration_seconds() > 0)
? (playback_start_time_ + policy_.playback_duration_seconds())
: NEVER_EXPIRES;
int64_t PolicyEngine::GetRentalExpiryTime() {
const int64_t hard_limit = GetHardLicenseExpiryTime();
if (policy_.rental_duration_seconds() == 0) return hard_limit;
const int64_t expiry_time =
license_start_time_ + policy_.rental_duration_seconds();
if (hard_limit == NEVER_EXPIRES) return expiry_time;
return std::min(hard_limit, expiry_time);
}
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
int64_t license_expiry_time = GetLicenseExpiryTime();
int64_t PolicyEngine::GetExpiryTime(int64_t current_time, bool is_load) {
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_ &&
policy_.soft_enforce_playback_duration()) {
return hard_limit;
}
const int64_t expiry_time =
playback_start_time_ + policy_.playback_duration_seconds();
if (hard_limit == NEVER_EXPIRES)
return expiry_time;
return std::min(hard_limit, expiry_time);
}
int64_t PolicyEngine::GetLicenseOrRentalDurationRemaining(
int64_t current_time) {
// This is only used in Query. This should return the time remaining on
// license_duration_seconds for streaming licenses and rental_duration_seconds
// for offline licenses.
if (HasLicenseOrPlaybackDurationExpired(current_time)) return 0;
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_max_duration_seconds_);
policy_.license_duration_seconds());
}
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
int64_t playback_expiry_time = GetPlaybackExpiryTime();
if (playback_expiry_time == NEVER_EXPIRES) {
return (policy_.playback_duration_seconds() != 0)
? policy_.playback_duration_seconds()
: LLONG_MAX;
}
// This is only used in Query. This should return playback_duration_seconds,
// or the time remaining on it if playing.
const int64_t playback_duration = policy_.playback_duration_seconds();
if (playback_duration == 0) return LLONG_MAX;
if (playback_start_time_ == 0) return playback_duration;
const int64_t playback_expiry_time = playback_duration + playback_start_time_;
if (playback_expiry_time < current_time) return 0;
return std::min(playback_expiry_time - current_time,
policy_.playback_duration_seconds());
}
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
bool PolicyEngine::HasRenewalDelayExpired(int64_t current_time) {
return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) &&
license_start_time_ + policy_.renewal_delay_seconds() <= current_time;
}
bool PolicyEngine::IsRenewalRecoveryDurationExpired(int64_t current_time) {
bool PolicyEngine::HasRenewalRecoveryDurationExpired(int64_t current_time) {
// NOTE: Renewal Recovery Duration is currently not used.
return (policy_.renewal_recovery_duration_seconds() > 0) &&
license_start_time_ + policy_.renewal_recovery_duration_seconds() <=
current_time;
}
bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
return policy_.can_renew() &&
(policy_.renewal_retry_interval_seconds() > 0) &&
next_renewal_time_ <= current_time;
@@ -394,9 +433,8 @@ void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
}
}
void PolicyEngine::NotifyExpirationUpdate() {
int64_t expiry_time =
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) {
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) {
last_expiry_time_ = expiry_time;
if (event_listener_)

View File

@@ -31,7 +31,9 @@ const int kOaepMinPadding = 2 * CC_SHA1_DIGEST_LENGTH + 1;
template<typename T>
struct CFDeleter {
void operator()(T arg) {
CFRelease(arg);
if (arg) {
CFRelease(arg);
}
}
};
@@ -54,6 +56,8 @@ 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.
@@ -68,11 +72,13 @@ SecKeyRef ImportPublicKey(const std::string& key) {
keyData.get());
CFDictionarySetValue(addAttributes.get(), kSecAttrKeyClass,
kSecAttrKeyClassPublic);
CFDictionarySetValue(addAttributes.get(), kSecAttrAccessible,
kSecAttrAccessibleAfterFirstUnlock);
CFDictionarySetValue(addAttributes.get(), kSecReturnPersistentRef,
kCFBooleanTrue);
// Add the key to the keystore.
CFTypeRef temp;
CFTypeRef temp = NULL;
OSStatus status = SecItemAdd(addAttributes.get(), &temp);
CF<CFTypeRef> peer(temp);
if (!peer || (status != noErr && status != errSecDuplicateItem)) {
@@ -90,6 +96,8 @@ SecKeyRef ImportPublicKey(const std::string& key) {
kSecAttrKeyTypeRSA);
CFDictionarySetValue(queryAttributes.get(), kSecAttrKeyClass,
kSecAttrKeyClassPublic);
CFDictionarySetValue(queryAttributes.get(), kSecAttrAccessible,
kSecAttrAccessibleAfterFirstUnlock);
CFDictionarySetValue(queryAttributes.get(), kSecReturnRef, kCFBooleanTrue);
// Query the keychain to get the public key ref.
@@ -152,7 +160,6 @@ std::string ApplyOAEPPadding(const std::string& messageStr, size_t rsaSize) {
ret.resize(rsaSize);
size_t messageLength = messageStr.length();
size_t paddingLength = rsaSize - messageLength;
size_t psLen = paddingLength - kOaepMinPadding;
const uint8_t *message = reinterpret_cast<const uint8_t*>(messageStr.data());
uint8_t *result = reinterpret_cast<uint8_t*>(&ret[0]);
uint8_t *seed = result + 1;

View File

@@ -62,7 +62,7 @@ bool Properties::GetServiceCertificate(const CdmSessionId& session_id,
std::string* service_certificate) {
const CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
if (property_set == NULL) {
return false;
}
*service_certificate = property_set->service_certificate();
@@ -73,7 +73,7 @@ bool Properties::SetServiceCertificate(const CdmSessionId& session_id,
const std::string& service_certificate) {
CdmClientPropertySet* property_set =
GetCdmClientPropertySet(session_id);
if (NULL == property_set) {
if (property_set == NULL) {
return false;
}
property_set->set_service_certificate(service_certificate);

View File

@@ -0,0 +1,216 @@
// Copyright 2017 Google Inc. All Rights Reserved.
#include "service_certificate.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "log.h"
#include "privacy_crypto.h"
#include "properties.h"
#include "wv_cdm_constants.h"
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};
} // namespace
namespace wvcdm {
// Protobuf generated classes.
using video_widevine::ClientIdentification;
using video_widevine::DrmDeviceCertificate;
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& raw_certificate) {
return VerifyAndExtract(raw_certificate);
}
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 (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());
std::string iv(KEY_IV_SIZE, 0);
std::string key(KEY_SIZE, 0);
if (!crypto_session->GetRandom(key.size(),
reinterpret_cast<uint8_t*>(&key[0])))
return CLIENT_ID_GENERATE_RANDOM_ERROR;
if (!crypto_session->GetRandom(iv.size(),
reinterpret_cast<uint8_t*>(&iv[0])))
return CLIENT_ID_GENERATE_RANDOM_ERROR;
std::string id, enc_id, enc_key;
clear_client_id->SerializeToString(&id);
AesCbcKey aes;
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;
encrypted_client_id->set_encrypted_client_id_iv(iv);
encrypted_client_id->set_encrypted_privacy_key(enc_key);
encrypted_client_id->set_encrypted_client_id(enc_id);
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