Merge "Support Keybox, DRM Cert, and OEM Cert for Client ID"

This commit is contained in:
Rahul Frias
2017-01-23 00:45:39 +00:00
committed by Android (Google) Code Review
21 changed files with 898 additions and 382 deletions

View File

@@ -18,6 +18,7 @@ LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static
SRC_DIR := src SRC_DIR := src
CORE_SRC_DIR := core/src CORE_SRC_DIR := core/src
PROFILER_SRC_DIR := profiler/src
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
$(CORE_SRC_DIR)/buffer_reader.cpp \ $(CORE_SRC_DIR)/buffer_reader.cpp \
@@ -32,9 +33,9 @@ LOCAL_SRC_FILES := \
$(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \ $(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \
$(CORE_SRC_DIR)/policy_engine.cpp \ $(CORE_SRC_DIR)/policy_engine.cpp \
$(CORE_SRC_DIR)/privacy_crypto_openssl.cpp \ $(CORE_SRC_DIR)/privacy_crypto_openssl.cpp \
$(CORE_SRC_DIR)/service_certificate.cpp \
$(SRC_DIR)/wv_content_decryption_module.cpp \ $(SRC_DIR)/wv_content_decryption_module.cpp \
LOCAL_MODULE := libcdm LOCAL_MODULE := libcdm
LOCAL_MODULE_TAGS := optional LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_TARGET_ARCH := arm x86 mips LOCAL_MODULE_TARGET_ARCH := arm x86 mips

View File

@@ -7,6 +7,7 @@
#include "crypto_session.h" #include "crypto_session.h"
#include "oemcrypto_adapter.h" #include "oemcrypto_adapter.h"
#include "service_certificate.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace wvcdm { namespace wvcdm {
@@ -16,7 +17,9 @@ class FileSystem;
class CertificateProvisioning { class CertificateProvisioning {
public: public:
CertificateProvisioning() : cert_type_(kCertificateWidevine) {}; CertificateProvisioning() :
cert_type_(kCertificateWidevine),
service_certificate_(NULL) {};
~CertificateProvisioning() {}; ~CertificateProvisioning() {};
// Provisioning related methods // Provisioning related methods
@@ -33,6 +36,12 @@ class CertificateProvisioning {
std::string* wrapped_key); std::string* wrapped_key);
private: private:
bool GetProvisioningTokenType(
video_widevine::ClientIdentification::TokenType* token_type);
video_widevine::SignedProvisioningMessage::ProtocolVersion
GetProtocolVersion();
void ComposeJsonRequestAsQueryString(const std::string& message, void ComposeJsonRequestAsQueryString(const std::string& message,
CdmProvisioningRequest* request); CdmProvisioningRequest* request);
bool ParseJsonResponse(const CdmProvisioningResponse& json_str, bool ParseJsonResponse(const CdmProvisioningResponse& json_str,
@@ -40,9 +49,11 @@ class CertificateProvisioning {
const std::string& end_substr, std::string* result); const std::string& end_substr, std::string* result);
CryptoSession crypto_session_; CryptoSession crypto_session_;
CdmCertificateType cert_type_; CdmCertificateType cert_type_;
ServiceCertificate* service_certificate_;
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning); CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
}; };
} // namespace wvcdm } // namespace wvcdm
#endif // WVCDM_CORE_CERTIFICATE_PROVISIONING_H_ #endif // WVCDM_CORE_CERTIFICATE_PROVISIONING_H_

View File

@@ -29,8 +29,11 @@ class CryptoSession {
CryptoSession(); CryptoSession();
virtual ~CryptoSession(); virtual ~CryptoSession();
virtual bool ValidateKeybox(); virtual bool GetClientToken(std::string* client_token);
virtual bool GetToken(std::string* token); virtual bool GetProvisioningToken(std::string* client_token);
virtual CdmClientTokenType GetPreProvisionTokenType() {
return pre_provision_token_type_;
}
virtual CdmSecurityLevel GetSecurityLevel(); virtual CdmSecurityLevel GetSecurityLevel();
virtual bool GetDeviceUniqueId(std::string* device_id); virtual bool GetDeviceUniqueId(std::string* device_id);
virtual bool GetApiVersion(uint32_t* version); virtual bool GetApiVersion(uint32_t* version);
@@ -124,8 +127,11 @@ class CryptoSession {
const std::string& signature); const std::string& signature);
private: private:
bool GetProvisioningMethod(CdmClientTokenType* token_type);
void Init(); void Init();
void Terminate(); void Terminate();
bool GetTokenFromKeybox(std::string* token);
bool GetTokenFromOemCert(std::string* token);
void GenerateMacContext(const std::string& input_context, void GenerateMacContext(const std::string& input_context,
std::string* deriv_context); std::string* deriv_context);
void GenerateEncryptContext(const std::string& input_context, void GenerateEncryptContext(const std::string& input_context,
@@ -163,6 +169,7 @@ class CryptoSession {
static int session_count_; static int session_count_;
bool open_; bool open_;
CdmClientTokenType pre_provision_token_type_;
bool update_usage_table_after_close_session_; bool update_usage_table_after_close_session_;
CryptoSessionId oec_session_id_; CryptoSessionId oec_session_id_;

View File

@@ -6,7 +6,9 @@
#include <set> #include <set>
#include "initialization_data.h" #include "initialization_data.h"
#include "license_protocol.pb.h"
#include "scoped_ptr.h" #include "scoped_ptr.h"
#include "service_certificate.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace video_widevine { namespace video_widevine {
@@ -25,11 +27,12 @@ class CdmLicense {
CdmLicense(const CdmSessionId& session_id); CdmLicense(const CdmSessionId& session_id);
virtual ~CdmLicense(); virtual ~CdmLicense();
virtual bool Init(const std::string& token, CryptoSession* session, virtual bool Init(
PolicyEngine* policy_engine); const std::string& client_token, CdmClientTokenType client_token_type,
CryptoSession* session, PolicyEngine* policy_engine);
virtual CdmResponseType PrepareKeyRequest( virtual CdmResponseType PrepareKeyRequest(
const InitializationData& init_data, const CdmLicenseType license_type, const InitializationData& init_data, CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request, const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request,
std::string* server_url); std::string* server_url);
virtual CdmResponseType PrepareKeyUpdateRequest( virtual CdmResponseType PrepareKeyUpdateRequest(
@@ -59,33 +62,32 @@ class CdmLicense {
return is_offline_; return is_offline_;
} }
static CdmResponseType VerifySignedServiceCertificate(
const std::string& signed_service_certificate);
private: private:
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
std::string* server_url);
CdmResponseType HandleKeyErrorResponse( CdmResponseType HandleKeyErrorResponse(
const video_widevine::SignedMessage& signed_message); const video_widevine::SignedMessage& signed_message);
bool GetClientTokenType(
video_widevine::ClientIdentification::TokenType* token_type);
CdmResponseType PrepareClientId( CdmResponseType PrepareClientId(
bool encrypt, const std::string& certificate,
const CdmAppParameterMap& app_parameters, const CdmAppParameterMap& app_parameters,
video_widevine::LicenseRequest* license_request); video_widevine::LicenseRequest* license_request);
CdmResponseType PrepareContentId(
const InitializationData& init_data, CdmLicenseType license_type,
const std::string& request_id,
video_widevine::LicenseRequest* license_request);
template <typename T> template <typename T>
bool PrepareContentId(const CdmLicenseType license_type, bool SetTypeAndId(CdmLicenseType license_type,
const std::string& request_id, T* content_id); const std::string& request_id, T* content_id);
static CdmResponseType VerifyAndExtractSignedServiceCertificate( CryptoSession* crypto_session_;
const std::string& signed_service_certificate,
std::string* service_certificate);
bool GetServiceCertificate(std::string* service_certificate);
CryptoSession* session_;
PolicyEngine* policy_engine_; PolicyEngine* policy_engine_;
std::string server_url_; std::string server_url_;
std::string token_; std::string client_token_;
CdmClientTokenType client_token_type_;
const CdmSessionId session_id_; const CdmSessionId session_id_;
scoped_ptr<InitializationData> stored_init_data_; scoped_ptr<InitializationData> stored_init_data_;
bool initialized_; bool initialized_;
@@ -94,6 +96,9 @@ class CdmLicense {
bool renew_with_client_id_; bool renew_with_client_id_;
bool is_offline_; bool is_offline_;
// Used to encrypt ClientIdentification message
scoped_ptr<ServiceCertificate> service_certificate_;
// Used for certificate based licensing // Used for certificate based licensing
CdmKeyMessage key_request_; CdmKeyMessage key_request_;

View File

@@ -95,6 +95,7 @@ class Properties {
} }
#if defined(UNIT_TEST) #if defined(UNIT_TEST)
FRIEND_TEST(CdmSessionTest, InitWithBuiltInCertificate);
FRIEND_TEST(CdmSessionTest, InitWithCertificate); FRIEND_TEST(CdmSessionTest, InitWithCertificate);
FRIEND_TEST(CdmSessionTest, InitWithKeybox); FRIEND_TEST(CdmSessionTest, InitWithKeybox);
FRIEND_TEST(CdmSessionTest, ReInitFail); FRIEND_TEST(CdmSessionTest, ReInitFail);

View File

@@ -0,0 +1,81 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
#ifndef WVCDM_CORE_SERVICE_CERTIFICATE_H_
#define WVCDM_CORE_SERVICE_CERTIFICATE_H_
// Service Certificates are used to encrypt the ClientIdentification message
// that is part of Device Provisioning, License, Renewal, and Release requests.
// They may be supplied by the application, or a default certificate may be
// configured into the CDM, or the CDM may send a Service Certificate Request
// to the target server to get one. Separate certificates are maintained for
// the License and Provisioning Servers (the default service certificates
// are currently identical for both servers). Once the Service Certificates are
// established for the session, they should not change.
#include "license_protocol.pb.h"
#include "wv_cdm_types.h"
namespace video_widevine {
class SignedMessage;
class LicenseRequest;
} // namespace video_widevine
namespace wvcdm {
class CryptoSession;
class ServiceCertificate {
public:
ServiceCertificate();
virtual ~ServiceCertificate();
virtual bool Init(const CdmSessionId& session_id, CryptoSession* session);
virtual bool IsRequired();
virtual bool IsAvailable();
virtual bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request);
virtual CdmResponseType VerifyAndSet(
const std::string& signed_service_certificate);
virtual CdmResponseType EncryptClientId(
const video_widevine::ClientIdentification* clear_client_id,
video_widevine::EncryptedClientIdentification* encrypted_client_id);
static CdmResponseType VerifySignedServiceCertificate(
const std::string& signed_certificate) {
bool has_provider_id;
return VerifyAndExtractFromSignedCertificate(signed_certificate, NULL,
&has_provider_id, NULL);
}
private:
// Take a signed certificate, parse it, and verify it.
// If a pointer to a string object is passed in, the certificate
// will be copied to it.
static CdmResponseType VerifyAndExtractFromSignedCertificate(
const std::string& signed_service_certificate,
std::string* service_certificate, bool* has_provider_id,
std::string* provider_id);
virtual bool SetupServiceCertificate();
CryptoSession* crypto_session_;
CdmSessionId session_id_;
bool privacy_mode_enabled_;
bool valid_;
bool initialized_;
// Certificate, verified and extracted from signed message.
std::string certificate_;
// Provider ID, extracted from certificate message.
bool has_provider_id_;
std::string provider_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ServiceCertificate);
};
} // namespace wvcdm
#endif // WVCDM_CORE_SERVICE_CERTIFICATE_H_

View File

@@ -14,6 +14,10 @@ static const size_t KEY_SIZE = 16;
static const size_t MAC_KEY_SIZE = 32; static const size_t MAC_KEY_SIZE = 32;
static const size_t KEYBOX_KEY_DATA_SIZE = 72; static const size_t KEYBOX_KEY_DATA_SIZE = 72;
// Initial estimate of certificate size. Code that
// uses this estimate should be able to adapt to a larger or smaller size.
static const size_t CERTIFICATE_DATA_SIZE = 4 * 1024;
// Use 0 to represent never expired license as specified in EME spec // Use 0 to represent never expired license as specified in EME spec
// (NaN in JS translates to 0 in unix timestamp). // (NaN in JS translates to 0 in unix timestamp).
static const int64_t NEVER_EXPIRES = 0; static const int64_t NEVER_EXPIRES = 0;

View File

@@ -151,9 +151,9 @@ enum CdmResponseType {
LICENSE_RENEWAL_SIGNING_ERROR, LICENSE_RENEWAL_SIGNING_ERROR,
UNUSED_4, /* previously RESTORE_OFFLINE_LICENSE_ERROR_1 */ UNUSED_4, /* previously RESTORE_OFFLINE_LICENSE_ERROR_1 */
RESTORE_OFFLINE_LICENSE_ERROR_2, RESTORE_OFFLINE_LICENSE_ERROR_2,
UNUSED_5, /* SESSION_INIT_ERROR_1 */ SESSION_INIT_ERROR_1,
SESSION_INIT_ERROR_2, /* 115 */ SESSION_INIT_ERROR_2, /* 115 */
SESSION_INIT_GET_KEYBOX_ERROR, UNUSED_5, /* previously SESSION_INIT_GET_KEYBOX_ERROR */
SESSION_NOT_FOUND_1, SESSION_NOT_FOUND_1,
SESSION_NOT_FOUND_2, SESSION_NOT_FOUND_2,
SESSION_NOT_FOUND_3, SESSION_NOT_FOUND_3,
@@ -245,6 +245,10 @@ enum CdmResponseType {
INVALID_PARAMETERS_ENG_14, /* 205 */ INVALID_PARAMETERS_ENG_14, /* 205 */
INVALID_PARAMETERS_ENG_15, INVALID_PARAMETERS_ENG_15,
INVALID_PARAMETERS_ENG_16, INVALID_PARAMETERS_ENG_16,
DEVICE_CERTIFICATE_ERROR_5,
CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1,
CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2,
LICENSING_CLIENT_TOKEN_ERROR_1,
}; };
enum CdmKeyStatus { enum CdmKeyStatus {
@@ -311,6 +315,12 @@ enum CdmSigningAlgorithm {
kSigningAlgorithmHmacSha256 kSigningAlgorithmHmacSha256
}; };
enum CdmClientTokenType {
kClientTokenKeybox,
kClientTokenDrmCert,
kClientTokenOemCert
};
class CdmKeyAllowedUsage { class CdmKeyAllowedUsage {
public: public:
CdmKeyAllowedUsage() { CdmKeyAllowedUsage() {

View File

@@ -125,7 +125,6 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CloseExpiredReleaseSessions(); CloseExpiredReleaseSessions();
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_)); scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
CdmResponseType sts = new_session->Init(property_set, forced_session_id, CdmResponseType sts = new_session->Init(property_set, forced_session_id,
event_listener); event_listener);
if (sts != NO_ERROR) { if (sts != NO_ERROR) {
@@ -158,12 +157,15 @@ CdmResponseType CdmEngine::OpenKeySetSession(
return EMPTY_KEYSET_ID_ENG_1; return EMPTY_KEYSET_ID_ENG_1;
} }
bool exists = false; // 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_); AutoLock lock(release_key_sets_lock_);
exists = release_key_sets_.find(key_set_id) != release_key_sets_.end(); key_set_in_use =
release_key_sets_.find(key_set_id) != release_key_sets_.end();
} }
if (exists) if (key_set_in_use)
CloseKeySetSession(key_set_id); CloseKeySetSession(key_set_id);
CdmSessionId session_id; CdmSessionId session_id;
@@ -497,7 +499,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
} else if (query_token == QUERY_KEY_DEVICE_ID) { } else if (query_token == QUERY_KEY_DEVICE_ID) {
std::string deviceId; std::string deviceId;
if (!crypto_session.GetDeviceUniqueId(&deviceId)) { if (!crypto_session.GetDeviceUniqueId(&deviceId)) {
LOGW("CdmEngine::QueryStatus: GetDeviceUniqueId failed"); LOGW("CdmEngine::QueryStatus: QUERY_KEY_DEVICE_ID unknown failure");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
@@ -505,7 +507,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
} else if (query_token == QUERY_KEY_SYSTEM_ID) { } else if (query_token == QUERY_KEY_SYSTEM_ID) {
uint32_t system_id; uint32_t system_id;
if (!crypto_session.GetSystemId(&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; return UNKNOWN_ERROR;
} }

View File

@@ -76,16 +76,32 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
return SESSION_FILE_HANDLE_INIT_ERROR; return SESSION_FILE_HANDLE_INIT_ERROR;
} }
std::string token; // Device Provisioning state is not yet known.
if (Properties::use_certificates_as_identification()) { // 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.
std::string client_token;
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; std::string wrapped_key;
if (!file_handle_->RetrieveCertificate(&token, &wrapped_key) || if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) { !crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING; return NEED_PROVISIONING;
} }
} else { client_token_type = kClientTokenDrmCert;
if (!crypto_session_->GetToken(&token))
return SESSION_INIT_GET_KEYBOX_ERROR;
} }
if (forced_session_id) { if (forced_session_id) {
@@ -112,8 +128,8 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
policy_engine_.reset(new PolicyEngine( policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get())); session_id_, event_listener, crypto_session_.get()));
if (!license_parser_->Init(token, crypto_session_.get(), if (!license_parser_->Init(client_token, client_token_type,
policy_engine_.get())) crypto_session_.get(), policy_engine_.get()))
return LICENSE_PARSER_INIT_ERROR; return LICENSE_PARSER_INIT_ERROR;
license_received_ = false; license_received_ = false;
@@ -299,13 +315,13 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
if (is_release_) { if (is_release_) {
CdmResponseType sts = ReleaseKey(key_response); CdmResponseType sts = ReleaseKey(key_response);
return (NO_ERROR == sts) ? KEY_ADDED : sts; return (sts == NO_ERROR) ? KEY_ADDED : sts;
} else if (license_received_) { // renewal } else if (license_received_) { // renewal
return RenewKey(key_response); return RenewKey(key_response);
} else { } else {
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response); 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; license_received_ = true;
key_response_ = key_response; key_response_ = key_response;
@@ -445,7 +461,7 @@ CdmResponseType CdmSession::GenerateRenewalRequest(
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) { CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType sts = CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(true, key_response); 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_) { if (is_offline_) {
offline_key_renewal_response_ = key_response; offline_key_renewal_response_ = key_response;
@@ -477,7 +493,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
CdmResponseType sts = CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(false, key_response); 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()) { if (is_offline_ || !license_parser_->provider_session_token().empty()) {
DeleteLicense(); DeleteLicense();

View File

@@ -48,6 +48,38 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
request->assign(message_b64); 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;
}
}
/*
* 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 * Composes a device provisioning request and output the request in JSON format
* in *request. It also returns the default url for the provisioning server * in *request. It also returns the default url for the provisioning server
@@ -74,14 +106,35 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
// Prepares device provisioning request. // Prepares device provisioning request.
ProvisioningRequest provisioning_request; ProvisioningRequest provisioning_request;
ClientIdentification* client_id = provisioning_request.mutable_client_id();
client_id->set_type(ClientIdentification::KEYBOX);
std::string token; std::string token;
if (!crypto_session_.GetToken(&token)) { ClientIdentification* client_id = provisioning_request.mutable_client_id();
LOGE("GetProvisioningRequest: fails to get token"); ClientIdentification::TokenType token_type;
return CERT_PROVISIONING_GET_KEYBOX_ERROR_1;
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_token(token);
client_id->set_type(token_type);
#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification.
if (encrypt) {
EncryptedClientIdentification* encrypted_client_id =
provisioning_request->mutable_encrypted_client_id();
CdmResponseType sts;
sts = EncryptClientId(client_id, encrypted_client_id, certificate);
if (NO_ERROR == sts) {
provisioning_request->clear_client_id();
} else {
provisioning_request->clear_encrypted_client_id();
}
return sts;
}
#endif
uint32_t nonce; uint32_t nonce;
if (!crypto_session_.GenerateNonce(&nonce)) { if (!crypto_session_.GenerateNonce(&nonce)) {
@@ -112,12 +165,14 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
cert_type_ = cert_type; cert_type_ = cert_type;
options->set_certificate_authority(cert_authority); options->set_certificate_authority(cert_authority);
// TODO(gmorgan): use provider ID.
if (origin != EMPTY_ORIGIN) { if (origin != EMPTY_ORIGIN) {
std::string device_unique_id; std::string device_unique_id;
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) { if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
LOGE("GetProvisioningRequest: fails to get device unique ID"); LOGE("GetProvisioningRequest: fails to get device unique ID");
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2; return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
} }
// TODO(gmorgan): handle provider id variants.
provisioning_request.set_stable_id(device_unique_id + origin); provisioning_request.set_stable_id(device_unique_id + origin);
} }
@@ -139,6 +194,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SignedProvisioningMessage signed_provisioning_msg; SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message); signed_provisioning_msg.set_message(serialized_message);
signed_provisioning_msg.set_signature(request_signature); signed_provisioning_msg.set_signature(request_signature);
signed_provisioning_msg.set_protocol_version(GetProtocolVersion());
std::string serialized_request; std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request); signed_provisioning_msg.SerializeToString(&serialized_request);

View File

@@ -54,6 +54,26 @@ CryptoSession::~CryptoSession() {
Terminate(); 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() { void CryptoSession::Init() {
LOGV("CryptoSession::Init"); LOGV("CryptoSession::Init");
AutoLock auto_lock(crypto_lock_); AutoLock auto_lock(crypto_lock_);
@@ -66,6 +86,9 @@ void CryptoSession::Init() {
} }
initialized_ = true; initialized_ = true;
} }
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
initialized_ = false;
}
} }
void CryptoSession::Terminate() { void CryptoSession::Terminate() {
@@ -85,35 +108,79 @@ void CryptoSession::Terminate() {
initialized_ = false; initialized_ = false;
} }
bool CryptoSession::ValidateKeybox() { bool CryptoSession::GetTokenFromKeybox(std::string* token) {
LOGV("CryptoSession::ValidateKeybox: Lock"); OEMCryptoResult status;
AutoLock auto_lock(crypto_lock_); std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0');
if (!initialized_) { // lock is held by caller
return false; 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 false;
return (OEMCrypto_SUCCESS == result);
} }
bool CryptoSession::GetToken(std::string* token) { bool CryptoSession::GetTokenFromOemCert(std::string* token) {
if (!token) { OEMCryptoResult status;
LOGE("CryptoSession::GetToken : No token passed to method."); 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;
}
return false; 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_); AutoLock auto_lock(crypto_lock_);
if (!initialized_) { if (!initialized_) {
return false; return false;
} }
OEMCryptoResult sts =
OEMCrypto_GetKeyData(buf, &bufSize, requested_security_level_); // Only keybox is used for client token. All other cases use DRM Cert.
if (OEMCrypto_SUCCESS != sts) { 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; return false;
} }
token->assign((const char*)buf, (size_t)bufSize);
return true;
} }
CdmSecurityLevel CryptoSession::GetSecurityLevel() { CdmSecurityLevel CryptoSession::GetSecurityLevel() {
@@ -160,11 +227,13 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
if (!initialized_) { if (!initialized_) {
return false; return false;
} }
OEMCryptoResult sts = if (pre_provision_token_type_ == kClientTokenKeybox) {
OEMCryptoResult sts =
OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_); OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_);
if (OEMCrypto_SUCCESS != sts) { if (OEMCrypto_SUCCESS != sts) {
return false; return false;
}
} }
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length); device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
@@ -312,6 +381,7 @@ bool CryptoSession::PrepareRequest(const std::string& message,
return false; return false;
} }
// TODO(gmorgan): rework this for OEM certs.
if (!Properties::use_certificates_as_identification() || is_provisioning) { if (!Properties::use_certificates_as_identification() || is_provisioning) {
if (!GenerateDerivedKeys(message)) return false; if (!GenerateDerivedKeys(message)) return false;

View File

@@ -27,42 +27,8 @@ std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id"; std::string kDeviceIdKey = "device_id";
std::string kWVCdmVersionKey = "widevine_cdm_version"; std::string kWVCdmVersionKey = "widevine_cdm_version";
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level"; std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
const unsigned char kServiceCertificateCAPublicKey[] = { } // namespace
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};
}
const uint32_t kFourCcCbc1 = 0x63626331; const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373; const uint32_t kFourCcCbcs = 0x63626373;
const uint32_t kFourCcLittleEndianCbc1 = 0x31636263; const uint32_t kFourCcLittleEndianCbc1 = 0x31636263;
@@ -155,7 +121,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
} }
CdmLicense::CdmLicense(const CdmSessionId& session_id) CdmLicense::CdmLicense(const CdmSessionId& session_id)
: session_(NULL), : crypto_session_(NULL),
policy_engine_(NULL), policy_engine_(NULL),
session_id_(session_id), session_id_(session_id),
initialized_(false), initialized_(false),
@@ -164,7 +130,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
clock_(new Clock()) {} clock_(new Clock()) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock) CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
: session_(NULL), : crypto_session_(NULL),
policy_engine_(NULL), policy_engine_(NULL),
session_id_(session_id), session_id_(session_id),
initialized_(false), initialized_(false),
@@ -175,8 +141,9 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
CdmLicense::~CdmLicense() {} CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(const std::string& token, CryptoSession* session, bool CdmLicense::Init(
PolicyEngine* policy_engine) { const std::string& client_token, CdmClientTokenType client_token_type,
CryptoSession* session, PolicyEngine* policy_engine) {
if (clock_.get() == NULL) { if (clock_.get() == NULL) {
LOGE("CdmLicense::Init: clock parameter not provided"); LOGE("CdmLicense::Init: clock parameter not provided");
return false; return false;
@@ -185,8 +152,8 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
LOGE("CdmLicense::Init: empty session id provided"); LOGE("CdmLicense::Init: empty session id provided");
return false; return false;
} }
if (token.size() == 0) { if (client_token.size() == 0) {
LOGE("CdmLicense::Init: empty token provided"); LOGE("CdmLicense::Init: empty client token provided");
return false; return false;
} }
if (session == NULL || !session->IsOpen()) { if (session == NULL || !session->IsOpen()) {
@@ -197,15 +164,26 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
LOGE("CdmLicense::Init: no policy engine provided"); LOGE("CdmLicense::Init: no policy engine provided");
return false; return false;
} }
token_ = token;
session_ = session; client_token_ = client_token;
client_token_type_ = client_token_type;
crypto_session_ = session;
policy_engine_ = policy_engine; policy_engine_ = policy_engine;
service_certificate_.reset(new ServiceCertificate());
if (service_certificate_.get() == NULL) {
LOGE("CdmLicense::Init: creation of service_certificate failed");
return false;
}
if (!service_certificate_->Init(session_id_, crypto_session_)) {
LOGE("CdmLicense::Init: init of service_certificate failed");
return false;
}
initialized_ = true; initialized_ = true;
return true; return true;
} }
CdmResponseType CdmLicense::PrepareKeyRequest( 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, const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request,
std::string* server_url) { std::string* server_url) {
if (!initialized_) { if (!initialized_) {
@@ -236,63 +214,27 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return INVALID_PARAMETERS_LIC_7; return INVALID_PARAMETERS_LIC_7;
} }
std::string service_certificate; if (service_certificate_->IsRequired() &&
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id_); !service_certificate_->IsAvailable()) {
if (privacy_mode_enabled) { stored_init_data_.reset(new InitializationData(init_data));
if (!GetServiceCertificate(&service_certificate)) { *server_url = server_url_;
stored_init_data_.reset(new InitializationData(init_data)); if (service_certificate_->PrepareServiceCertificateRequest(signed_request))
return PrepareServiceCertificateRequest(signed_request, server_url) return KEY_MESSAGE;
? KEY_MESSAGE else
: LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR; return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
} }
std::string request_id; std::string request_id;
session_->GenerateRequestId(&request_id); crypto_session_->GenerateRequestId(&request_id);
LicenseRequest license_request; LicenseRequest license_request;
CdmResponseType status = CdmResponseType status;
PrepareClientId(privacy_mode_enabled, service_certificate, app_parameters, status = PrepareClientId(app_parameters, &license_request);
&license_request);
if (NO_ERROR != status) return status; if (NO_ERROR != status) return status;
// Content Identification may be a cenc_id, a webm_id or a license_id status = PrepareContentId(init_data, license_type, request_id,
LicenseRequest_ContentIdentification* content_id = &license_request);
license_request.mutable_content_id(); if (NO_ERROR != status) return status;
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 (!PrepareContentId(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 (!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;
}
license_request.set_type(LicenseRequest::NEW); license_request.set_type(LicenseRequest::NEW);
@@ -301,7 +243,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// Get/set the nonce. This value will be reflected in the Key Control Block // Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response. // of the license response.
uint32_t nonce; uint32_t nonce;
if (!session_->GenerateNonce(&nonce)) { if (!crypto_session_->GenerateNonce(&nonce)) {
return LICENSE_REQUEST_NONCE_GENERATION_ERROR; return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
} }
license_request.set_key_control_nonce(nonce); license_request.set_key_control_nonce(nonce);
@@ -317,8 +259,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
// Derive signing and encryption keys and construct signature. // Derive signing and encryption keys and construct signature.
std::string license_request_signature; std::string license_request_signature;
if (!session_->PrepareRequest(serialized_license_req, false, if (!crypto_session_->PrepareRequest(serialized_license_req, false,
&license_request_signature)) { &license_request_signature)) {
signed_request->clear(); signed_request->clear();
return LICENSE_REQUEST_SIGNING_ERROR; return LICENSE_REQUEST_SIGNING_ERROR;
} }
@@ -329,7 +271,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return EMPTY_LICENSE_REQUEST; return EMPTY_LICENSE_REQUEST;
} }
// Put serialize license request and signature together // Put serialized license request and signature together
SignedMessage signed_message; SignedMessage signed_message;
signed_message.set_type(SignedMessage::LICENSE_REQUEST); signed_message.set_type(SignedMessage::LICENSE_REQUEST);
signed_message.set_signature(license_request_signature); signed_message.set_signature(license_request_signature);
@@ -362,6 +304,18 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
return LICENSE_RENEWAL_PROHIBITED; return LICENSE_RENEWAL_PROHIBITED;
} }
if (renew_with_client_id_) {
if (service_certificate_->IsRequired() &&
!service_certificate_->IsAvailable()) {
*server_url = server_url_;
if (service_certificate_->
PrepareServiceCertificateRequest(signed_request))
return KEY_MESSAGE;
else
return LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
}
LicenseRequest license_request; LicenseRequest license_request;
if (is_renewal) if (is_renewal)
license_request.set_type(LicenseRequest::RENEWAL); license_request.set_type(LicenseRequest::RENEWAL);
@@ -371,18 +325,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
license_request.set_request_time(clock_->GetCurrentTime()); license_request.set_request_time(clock_->GetCurrentTime());
if (renew_with_client_id_) { if (renew_with_client_id_) {
std::string service_certificate; CdmResponseType status = PrepareClientId(app_parameters, &license_request);
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);
if (NO_ERROR != status) return status; if (NO_ERROR != status) return status;
} }
@@ -397,12 +340,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
if (!provider_session_token_.empty()) { if (!provider_session_token_.empty()) {
if (!is_renewal) { if (!is_renewal) {
CdmResponseType status = CdmResponseType status =
session_->DeactivateUsageInformation(provider_session_token_); crypto_session_->DeactivateUsageInformation(provider_session_token_);
if (NO_ERROR != status) return status; if (NO_ERROR != status) return status;
} }
std::string usage_report; std::string usage_report;
CdmResponseType status = session_->GenerateUsageReport( CdmResponseType status = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status, provider_session_token_, &usage_report, &usage_duration_status,
&seconds_since_started, &seconds_since_last_played); &seconds_since_started, &seconds_since_last_played);
if (!is_renewal) { if (!is_renewal) {
@@ -427,7 +370,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
// Get/set the nonce. This value will be reflected in the Key Control Block // Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response. // of the license response.
uint32_t nonce; uint32_t nonce;
if (!session_->GenerateNonce(&nonce)) { if (!crypto_session_->GenerateNonce(&nonce)) {
return LICENSE_RENEWAL_NONCE_GENERATION_ERROR; return LICENSE_RENEWAL_NONCE_GENERATION_ERROR;
} }
license_request.set_key_control_nonce(nonce); license_request.set_key_control_nonce(nonce);
@@ -440,8 +383,8 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
// Construct signature. // Construct signature.
std::string license_request_signature; std::string license_request_signature;
if (!session_->PrepareRenewalRequest(serialized_license_req, if (!crypto_session_->PrepareRenewalRequest(serialized_license_req,
&license_request_signature)) &license_request_signature))
return LICENSE_RENEWAL_SIGNING_ERROR; return LICENSE_RENEWAL_SIGNING_ERROR;
if (license_request_signature.empty()) { if (license_request_signature.empty()) {
@@ -486,11 +429,10 @@ CdmResponseType CdmLicense::HandleKeyResponse(
break; break;
case SignedMessage::SERVICE_CERTIFICATE: { case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status = CdmResponseType status =
VerifySignedServiceCertificate(signed_response.msg()); service_certificate_->VerifyAndSet(signed_response.msg());
if (status != NO_ERROR) { if (status != NO_ERROR) {
return status; return status;
} }
Properties::SetServiceCertificate(session_id_, signed_response.msg());
return NEED_KEY; return NEED_KEY;
} }
case SignedMessage::ERROR_RESPONSE: case SignedMessage::ERROR_RESPONSE:
@@ -519,8 +461,8 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return SESSION_KEYS_NOT_FOUND; return SESSION_KEYS_NOT_FOUND;
} }
if (!session_->GenerateDerivedKeys(key_request_, if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key())) signed_response.session_key()))
return GENERATE_DERIVED_KEYS_ERROR; return GENERATE_DERIVED_KEYS_ERROR;
} }
@@ -567,7 +509,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
renew_with_client_id_ = license.policy().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, signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
key_array, provider_session_token_); key_array, provider_session_token_);
@@ -604,11 +546,10 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
break; break;
case SignedMessage::SERVICE_CERTIFICATE: { case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status = CdmResponseType status =
VerifySignedServiceCertificate(signed_response.msg()); service_certificate_->VerifyAndSet(signed_response.msg());
if (status != NO_ERROR) { if (status != NO_ERROR) {
return status; return status;
} }
Properties::SetServiceCertificate(session_id_, signed_response.msg());
return NEED_KEY; return NEED_KEY;
} }
case SignedMessage::ERROR_RESPONSE: case SignedMessage::ERROR_RESPONSE:
@@ -647,7 +588,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
if (!license.id().has_provider_session_token()) return KEY_ADDED; if (!license.id().has_provider_session_token()) return KEY_ADDED;
provider_session_token_ = license.id().provider_session_token(); provider_session_token_ = license.id().provider_session_token();
CdmResponseType status = session_->ReleaseUsageInformation( CdmResponseType status = crypto_session_->ReleaseUsageInformation(
signed_response.msg(), signed_response.signature(), signed_response.msg(), signed_response.signature(),
provider_session_token_); provider_session_token_);
return (NO_ERROR == status) ? KEY_ADDED : status; return (NO_ERROR == status) ? KEY_ADDED : status;
@@ -660,8 +601,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
std::vector<CryptoKey> key_array = ExtractContentKeys(license); std::vector<CryptoKey> key_array = ExtractContentKeys(license);
if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(), if (crypto_session_->RefreshKeys(signed_response.msg(),
key_array.size(), &key_array[0])) { signed_response.signature(),
key_array.size(), &key_array[0])) {
policy_engine_->UpdateLicense(license); policy_engine_->UpdateLicense(license);
return KEY_ADDED; return KEY_ADDED;
@@ -700,7 +642,9 @@ bool CdmLicense::RestoreOfflineLicense(
if (Properties::use_certificates_as_identification()) { if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg(); key_request_ = signed_request.msg();
} else { } else {
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false; if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
} }
CdmResponseType sts = HandleKeyResponse(license_response); CdmResponseType sts = HandleKeyResponse(license_response);
@@ -718,7 +662,7 @@ bool CdmLicense::RestoreOfflineLicense(
CryptoSession::UsageDurationStatus usage_duration_status = CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid; CryptoSession::kUsageDurationsInvalid;
int64_t seconds_since_started, seconds_since_last_played; int64_t seconds_since_started, seconds_since_last_played;
sts = session_->GenerateUsageReport( sts = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status, provider_session_token_, &usage_report, &usage_duration_status,
&seconds_since_started, &seconds_since_last_played); &seconds_since_started, &seconds_since_last_played);
@@ -775,7 +719,9 @@ bool CdmLicense::RestoreLicenseForRelease(
if (Properties::use_certificates_as_identification()) { if (Properties::use_certificates_as_identification()) {
key_request_ = signed_request.msg(); key_request_ = signed_request.msg();
} else { } else {
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false; if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
return false;
}
} }
SignedMessage signed_response; SignedMessage signed_response;
@@ -822,8 +768,8 @@ bool CdmLicense::RestoreLicenseForRelease(
} }
if (license.id().has_provider_session_token()) { if (license.id().has_provider_session_token()) {
if (!session_->GenerateDerivedKeys(key_request_, if (!crypto_session_->GenerateDerivedKeys(key_request_,
signed_response.session_key())) signed_response.session_key()))
return false; return false;
} else { } else {
return KEY_ADDED == HandleKeyResponse(license_response); return KEY_ADDED == HandleKeyResponse(license_response);
@@ -843,92 +789,6 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end(); 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) {
SignedDrmDeviceCertificate 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.drm_certificate(),
signed_service_certificate.signature())) {
LOGE(
"CdmLicense::VerifyAndExtractSignedServiceCertificate: service "
"certificate verification failed");
return DEVICE_CERTIFICATE_ERROR_3;
}
DrmDeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(
signed_service_certificate.drm_certificate())) {
LOGE(
"CdmLicense::VerifyAndExtractSignedServiceCertificate: unable to parse "
"retrieved service certificate");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (service_certificate.type() !=
video_widevine::DrmDeviceCertificate_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.drm_certificate();
}
return NO_ERROR;
}
CdmResponseType CdmLicense::HandleKeyErrorResponse( CdmResponseType CdmLicense::HandleKeyErrorResponse(
const SignedMessage& signed_message) { const SignedMessage& signed_message) {
LicenseError license_error; LicenseError license_error;
@@ -944,22 +804,40 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
return DEVICE_REVOKED; return DEVICE_REVOKED;
case LicenseError::SERVICE_UNAVAILABLE: case LicenseError::SERVICE_UNAVAILABLE:
default: default:
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d", LOGW("CdmLicense::HandleKeyErrorResponse: Unknown error type = %d",
license_error.error_code()); license_error.error_code());
return KEY_ERROR; 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( CdmResponseType CdmLicense::PrepareClientId(
bool encrypt, const std::string& certificate,
const CdmAppParameterMap& app_parameters, LicenseRequest* license_request) { const CdmAppParameterMap& app_parameters, LicenseRequest* license_request) {
ClientIdentification* client_id = license_request->mutable_client_id(); ClientIdentification* client_id = license_request->mutable_client_id();
if (Properties::use_certificates_as_identification()) ClientIdentification::TokenType token_type;
client_id->set_type(ClientIdentification::DRM_DEVICE_CERTIFICATE); if (!GetClientTokenType(&token_type)) {
else return LICENSING_CLIENT_TOKEN_ERROR_1;
client_id->set_type(ClientIdentification::KEYBOX); }
client_id->set_token(token_); client_id->set_type(token_type);
client_id->set_token(client_token_);
ClientIdentification_NameValue* client_info; ClientIdentification_NameValue* client_info;
CdmAppParameterMap::const_iterator iter; CdmAppParameterMap::const_iterator iter;
@@ -999,7 +877,7 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info->set_name(kBuildInfoKey); client_info->set_name(kBuildInfoKey);
client_info->set_value(value); client_info->set_value(value);
} }
if (session_->GetDeviceUniqueId(&value)) { if (crypto_session_->GetDeviceUniqueId(&value)) {
client_info = client_id->add_client_info(); client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey); client_info->set_name(kDeviceIdKey);
client_info->set_value(value); client_info->set_value(value);
@@ -1012,26 +890,26 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info = client_id->add_client_info(); client_info = client_id->add_client_info();
client_info->set_name(kOemCryptoSecurityPatchLevelKey); client_info->set_name(kOemCryptoSecurityPatchLevelKey);
std::stringstream ss; std::stringstream ss;
ss << (uint32_t)session_->GetSecurityPatchLevel(); ss << (uint32_t)crypto_session_->GetSecurityPatchLevel();
client_info->set_value(ss.str()); client_info->set_value(ss.str());
ClientIdentification_ClientCapabilities* client_capabilities = ClientIdentification_ClientCapabilities* client_capabilities =
client_id->mutable_client_capabilities(); client_id->mutable_client_capabilities();
bool supports_usage_information; 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_session_token(supports_usage_information);
} }
client_capabilities->set_anti_rollback_usage_table( client_capabilities->set_anti_rollback_usage_table(
session_->IsAntiRollbackHwPresent()); crypto_session_->IsAntiRollbackHwPresent());
uint32_t api_version = 0; 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); client_capabilities->set_oem_crypto_api_version(api_version);
} }
CryptoSession::HdcpCapability current_version, max_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) { switch (max_version) {
case HDCP_NONE: case HDCP_NONE:
client_capabilities->set_max_hdcp_version( client_capabilities->set_max_hdcp_version(
@@ -1071,71 +949,73 @@ CdmResponseType CdmLicense::PrepareClientId(
} }
} }
if (encrypt) { if (service_certificate_->IsRequired()) {
if (!service_certificate_->IsAvailable()) {
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
}
EncryptedClientIdentification* encrypted_client_id = EncryptedClientIdentification* encrypted_client_id =
license_request->mutable_encrypted_client_id(); license_request->mutable_encrypted_client_id();
DrmDeviceCertificate service_certificate; CdmResponseType status;
status = service_certificate_->EncryptClientId(client_id,
if (!service_certificate.ParseFromString(certificate)) { encrypted_client_id);
LOGE( if (NO_ERROR == status) {
"CdmLicense::PrepareClientId: unable to parse retrieved " license_request->clear_client_id();
"service certificate"); } else {
return PARSE_SERVICE_CERTIFICATE_ERROR; license_request->clear_encrypted_client_id();
} }
return status;
if (service_certificate.type() !=
video_widevine::DrmDeviceCertificate_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 NO_ERROR; return NO_ERROR;
} }
bool CdmLicense::GetServiceCertificate(std::string* service_certificate) { CdmResponseType CdmLicense::PrepareContentId(
std::string signed_service_certificate; const InitializationData& init_data, CdmLicenseType license_type,
return Properties::GetServiceCertificate(session_id_, const std::string& request_id, LicenseRequest* license_request) {
&signed_service_certificate) && // Content Identification may be a cenc_id, a webm_id or a license_id
!signed_service_certificate.empty() && LicenseRequest_ContentIdentification* content_id =
NO_ERROR == VerifyAndExtractSignedServiceCertificate( license_request->mutable_content_id();
signed_service_certificate, service_certificate) &&
!service_certificate->empty(); 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> template <typename T>
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type, bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
const std::string& request_id, const std::string& request_id,
T* content_id) { T* content_id) {
switch (license_type) { switch (license_type) {
case kLicenseTypeOffline: case kLicenseTypeOffline:
content_id->set_license_type(video_widevine::OFFLINE); content_id->set_license_type(video_widevine::OFFLINE);

View File

@@ -422,12 +422,16 @@ message ProvisioningRequest {
optional bytes nonce = 2; optional bytes nonce = 2;
// Options for type of certificate to generate. Optional. // Options for type of certificate to generate. Optional.
optional ProvisioningOptions options = 3; optional ProvisioningOptions options = 3;
//oneof origin_id { //oneof spoid_param {
// Stable identifier, unique for each device + application (or origin). // Stable identifier, unique for each device + application (or origin).
// Required if doing per-origin provisioning. // To be deprecated.
optional bytes stable_id = 4; optional bytes stable_id = 4;
// Stable content provider ID. // Service provider ID from the service certificate's provider_id field.
// Preferred parameter.
optional bytes provider_id = 6; 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;
//} //}
} }
@@ -533,9 +537,9 @@ message ClientIdentification {
// EncryptedClientIdentification message used to hold ClientIdentification // EncryptedClientIdentification message used to hold ClientIdentification
// messages encrypted for privacy purposes. // messages encrypted for privacy purposes.
message EncryptedClientIdentification { message EncryptedClientIdentification {
// Service ID for which the ClientIdentifcation is encrypted (owner of service // Provider ID for which the ClientIdentifcation is encrypted (owner of
// certificate). // service certificate).
optional string service_id = 1; optional string provider_id = 1;
// Serial number for the service certificate for which ClientIdentification is // Serial number for the service certificate for which ClientIdentification is
// encrypted. // encrypted.
optional bytes service_certificate_serial_number = 2; optional bytes service_certificate_serial_number = 2;
@@ -562,7 +566,7 @@ message DrmDeviceCertificate {
DRM_INTERMEDIATE = 1; DRM_INTERMEDIATE = 1;
DRM_USER_DEVICE = 2; DRM_USER_DEVICE = 2;
SERVICE = 3; SERVICE = 3;
PROVISIONING_PROVIDER = 4; PROVISIONER = 4;
} }
// Type of certificate. Required. // Type of certificate. Required.
@@ -581,9 +585,9 @@ message DrmDeviceCertificate {
// (non-production) device. The test_device field in ProvisionedDeviceInfo // (non-production) device. The test_device field in ProvisionedDeviceInfo
// below should be observed instead. // below should be observed instead.
optional bool test_device_deprecated = 6 [deprecated = true]; optional bool test_device_deprecated = 6 [deprecated = true];
// Service identifier (web origin) for the service which owns the certificate. // Service identifier (web origin) for the provider which owns the
// Required for service certificates. // certificate. Required for service and provisioner certificates.
optional string service_id = 7; optional string provider_id = 7;
} }
// Contains DRM and OEM certificate status and device information for a // Contains DRM and OEM certificate status and device information for a

View File

@@ -0,0 +1,274 @@
// 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;
ServiceCertificate::ServiceCertificate()
: crypto_session_(NULL),
valid_(false),
initialized_(false) {
certificate_.clear();
}
ServiceCertificate::~ServiceCertificate() {}
bool ServiceCertificate::Init(const CdmSessionId& session_id,
CryptoSession* session) {
if (session_id.empty()) {
LOGE("ServiceCertificate::Init: empty session id provided");
return false;
}
if (session == NULL) {
LOGE("ServiceCertificate::Init: crypto session not provided");
return false;
}
session_id_.assign(session_id);
crypto_session_ = session;
privacy_mode_enabled_ = Properties::UsePrivacyMode(session_id_);
SetupServiceCertificate();
initialized_ = true;
return true;
}
bool ServiceCertificate::IsRequired() {
return privacy_mode_enabled_;
}
bool ServiceCertificate::IsAvailable() {
return valid_;
}
CdmResponseType ServiceCertificate::VerifyAndSet(
const std::string& signed_service_certificate) {
CdmResponseType status;
std::string certificate;
bool has_provider_id;
status = VerifyAndExtractFromSignedCertificate(signed_service_certificate,
&certificate, &has_provider_id,
NULL);
if (status == NO_ERROR) {
Properties::SetServiceCertificate(session_id_, certificate);
} else {
LOGE("ServiceCertificate::VerifyAndSet: verify and extract failed with "
"status %d", status);
}
return status;
}
bool ServiceCertificate::PrepareServiceCertificateRequest(
CdmKeyMessage* signed_request) {
if (!initialized_) {
LOGE("ServiceCertificate::PrepareServiceCertificateRequest: "
"not initialized");
return false;
}
if (!signed_request) {
LOGE("ServiceCertificate::PrepareServiceCertificateRequest: "
"no signed request provided");
return false;
}
SignedMessage signed_message;
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
signed_message.SerializeToString(signed_request);
return true;
}
CdmResponseType ServiceCertificate::VerifyAndExtractFromSignedCertificate(
const std::string& signed_certificate, std::string* certificate,
bool* has_provider_id, std::string* provider_id) {
SignedDrmDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(signed_certificate)) {
LOGE(
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: "
"unable to parse signed service certificate");
return DEVICE_CERTIFICATE_ERROR_1;
}
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::VerifyAndExtractFromSignedCertificate: public key "
"initialization failed");
return DEVICE_CERTIFICATE_ERROR_2;
}
if (!root_ca_key.VerifySignature(
signed_service_certificate.drm_certificate(),
signed_service_certificate.signature())) {
LOGE(
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: service "
"certificate verification failed");
return DEVICE_CERTIFICATE_ERROR_3;
}
DrmDeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(
signed_service_certificate.drm_certificate())) {
LOGE(
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: unable to "
"parse retrieved service certificate");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (service_certificate.type() !=
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
LOGE(
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: "
"certificate not of type service, %d", service_certificate.type());
return INVALID_DEVICE_CERTIFICATE_TYPE;
}
#if 0 // TODO(gmorgan): service cert has no provider_id
*has_provider_id = service_certificate.has_provider_id();
if (*has_provider_id && provider_id != NULL) {
*provider_id = service_certificate.provider_id();
} else {
return DEVICE_CERTIFICATE_ERROR_5;
}
#endif
if (certificate != NULL) {
*certificate = signed_service_certificate.drm_certificate();
}
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
DrmDeviceCertificate service_certificate;
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); // TODO(gmorgan) randomize
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::SetupServiceCertificate() {
std::string signed_certificate;
valid_ = false;
certificate_.clear();
if (!Properties::GetServiceCertificate(session_id_, &signed_certificate)) {
return false;
}
if (signed_certificate.empty()) {
return false;
}
std::string extracted_certificate;
std::string extracted_provider_id;
bool has_provider_id;
if (NO_ERROR != VerifyAndExtractFromSignedCertificate(
signed_certificate, &extracted_certificate,
&has_provider_id, &extracted_provider_id)) {
return false;
}
if (extracted_certificate.empty()) {
return false;
}
has_provider_id_ = has_provider_id;
if (has_provider_id_) {
provider_id_ = extracted_provider_id;
}
certificate_ = extracted_certificate;
valid_ = true;
return true;
}
} // namespace wvcdm

View File

@@ -98,7 +98,9 @@ class MockDeviceFiles : public DeviceFiles {
class MockCryptoSession : public CryptoSession { class MockCryptoSession : public CryptoSession {
public: public:
MOCK_METHOD1(GetToken, bool(std::string*)); MOCK_METHOD1(GetClientToken, bool(std::string*));
MOCK_METHOD1(GetProvisioningToken, bool(std::string*));
MOCK_METHOD0(GetPreProvisionTokenType, CdmClientTokenType());
MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel()); MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel());
MOCK_METHOD0(Open, CdmResponseType()); MOCK_METHOD0(Open, CdmResponseType());
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel)); MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
@@ -118,7 +120,8 @@ class MockCdmLicense : public CdmLicense {
MockCdmLicense(const CdmSessionId& session_id) MockCdmLicense(const CdmSessionId& session_id)
: CdmLicense(session_id) {} : CdmLicense(session_id) {}
MOCK_METHOD3(Init, bool(const std::string&, CryptoSession*, PolicyEngine*)); MOCK_METHOD4(Init, bool(const std::string&, CdmClientTokenType,
CryptoSession*, PolicyEngine*));
}; };
} // namespace } // namespace
@@ -154,6 +157,34 @@ class CdmSessionTest : public ::testing::Test {
MockDeviceFiles* file_handle_; MockDeviceFiles* file_handle_;
}; };
TEST_F(CdmSessionTest, InitWithBuiltInCertificate) {
Sequence crypto_session_seq;
CdmSecurityLevel level = kSecurityLevelL1;
EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault)))
.InSequence(crypto_session_seq)
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
.WillOnce(Return(kClientTokenDrmCert));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
.InSequence(crypto_session_seq)
.WillOnce(Return(true));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*license_parser_,
Init(Eq(kToken), Eq(kClientTokenDrmCert),
Eq(crypto_session_), Eq(policy_engine_)))
.WillOnce(Return(true));
Properties::set_use_certificates_as_identification(true);
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
}
TEST_F(CdmSessionTest, InitWithCertificate) { TEST_F(CdmSessionTest, InitWithCertificate) {
Sequence crypto_session_seq; Sequence crypto_session_seq;
CdmSecurityLevel level = kSecurityLevelL1; CdmSecurityLevel level = kSecurityLevelL1;
@@ -163,6 +194,8 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
EXPECT_CALL(*crypto_session_, GetSecurityLevel()) EXPECT_CALL(*crypto_session_, GetSecurityLevel())
.InSequence(crypto_session_seq) .InSequence(crypto_session_seq)
.WillOnce(Return(level)); .WillOnce(Return(level));
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey), .WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
@@ -171,7 +204,8 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
.InSequence(crypto_session_seq) .InSequence(crypto_session_seq)
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_CALL(*license_parser_, EXPECT_CALL(*license_parser_,
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_))) Init(Eq(kToken), Eq(kClientTokenDrmCert),
Eq(crypto_session_), Eq(policy_engine_)))
.WillOnce(Return(true)); .WillOnce(Return(true));
Properties::set_use_certificates_as_identification(true); Properties::set_use_certificates_as_identification(true);
@@ -188,12 +222,15 @@ TEST_F(CdmSessionTest, InitWithKeybox) {
EXPECT_CALL(*crypto_session_, GetSecurityLevel()) EXPECT_CALL(*crypto_session_, GetSecurityLevel())
.InSequence(crypto_session_seq) .InSequence(crypto_session_seq)
.WillOnce(Return(level)); .WillOnce(Return(level));
EXPECT_CALL(*crypto_session_, GetToken(NotNull())) EXPECT_CALL(*crypto_session_, GetClientToken(NotNull()))
.InSequence(crypto_session_seq) .InSequence(crypto_session_seq)
.WillOnce(DoAll(SetArgPointee<0>(kToken), Return(true))); .WillOnce(DoAll(SetArgPointee<0>(kToken), Return(true)));
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*license_parser_, EXPECT_CALL(*license_parser_,
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_))) Init(Eq(kToken), Eq(kClientTokenKeybox),
Eq(crypto_session_), Eq(policy_engine_)))
.WillOnce(Return(true)); .WillOnce(Return(true));
Properties::set_use_certificates_as_identification(false); Properties::set_use_certificates_as_identification(false);
@@ -210,6 +247,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
EXPECT_CALL(*crypto_session_, GetSecurityLevel()) EXPECT_CALL(*crypto_session_, GetSecurityLevel())
.InSequence(crypto_session_seq) .InSequence(crypto_session_seq)
.WillOnce(Return(level)); .WillOnce(Return(level));
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey), .WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
@@ -218,7 +257,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
.InSequence(crypto_session_seq) .InSequence(crypto_session_seq)
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_CALL(*license_parser_, EXPECT_CALL(*license_parser_,
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_))) Init(Eq(kToken), Eq(kClientTokenDrmCert),
Eq(crypto_session_), Eq(policy_engine_)))
.WillOnce(Return(true)); .WillOnce(Return(true));
Properties::set_use_certificates_as_identification(true); Properties::set_use_certificates_as_identification(true);
@@ -245,6 +285,8 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
EXPECT_CALL(*crypto_session_, GetSecurityLevel()) EXPECT_CALL(*crypto_session_, GetSecurityLevel())
.InSequence(crypto_session_seq) .InSequence(crypto_session_seq)
.WillOnce(Return(level)); .WillOnce(Return(level));
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull())) EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(Return(false)); .WillOnce(Return(false));

View File

@@ -156,24 +156,28 @@ TEST_F(CdmLicenseTest, InitSuccess) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense(); CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_)); EXPECT_TRUE(cdm_license_->Init(kToken, kClientTokenDrmCert,
crypto_session_, policy_engine_));
} }
TEST_F(CdmLicenseTest, InitFail_EmptyToken) { TEST_F(CdmLicenseTest, InitFail_EmptyToken) {
CreateCdmLicense(); CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init("", crypto_session_, policy_engine_)); EXPECT_FALSE(cdm_license_->Init("", kClientTokenDrmCert,
crypto_session_, policy_engine_));
} }
TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) { TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) {
CreateCdmLicense(); CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(kToken, NULL, policy_engine_)); EXPECT_FALSE(cdm_license_->Init(kToken, kClientTokenDrmCert,
NULL, policy_engine_));
} }
TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) { TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) {
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
CreateCdmLicense(); CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(kToken, crypto_session_, NULL)); EXPECT_FALSE(cdm_license_->Init(kToken, kClientTokenDrmCert,
crypto_session_, NULL));
} }
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
@@ -182,7 +186,8 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1; CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1;
uint32_t crypto_session_api_version = 9; uint32_t crypto_session_api_version = 9;
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); EXPECT_CALL(*crypto_session_, IsOpen())
.WillOnce(Return(true));
EXPECT_CALL(*crypto_session_, GenerateRequestId(NotNull())) EXPECT_CALL(*crypto_session_, GenerateRequestId(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true))); .WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true)));
EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull())) EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull()))
@@ -202,7 +207,8 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true))); DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true)));
CreateCdmLicense(); CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_)); EXPECT_TRUE(cdm_license_->Init(kToken, kClientTokenDrmCert,
crypto_session_, policy_engine_));
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
CdmKeyMessage signed_request; CdmKeyMessage signed_request;

View File

@@ -95,6 +95,8 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break; break;
case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4"; case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4";
break; break;
case DEVICE_CERTIFICATE_ERROR_5: *os << "DEVICE_CERTIFICATE_ERROR_5";
break;
case EMPTY_KEY_DATA_1: *os << "EMPTY_KEY_DATA_1"; case EMPTY_KEY_DATA_1: *os << "EMPTY_KEY_DATA_1";
break; break;
case EMPTY_KEY_DATA_2: *os << "EMPTY_KEY_DATA_2"; case EMPTY_KEY_DATA_2: *os << "EMPTY_KEY_DATA_2";
@@ -268,9 +270,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case RESTORE_OFFLINE_LICENSE_ERROR_2: case RESTORE_OFFLINE_LICENSE_ERROR_2:
*os << "RESTORE_OFFLINE_LICENSE_ERROR_2"; *os << "RESTORE_OFFLINE_LICENSE_ERROR_2";
break; break;
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2"; case SESSION_INIT_ERROR_1: *os << "SESSION_INIT_ERROR_1";
break; break;
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR"; case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
break; break;
case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1"; case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1";
break; break;
@@ -445,6 +447,15 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break; break;
case INVALID_PARAMETERS_ENG_16: *os << "INVALID_PARAMETERS_ENG_16"; case INVALID_PARAMETERS_ENG_16: *os << "INVALID_PARAMETERS_ENG_16";
break; break;
case CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1:
*os << "CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1";
break;
case CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2:
*os << "CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2";
break;
case LICENSING_CLIENT_TOKEN_ERROR_1:
*os << "LICENSING_CLIENT_TOKEN_ERROR_1";
break;
default: default:
*os << "Unknown CdmResponseType"; *os << "Unknown CdmResponseType";
break; break;

View File

@@ -8,6 +8,7 @@
#include "license.h" #include "license.h"
#include "log.h" #include "log.h"
#include "properties.h" #include "properties.h"
#include "service_certificate.h"
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h" #include "wv_cdm_event_listener.h"
@@ -257,7 +258,8 @@ void WvContentDecryptionModule::NotifyResolution(const CdmSessionId& session_id,
bool WvContentDecryptionModule::IsValidServiceCertificate( bool WvContentDecryptionModule::IsValidServiceCertificate(
const std::string& certificate) { const std::string& certificate) {
return CdmLicense::VerifySignedServiceCertificate(certificate) == NO_ERROR; return ServiceCertificate::VerifySignedServiceCertificate(certificate) ==
NO_ERROR;
} }
WvContentDecryptionModule::CdmInfo::CdmInfo() WvContentDecryptionModule::CdmInfo::CdmInfo()

View File

@@ -213,10 +213,13 @@ enum {
kInvalidParametersEng14 = ERROR_DRM_VENDOR_MIN + 199, kInvalidParametersEng14 = ERROR_DRM_VENDOR_MIN + 199,
kInvalidParametersEng15 = ERROR_DRM_VENDOR_MIN + 200, kInvalidParametersEng15 = ERROR_DRM_VENDOR_MIN + 200,
kInvalidParametersEng16 = ERROR_DRM_VENDOR_MIN + 201, kInvalidParametersEng16 = ERROR_DRM_VENDOR_MIN + 201,
kDeviceCertificateError5 = ERROR_DRM_VENDOR_MIN + 202,
kCertProvisioningClientTokenError1 = ERROR_DRM_VENDOR_MIN + 203,
kCertProvisioningClientTokenError2 = ERROR_DRM_VENDOR_MIN + 204,
kLicensingClientTokenError1 = ERROR_DRM_VENDOR_MIN + 205,
// This should always follow the last error code. // This should always follow the last error code.
// The offset value should be updated each time a new error code is added. // The offset value should be updated each time a new error code is added.
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 201, kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 205,
// Used by crypto test mode // Used by crypto test mode
kErrorTestMode = ERROR_DRM_VENDOR_MAX, kErrorTestMode = ERROR_DRM_VENDOR_MAX,

View File

@@ -81,6 +81,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
return kDeviceCertificateError3; return kDeviceCertificateError3;
case wvcdm::DEVICE_CERTIFICATE_ERROR_4: case wvcdm::DEVICE_CERTIFICATE_ERROR_4:
return kDeviceCertificateError4; return kDeviceCertificateError4;
case wvcdm::DEVICE_CERTIFICATE_ERROR_5:
return kDeviceCertificateError5;
case wvcdm::EMPTY_KEY_DATA_1: case wvcdm::EMPTY_KEY_DATA_1:
return kEmptyKeyData1; return kEmptyKeyData1;
case wvcdm::EMPTY_KEY_DATA_2: case wvcdm::EMPTY_KEY_DATA_2:
@@ -165,6 +167,20 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
return kInvalidParametersEng4; return kInvalidParametersEng4;
case wvcdm::INVALID_PARAMETERS_ENG_5: case wvcdm::INVALID_PARAMETERS_ENG_5:
return kInvalidParametersEng5; return kInvalidParametersEng5;
case wvcdm::INVALID_PARAMETERS_ENG_6:
return kInvalidParametersEng6;
case wvcdm::INVALID_PARAMETERS_ENG_7:
return kInvalidParametersEng7;
case wvcdm::INVALID_PARAMETERS_ENG_8:
return kInvalidParametersEng8;
case wvcdm::INVALID_PARAMETERS_ENG_9:
return kInvalidParametersEng9;
case wvcdm::INVALID_PARAMETERS_ENG_10:
return kInvalidParametersEng10;
case wvcdm::INVALID_PARAMETERS_ENG_11:
return kInvalidParametersEng11;
case wvcdm::INVALID_PARAMETERS_ENG_12:
return kInvalidParametersEng12;
case wvcdm::INVALID_PARAMETERS_LIC_1: case wvcdm::INVALID_PARAMETERS_LIC_1:
return kInvalidParametersLic1; return kInvalidParametersLic1;
case wvcdm::INVALID_PARAMETERS_LIC_2: case wvcdm::INVALID_PARAMETERS_LIC_2:
@@ -237,10 +253,10 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
return kLicenseRenewalSigningError; return kLicenseRenewalSigningError;
case wvcdm::RESTORE_OFFLINE_LICENSE_ERROR_2: case wvcdm::RESTORE_OFFLINE_LICENSE_ERROR_2:
return kRestoreOfflineLicenseError2; return kRestoreOfflineLicenseError2;
case wvcdm::SESSION_INIT_ERROR_1:
return kSessionInitError1;
case wvcdm::SESSION_INIT_ERROR_2: case wvcdm::SESSION_INIT_ERROR_2:
return kSessionInitError2; return kSessionInitError2;
case wvcdm::SESSION_INIT_GET_KEYBOX_ERROR:
return kSessionInitGetKeyboxError;
case wvcdm::SESSION_NOT_FOUND_1: case wvcdm::SESSION_NOT_FOUND_1:
return kSessionNotFound1; return kSessionNotFound1;
case wvcdm::SESSION_NOT_FOUND_2: case wvcdm::SESSION_NOT_FOUND_2:
@@ -363,6 +379,14 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
return android::ERROR_DRM_CANNOT_HANDLE; return android::ERROR_DRM_CANNOT_HANDLE;
case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION: case wvcdm::INSUFFICIENT_OUTPUT_PROTECTION:
return android::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; return android::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION;
case wvcdm::SESSION_NOT_FOUND_12:
return kSessionNotFound12;
case wvcdm::KEY_NOT_FOUND_1:
return kKeyNotFound1;
case wvcdm::KEY_NOT_FOUND_2:
return kKeyNotFound2;
case wvcdm::KEY_CONFLICT_1:
return kKeyConflict1;
case wvcdm::SESSION_NOT_FOUND_13: case wvcdm::SESSION_NOT_FOUND_13:
return kSessionNotFound13; return kSessionNotFound13;
case wvcdm::SESSION_NOT_FOUND_14: case wvcdm::SESSION_NOT_FOUND_14:
@@ -395,6 +419,12 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
return kInvalidParametersEng15; return kInvalidParametersEng15;
case wvcdm::INVALID_PARAMETERS_ENG_16: case wvcdm::INVALID_PARAMETERS_ENG_16:
return kInvalidParametersEng16; return kInvalidParametersEng16;
case wvcdm::CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1:
return kCertProvisioningClientTokenError1;
case wvcdm::CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2:
return kCertProvisioningClientTokenError2;
case wvcdm::LICENSING_CLIENT_TOKEN_ERROR_1:
return kLicensingClientTokenError1;
case wvcdm::UNUSED_1: case wvcdm::UNUSED_1:
case wvcdm::UNUSED_2: case wvcdm::UNUSED_2:
case wvcdm::UNUSED_3: case wvcdm::UNUSED_3: