Merge "Support Keybox, DRM Cert, and OEM Cert for Client ID"
This commit is contained in:
@@ -18,6 +18,7 @@ LOCAL_STATIC_LIBRARIES := libcdm_protos libcrypto_static
|
||||
|
||||
SRC_DIR := src
|
||||
CORE_SRC_DIR := core/src
|
||||
PROFILER_SRC_DIR := profiler/src
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
$(CORE_SRC_DIR)/buffer_reader.cpp \
|
||||
@@ -32,9 +33,9 @@ LOCAL_SRC_FILES := \
|
||||
$(CORE_SRC_DIR)/oemcrypto_adapter_dynamic.cpp \
|
||||
$(CORE_SRC_DIR)/policy_engine.cpp \
|
||||
$(CORE_SRC_DIR)/privacy_crypto_openssl.cpp \
|
||||
$(CORE_SRC_DIR)/service_certificate.cpp \
|
||||
$(SRC_DIR)/wv_content_decryption_module.cpp \
|
||||
|
||||
|
||||
LOCAL_MODULE := libcdm
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_TARGET_ARCH := arm x86 mips
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "service_certificate.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -16,7 +17,9 @@ class FileSystem;
|
||||
|
||||
class CertificateProvisioning {
|
||||
public:
|
||||
CertificateProvisioning() : cert_type_(kCertificateWidevine) {};
|
||||
CertificateProvisioning() :
|
||||
cert_type_(kCertificateWidevine),
|
||||
service_certificate_(NULL) {};
|
||||
~CertificateProvisioning() {};
|
||||
|
||||
// Provisioning related methods
|
||||
@@ -33,6 +36,12 @@ class CertificateProvisioning {
|
||||
std::string* wrapped_key);
|
||||
|
||||
private:
|
||||
bool GetProvisioningTokenType(
|
||||
video_widevine::ClientIdentification::TokenType* token_type);
|
||||
|
||||
video_widevine::SignedProvisioningMessage::ProtocolVersion
|
||||
GetProtocolVersion();
|
||||
|
||||
void ComposeJsonRequestAsQueryString(const std::string& message,
|
||||
CdmProvisioningRequest* request);
|
||||
bool ParseJsonResponse(const CdmProvisioningResponse& json_str,
|
||||
@@ -40,9 +49,11 @@ class CertificateProvisioning {
|
||||
const std::string& end_substr, std::string* result);
|
||||
CryptoSession crypto_session_;
|
||||
CdmCertificateType cert_type_;
|
||||
ServiceCertificate* service_certificate_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
||||
|
||||
@@ -29,8 +29,11 @@ class CryptoSession {
|
||||
CryptoSession();
|
||||
virtual ~CryptoSession();
|
||||
|
||||
virtual bool ValidateKeybox();
|
||||
virtual bool GetToken(std::string* token);
|
||||
virtual bool GetClientToken(std::string* client_token);
|
||||
virtual bool GetProvisioningToken(std::string* client_token);
|
||||
virtual CdmClientTokenType GetPreProvisionTokenType() {
|
||||
return pre_provision_token_type_;
|
||||
}
|
||||
virtual CdmSecurityLevel GetSecurityLevel();
|
||||
virtual bool GetDeviceUniqueId(std::string* device_id);
|
||||
virtual bool GetApiVersion(uint32_t* version);
|
||||
@@ -124,8 +127,11 @@ class CryptoSession {
|
||||
const std::string& signature);
|
||||
|
||||
private:
|
||||
bool GetProvisioningMethod(CdmClientTokenType* token_type);
|
||||
void Init();
|
||||
void Terminate();
|
||||
bool GetTokenFromKeybox(std::string* token);
|
||||
bool GetTokenFromOemCert(std::string* token);
|
||||
void GenerateMacContext(const std::string& input_context,
|
||||
std::string* deriv_context);
|
||||
void GenerateEncryptContext(const std::string& input_context,
|
||||
@@ -163,6 +169,7 @@ class CryptoSession {
|
||||
static int session_count_;
|
||||
|
||||
bool open_;
|
||||
CdmClientTokenType pre_provision_token_type_;
|
||||
bool update_usage_table_after_close_session_;
|
||||
CryptoSessionId oec_session_id_;
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include <set>
|
||||
|
||||
#include "initialization_data.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "service_certificate.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace video_widevine {
|
||||
@@ -25,11 +27,12 @@ class CdmLicense {
|
||||
CdmLicense(const CdmSessionId& session_id);
|
||||
virtual ~CdmLicense();
|
||||
|
||||
virtual bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
virtual bool Init(
|
||||
const std::string& client_token, CdmClientTokenType client_token_type,
|
||||
CryptoSession* session, PolicyEngine* policy_engine);
|
||||
|
||||
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,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType PrepareKeyUpdateRequest(
|
||||
@@ -59,33 +62,32 @@ class CdmLicense {
|
||||
return is_offline_;
|
||||
}
|
||||
|
||||
static CdmResponseType VerifySignedServiceCertificate(
|
||||
const std::string& signed_service_certificate);
|
||||
|
||||
private:
|
||||
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
|
||||
CdmResponseType HandleKeyErrorResponse(
|
||||
const video_widevine::SignedMessage& signed_message);
|
||||
|
||||
bool GetClientTokenType(
|
||||
video_widevine::ClientIdentification::TokenType* token_type);
|
||||
|
||||
CdmResponseType PrepareClientId(
|
||||
bool encrypt, const std::string& certificate,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
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>
|
||||
bool PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id);
|
||||
bool SetTypeAndId(CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id);
|
||||
|
||||
static CdmResponseType VerifyAndExtractSignedServiceCertificate(
|
||||
const std::string& signed_service_certificate,
|
||||
std::string* service_certificate);
|
||||
bool GetServiceCertificate(std::string* service_certificate);
|
||||
|
||||
CryptoSession* session_;
|
||||
CryptoSession* crypto_session_;
|
||||
PolicyEngine* policy_engine_;
|
||||
std::string server_url_;
|
||||
std::string token_;
|
||||
std::string client_token_;
|
||||
CdmClientTokenType client_token_type_;
|
||||
const CdmSessionId session_id_;
|
||||
scoped_ptr<InitializationData> stored_init_data_;
|
||||
bool initialized_;
|
||||
@@ -94,6 +96,9 @@ class CdmLicense {
|
||||
bool renew_with_client_id_;
|
||||
bool is_offline_;
|
||||
|
||||
// Used to encrypt ClientIdentification message
|
||||
scoped_ptr<ServiceCertificate> service_certificate_;
|
||||
|
||||
// Used for certificate based licensing
|
||||
CdmKeyMessage key_request_;
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ class Properties {
|
||||
}
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(CdmSessionTest, InitWithBuiltInCertificate);
|
||||
FRIEND_TEST(CdmSessionTest, InitWithCertificate);
|
||||
FRIEND_TEST(CdmSessionTest, InitWithKeybox);
|
||||
FRIEND_TEST(CdmSessionTest, ReInitFail);
|
||||
|
||||
81
libwvdrmengine/cdm/core/include/service_certificate.h
Normal file
81
libwvdrmengine/cdm/core/include/service_certificate.h
Normal 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_
|
||||
@@ -14,6 +14,10 @@ static const size_t KEY_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
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
|
||||
// (NaN in JS translates to 0 in unix timestamp).
|
||||
static const int64_t NEVER_EXPIRES = 0;
|
||||
|
||||
@@ -151,9 +151,9 @@ enum CdmResponseType {
|
||||
LICENSE_RENEWAL_SIGNING_ERROR,
|
||||
UNUSED_4, /* previously RESTORE_OFFLINE_LICENSE_ERROR_1 */
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_2,
|
||||
UNUSED_5, /* SESSION_INIT_ERROR_1 */
|
||||
SESSION_INIT_ERROR_1,
|
||||
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_2,
|
||||
SESSION_NOT_FOUND_3,
|
||||
@@ -245,6 +245,10 @@ enum CdmResponseType {
|
||||
INVALID_PARAMETERS_ENG_14, /* 205 */
|
||||
INVALID_PARAMETERS_ENG_15,
|
||||
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 {
|
||||
@@ -311,6 +315,12 @@ enum CdmSigningAlgorithm {
|
||||
kSigningAlgorithmHmacSha256
|
||||
};
|
||||
|
||||
enum CdmClientTokenType {
|
||||
kClientTokenKeybox,
|
||||
kClientTokenDrmCert,
|
||||
kClientTokenOemCert
|
||||
};
|
||||
|
||||
class CdmKeyAllowedUsage {
|
||||
public:
|
||||
CdmKeyAllowedUsage() {
|
||||
|
||||
@@ -125,7 +125,6 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CloseExpiredReleaseSessions();
|
||||
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
|
||||
|
||||
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
|
||||
event_listener);
|
||||
if (sts != NO_ERROR) {
|
||||
@@ -158,12 +157,15 @@ CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
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_);
|
||||
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);
|
||||
|
||||
CdmSessionId session_id;
|
||||
@@ -497,7 +499,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;
|
||||
}
|
||||
|
||||
@@ -505,7 +507,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,16 +76,32 @@ 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.
|
||||
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;
|
||||
if (!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
|
||||
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key) ||
|
||||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
} else {
|
||||
if (!crypto_session_->GetToken(&token))
|
||||
return SESSION_INIT_GET_KEYBOX_ERROR;
|
||||
client_token_type = kClientTokenDrmCert;
|
||||
}
|
||||
|
||||
if (forced_session_id) {
|
||||
@@ -112,8 +128,8 @@ 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(client_token, client_token_type,
|
||||
crypto_session_.get(), policy_engine_.get()))
|
||||
return LICENSE_PARSER_INIT_ERROR;
|
||||
|
||||
license_received_ = false;
|
||||
@@ -299,13 +315,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;
|
||||
@@ -445,7 +461,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;
|
||||
@@ -477,7 +493,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();
|
||||
|
||||
@@ -48,6 +48,38 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -74,14 +106,35 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
|
||||
// Prepares 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 (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;
|
||||
if (!crypto_session_.GenerateNonce(&nonce)) {
|
||||
@@ -112,12 +165,14 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
cert_type_ = cert_type;
|
||||
options->set_certificate_authority(cert_authority);
|
||||
|
||||
// TODO(gmorgan): use provider ID.
|
||||
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;
|
||||
}
|
||||
// TODO(gmorgan): handle provider id variants.
|
||||
provisioning_request.set_stable_id(device_unique_id + origin);
|
||||
}
|
||||
|
||||
@@ -139,6 +194,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);
|
||||
|
||||
@@ -54,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_);
|
||||
@@ -66,6 +86,9 @@ void CryptoSession::Init() {
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CryptoSession::Terminate() {
|
||||
@@ -85,35 +108,79 @@ 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);
|
||||
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;
|
||||
}
|
||||
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() {
|
||||
@@ -160,11 +227,13 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts =
|
||||
if (pre_provision_token_type_ == kClientTokenKeybox) {
|
||||
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);
|
||||
@@ -312,6 +381,7 @@ bool CryptoSession::PrepareRequest(const std::string& message,
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(gmorgan): rework this for OEM certs.
|
||||
if (!Properties::use_certificates_as_identification() || is_provisioning) {
|
||||
if (!GenerateDerivedKeys(message)) return false;
|
||||
|
||||
|
||||
@@ -27,42 +27,8 @@ 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;
|
||||
@@ -155,7 +121,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),
|
||||
@@ -164,7 +130,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),
|
||||
@@ -175,8 +141,9 @@ 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(
|
||||
const std::string& client_token, CdmClientTokenType client_token_type,
|
||||
CryptoSession* session, PolicyEngine* policy_engine) {
|
||||
if (clock_.get() == NULL) {
|
||||
LOGE("CdmLicense::Init: clock parameter not provided");
|
||||
return false;
|
||||
@@ -185,8 +152,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()) {
|
||||
@@ -197,15 +164,26 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
||||
LOGE("CdmLicense::Init: no policy engine provided");
|
||||
return false;
|
||||
}
|
||||
token_ = token;
|
||||
session_ = session;
|
||||
|
||||
client_token_ = client_token;
|
||||
client_token_type_ = client_token_type;
|
||||
crypto_session_ = session;
|
||||
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;
|
||||
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_) {
|
||||
@@ -236,63 +214,27 @@ 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 (service_certificate_->IsRequired() &&
|
||||
!service_certificate_->IsAvailable()) {
|
||||
stored_init_data_.reset(new InitializationData(init_data));
|
||||
*server_url = server_url_;
|
||||
if (service_certificate_->PrepareServiceCertificateRequest(signed_request))
|
||||
return KEY_MESSAGE;
|
||||
else
|
||||
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_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;
|
||||
}
|
||||
status = PrepareContentId(init_data, license_type, request_id,
|
||||
&license_request);
|
||||
if (NO_ERROR != status) return status;
|
||||
|
||||
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
|
||||
// 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);
|
||||
@@ -317,8 +259,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;
|
||||
}
|
||||
@@ -329,7 +271,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);
|
||||
@@ -362,6 +304,18 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
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;
|
||||
if (is_renewal)
|
||||
license_request.set_type(LicenseRequest::RENEWAL);
|
||||
@@ -371,18 +325,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -397,12 +340,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) {
|
||||
@@ -427,7 +370,7 @@ 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);
|
||||
@@ -440,8 +383,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()) {
|
||||
@@ -486,11 +429,10 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status =
|
||||
VerifySignedServiceCertificate(signed_response.msg());
|
||||
service_certificate_->VerifyAndSet(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
Properties::SetServiceCertificate(session_id_, signed_response.msg());
|
||||
return NEED_KEY;
|
||||
}
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
@@ -519,8 +461,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;
|
||||
}
|
||||
|
||||
@@ -567,7 +509,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
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_);
|
||||
|
||||
@@ -604,11 +546,10 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status =
|
||||
VerifySignedServiceCertificate(signed_response.msg());
|
||||
service_certificate_->VerifyAndSet(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
Properties::SetServiceCertificate(session_id_, signed_response.msg());
|
||||
return NEED_KEY;
|
||||
}
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
@@ -647,7 +588,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
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;
|
||||
@@ -660,8 +601,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;
|
||||
@@ -700,7 +642,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);
|
||||
@@ -718,7 +662,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);
|
||||
|
||||
@@ -775,7 +719,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;
|
||||
@@ -822,8 +768,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);
|
||||
@@ -843,92 +789,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) {
|
||||
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(
|
||||
const SignedMessage& signed_message) {
|
||||
LicenseError license_error;
|
||||
@@ -944,22 +804,40 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
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::DRM_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;
|
||||
@@ -999,7 +877,7 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
client_info->set_name(kBuildInfoKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (session_->GetDeviceUniqueId(&value)) {
|
||||
if (crypto_session_->GetDeviceUniqueId(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(value);
|
||||
@@ -1012,26 +890,26 @@ 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(¤t_version, &max_version)) {
|
||||
if (crypto_session_->GetHdcpCapabilities(¤t_version, &max_version)) {
|
||||
switch (max_version) {
|
||||
case HDCP_NONE:
|
||||
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 =
|
||||
license_request->mutable_encrypted_client_id();
|
||||
DrmDeviceCertificate 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(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::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 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::OFFLINE);
|
||||
|
||||
@@ -422,12 +422,16 @@ message ProvisioningRequest {
|
||||
optional bytes nonce = 2;
|
||||
// Options for type of certificate to generate. Optional.
|
||||
optional ProvisioningOptions options = 3;
|
||||
//oneof origin_id {
|
||||
//oneof spoid_param {
|
||||
// Stable identifier, unique for each device + application (or origin).
|
||||
// Required if doing per-origin provisioning.
|
||||
// To be deprecated.
|
||||
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;
|
||||
// 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
|
||||
// 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;
|
||||
@@ -562,7 +566,7 @@ message DrmDeviceCertificate {
|
||||
DRM_INTERMEDIATE = 1;
|
||||
DRM_USER_DEVICE = 2;
|
||||
SERVICE = 3;
|
||||
PROVISIONING_PROVIDER = 4;
|
||||
PROVISIONER = 4;
|
||||
}
|
||||
|
||||
// Type of certificate. Required.
|
||||
@@ -581,9 +585,9 @@ message DrmDeviceCertificate {
|
||||
// (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;
|
||||
}
|
||||
|
||||
// Contains DRM and OEM certificate status and device information for a
|
||||
|
||||
274
libwvdrmengine/cdm/core/src/service_certificate.cpp
Normal file
274
libwvdrmengine/cdm/core/src/service_certificate.cpp
Normal 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
|
||||
@@ -98,7 +98,9 @@ class MockDeviceFiles : public DeviceFiles {
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
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(Open, CdmResponseType());
|
||||
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
|
||||
@@ -118,7 +120,8 @@ class MockCdmLicense : public CdmLicense {
|
||||
MockCdmLicense(const CdmSessionId& 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
|
||||
@@ -154,6 +157,34 @@ class CdmSessionTest : public ::testing::Test {
|
||||
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) {
|
||||
Sequence crypto_session_seq;
|
||||
CdmSecurityLevel level = kSecurityLevelL1;
|
||||
@@ -163,6 +194,8 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.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_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
@@ -171,7 +204,8 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(true));
|
||||
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));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
@@ -188,12 +222,15 @@ TEST_F(CdmSessionTest, InitWithKeybox) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*crypto_session_, GetToken(NotNull()))
|
||||
EXPECT_CALL(*crypto_session_, GetClientToken(NotNull()))
|
||||
.InSequence(crypto_session_seq)
|
||||
.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(*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));
|
||||
|
||||
Properties::set_use_certificates_as_identification(false);
|
||||
@@ -210,6 +247,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.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_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
@@ -218,7 +257,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(true));
|
||||
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));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
@@ -245,6 +285,8 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.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_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
@@ -156,24 +156,28 @@ TEST_F(CdmLicenseTest, InitSuccess) {
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
|
||||
CreateCdmLicense();
|
||||
EXPECT_FALSE(cdm_license_->Init(kToken, crypto_session_, NULL));
|
||||
EXPECT_FALSE(cdm_license_->Init(kToken, kClientTokenDrmCert,
|
||||
crypto_session_, NULL));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
@@ -182,7 +186,8 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1;
|
||||
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()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull()))
|
||||
@@ -202,7 +207,8 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true)));
|
||||
|
||||
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;
|
||||
CdmKeyMessage signed_request;
|
||||
|
||||
@@ -95,6 +95,8 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_5: *os << "DEVICE_CERTIFICATE_ERROR_5";
|
||||
break;
|
||||
case EMPTY_KEY_DATA_1: *os << "EMPTY_KEY_DATA_1";
|
||||
break;
|
||||
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:
|
||||
*os << "RESTORE_OFFLINE_LICENSE_ERROR_2";
|
||||
break;
|
||||
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
|
||||
case SESSION_INIT_ERROR_1: *os << "SESSION_INIT_ERROR_1";
|
||||
break;
|
||||
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR";
|
||||
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1";
|
||||
break;
|
||||
@@ -445,6 +447,15 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_16: *os << "INVALID_PARAMETERS_ENG_16";
|
||||
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:
|
||||
*os << "Unknown CdmResponseType";
|
||||
break;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "license.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "service_certificate.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
@@ -257,7 +258,8 @@ void WvContentDecryptionModule::NotifyResolution(const CdmSessionId& session_id,
|
||||
|
||||
bool WvContentDecryptionModule::IsValidServiceCertificate(
|
||||
const std::string& certificate) {
|
||||
return CdmLicense::VerifySignedServiceCertificate(certificate) == NO_ERROR;
|
||||
return ServiceCertificate::VerifySignedServiceCertificate(certificate) ==
|
||||
NO_ERROR;
|
||||
}
|
||||
|
||||
WvContentDecryptionModule::CdmInfo::CdmInfo()
|
||||
|
||||
@@ -213,10 +213,13 @@ enum {
|
||||
kInvalidParametersEng14 = ERROR_DRM_VENDOR_MIN + 199,
|
||||
kInvalidParametersEng15 = ERROR_DRM_VENDOR_MIN + 200,
|
||||
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.
|
||||
// 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
|
||||
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
||||
|
||||
@@ -81,6 +81,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return kDeviceCertificateError3;
|
||||
case wvcdm::DEVICE_CERTIFICATE_ERROR_4:
|
||||
return kDeviceCertificateError4;
|
||||
case wvcdm::DEVICE_CERTIFICATE_ERROR_5:
|
||||
return kDeviceCertificateError5;
|
||||
case wvcdm::EMPTY_KEY_DATA_1:
|
||||
return kEmptyKeyData1;
|
||||
case wvcdm::EMPTY_KEY_DATA_2:
|
||||
@@ -165,6 +167,20 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return kInvalidParametersEng4;
|
||||
case wvcdm::INVALID_PARAMETERS_ENG_5:
|
||||
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:
|
||||
return kInvalidParametersLic1;
|
||||
case wvcdm::INVALID_PARAMETERS_LIC_2:
|
||||
@@ -237,10 +253,10 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return kLicenseRenewalSigningError;
|
||||
case wvcdm::RESTORE_OFFLINE_LICENSE_ERROR_2:
|
||||
return kRestoreOfflineLicenseError2;
|
||||
case wvcdm::SESSION_INIT_ERROR_1:
|
||||
return kSessionInitError1;
|
||||
case wvcdm::SESSION_INIT_ERROR_2:
|
||||
return kSessionInitError2;
|
||||
case wvcdm::SESSION_INIT_GET_KEYBOX_ERROR:
|
||||
return kSessionInitGetKeyboxError;
|
||||
case wvcdm::SESSION_NOT_FOUND_1:
|
||||
return kSessionNotFound1;
|
||||
case wvcdm::SESSION_NOT_FOUND_2:
|
||||
@@ -363,6 +379,14 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||
case wvcdm::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:
|
||||
return kSessionNotFound13;
|
||||
case wvcdm::SESSION_NOT_FOUND_14:
|
||||
@@ -395,6 +419,12 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return kInvalidParametersEng15;
|
||||
case wvcdm::INVALID_PARAMETERS_ENG_16:
|
||||
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_2:
|
||||
case wvcdm::UNUSED_3:
|
||||
|
||||
Reference in New Issue
Block a user