Source release v2.2.0-0-903 + third_party libs
Change-Id: I03f670eaeb052bc741abb347be06f8ddc58418e7
This commit is contained in:
@@ -24,13 +24,13 @@ namespace wvcdm {
|
||||
class BufferReader {
|
||||
public:
|
||||
BufferReader(const uint8_t* buf, size_t size)
|
||||
: buf_(buf), size_(size), pos_(0) {}
|
||||
: buf_(buf), size_(size), pos_(0) {}
|
||||
|
||||
bool HasBytes(int count) { return (pos() + count <= size()); }
|
||||
|
||||
// Read a value from the stream, performing endian correction,
|
||||
// and advance the stream pointer.
|
||||
bool Read1(uint8_t* v) WARN_UNUSED_RESULT;
|
||||
bool Read1(uint8_t* v) WARN_UNUSED_RESULT;
|
||||
bool Read2(uint16_t* v) WARN_UNUSED_RESULT;
|
||||
bool Read2s(int16_t* v) WARN_UNUSED_RESULT;
|
||||
bool Read4(uint32_t* v) WARN_UNUSED_RESULT;
|
||||
@@ -58,7 +58,8 @@ class BufferReader {
|
||||
size_t size_;
|
||||
size_t pos_;
|
||||
|
||||
template<typename T> bool Read(T* t) WARN_UNUSED_RESULT;
|
||||
template <typename T>
|
||||
bool Read(T* t) WARN_UNUSED_RESULT;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(BufferReader);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#ifndef WVCDM_CORE_CDM_ENGINE_H_
|
||||
#define WVCDM_CORE_CDM_ENGINE_H_
|
||||
|
||||
#include "cdm_session.h"
|
||||
#include "certificate_provisioning.h"
|
||||
#include "initialization_data.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
@@ -13,6 +12,7 @@
|
||||
namespace wvcdm {
|
||||
|
||||
class CdmClientPropertySet;
|
||||
class CdmSession;
|
||||
class CryptoEngine;
|
||||
class WvCdmEventListener;
|
||||
|
||||
@@ -34,12 +34,33 @@ class CdmEngine {
|
||||
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
|
||||
// License related methods
|
||||
// Construct a valid license request
|
||||
|
||||
// Construct a valid license request. The arguments are used as follows:
|
||||
// session_id: The Session ID of the session the request is being generated
|
||||
// for. This is ignored for license release requests.
|
||||
// key_set_id: The Key Set ID of the key set the request is being generated
|
||||
// for. This is ignored except for license release requests.
|
||||
// init_data: The initialization data from the media file, which is used to
|
||||
// build the key request. This is ignored for release and renewal
|
||||
// requests.
|
||||
// license_type: The type of license being requested. Never ignored.
|
||||
// app_parameters: Additional, application-specific parameters that factor
|
||||
// into the request generation. This is ignored for release
|
||||
// and renewal requests.
|
||||
// key_request: This must be non-null and point to a CdmKeyMessage. The buffer
|
||||
// will have its contents replaced with the key request.
|
||||
// server_url: This must be non-null and point to a string. The string will
|
||||
// have its contents replaced with the default URL (if one is
|
||||
// known) to send this key request to.
|
||||
// key_set_id_out: May be null. If it is non-null, the CdmKeySetId pointed to
|
||||
// will have its contents replaced with the key set ID of the
|
||||
// session. Note that for non-offline license requests, the
|
||||
// key set ID is empty, so the CdmKeySetId will be cleared.
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
std::string* server_url, CdmKeySetId* key_set_id_out);
|
||||
|
||||
// Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
|
||||
@@ -36,7 +36,7 @@ class CdmSession {
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
std::string* server_url, CdmKeySetId* key_set_id);
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response,
|
||||
@@ -81,7 +81,8 @@ class CdmSession {
|
||||
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
|
||||
virtual SecurityLevel GetRequestedSecurityLevel() {
|
||||
return requested_security_level_; }
|
||||
return requested_security_level_;
|
||||
}
|
||||
virtual CdmSecurityLevel GetSecurityLevel() { return security_level_; }
|
||||
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
|
||||
@@ -31,8 +31,7 @@ class CertificateProvisioning {
|
||||
CdmProvisioningRequest* request);
|
||||
bool ParseJsonResponse(const CdmProvisioningResponse& json_str,
|
||||
const std::string& start_substr,
|
||||
const std::string& end_substr,
|
||||
std::string* result);
|
||||
const std::string& end_substr, std::string* result);
|
||||
CryptoSession crypto_session_;
|
||||
CdmCertificateType cert_type_;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace wvcdm {
|
||||
|
||||
// Provides time related information. The implementation is platform dependent.
|
||||
class Clock {
|
||||
|
||||
public:
|
||||
Clock() {}
|
||||
virtual ~Clock() {}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
namespace wvcdm {
|
||||
|
||||
class CryptoKey {
|
||||
public:
|
||||
public:
|
||||
CryptoKey() {};
|
||||
~CryptoKey() {};
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
|
||||
bool HasKeyControl() const { return key_control_.size() >= 16; }
|
||||
|
||||
private:
|
||||
private:
|
||||
std::string key_id_;
|
||||
std::string key_data_iv_;
|
||||
std::string key_data_;
|
||||
@@ -35,6 +35,6 @@ private:
|
||||
std::string key_control_iv_;
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
}; // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CRYPTO_KEY_H_
|
||||
|
||||
@@ -18,6 +18,17 @@ typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
|
||||
|
||||
class CryptoSession {
|
||||
public:
|
||||
// This enum should be kept in sync with the values specified for
|
||||
// HDCP capabilities in OEMCryptoCENC.h. (See comments for
|
||||
// OEMCrypto_GetHDCPCapability)
|
||||
typedef enum {
|
||||
kOemCryptoHdcpNotSupported = 0,
|
||||
kOemCryptoHdcpVersion1 = 1,
|
||||
kOemCryptoHdcpVersion2 = 2,
|
||||
kOemCryptoHdcpVersion2_1 = 3,
|
||||
kOemCryptoHdcpVersion2_2 = 4,
|
||||
kOemCryptoNoHdcpDeviceAttached = 0xff,
|
||||
} OemCryptoHdcpVersion;
|
||||
CryptoSession();
|
||||
virtual ~CryptoSession();
|
||||
|
||||
@@ -37,7 +48,7 @@ class CryptoSession {
|
||||
virtual CryptoSessionId oec_session_id() { return oec_session_id_; }
|
||||
|
||||
// Key request/response
|
||||
virtual void GenerateRequestId(std::string& req_id_str);
|
||||
virtual bool GenerateRequestId(std::string* req_id_str);
|
||||
virtual bool PrepareRequest(const std::string& key_deriv_message,
|
||||
bool is_provisioning, std::string* signature);
|
||||
virtual bool PrepareRenewalRequest(const std::string& message,
|
||||
@@ -66,6 +77,7 @@ class CryptoSession {
|
||||
// Media data path
|
||||
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
|
||||
virtual bool UsageInformationSupport(bool* has_support);
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
virtual CdmResponseType GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report);
|
||||
@@ -73,6 +85,8 @@ class CryptoSession {
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& provider_session_token);
|
||||
|
||||
virtual bool GetHdcpCapabilities(OemCryptoHdcpVersion* current,
|
||||
OemCryptoHdcpVersion* max);
|
||||
virtual bool GetRandom(size_t data_length, uint8_t* random_data);
|
||||
|
||||
private:
|
||||
|
||||
@@ -27,7 +27,7 @@ class DeviceFiles {
|
||||
|
||||
virtual bool Init(CdmSecurityLevel security_level);
|
||||
virtual bool Reset(CdmSecurityLevel security_level) {
|
||||
return Init(security_level);
|
||||
return Init(security_level);
|
||||
}
|
||||
|
||||
virtual bool StoreCertificate(const std::string& certificate,
|
||||
@@ -44,8 +44,7 @@ class DeviceFiles {
|
||||
const CdmKeyResponse& key_renewal_response,
|
||||
const std::string& release_server_url);
|
||||
virtual bool RetrieveLicense(const std::string& key_set_id,
|
||||
LicenseState* state,
|
||||
CdmInitData* pssh_data,
|
||||
LicenseState* state, CdmInitData* pssh_data,
|
||||
CdmKeyMessage* key_request,
|
||||
CdmKeyResponse* key_response,
|
||||
CdmKeyMessage* key_renewal_request,
|
||||
@@ -55,6 +54,7 @@ class DeviceFiles {
|
||||
virtual bool DeleteAllFiles();
|
||||
virtual bool DeleteAllLicenses();
|
||||
virtual bool LicenseExists(const std::string& key_set_id);
|
||||
virtual bool ReserveLicenseId(const std::string& key_set_id);
|
||||
|
||||
virtual bool StoreUsageInfo(const std::string& provider_session_token,
|
||||
const CdmKeyMessage& key_request,
|
||||
@@ -65,8 +65,9 @@ class DeviceFiles {
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
|
||||
|
||||
private:
|
||||
bool StoreFile(const char* name, const std::string& serialized_file);
|
||||
bool RetrieveFile(const char* name, std::string* serialized_file);
|
||||
bool StoreFileWithHash(const char* name, const std::string& serialized_file);
|
||||
bool StoreFileRaw(const char* name, const std::string& serialized_file);
|
||||
bool RetrieveHashedFile(const char* name, std::string* serialized_file);
|
||||
|
||||
// Certificate and offline licenses are now stored in security
|
||||
// level specific directories. In an earlier version they were
|
||||
@@ -77,6 +78,7 @@ class DeviceFiles {
|
||||
static std::string GetCertificateFileName();
|
||||
static std::string GetLicenseFileNameExtension();
|
||||
static std::string GetUsageInfoFileName();
|
||||
static std::string GetBlankFileData();
|
||||
void SetTestFile(File* file);
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
|
||||
@@ -84,6 +86,7 @@ class DeviceFiles {
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIds);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility);
|
||||
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
|
||||
|
||||
@@ -44,7 +44,7 @@ class File {
|
||||
virtual ssize_t FileSize(const std::string& file_path);
|
||||
|
||||
private:
|
||||
Impl *impl_;
|
||||
Impl* impl_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(File);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ class WvCdmEngineTest;
|
||||
|
||||
class InitializationData {
|
||||
public:
|
||||
InitializationData(const std::string& type,
|
||||
InitializationData(const std::string& type = std::string(),
|
||||
const CdmInitData& data = CdmInitData());
|
||||
|
||||
bool is_supported() const { return is_cenc_ || is_webm_; }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "initialization_data.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace video_widevine_server {
|
||||
@@ -16,39 +17,44 @@ class SignedMessage;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Clock;
|
||||
class CryptoSession;
|
||||
class PolicyEngine;
|
||||
|
||||
class CdmLicense {
|
||||
public:
|
||||
|
||||
CdmLicense() : session_(NULL), initialized_(false) {}
|
||||
virtual ~CdmLicense() {}
|
||||
CdmLicense();
|
||||
virtual ~CdmLicense();
|
||||
|
||||
virtual bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
PolicyEngine* policy_engine);
|
||||
|
||||
virtual bool PrepareKeyRequest(const InitializationData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
const CdmSessionId& session_id,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
||||
const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
const CdmSessionId& session_id,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual bool PrepareKeyUpdateRequest(bool is_renewal,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType HandleKeyResponse(
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual CdmResponseType HandleKeyUpdateResponse(
|
||||
bool is_renewal, const CdmKeyResponse& license_response);
|
||||
|
||||
virtual bool RestoreOfflineLicense(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response);
|
||||
virtual bool RestoreOfflineLicense(
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response);
|
||||
virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response);
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual bool HasInitData() { return !stored_init_data_.empty(); }
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
|
||||
virtual std::string provider_session_token() { return provider_session_token_; }
|
||||
virtual std::string provider_session_token() {
|
||||
return provider_session_token_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
@@ -59,9 +65,9 @@ class CdmLicense {
|
||||
CdmResponseType HandleKeyErrorResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
|
||||
template<typename T> bool PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id,
|
||||
T* content_id);
|
||||
template <typename T>
|
||||
bool PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id);
|
||||
|
||||
CryptoSession* session_;
|
||||
PolicyEngine* policy_engine_;
|
||||
@@ -76,6 +82,14 @@ class CdmLicense {
|
||||
// Used for certificate based licensing
|
||||
CdmKeyMessage key_request_;
|
||||
|
||||
scoped_ptr<Clock> clock_;
|
||||
|
||||
// For testing
|
||||
CdmLicense(Clock* clock); // CdmLicense takes ownership of the clock.
|
||||
#if defined(UNIT_TEST)
|
||||
friend class CdmLicenseTest;
|
||||
#endif
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmLicense);
|
||||
};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class Lock {
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl *impl_;
|
||||
Impl* impl_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(Lock);
|
||||
};
|
||||
@@ -38,20 +38,14 @@ class Lock {
|
||||
// is constructed and release when AutoLock goes out of scope.
|
||||
class AutoLock {
|
||||
public:
|
||||
explicit AutoLock(Lock& lock) : lock_(&lock) {
|
||||
lock_->Acquire();
|
||||
}
|
||||
explicit AutoLock(Lock& lock) : lock_(&lock) { lock_->Acquire(); }
|
||||
|
||||
explicit AutoLock(Lock* lock) : lock_(lock) {
|
||||
lock_->Acquire();
|
||||
}
|
||||
explicit AutoLock(Lock* lock) : lock_(lock) { lock_->Acquire(); }
|
||||
|
||||
~AutoLock() {
|
||||
lock_->Release();
|
||||
}
|
||||
~AutoLock() { lock_->Release(); }
|
||||
|
||||
private:
|
||||
Lock *lock_;
|
||||
Lock* lock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock);
|
||||
};
|
||||
|
||||
@@ -7,10 +7,7 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
enum SecurityLevel {
|
||||
kLevelDefault,
|
||||
kLevel3
|
||||
};
|
||||
enum SecurityLevel { kLevelDefault, kLevel3 };
|
||||
|
||||
/* This attempts to open a session at the desired security level.
|
||||
If one level is not available, the other will be used instead. */
|
||||
|
||||
@@ -52,10 +52,9 @@ class RsaPublicKey {
|
||||
|
||||
// Encrypt a message using RSA-OAEP. Caller retains ownership of all
|
||||
// parameters. Returns true if successful, false otherwise.
|
||||
bool Encrypt(const std::string& plaintext,
|
||||
std::string* ciphertext);
|
||||
bool Encrypt(const std::string& plaintext, std::string* ciphertext);
|
||||
|
||||
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||
// Verify RSASSA-PSS signature. Caller retains ownership of all parameters.
|
||||
// Returns true if validation succeeds, false otherwise.
|
||||
bool VerifySignature(const std::string& message,
|
||||
const std::string& signature);
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
# include "gtest/gtest_prod.h"
|
||||
#endif
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::map<CdmSessionId, const CdmClientPropertySet*>
|
||||
@@ -93,6 +97,7 @@ class Properties {
|
||||
FRIEND_TEST(CdmSessionTest, ReInitFail);
|
||||
FRIEND_TEST(CdmSessionTest, InitFailCryptoError);
|
||||
FRIEND_TEST(CdmSessionTest, InitNeedsProvisioning);
|
||||
FRIEND_TEST(CdmLicenseTest, PrepareKeyRequestValidation);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,6 +53,7 @@ class scoped_ptr {
|
||||
ptr_ = p;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
T* ptr_;
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ bool BufferReader::Read1(uint8_t* v) {
|
||||
}
|
||||
|
||||
// Internal implementation of multi-byte reads
|
||||
template<typename T> bool BufferReader::Read(T* v) {
|
||||
template <typename T>
|
||||
bool BufferReader::Read(T* v) {
|
||||
if (!HasBytes(sizeof(T))) {
|
||||
LOGW("BufferReader::Read<T> : Failure during parse: Not enough bytes (%u)",
|
||||
sizeof(T));
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
namespace {
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
} // unnamed namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -47,10 +47,9 @@ CdmEngine::~CdmEngine() {
|
||||
sessions_.clear();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system,
|
||||
const CdmClientPropertySet* property_set,
|
||||
CdmSessionId* session_id) {
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
const CdmClientPropertySet* property_set,
|
||||
CdmSessionId* session_id) {
|
||||
LOGI("CdmEngine::OpenSession");
|
||||
|
||||
if (!ValidateKeySystem(key_system)) {
|
||||
@@ -95,8 +94,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
CdmSessionId session_id;
|
||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id);
|
||||
|
||||
if (sts != NO_ERROR)
|
||||
return sts;
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
release_key_sets_[key_set_id] = session_id;
|
||||
return NO_ERROR;
|
||||
@@ -123,7 +121,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
key_set_id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -133,13 +131,10 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id_out) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSessionId id = session_id;
|
||||
@@ -153,14 +148,14 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
|
||||
if (!session_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s",
|
||||
session_id.c_str());
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
key_set_id.c_str());
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -170,7 +165,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
|
||||
id.c_str());
|
||||
id.c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -184,23 +179,27 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
|
||||
if (sts != KEY_ADDED) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d", (int)sts);
|
||||
LOGE(
|
||||
"CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d",
|
||||
(int)sts);
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
|
||||
sts = iter->second->GenerateKeyRequest(init_data, license_type,
|
||||
app_parameters, key_request,
|
||||
server_url);
|
||||
sts = iter->second->GenerateKeyRequest(
|
||||
init_data, license_type, app_parameters, key_request, server_url,
|
||||
key_set_id_out);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
}
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d", (int)sts);
|
||||
LOGE(
|
||||
"CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d",
|
||||
(int)sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -211,10 +210,9 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::AddKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId* key_set_id) {
|
||||
CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId* key_set_id) {
|
||||
LOGI("CdmEngine::AddKey");
|
||||
|
||||
CdmSessionId id = session_id;
|
||||
@@ -262,9 +260,8 @@ CdmResponseType CdmEngine::AddKey(
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RestoreKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id) {
|
||||
CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::RestoreKey");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
@@ -275,7 +272,7 @@ CdmResponseType CdmEngine::RestoreKey(
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -285,6 +282,9 @@ CdmResponseType CdmEngine::RestoreKey(
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
}
|
||||
if (sts != KEY_ADDED) {
|
||||
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -303,8 +303,7 @@ CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
const CdmSessionId& session_id,
|
||||
CdmKeyMessage* key_request,
|
||||
const CdmSessionId& session_id, CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
LOGI("CdmEngine::GenerateRenewalRequest");
|
||||
|
||||
@@ -322,8 +321,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
|
||||
key_request->clear();
|
||||
|
||||
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request,
|
||||
server_url);
|
||||
CdmResponseType sts =
|
||||
iter->second->GenerateRenewalRequest(key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
|
||||
@@ -334,9 +333,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RenewKey(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data) {
|
||||
CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data) {
|
||||
LOGI("CdmEngine::RenewKey");
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
@@ -416,9 +414,8 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
|
||||
return iter->second->QueryStatus(key_info);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyStatus(
|
||||
const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyStatus");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
@@ -429,9 +426,8 @@ CdmResponseType CdmEngine::QueryKeyStatus(
|
||||
return iter->second->QueryKeyStatus(key_info);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyControlInfo(
|
||||
const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyControlInfo");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
@@ -450,20 +446,15 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
*/
|
||||
CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
CdmCertificateType cert_type,
|
||||
const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
if (!request || !default_url) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return cert_provisioning_.GetProvisioningRequest(
|
||||
cert_provisioning_requested_security_level_,
|
||||
cert_type,
|
||||
cert_authority,
|
||||
request,
|
||||
default_url);
|
||||
cert_provisioning_requested_security_level_, cert_type, cert_authority,
|
||||
request, default_url);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -474,21 +465,22 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
*/
|
||||
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
if (response.empty()) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (NULL == cert) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: invalid certificate "
|
||||
"destination");
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid certificate "
|
||||
"destination");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (NULL == wrapped_key) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return cert_provisioning_.HandleProvisioningResponse(response, cert,
|
||||
@@ -540,15 +532,16 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
|
||||
uint32_t index = rand() % license_info.size();
|
||||
status = usage_session_->RestoreUsageSession(license_info[index].first,
|
||||
license_info[index].second);
|
||||
license_info[index].second);
|
||||
if (KEY_ADDED != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld",
|
||||
index, status);
|
||||
LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld", index,
|
||||
status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
status = usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
status =
|
||||
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld",
|
||||
@@ -573,9 +566,8 @@ CdmResponseType CdmEngine::ReleaseUsageInfo(
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::Decrypt(
|
||||
const CdmSessionId& session_id,
|
||||
const CdmDecryptionParameters& parameters) {
|
||||
CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
|
||||
const CdmDecryptionParameters& parameters) {
|
||||
if (parameters.key_id == NULL) {
|
||||
LOGE("CdmEngine::Decrypt: no key_id");
|
||||
return KEY_ERROR;
|
||||
@@ -596,7 +588,7 @@ CdmResponseType CdmEngine::Decrypt(
|
||||
!Properties::Properties::oem_crypto_use_fifo()) {
|
||||
LOGE("CdmEngine::Decrypt: no dest decrypt buffer");
|
||||
return KEY_ERROR;
|
||||
} // else we must be level 1 direct and we don't need to return a buffer.
|
||||
} // else we must be level 1 direct and we don't need to return a buffer.
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter;
|
||||
@@ -612,8 +604,7 @@ CdmResponseType CdmEngine::Decrypt(
|
||||
}
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
|
||||
session_id.c_str(),
|
||||
session_id.size());
|
||||
session_id.c_str(), session_id.size());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -630,9 +621,8 @@ bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CdmEngine::FindSessionForKey(
|
||||
const KeyId& key_id,
|
||||
CdmSessionId* session_id) {
|
||||
bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
CdmSessionId* session_id) {
|
||||
if (NULL == session_id) {
|
||||
LOGE("CdmEngine::FindSessionForKey: session id not provided");
|
||||
return false;
|
||||
@@ -660,10 +650,8 @@ bool CdmEngine::FindSessionForKey(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CdmEngine::AttachEventListener(
|
||||
const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
|
||||
bool CdmEngine::AttachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
@@ -672,10 +660,8 @@ bool CdmEngine::AttachEventListener(
|
||||
return iter->second->AttachEventListener(listener);
|
||||
}
|
||||
|
||||
bool CdmEngine::DetachEventListener(
|
||||
const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
|
||||
bool CdmEngine::DetachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
@@ -700,7 +686,7 @@ void CdmEngine::OnTimerEvent() {
|
||||
}
|
||||
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnTimerEvent();
|
||||
|
||||
if (update_usage_information && iter->second->is_usage_update_needed()) {
|
||||
@@ -718,7 +704,6 @@ void CdmEngine::OnTimerEvent() {
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnKeyReleaseEvent(key_set_id);
|
||||
|
||||
@@ -29,22 +29,18 @@ CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) {
|
||||
new DeviceFiles(), cdm_client_property_set);
|
||||
}
|
||||
|
||||
CdmSession::CdmSession(
|
||||
CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine,
|
||||
DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
CdmSession::CdmSession(CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
Create(license_parser, crypto_session, policy_engine, file_handle,
|
||||
cdm_client_property_set);
|
||||
}
|
||||
|
||||
void CdmSession::Create(
|
||||
CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine,
|
||||
DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
void CdmSession::Create(CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
// Just return on failures. Failures will be signaled in Init.
|
||||
if (NULL == license_parser) {
|
||||
LOGE("CdmSession::Create: License parser not provided");
|
||||
@@ -76,7 +72,7 @@ void CdmSession::Create(
|
||||
requested_security_level_ = kLevelDefault;
|
||||
if (NULL != cdm_client_property_set) {
|
||||
if (QUERY_VALUE_SECURITY_LEVEL_L3.compare(
|
||||
cdm_client_property_set->security_level()) == 0) {
|
||||
cdm_client_property_set->security_level()) == 0) {
|
||||
requested_security_level_ = kLevel3;
|
||||
}
|
||||
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
|
||||
@@ -125,17 +121,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return UNKNOWN_ERROR;
|
||||
if (!file_handle_->Reset(security_level_)) return UNKNOWN_ERROR;
|
||||
|
||||
DeviceFiles::LicenseState license_state;
|
||||
|
||||
if (!file_handle_->RetrieveLicense(key_set_id, &license_state,
|
||||
&offline_init_data_,
|
||||
&key_request_, &key_response_,
|
||||
&offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_,
|
||||
&offline_release_server_url_)) {
|
||||
if (!file_handle_->RetrieveLicense(
|
||||
key_set_id, &license_state, &offline_init_data_, &key_request_,
|
||||
&key_response_, &offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_, &offline_release_server_url_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -158,9 +151,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::RestoreUsageSession(
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response) {
|
||||
|
||||
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response) {
|
||||
key_request_ = key_request;
|
||||
key_response_ = key_response;
|
||||
|
||||
@@ -177,7 +168,7 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
std::string* server_url, CdmKeySetId* key_set_id) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -189,9 +180,15 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
}
|
||||
|
||||
switch (license_type) {
|
||||
case kLicenseTypeStreaming: is_offline_ = false; break;
|
||||
case kLicenseTypeOffline: is_offline_ = true; break;
|
||||
case kLicenseTypeRelease: is_release_ = true; break;
|
||||
case kLicenseTypeStreaming:
|
||||
is_offline_ = false;
|
||||
break;
|
||||
case kLicenseTypeOffline:
|
||||
is_offline_ = true;
|
||||
break;
|
||||
case kLicenseTypeRelease:
|
||||
is_release_ = true;
|
||||
break;
|
||||
default:
|
||||
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
|
||||
license_type);
|
||||
@@ -212,6 +209,10 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
if (is_offline_ && !GenerateKeySetId(&key_set_id_)) {
|
||||
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_->PrepareKeyRequest(init_data, license_type,
|
||||
app_parameters, session_id_,
|
||||
@@ -225,6 +226,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
offline_release_server_url_ = *server_url;
|
||||
}
|
||||
|
||||
if (key_set_id) *key_set_id = key_set_id_;
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
}
|
||||
@@ -260,7 +262,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
if (sts != NO_ERROR) return sts;
|
||||
}
|
||||
|
||||
*key_set_id = key_set_id_;
|
||||
if (key_set_id) *key_set_id = key_set_id_;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
}
|
||||
@@ -332,7 +334,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
}
|
||||
if (!is_usage_update_needed_) {
|
||||
is_usage_update_needed_ =
|
||||
!license_parser_->provider_session_token().empty();
|
||||
!license_parser_->provider_session_token().empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,10 +386,9 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
|
||||
// ReleaseKey() - Accept release response and release license.
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(false,
|
||||
key_response);
|
||||
if (KEY_ADDED != sts)
|
||||
return sts;
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(false, key_response);
|
||||
if (KEY_ADDED != sts) return sts;
|
||||
|
||||
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
|
||||
DeleteLicense();
|
||||
@@ -413,8 +414,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
||||
std::vector<uint8_t> random_data(
|
||||
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
|
||||
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return false;
|
||||
if (!file_handle_->Reset(security_level_)) return false;
|
||||
|
||||
while (key_set_id->empty()) {
|
||||
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0]))
|
||||
@@ -427,13 +427,14 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
||||
key_set_id->clear();
|
||||
}
|
||||
}
|
||||
file_handle_->ReserveLicenseId(*key_set_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::StoreLicense() {
|
||||
if (is_offline_) {
|
||||
if (!GenerateKeySetId(&key_set_id_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to generate key set Id");
|
||||
if (key_set_id_.empty()) {
|
||||
LOGE("CdmSession::StoreLicense: No key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -472,13 +473,12 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
}
|
||||
|
||||
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return false;
|
||||
if (!file_handle_->Reset(security_level_)) return false;
|
||||
|
||||
return file_handle_->StoreLicense(
|
||||
key_set_id_, state, offline_init_data_, key_request_,
|
||||
key_response_, offline_key_renewal_request_,
|
||||
offline_key_renewal_response_, offline_release_server_url_);
|
||||
key_set_id_, state, offline_init_data_, key_request_, key_response_,
|
||||
offline_key_renewal_request_, offline_key_renewal_response_,
|
||||
offline_release_server_url_);
|
||||
}
|
||||
|
||||
bool CdmSession::DeleteLicense() {
|
||||
@@ -494,7 +494,7 @@ bool CdmSession::DeleteLicense() {
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
else
|
||||
return file_handle_->DeleteUsageInfo(
|
||||
license_parser_->provider_session_token());
|
||||
license_parser_->provider_session_token());
|
||||
}
|
||||
|
||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace {
|
||||
// The provisioning server supplies the certificate that is needed
|
||||
// to communicate with the License Server.
|
||||
const std::string kProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -41,9 +41,7 @@ using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
* base64 encoded message
|
||||
*/
|
||||
void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
const std::string& message,
|
||||
CdmProvisioningRequest* request) {
|
||||
|
||||
const std::string& message, CdmProvisioningRequest* request) {
|
||||
// Performs base64 encoding for message
|
||||
std::vector<uint8_t> message_vector(message.begin(), message.end());
|
||||
std::string message_b64 = Base64SafeEncodeNoPad(message_vector);
|
||||
@@ -58,10 +56,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
SecurityLevel requested_security_level,
|
||||
CdmCertificateType cert_type,
|
||||
const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request,
|
||||
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
||||
const std::string& cert_authority, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!default_url) {
|
||||
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
|
||||
@@ -123,7 +119,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
// Derives signing and encryption keys and constructs signature.
|
||||
std::string request_signature;
|
||||
if (!crypto_session_.PrepareRequest(serialized_message, true,
|
||||
&request_signature)) {
|
||||
&request_signature)) {
|
||||
LOGE("GetProvisioningRequest: fails to prepare request");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -152,10 +148,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
* Returns true for success and false if fails.
|
||||
*/
|
||||
bool CertificateProvisioning::ParseJsonResponse(
|
||||
const CdmProvisioningResponse& json_str,
|
||||
const std::string& start_substr,
|
||||
const std::string& end_substr,
|
||||
std::string* result) {
|
||||
const CdmProvisioningResponse& json_str, const std::string& start_substr,
|
||||
const std::string& end_substr, std::string* result) {
|
||||
std::string b64_string;
|
||||
size_t start = json_str.find(start_substr);
|
||||
if (start == json_str.npos) {
|
||||
@@ -186,10 +180,8 @@ bool CertificateProvisioning::ParseJsonResponse(
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
|
||||
// Extracts signed response from JSON string, decodes base64 signed response
|
||||
const std::string kMessageStart = "\"signedResponse\": \"";
|
||||
const std::string kMessageEnd = "\"";
|
||||
@@ -220,8 +212,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error)
|
||||
return UNKNOWN_ERROR;
|
||||
if (error) return UNKNOWN_ERROR;
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
ProvisioningResponse provisioning_response;
|
||||
@@ -241,12 +232,9 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
|
||||
const std::string& signature = signed_response.signature();
|
||||
std::string wrapped_rsa_key;
|
||||
if (!crypto_session_.RewrapDeviceRSAKey(signed_message,
|
||||
signature,
|
||||
nonce,
|
||||
enc_rsa_key,
|
||||
rsa_key_iv,
|
||||
&wrapped_rsa_key)){
|
||||
if (!crypto_session_.RewrapDeviceRSAKey(signed_message, signature, nonce,
|
||||
enc_rsa_key, rsa_key_iv,
|
||||
&wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -262,13 +262,19 @@ void CryptoSession::Close() {
|
||||
}
|
||||
}
|
||||
|
||||
void CryptoSession::GenerateRequestId(std::string& req_id_str) {
|
||||
bool CryptoSession::GenerateRequestId(std::string* req_id_str) {
|
||||
LOGV("CryptoSession::GenerateRequestId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
req_id_str = HexEncode(reinterpret_cast<uint8_t*>(&request_id_base_),
|
||||
sizeof(request_id_base_)) +
|
||||
HexEncode(reinterpret_cast<uint8_t*>(&request_id_index_),
|
||||
sizeof(request_id_index_));
|
||||
if (!req_id_str) {
|
||||
LOGE("CryptoSession::GenerateRequestId: No output destination provided.");
|
||||
return false;
|
||||
}
|
||||
|
||||
*req_id_str = HexEncode(reinterpret_cast<uint8_t*>(&request_id_base_),
|
||||
sizeof(request_id_base_)) +
|
||||
HexEncode(reinterpret_cast<uint8_t*>(&request_id_index_),
|
||||
sizeof(request_id_index_));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::PrepareRequest(const std::string& message,
|
||||
@@ -406,8 +412,10 @@ CdmResponseType CryptoSession::LoadKeys(
|
||||
if (OEMCrypto_SUCCESS == sts) {
|
||||
return KEY_ADDED;
|
||||
} else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) {
|
||||
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
} else {
|
||||
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -570,8 +578,8 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(),
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
|
||||
&length, kSign_RSASSA_PSS);
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())), &length,
|
||||
kSign_RSASSA_PSS);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
@@ -656,9 +664,26 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
}
|
||||
}
|
||||
|
||||
bool CryptoSession::UsageInformationSupport(bool* has_support) {
|
||||
LOGV("UsageInformationSupport: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!initialized_) return false;
|
||||
|
||||
*has_support = OEMCrypto_SupportsUsageTable(
|
||||
kSecurityLevelL3 == GetSecurityLevel() ? kLevel3 : kLevelDefault);
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::UpdateUsageInformation() {
|
||||
return (OEMCrypto_UpdateUsageTable() == OEMCrypto_SUCCESS) ? NO_ERROR
|
||||
: UNKNOWN_ERROR;
|
||||
LOGV("UpdateUsageInformation: id=%ld", (uint32_t)oec_session_id_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) return UNKNOWN_ERROR;
|
||||
|
||||
OEMCryptoResult status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::UsageUsageInformation: error=%ld", status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
@@ -816,6 +841,23 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetHdcpCapabilities(OemCryptoHdcpVersion* current_version,
|
||||
OemCryptoHdcpVersion* max_version) {
|
||||
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!initialized_) return UNKNOWN_ERROR;
|
||||
OEMCrypto_HDCP_Capability current, max;
|
||||
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(¤t, &max);
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("OEMCrypto_GetHDCPCapability fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
*current_version = static_cast<OemCryptoHdcpVersion>(current);
|
||||
*max_version = static_cast<OemCryptoHdcpVersion>(max);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) {
|
||||
if (random_data == NULL) {
|
||||
LOGE("CryptoSession::GetRandom: random data destination not provided");
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#include "device_files.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <CommonCrypto/CommonDigest.h>
|
||||
# define SHA256 CC_SHA256
|
||||
# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#define SHA256 CC_SHA256
|
||||
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
||||
#else
|
||||
# include <openssl/sha.h>
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
@@ -39,13 +39,15 @@ const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
|
||||
size_t kSecurityLevelPathCompatibilityExclusionListSize =
|
||||
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
|
||||
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
|
||||
// Some platforms cannot store a truly blank file, so we use a W for Widevine.
|
||||
const char kBlankFileData[] = "W";
|
||||
|
||||
bool Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash) return false;
|
||||
hash->resize(SHA256_DIGEST_LENGTH);
|
||||
|
||||
const unsigned char* input =
|
||||
reinterpret_cast<const unsigned char*>(data.data());
|
||||
reinterpret_cast<const unsigned char*>(data.data());
|
||||
unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
|
||||
SHA256(input, data.size(), output);
|
||||
return true;
|
||||
@@ -56,9 +58,10 @@ bool Hash(const std::string& data, std::string* hash) {
|
||||
namespace wvcdm {
|
||||
|
||||
DeviceFiles::DeviceFiles()
|
||||
: file_(NULL), security_level_(kSecurityLevelUninitialized),
|
||||
initialized_(false), test_file_(false) {
|
||||
}
|
||||
: file_(NULL),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
initialized_(false),
|
||||
test_file_(false) {}
|
||||
|
||||
DeviceFiles::~DeviceFiles() {
|
||||
if (test_file_) file_.release();
|
||||
@@ -100,7 +103,7 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
return StoreFile(kCertificateFileName, serialized_file);
|
||||
return StoreFileWithHash(kCertificateFileName, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
@@ -115,8 +118,7 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveFile(kCertificateFileName, &serialized_file))
|
||||
return false;
|
||||
if (!RetrieveHashedFile(kCertificateFileName, &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -189,7 +191,7 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFile(file_name.c_str(), serialized_file);
|
||||
return StoreFileWithHash(file_name.c_str(), serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
@@ -206,7 +208,7 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
|
||||
std::string serialized_file;
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false;
|
||||
if (!RetrieveHashedFile(file_name.c_str(), &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -310,7 +312,7 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
LOGW("DeviceFiles::LicenseExists: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(key_set_id);
|
||||
@@ -319,6 +321,16 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
return file_->Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::ReserveLicenseId: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFileRaw(file_name.c_str(), kBlankFileData);
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response) {
|
||||
@@ -329,7 +341,7 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
|
||||
std::string serialized_file;
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
|
||||
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
} else {
|
||||
@@ -344,13 +356,11 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
|
||||
provider_session->set_token(provider_session_token.data(),
|
||||
provider_session_token.size());
|
||||
provider_session->set_license_request(key_request.data(),
|
||||
key_request.size());
|
||||
provider_session->set_license(key_response.data(),
|
||||
key_response.size());
|
||||
provider_session->set_license_request(key_request.data(), key_request.size());
|
||||
provider_session->set_license(key_response.data(), key_response.size());
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFile(kUsageInfoFileName, serialized_file);
|
||||
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
@@ -360,7 +370,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) return false;
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -372,15 +382,18 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
int index = 0;
|
||||
bool found = false;
|
||||
for (; index < usage_info->sessions_size(); ++index) {
|
||||
if (usage_info->sessions(index).token().compare(provider_session_token) == 0) {
|
||||
if (usage_info->sessions(index).token().compare(provider_session_token) ==
|
||||
0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: Unable to find provider session "
|
||||
"token: %s", b2a_hex(provider_session_token).c_str());
|
||||
LOGW(
|
||||
"DeviceFiles::DeleteUsageInfo: Unable to find provider session "
|
||||
"token: %s",
|
||||
b2a_hex(provider_session_token).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -392,7 +405,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
sessions->RemoveLast();
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFile(kUsageInfoFileName, serialized_file);
|
||||
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo() {
|
||||
@@ -411,21 +424,22 @@ bool DeviceFiles::DeleteUsageInfo() {
|
||||
return file_->Remove(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveUsageInfo(std::vector<
|
||||
std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
||||
bool DeviceFiles::RetrieveUsageInfo(
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NULL == usage_info) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: license destination not "
|
||||
LOGW(
|
||||
"DeviceFiles::RetrieveUsageInfo: license destination not "
|
||||
"provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) {
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
return false;
|
||||
@@ -457,22 +471,22 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector<
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFile(const char* name,
|
||||
const std::string& serialized_file) {
|
||||
bool DeviceFiles::StoreFileWithHash(const char* name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFile: Invalid file handle");
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::StoreFile: Unspecified file name parameter");
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_file, &hash)) {
|
||||
LOGW("DeviceFiles::StoreFile: Hash computation failed");
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -484,9 +498,24 @@ bool DeviceFiles::StoreFile(const char* name,
|
||||
std::string serialized_hash_file;
|
||||
hash_file.SerializeToString(&serialized_hash_file);
|
||||
|
||||
return StoreFileRaw(name, serialized_hash_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileRaw(const char* name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -497,59 +526,62 @@ bool DeviceFiles::StoreFile(const char* name,
|
||||
path += name;
|
||||
|
||||
if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) {
|
||||
LOGW("DeviceFiles::StoreFile: File open failed: %s", path.c_str());
|
||||
LOGW("DeviceFiles::StoreFileRaw: File open failed: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t bytes = file_->Write(serialized_hash_file.data(),
|
||||
serialized_hash_file.size());
|
||||
ssize_t bytes = file_->Write(serialized_file.data(), serialized_file.size());
|
||||
file_->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
|
||||
LOGW("DeviceFiles::StoreFile: write failed: (actual: %d, expected: %d)",
|
||||
bytes,
|
||||
serialized_hash_file.size());
|
||||
if (bytes != static_cast<ssize_t>(serialized_file.size())) {
|
||||
LOGW(
|
||||
"DeviceFiles::StoreFileRaw: write failed: (actual: %d, "
|
||||
"expected: %d)",
|
||||
bytes, serialized_file.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::StoreFile: success: %s (%db)",
|
||||
path.c_str(),
|
||||
serialized_hash_file.size());
|
||||
LOGV("DeviceFiles::StoreFileRaw: success: %s (%db)", path.c_str(),
|
||||
serialized_file.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveFile(const char* name, std::string* serialized_file) {
|
||||
bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
std::string* serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Invalid file handle");
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unspecified file name parameter");
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!serialized_file) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unspecified serialized_file parameter");
|
||||
LOGW(
|
||||
"DeviceFiles::RetrieveHashedFile: Unspecified serialized_file "
|
||||
"parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFile: Unable to get base path");
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
path += name;
|
||||
|
||||
if (!file_->Exists(path)) {
|
||||
LOGW("DeviceFiles::RetrieveFile: %s does not exist", path.c_str());
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: %s does not exist", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t bytes = file_->FileSize(path);
|
||||
if (bytes <= 0) {
|
||||
LOGW("DeviceFiles::RetrieveFile: File size invalid: %s", path.c_str());
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s",
|
||||
path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -563,27 +595,27 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* serialized_file) {
|
||||
file_->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
|
||||
LOGW("DeviceFiles::RetrieveFile: read failed");
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("DeviceFiles::RetrieveFile: success: %s (%db)", path.c_str(),
|
||||
LOGV("DeviceFiles::RetrieveHashedFile: success: %s (%db)", path.c_str(),
|
||||
serialized_hash_file.size());
|
||||
|
||||
HashedFile hash_file;
|
||||
if (!hash_file.ParseFromString(serialized_hash_file)) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Unable to parse hash file");
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse hash file");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash;
|
||||
if (!Hash(hash_file.file(), &hash)) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Hash computation failed");
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Hash computation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hash_file.hash())) {
|
||||
LOGW("DeviceFiles::RetrieveFile: Hash mismatch");
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -663,9 +695,9 @@ std::string DeviceFiles::GetLicenseFileNameExtension() {
|
||||
return kLicenseFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetUsageInfoFileName() {
|
||||
return kUsageInfoFileName;
|
||||
}
|
||||
std::string DeviceFiles::GetUsageInfoFileName() { return kUsageInfoFileName; }
|
||||
|
||||
std::string DeviceFiles::GetBlankFileData() { return kBlankFileData; }
|
||||
|
||||
void DeviceFiles::SetTestFile(File* file) {
|
||||
file_.reset(file);
|
||||
|
||||
@@ -14,12 +14,10 @@ namespace wvcdm {
|
||||
InitializationData::InitializationData(const std::string& type,
|
||||
const CdmInitData& data)
|
||||
: type_(type), is_cenc_(false), is_webm_(false) {
|
||||
if (type == ISO_BMFF_VIDEO_MIME_TYPE ||
|
||||
type == ISO_BMFF_AUDIO_MIME_TYPE ||
|
||||
if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE ||
|
||||
type == CENC_INIT_DATA_FORMAT) {
|
||||
is_cenc_ = true;
|
||||
} else if (type == WEBM_VIDEO_MIME_TYPE ||
|
||||
type == WEBM_AUDIO_MIME_TYPE ||
|
||||
} else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE ||
|
||||
type == WEBM_INIT_DATA_FORMAT) {
|
||||
is_webm_ = true;
|
||||
}
|
||||
@@ -35,10 +33,10 @@ InitializationData::InitializationData(const std::string& type,
|
||||
|
||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||
// Widevine PSSH.
|
||||
bool InitializationData::ExtractWidevinePssh(
|
||||
const CdmInitData& init_data, CdmInitData* output) {
|
||||
BufferReader reader(
|
||||
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length());
|
||||
bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
CdmInitData* output) {
|
||||
BufferReader reader(reinterpret_cast<const uint8_t*>(init_data.data()),
|
||||
init_data.length());
|
||||
|
||||
// TODO(kqyang): Extracted from an actual init_data;
|
||||
// Need to find out where it comes from.
|
||||
@@ -90,12 +88,10 @@ bool InitializationData::ExtractWidevinePssh(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(&system_id[0], kWidevineSystemId,
|
||||
sizeof(kWidevineSystemId))) {
|
||||
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
|
||||
// skip the remaining contents of the atom,
|
||||
// after size field, atom name, flags and system id
|
||||
if (!reader.SkipBytes(
|
||||
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
|
||||
if (!reader.SkipBytes(size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -23,64 +23,48 @@ std::string kDeviceNameKey = "device_name";
|
||||
std::string kProductNameKey = "product_name";
|
||||
std::string kBuildInfoKey = "build_info";
|
||||
std::string kDeviceIdKey = "device_id";
|
||||
std::string kOemCryptoApiVersion = "oemcrypto_api_version";
|
||||
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};
|
||||
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 wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::ClientIdentification_ClientCapabilities;
|
||||
using video_widevine_server::sdk::ClientIdentification_NameValue;
|
||||
using video_widevine_server::sdk::DeviceCertificate;
|
||||
using video_widevine_server::sdk::EncryptedClientIdentification;
|
||||
@@ -141,6 +125,19 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
return key_array;
|
||||
}
|
||||
|
||||
CdmLicense::CdmLicense()
|
||||
: session_(NULL), initialized_(false), clock_(new Clock()) {}
|
||||
|
||||
CdmLicense::CdmLicense(Clock* clock) : session_(NULL), initialized_(false) {
|
||||
if (NULL == clock) {
|
||||
LOGE("CdmLicense::CdmLicense: clock parameter not provided");
|
||||
return;
|
||||
}
|
||||
clock_.reset(clock);
|
||||
}
|
||||
|
||||
CdmLicense::~CdmLicense() {}
|
||||
|
||||
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine) {
|
||||
if (token.size() == 0) {
|
||||
@@ -207,7 +204,7 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
}
|
||||
|
||||
std::string request_id;
|
||||
session_->GenerateRequestId(request_id);
|
||||
session_->GenerateRequestId(&request_id);
|
||||
|
||||
LicenseRequest license_request;
|
||||
ClientIdentification* client_id = license_request.mutable_client_id();
|
||||
@@ -261,11 +258,54 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
|
||||
ClientIdentification_ClientCapabilities* client_capabilities =
|
||||
client_id->mutable_client_capabilities();
|
||||
bool supports_usage_information;
|
||||
if (session_->UsageInformationSupport(&supports_usage_information)) {
|
||||
client_capabilities->set_session_token(supports_usage_information);
|
||||
}
|
||||
|
||||
CryptoSession::OemCryptoHdcpVersion current_version, max_version;
|
||||
if (session_->GetHdcpCapabilities(¤t_version, &max_version)) {
|
||||
switch (max_version) {
|
||||
case CryptoSession::kOemCryptoHdcpNotSupported:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
|
||||
break;
|
||||
case CryptoSession::kOemCryptoHdcpVersion1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
|
||||
break;
|
||||
case CryptoSession::kOemCryptoHdcpVersion2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
|
||||
break;
|
||||
case CryptoSession::kOemCryptoHdcpVersion2_1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
|
||||
break;
|
||||
case CryptoSession::kOemCryptoHdcpVersion2_2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
|
||||
break;
|
||||
case CryptoSession::kOemCryptoNoHdcpDeviceAttached:
|
||||
default:
|
||||
LOGW(
|
||||
"CdmLicense::PrepareKeyRequest: unexpected HDCP max capability "
|
||||
"version %d",
|
||||
max_version);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t version = 0;
|
||||
if (session_->GetApiVersion(&version)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kOemCryptoApiVersion);
|
||||
client_info->set_value(UintToString(version));
|
||||
client_capabilities->set_oem_crypto_api_version(version);
|
||||
}
|
||||
|
||||
if (privacy_mode_enabled) {
|
||||
@@ -362,8 +402,7 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
|
||||
license_request.set_type(LicenseRequest::NEW);
|
||||
|
||||
Clock clock;
|
||||
license_request.set_request_time(clock.GetCurrentTime());
|
||||
license_request.set_request_time(clock_->GetCurrentTime());
|
||||
|
||||
// Get/set the nonce. This value will be reflected in the Key Control Block
|
||||
// of the license response.
|
||||
@@ -430,8 +469,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
||||
else
|
||||
license_request.set_type(LicenseRequest::RELEASE);
|
||||
|
||||
Clock clock;
|
||||
license_request.set_request_time(clock.GetCurrentTime());
|
||||
license_request.set_request_time(clock_->GetCurrentTime());
|
||||
|
||||
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
|
||||
license_request.mutable_content_id()->mutable_license();
|
||||
@@ -515,8 +553,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
default:
|
||||
LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d"
|
||||
, signed_response.type());
|
||||
LOGE(
|
||||
"CdmLicense::HandleKeyResponse: unrecognized signed message type: %d",
|
||||
signed_response.type());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -579,18 +618,14 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
|
||||
policy_engine_->SetLicense(license);
|
||||
|
||||
CdmResponseType resp = session_->LoadKeys(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
mac_key_iv,
|
||||
mac_key,
|
||||
key_array,
|
||||
provider_session_token_);
|
||||
CdmResponseType resp = session_->LoadKeys(
|
||||
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
|
||||
key_array, provider_session_token_);
|
||||
|
||||
if (KEY_ADDED == resp) {
|
||||
loaded_keys_.clear();
|
||||
for (std::vector<CryptoKey>::iterator it = key_array.begin();
|
||||
it != key_array.end();
|
||||
++it) {
|
||||
it != key_array.end(); ++it) {
|
||||
loaded_keys_.insert(it->key_id());
|
||||
}
|
||||
}
|
||||
@@ -642,10 +677,9 @@ 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(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
provider_session_token_);
|
||||
CdmResponseType status = session_->ReleaseUsageInformation(
|
||||
signed_response.msg(), signed_response.signature(),
|
||||
provider_session_token_);
|
||||
return (NO_ERROR == status) ? KEY_ADDED : status;
|
||||
}
|
||||
|
||||
@@ -668,7 +702,6 @@ bool CdmLicense::RestoreOfflineLicense(
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response) {
|
||||
|
||||
if (license_request.empty() || license_response.empty()) {
|
||||
LOGE(
|
||||
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
|
||||
@@ -712,7 +745,6 @@ bool CdmLicense::RestoreOfflineLicense(
|
||||
|
||||
bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response) {
|
||||
|
||||
if (license_request.empty() || license_response.empty()) {
|
||||
LOGE(
|
||||
"CdmLicense::RestoreUsageLicense: key_request or response empty: %u %u",
|
||||
@@ -738,14 +770,16 @@ bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request,
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(license_response)) {
|
||||
LOGE("CdmLicense::RestoreUsageLicense: unable to parse signed license"
|
||||
LOGE(
|
||||
"CdmLicense::RestoreUsageLicense: unable to parse signed license"
|
||||
" response");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SignedMessage::LICENSE != signed_response.type()) {
|
||||
LOGE("CdmLicense::RestoreUsageLicense: unrecognized signed message type: %d"
|
||||
, signed_response.type());
|
||||
LOGE(
|
||||
"CdmLicense::RestoreUsageLicense: unrecognized signed message type: %d",
|
||||
signed_response.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -809,7 +843,6 @@ bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
|
||||
CdmResponseType CdmLicense::HandleServiceCertificateResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_response) {
|
||||
|
||||
SignedDeviceCertificate signed_service_certificate;
|
||||
if (!signed_service_certificate.ParseFromString(signed_response.msg())) {
|
||||
LOGE(
|
||||
@@ -862,7 +895,6 @@ CdmResponseType CdmLicense::HandleServiceCertificateResponse(
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
const SignedMessage& signed_message) {
|
||||
|
||||
LicenseError license_error;
|
||||
if (!license_error.ParseFromString(signed_message.msg())) {
|
||||
LOGE("CdmLicense::HandleKeyErrorResponse: Unable to parse license error");
|
||||
@@ -882,7 +914,7 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id,
|
||||
T* content_id) {
|
||||
@@ -891,8 +923,7 @@ bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
|
||||
content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
|
||||
break;
|
||||
case kLicenseTypeStreaming:
|
||||
content_id->set_license_type(
|
||||
video_widevine_server::sdk::STREAMING);
|
||||
content_id->set_license_type(video_widevine_server::sdk::STREAMING);
|
||||
break;
|
||||
default:
|
||||
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
|
||||
|
||||
@@ -287,12 +287,6 @@ message SignedMessage {
|
||||
|
||||
// This message is used to pass optional data on initial license issuance.
|
||||
message SessionInit {
|
||||
enum ReplayControl {
|
||||
NO_SESSION_USAGE = 0;
|
||||
NONCE_REQUIRED_AND_NEW_SESSION_USAGE = 1;
|
||||
NONCE_REQUIRED_OR_EXISTING_SESSION_USAGE = 2;
|
||||
}
|
||||
|
||||
optional bytes session_id = 1;
|
||||
optional bytes purchase_id = 2;
|
||||
// master_signing_key should be 128 bits in length.
|
||||
@@ -312,8 +306,6 @@ message SessionInit {
|
||||
// LicenseIdentfication::provider_session_token, and sent back in all
|
||||
// license renewal and release requests for the session thereafter.
|
||||
optional bytes provider_session_token = 7;
|
||||
// Replay control indicator which will be encoded into V9+ KeyControl blocks.
|
||||
optional ReplayControl replay_control = 8 [default = NO_SESSION_USAGE];
|
||||
}
|
||||
|
||||
// This message is used by the server to preserve and restore session state.
|
||||
@@ -415,6 +407,7 @@ message ClientIdentification {
|
||||
optional bool session_token = 2 [default = false];
|
||||
optional bool video_resolution_constraints = 3 [default = false];
|
||||
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
|
||||
optional uint32 oem_crypto_api_version = 5;
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
|
||||
@@ -43,4 +43,8 @@ const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
|
||||
return ::OEMCrypto_SupportsUsageTable();
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
@@ -15,25 +15,15 @@
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_LoadKeys_V8(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
const uint8_t* enc_mac_keys_iv,
|
||||
const uint8_t* enc_mac_keys,
|
||||
size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array);
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t *signature_length);
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V8(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,
|
||||
size_t num_keys, const OEMCrypto_KeyObject* key_array);
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
@@ -70,12 +60,16 @@ const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
|
||||
return ::OEMCrypto_SupportsUsageTable();
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array,
|
||||
const uint8_t* pst, size_t pst_length) {
|
||||
const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
return OEMCrypto_LoadKeys_V8(session, message, message_length, signature,
|
||||
signature_length, enc_mac_key_iv, enc_mac_key,
|
||||
num_keys, key_array);
|
||||
@@ -83,45 +77,40 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme) {
|
||||
uint8_t* signature, size_t* signature_length,
|
||||
RSA_Padding_Scheme padding_scheme) {
|
||||
return OEMCrypto_GenerateRSASignature_V8(session, message, message_length,
|
||||
signature, signature_length);
|
||||
signature, signature_length);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current,
|
||||
OEMCrypto_HDCP_Capability *maximum) {
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" bool OEMCrypto_SupportsUsageTable() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
extern "C" bool OEMCrypto_SupportsUsageTable() { return false; }
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst,
|
||||
extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
|
||||
const uint8_t *pst,
|
||||
const uint8_t* pst,
|
||||
size_t pst_length,
|
||||
OEMCrypto_PST_Report *buffer,
|
||||
size_t *buffer_length) {
|
||||
OEMCrypto_PST_Report* buffer,
|
||||
size_t* buffer_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session,
|
||||
const uint8_t* pst,
|
||||
size_t pst_length,
|
||||
const uint8_t *message,
|
||||
size_t message_length,
|
||||
const uint8_t *signature,
|
||||
size_t signature_length) {
|
||||
extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
|
||||
OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length,
|
||||
const uint8_t* message, size_t message_length, const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}; // namespace wvcdm
|
||||
|
||||
@@ -16,17 +16,12 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
PolicyEngine::PolicyEngine() {
|
||||
Init(new Clock());
|
||||
}
|
||||
PolicyEngine::PolicyEngine() { Init(new Clock()); }
|
||||
|
||||
PolicyEngine::PolicyEngine(Clock* clock) {
|
||||
Init(clock);
|
||||
}
|
||||
PolicyEngine::PolicyEngine(Clock* clock) { Init(clock); }
|
||||
|
||||
PolicyEngine::~PolicyEngine() {
|
||||
if (clock_)
|
||||
delete clock_;
|
||||
if (clock_) delete clock_;
|
||||
}
|
||||
|
||||
void PolicyEngine::Init(Clock* clock) {
|
||||
@@ -45,7 +40,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
|
||||
// License expiration trumps all.
|
||||
if ((IsLicenseDurationExpired(current_time) ||
|
||||
IsPlaybackDurationExpired(current_time)) &&
|
||||
IsPlaybackDurationExpired(current_time)) &&
|
||||
license_state_ != kLicenseStateExpired) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
can_decrypt_ = false;
|
||||
@@ -59,8 +54,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateCanPlay: {
|
||||
if (IsRenewalDelayExpired(current_time))
|
||||
renewal_needed = true;
|
||||
if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -70,8 +64,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
}
|
||||
|
||||
case kLicenseStateWaitingLicenseUpdate: {
|
||||
if (IsRenewalRetryIntervalExpired(current_time))
|
||||
renewal_needed = true;
|
||||
if (IsRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -112,8 +105,7 @@ void PolicyEngine::SetLicense(
|
||||
|
||||
void PolicyEngine::UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
if (!license.has_policy())
|
||||
return;
|
||||
if (!license.has_policy()) return;
|
||||
|
||||
if (kLicenseStateExpired == license_state_) {
|
||||
LOGD("PolicyEngine::UpdateLicense: updating an expired license");
|
||||
@@ -147,8 +139,7 @@ void PolicyEngine::UpdateLicense(
|
||||
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
||||
|
||||
if ((policy_.license_duration_seconds() > 0) &&
|
||||
((policy_.license_duration_seconds() <
|
||||
policy_max_duration_seconds_) ||
|
||||
((policy_.license_duration_seconds() < policy_max_duration_seconds_) ||
|
||||
policy_max_duration_seconds_ == 0)) {
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
@@ -197,18 +188,18 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
std::stringstream ss;
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
if (license_state_ == kLicenseStateInitial)
|
||||
return UNKNOWN_ERROR;
|
||||
if (license_state_ == kLicenseStateInitial) return UNKNOWN_ERROR;
|
||||
|
||||
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
|
||||
license_id_.type() == video_widevine_server::sdk::STREAMING ?
|
||||
QUERY_VALUE_STREAMING : QUERY_VALUE_OFFLINE;
|
||||
(*key_info)[QUERY_KEY_PLAY_ALLOWED] = policy_.can_play() ?
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_PERSIST_ALLOWED] = policy_.can_persist() ?
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ?
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
license_id_.type() == video_widevine_server::sdk::STREAMING
|
||||
? QUERY_VALUE_STREAMING
|
||||
: QUERY_VALUE_OFFLINE;
|
||||
(*key_info)[QUERY_KEY_PLAY_ALLOWED] =
|
||||
policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_PERSIST_ALLOWED] =
|
||||
policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_RENEW_ALLOWED] =
|
||||
policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
ss << GetLicenseDurationRemaining(current_time);
|
||||
(*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
|
||||
ss.str("");
|
||||
@@ -229,15 +220,14 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
// will always return false if the value is 0.
|
||||
bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
|
||||
return policy_max_duration_seconds_ &&
|
||||
license_start_time_ + policy_max_duration_seconds_ <=
|
||||
current_time;
|
||||
license_start_time_ + policy_max_duration_seconds_ <= current_time;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_max_duration_seconds_) return LLONG_MAX;
|
||||
|
||||
int64_t remaining_time = policy_max_duration_seconds_
|
||||
+ license_start_time_ - current_time;
|
||||
int64_t remaining_time =
|
||||
policy_max_duration_seconds_ + license_start_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0)
|
||||
remaining_time = 0;
|
||||
@@ -247,43 +237,38 @@ int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
|
||||
return (policy_.playback_duration_seconds() > 0) &&
|
||||
playback_start_time_ &&
|
||||
playback_start_time_ + policy_.playback_duration_seconds() <=
|
||||
current_time;
|
||||
return (policy_.playback_duration_seconds() > 0) && playback_start_time_ &&
|
||||
playback_start_time_ + policy_.playback_duration_seconds() <=
|
||||
current_time;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_.playback_duration_seconds()) return LLONG_MAX;
|
||||
if (0 == playback_start_time_) return policy_.playback_duration_seconds();
|
||||
|
||||
int64_t remaining_time = policy_.playback_duration_seconds()
|
||||
+ playback_start_time_ - current_time;
|
||||
int64_t remaining_time =
|
||||
policy_.playback_duration_seconds() + playback_start_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0) remaining_time = 0;
|
||||
return remaining_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
|
||||
return policy_.can_renew() &&
|
||||
(policy_.renewal_delay_seconds() > 0) &&
|
||||
license_start_time_ + policy_.renewal_delay_seconds() <=
|
||||
current_time;
|
||||
return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) &&
|
||||
license_start_time_ + policy_.renewal_delay_seconds() <= current_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalRecoveryDurationExpired(
|
||||
int64_t current_time) {
|
||||
// NOTE: Renewal Recovery Duration is currently not used.
|
||||
bool PolicyEngine::IsRenewalRecoveryDurationExpired(int64_t current_time) {
|
||||
// NOTE: Renewal Recovery Duration is currently not used.
|
||||
return (policy_.renewal_recovery_duration_seconds() > 0) &&
|
||||
license_start_time_ + policy_.renewal_recovery_duration_seconds() <=
|
||||
current_time;
|
||||
license_start_time_ + policy_.renewal_recovery_duration_seconds() <=
|
||||
current_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalRetryIntervalExpired(
|
||||
int64_t current_time) {
|
||||
bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
return policy_.can_renew() &&
|
||||
(policy_.renewal_retry_interval_seconds() > 0) &&
|
||||
next_renewal_time_ <= current_time;
|
||||
(policy_.renewal_retry_interval_seconds() > 0) &&
|
||||
next_renewal_time_ <= current_time;
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
|
||||
@@ -13,9 +13,7 @@ AesCbcKey::AesCbcKey() {}
|
||||
|
||||
AesCbcKey::~AesCbcKey() {}
|
||||
|
||||
bool AesCbcKey::Init(const std::string& key) {
|
||||
return false;
|
||||
}
|
||||
bool AesCbcKey::Init(const std::string& key) { return false; }
|
||||
|
||||
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
std::string* iv) {
|
||||
@@ -26,9 +24,7 @@ RsaPublicKey::RsaPublicKey() {}
|
||||
|
||||
RsaPublicKey::~RsaPublicKey() {}
|
||||
|
||||
bool RsaPublicKey::Init(const std::string& serialized_key) {
|
||||
return false;
|
||||
}
|
||||
bool RsaPublicKey::Init(const std::string& serialized_key) { return false; }
|
||||
|
||||
bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
std::string* encrypted_message) {
|
||||
|
||||
@@ -43,7 +43,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
unsigned char lsb = 0; // least significant 4 bits
|
||||
if (!CharToDigit(byte[i * 2], &msb) ||
|
||||
!CharToDigit(byte[i * 2 + 1], &lsb)) {
|
||||
LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i);
|
||||
LOGE("Invalid hex value %c%c at index %d",
|
||||
byte[i * 2], byte[i * 2 + 1], i);
|
||||
return array;
|
||||
}
|
||||
array.push_back((msb << 4) | lsb);
|
||||
@@ -53,8 +54,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
|
||||
// converts an ascii hex string(2 bytes per digit) into a decimal byte string
|
||||
// dump the string with the label.
|
||||
std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& byte) {
|
||||
|
||||
std::vector<uint8_t> a2b_hex(const std::string& label,
|
||||
const std::string& byte) {
|
||||
std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]"
|
||||
<< std::endl << std::endl;
|
||||
|
||||
@@ -71,7 +72,7 @@ std::string b2a_hex(const std::vector<uint8_t>& byte) {
|
||||
}
|
||||
|
||||
std::string b2a_hex(const std::string& byte) {
|
||||
return HexEncode(reinterpret_cast<const uint8_t *>(byte.data()),
|
||||
return HexEncode(reinterpret_cast<const uint8_t*>(byte.data()),
|
||||
byte.length());
|
||||
}
|
||||
|
||||
@@ -90,9 +91,8 @@ std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
||||
int in_size = bin_input.size();
|
||||
std::string b64_output(modp_b64w_encode_len(in_size), 0);
|
||||
|
||||
int out_size = modp_b64w_encode(&b64_output[0],
|
||||
reinterpret_cast<const char*>(&bin_input[0]),
|
||||
in_size);
|
||||
int out_size = modp_b64w_encode(
|
||||
&b64_output[0], reinterpret_cast<const char*>(&bin_input[0]), in_size);
|
||||
if (out_size == -1) {
|
||||
LOGE("Base64SafeEncode failed");
|
||||
return std::string();
|
||||
@@ -119,8 +119,7 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
||||
int in_size = b64_input.size();
|
||||
std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0);
|
||||
int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]),
|
||||
b64_input.data(),
|
||||
in_size);
|
||||
b64_input.data(), in_size);
|
||||
if (out_size == -1) {
|
||||
LOGE("Base64SafeDecode failed");
|
||||
return std::vector<uint8_t>(0);
|
||||
|
||||
@@ -38,28 +38,28 @@ const std::string kTwoBytesOverB64Data("SGVsbG8gRnJpZW5kISE=");
|
||||
const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
|
||||
|
||||
const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
|
||||
make_pair(&kNullString, &kNullString),
|
||||
make_pair(&kf, &kfB64),
|
||||
make_pair(&kfo, &kfoB64),
|
||||
make_pair(&kfoo, &kfooB64),
|
||||
make_pair(&kfoob, &kfoobB64),
|
||||
make_pair(&kfooba, &kfoobaB64),
|
||||
make_pair(&kfoobar, &kfoobarB64),
|
||||
make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data),
|
||||
make_pair(&kOneByteOverData, &kOneByteOverB64Data),
|
||||
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
|
||||
make_pair(&kTestData, &kB64TestData),
|
||||
};
|
||||
make_pair(&kNullString, &kNullString),
|
||||
make_pair(&kf, &kfB64),
|
||||
make_pair(&kfo, &kfoB64),
|
||||
make_pair(&kfoo, &kfooB64),
|
||||
make_pair(&kfoob, &kfoobB64),
|
||||
make_pair(&kfooba, &kfoobaB64),
|
||||
make_pair(&kfoobar, &kfoobarB64),
|
||||
make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data),
|
||||
make_pair(&kOneByteOverData, &kOneByteOverB64Data),
|
||||
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
|
||||
make_pair(&kTestData, &kB64TestData)};
|
||||
|
||||
} // unnamed namespace
|
||||
} // unnamed namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Base64EncodeDecodeTest : public ::testing::TestWithParam<
|
||||
std::pair<const std::string*, const std::string*> > {};
|
||||
class Base64EncodeDecodeTest
|
||||
: public ::testing::TestWithParam<
|
||||
std::pair<const std::string*, const std::string*> > {};
|
||||
|
||||
TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
|
||||
std::pair<const std::string*, const std::string*> values = GetParam();
|
||||
std::pair<const std::string *, const std::string *> values = GetParam();
|
||||
std::vector<uint8_t> decoded_vector = Base64SafeDecode(values.second->data());
|
||||
std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
|
||||
EXPECT_STREQ(values.first->data(), decoded_string.data());
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
// These tests are for the cdm engine, and code below it in the stack. In
|
||||
// particular, we assume that the OEMCrypo layer works, and has a valid keybox.
|
||||
// This is because we need a valid RSA certificate, and will attempt to connect
|
||||
// to the provisioning server to request one if we don't.
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
@@ -14,6 +18,7 @@
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "test_printers.h"
|
||||
#include "url_request.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
@@ -31,60 +36,6 @@ wvcdm::CdmKeySystem g_key_system;
|
||||
std::string g_license_server;
|
||||
wvcdm::KeyId g_wrong_key_id;
|
||||
|
||||
// This is an RSA certificate message from the provisioning server.
|
||||
// The client sends this certificate to a license server for device
|
||||
// authentication by the license server.
|
||||
// This certificate is used to test the CDM engine's provisioning
|
||||
// response handling.
|
||||
static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
|
||||
"{\"signedResponse\": {"
|
||||
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
|
||||
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
|
||||
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
|
||||
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
|
||||
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
|
||||
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
|
||||
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
|
||||
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
|
||||
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
|
||||
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
|
||||
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
|
||||
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
|
||||
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
|
||||
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
|
||||
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
|
||||
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
|
||||
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
|
||||
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
|
||||
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
|
||||
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
|
||||
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
|
||||
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
|
||||
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
|
||||
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
|
||||
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
|
||||
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
|
||||
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
|
||||
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
|
||||
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
|
||||
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
|
||||
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
|
||||
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
|
||||
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
|
||||
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
|
||||
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
|
||||
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
|
||||
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
|
||||
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
|
||||
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
|
||||
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
|
||||
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
|
||||
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
|
||||
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
|
||||
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
|
||||
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
|
||||
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
|
||||
|
||||
const std::string kCencMimeType = "video/mp4";
|
||||
const std::string kWebmMimeType = "video/webm";
|
||||
|
||||
@@ -95,14 +46,39 @@ namespace wvcdm {
|
||||
class WvCdmEngineTest : public testing::Test {
|
||||
public:
|
||||
virtual void SetUp() {
|
||||
cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
CdmResponseType status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
if (status == NEED_PROVISIONING) {
|
||||
Provision();
|
||||
status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
}
|
||||
ASSERT_EQ(NO_ERROR, status);
|
||||
ASSERT_NE("", session_id_) << "Could not open CDM session.";
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
cdm_engine_.CloseSession(session_id_);
|
||||
}
|
||||
virtual void TearDown() { cdm_engine_.CloseSession(session_id_); }
|
||||
|
||||
protected:
|
||||
void Provision() {
|
||||
CdmProvisioningRequest prov_request;
|
||||
std::string provisioning_server_url;
|
||||
CdmCertificateType cert_type = kCertificateWidevine;
|
||||
std::string cert_authority;
|
||||
std::string cert, wrapped_key;
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
cdm_engine_.GetProvisioningRequest(cert_type,
|
||||
cert_authority,
|
||||
&prov_request,
|
||||
&provisioning_server_url));
|
||||
UrlRequest url_request(provisioning_server_url);
|
||||
url_request.PostCertRequestInQueryString(prov_request);
|
||||
std::string message;
|
||||
bool ok = url_request.GetResponse(&message);
|
||||
EXPECT_TRUE(ok);
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
cdm_engine_.HandleProvisioningResponse(message,
|
||||
&cert, &wrapped_key));
|
||||
}
|
||||
|
||||
void GenerateKeyRequest(const std::string& key_id,
|
||||
const std::string& init_data_type_string) {
|
||||
CdmAppParameterMap app_parameters;
|
||||
@@ -112,25 +88,30 @@ class WvCdmEngineTest : public testing::Test {
|
||||
InitializationData init_data(init_data_type_string, key_id);
|
||||
|
||||
EXPECT_EQ(KEY_MESSAGE,
|
||||
cdm_engine_.GenerateKeyRequest(session_id_,
|
||||
key_set_id,
|
||||
init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
&key_msg_,
|
||||
&server_url));
|
||||
cdm_engine_.GenerateKeyRequest(
|
||||
session_id_, key_set_id, init_data, kLicenseTypeStreaming,
|
||||
app_parameters, &key_msg_, &server_url, NULL));
|
||||
}
|
||||
|
||||
void GenerateRenewalRequest() {
|
||||
EXPECT_EQ(KEY_MESSAGE,
|
||||
cdm_engine_.GenerateRenewalRequest(session_id_,
|
||||
&key_msg_,
|
||||
&server_url_));
|
||||
EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateRenewalRequest(
|
||||
session_id_, &key_msg_, &server_url_));
|
||||
}
|
||||
|
||||
std::string GetKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
return GetKeyRequestResponse(server_url, client_auth, true);
|
||||
}
|
||||
|
||||
std::string FailToGetKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
return GetKeyRequestResponse(server_url, client_auth, false);
|
||||
}
|
||||
|
||||
// posts a request and extracts the drm message from the response
|
||||
std::string GetKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
const std::string& client_auth,
|
||||
bool expect_success) {
|
||||
// Use secure connection and chunk transfer coding.
|
||||
UrlRequest url_request(server_url + client_auth);
|
||||
if (!url_request.is_connected()) {
|
||||
@@ -144,7 +125,7 @@ class WvCdmEngineTest : public testing::Test {
|
||||
EXPECT_TRUE(ok);
|
||||
|
||||
int status_code = url_request.GetStatusCode(response);
|
||||
EXPECT_EQ(kHttpOk, status_code);
|
||||
if (expect_success) EXPECT_EQ(kHttpOk, status_code);
|
||||
|
||||
if (status_code != kHttpOk) {
|
||||
return "";
|
||||
@@ -153,24 +134,23 @@ class WvCdmEngineTest : public testing::Test {
|
||||
LicenseRequest lic_request;
|
||||
lic_request.GetDrmMessage(response, drm_msg);
|
||||
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
|
||||
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
||||
drm_msg.size()).c_str());
|
||||
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
|
||||
drm_msg.size()).c_str());
|
||||
return drm_msg;
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyNewKeyResponse(const std::string& server_url,
|
||||
const std::string& client_auth){
|
||||
std::string resp = GetKeyRequestResponse(server_url,
|
||||
client_auth);
|
||||
const std::string& client_auth) {
|
||||
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
||||
CdmKeySetId key_set_id;
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.AddKey(session_id_, resp, &key_set_id));
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED,
|
||||
cdm_engine_.AddKey(session_id_, resp, &key_set_id));
|
||||
}
|
||||
|
||||
void VerifyRenewalKeyResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
std::string resp = GetKeyRequestResponse(server_url,
|
||||
client_auth);
|
||||
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
|
||||
}
|
||||
|
||||
@@ -180,18 +160,9 @@ class WvCdmEngineTest : public testing::Test {
|
||||
std::string server_url_;
|
||||
};
|
||||
|
||||
TEST(WvCdmProvisioningTest, ProvisioningTest) {
|
||||
CdmEngine cdm_engine;
|
||||
CdmProvisioningRequest prov_request;
|
||||
std::string provisioning_server_url;
|
||||
CdmCertificateType cert_type = kCertificateWidevine;
|
||||
std::string cert_authority;
|
||||
std::string cert, wrapped_key;
|
||||
|
||||
cdm_engine.GetProvisioningRequest(cert_type, cert_authority,
|
||||
&prov_request, &provisioning_server_url);
|
||||
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse,
|
||||
&cert, &wrapped_key);
|
||||
// Test that provisioning works, even if device is already provisioned.
|
||||
TEST_F(WvCdmEngineTest, ProvisioningTest) {
|
||||
Provision();
|
||||
}
|
||||
|
||||
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
|
||||
@@ -211,7 +182,7 @@ TEST_F(WvCdmEngineTest, WrongMessageTest) {
|
||||
|
||||
// We should receive a response with no license, i.e. the extracted license
|
||||
// response message should be empty.
|
||||
ASSERT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth));
|
||||
ASSERT_EQ("", FailToGetKeyRequestResponse(g_license_server, g_client_auth));
|
||||
}
|
||||
|
||||
TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
|
||||
@@ -236,11 +207,11 @@ TEST_F(WvCdmEngineTest, LicenseRenewal) {
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
wvcdm::InitLogging(argc, argv);
|
||||
|
||||
wvcdm::ConfigTestEnv config(wvcdm::kGooglePlayServer);
|
||||
wvcdm::ConfigTestEnv config(wvcdm::kContentProtectionServer);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
g_key_system.assign(config.key_system());
|
||||
g_wrong_key_id.assign(config.wrong_key_id());
|
||||
@@ -252,14 +223,14 @@ int main(int argc, char **argv) {
|
||||
|
||||
int show_usage = 0;
|
||||
static const struct option long_options[] = {
|
||||
{ "keyid", required_argument, NULL, 'k' },
|
||||
{ "server", required_argument, NULL, 's' },
|
||||
{ NULL, 0, NULL, '\0' }
|
||||
};
|
||||
{"keyid", required_argument, NULL, 'k'},
|
||||
{"server", required_argument, NULL, 's'},
|
||||
{NULL, 0, NULL, '\0'}};
|
||||
|
||||
int option_index = 0;
|
||||
int opt = 0;
|
||||
while ((opt = getopt_long(argc, argv, "k:s:v", long_options, &option_index)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "k:s:v", long_options,
|
||||
&option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'k': {
|
||||
g_key_id_pssh.clear();
|
||||
@@ -292,11 +263,15 @@ int main(int argc, char **argv) {
|
||||
if (show_usage) {
|
||||
std::cout << std::endl;
|
||||
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
|
||||
std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl;
|
||||
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl;
|
||||
std::cout << " enclose multiple arguments in '' when using adb shell"
|
||||
<< std::endl;
|
||||
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'"
|
||||
<< std::endl << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --server=<server_url>";
|
||||
std::cout << "configure the license server url, please include http[s] in the url" << std::endl;
|
||||
std::cout
|
||||
<< "configure the license server url, please include http[s] in the url"
|
||||
<< std::endl;
|
||||
std::cout << std::setw(30) << std::left << " ";
|
||||
std::cout << "default: " << license_server << std::endl;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "test_printers.h"
|
||||
|
||||
namespace {
|
||||
const std::string kToken = wvcdm::a2bs_hex(
|
||||
@@ -111,7 +112,7 @@ class MockCryptoSession : public CryptoSession {
|
||||
|
||||
class MockPolicyEngine : public PolicyEngine {
|
||||
public:
|
||||
// Leaving a place holder for when PolicyEngine methods need to be mocked
|
||||
// Leaving a place holder for when PolicyEngine methods need to be mocked
|
||||
};
|
||||
|
||||
class MockCdmLicense : public CdmLicense {
|
||||
@@ -132,10 +133,7 @@ class CdmSessionTest : public ::testing::Test {
|
||||
if (cdm_session_) delete cdm_session_;
|
||||
}
|
||||
|
||||
void CreateSession() {
|
||||
cdm_session_ = new CdmSession(license_parser_, crypto_session_,
|
||||
policy_engine_, file_handle_, NULL);
|
||||
}
|
||||
void CreateSession() { CreateSession(NULL); }
|
||||
|
||||
void CreateSession(const CdmClientPropertySet* cdm_client_property_set) {
|
||||
cdm_session_ =
|
||||
|
||||
@@ -6,25 +6,24 @@ namespace {
|
||||
const std::string kWidevineKeySystem = "com.widevine.alpha";
|
||||
|
||||
// Content Protection license server data
|
||||
const std::string kCpLicenseServer =
|
||||
"http://wv-ref-eme-player.appspot.com/proxy";
|
||||
const std::string kCpLicenseServer = "http://widevine-proxy.appspot.com/proxy";
|
||||
const std::string kCpClientAuth = "";
|
||||
const std::string kCpKeyId =
|
||||
"00000042" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000022" // pssh data size
|
||||
"00000042" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000022" // pssh data size
|
||||
// pssh data:
|
||||
"08011a0d7769646576696e655f746573"
|
||||
"74220f73747265616d696e675f636c69"
|
||||
"7031";
|
||||
const std::string kCpOfflineKeyId =
|
||||
"00000040" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000020" // pssh data size
|
||||
"00000040" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000020" // pssh data size
|
||||
// pssh data:
|
||||
"08011a0d7769646576696e655f746573"
|
||||
"74220d6f66666c696e655f636c697031";
|
||||
@@ -40,18 +39,17 @@ const std::string kGpLicenseServer =
|
||||
const std::string kGpClientAuth =
|
||||
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
|
||||
const std::string kGpKeyId =
|
||||
"00000034" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"00000034" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000014" // pssh data size
|
||||
"00000014" // pssh data size
|
||||
// pssh data:
|
||||
"08011210e02562e04cd55351b14b3d74"
|
||||
"8d36ed8e";
|
||||
const std::string kGpOfflineKeyId = kGpKeyId;
|
||||
|
||||
const std::string kGpClientOfflineQueryParameters =
|
||||
"&offline=true";
|
||||
const std::string kGpClientOfflineQueryParameters = "&offline=true";
|
||||
const std::string kGpClientOfflineRenewalQueryParameters =
|
||||
"&offline=true&renewal=true";
|
||||
const std::string kGpClientOfflineReleaseQueryParameters =
|
||||
@@ -59,11 +57,11 @@ const std::string kGpClientOfflineReleaseQueryParameters =
|
||||
|
||||
// An invalid key id, expected to fail
|
||||
const std::string kWrongKeyId =
|
||||
"00000034" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"00000034" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000014" // pssh data size
|
||||
"00000014" // pssh data size
|
||||
// pssh data:
|
||||
"0901121094889920e8d6520098577df8"
|
||||
"f2dd5546";
|
||||
@@ -75,24 +73,21 @@ const std::string kProductionProvisioningServerUrl =
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
|
||||
{ wvcdm::kGooglePlayServer, kGpLicenseServer,
|
||||
kGpClientAuth, kGpKeyId, kGpOfflineKeyId },
|
||||
{ wvcdm::kContentProtectionServer, kCpLicenseServer,
|
||||
kCpClientAuth, kCpKeyId, kCpOfflineKeyId },
|
||||
{wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
|
||||
kGpOfflineKeyId},
|
||||
{wvcdm::kContentProtectionServer, kCpLicenseServer, kCpClientAuth, kCpKeyId,
|
||||
kCpOfflineKeyId},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) {
|
||||
Init(server_id);
|
||||
}
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) { Init(server_id); }
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) {
|
||||
Init(server_id);
|
||||
if (!streaming)
|
||||
key_id_ = license_servers[server_id].offline_key_id;
|
||||
if (!streaming) key_id_ = license_servers[server_id].offline_key_id;
|
||||
}
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
|
||||
@@ -119,7 +114,7 @@ void ConfigTestEnv::Init(LicenseServerId server_id) {
|
||||
key_system_ = kWidevineKeySystem;
|
||||
license_server_ = license_servers[server_id].url;
|
||||
provisioning_server_url_ = kProductionProvisioningServerUrl;
|
||||
wrong_key_id_= kWrongKeyId;
|
||||
wrong_key_id_ = kWrongKeyId;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -16,6 +16,7 @@ using ::testing::AllOf;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnArg;
|
||||
@@ -1786,6 +1787,36 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
|
||||
EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id));
|
||||
}
|
||||
|
||||
TEST_F(DeviceFilesTest, ReserveLicenseIds) {
|
||||
MockFile file;
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
.Times(kNumberOfLicenses)
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
|
||||
for (size_t i = 0; i < kNumberOfLicenses; ++i) {
|
||||
std::string license_path = device_base_path_ +
|
||||
license_test_data[i].key_set_id +
|
||||
DeviceFiles::GetLicenseFileNameExtension();
|
||||
InSequence calls;
|
||||
EXPECT_CALL(file, Open(StrEq(license_path),
|
||||
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Write(StrEq(DeviceFiles::GetBlankFileData()),
|
||||
DeviceFiles::GetBlankFileData().size()))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
EXPECT_CALL(file, Close());
|
||||
}
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
for (size_t i = 0; i < kNumberOfLicenses; i++) {
|
||||
EXPECT_TRUE(device_files.ReserveLicenseId(license_test_data[i].key_set_id));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesUsageInfoTest, Read) {
|
||||
MockFile file;
|
||||
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
|
||||
@@ -1824,8 +1855,8 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
|
||||
for (size_t i = 0; i < license_info.size(); ++i) {
|
||||
bool found = false;
|
||||
for (size_t j = 0; j <= static_cast<size_t>(index); ++j) {
|
||||
if ((license_info[i].first.compare(
|
||||
kUsageInfoTestData[j].license_request) == 0) &&
|
||||
if ((license_info[i]
|
||||
.first.compare(kUsageInfoTestData[j].license_request) == 0) &&
|
||||
(license_info[i].second.compare(kUsageInfoTestData[j].license) ==
|
||||
0)) {
|
||||
found = true;
|
||||
|
||||
@@ -257,8 +257,7 @@ TEST_F(FileTest, ListFiles) {
|
||||
EXPECT_EQ(3u, files.size());
|
||||
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
EXPECT_TRUE(files[i] == kTestDirName ||
|
||||
files[i] == kTestFileName ||
|
||||
EXPECT_TRUE(files[i] == kTestDirName || files[i] == kTestFileName ||
|
||||
files[i] == kTestFileName2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ SSL_CTX* InitSslContext() {
|
||||
SSL_load_error_strings();
|
||||
method = SSLv3_client_method();
|
||||
ctx = SSL_CTX_new(method);
|
||||
if (!ctx)
|
||||
LOGE("failed to create SSL context");
|
||||
if (!ctx) LOGE("failed to create SSL context");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -77,8 +76,8 @@ bool SocketWait(int fd, bool for_read, int timeout_in_ms) {
|
||||
tv.tv_sec = timeout_in_ms / 1000;
|
||||
tv.tv_usec = (timeout_in_ms % 1000) * 1000;
|
||||
|
||||
fd_set *read_fds = NULL;
|
||||
fd_set *write_fds = NULL;
|
||||
fd_set* read_fds = NULL;
|
||||
fd_set* write_fds = NULL;
|
||||
if (for_read) {
|
||||
read_fds = &fds;
|
||||
} else {
|
||||
@@ -104,12 +103,9 @@ namespace wvcdm {
|
||||
|
||||
// Parses the URL and extracts all relevant information.
|
||||
// static
|
||||
bool HttpSocket::ParseUrl(const std::string& url,
|
||||
std::string* scheme,
|
||||
bool* secure_connect,
|
||||
std::string* domain_name,
|
||||
int* port,
|
||||
std::string* path) {
|
||||
bool HttpSocket::ParseUrl(const std::string& url, std::string* scheme,
|
||||
bool* secure_connect, std::string* domain_name,
|
||||
int* port, std::string* path) {
|
||||
size_t offset = 0;
|
||||
|
||||
if (!Tokenize(url, "://", offset, scheme, &offset)) {
|
||||
@@ -143,8 +139,7 @@ bool HttpSocket::ParseUrl(const std::string& url,
|
||||
// The domain name may optionally contain a port which overrides the default.
|
||||
std::string domain_name_without_port;
|
||||
size_t port_offset;
|
||||
if (Tokenize(*domain_name, ":", 0, &domain_name_without_port,
|
||||
&port_offset)) {
|
||||
if (Tokenize(*domain_name, ":", 0, &domain_name_without_port, &port_offset)) {
|
||||
*port = atoi(domain_name->c_str() + port_offset);
|
||||
if (*port <= 0 || *port >= 65536) {
|
||||
LOGE("Invalid URL, port not valid: %s", url.c_str());
|
||||
@@ -157,17 +152,13 @@ bool HttpSocket::ParseUrl(const std::string& url,
|
||||
}
|
||||
|
||||
HttpSocket::HttpSocket(const std::string& url)
|
||||
: socket_fd_(-1),
|
||||
ssl_(NULL),
|
||||
ssl_ctx_(NULL) {
|
||||
: socket_fd_(-1), ssl_(NULL), ssl_ctx_(NULL) {
|
||||
valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_,
|
||||
&resource_path_);
|
||||
SSL_library_init();
|
||||
}
|
||||
|
||||
HttpSocket::~HttpSocket() {
|
||||
CloseSocket();
|
||||
}
|
||||
HttpSocket::~HttpSocket() { CloseSocket(); }
|
||||
|
||||
void HttpSocket::CloseSocket() {
|
||||
if (socket_fd_ != -1) {
|
||||
@@ -223,8 +214,8 @@ bool HttpSocket::Connect(int timeout_in_ms) {
|
||||
}
|
||||
|
||||
// set the port
|
||||
struct sockaddr_in* addr_ipv4 = reinterpret_cast<struct sockaddr_in*>(
|
||||
addr_info->ai_addr);
|
||||
struct sockaddr_in* addr_ipv4 =
|
||||
reinterpret_cast<struct sockaddr_in*>(addr_info->ai_addr);
|
||||
addr_ipv4->sin_port = htons(port_);
|
||||
|
||||
// connect to the server
|
||||
@@ -276,8 +267,7 @@ bool HttpSocket::Connect(int timeout_in_ms) {
|
||||
ret = SSL_connect(ssl_);
|
||||
if (ret != 1) {
|
||||
int ssl_err = SSL_get_error(ssl_, ret);
|
||||
if (ssl_err != SSL_ERROR_WANT_READ &&
|
||||
ssl_err != SSL_ERROR_WANT_WRITE) {
|
||||
if (ssl_err != SSL_ERROR_WANT_READ && ssl_err != SSL_ERROR_WANT_WRITE) {
|
||||
char buf[256];
|
||||
LOGE("SSL_connect error: %s", ERR_error_string(ERR_get_error(), buf));
|
||||
CloseSocket();
|
||||
|
||||
@@ -32,12 +32,9 @@ class HttpSocket {
|
||||
int Write(const char* data, int len, int timeout_in_ms);
|
||||
|
||||
private:
|
||||
static bool ParseUrl(const std::string& url,
|
||||
std::string* scheme,
|
||||
bool* secure_connect,
|
||||
std::string* domain_name,
|
||||
int* port,
|
||||
std::string* path);
|
||||
static bool ParseUrl(const std::string& url, std::string* scheme,
|
||||
bool* secure_connect, std::string* domain_name,
|
||||
int* port, std::string* path);
|
||||
FRIEND_TEST(HttpSocketTest, ParseUrlTest);
|
||||
|
||||
std::string scheme_;
|
||||
|
||||
@@ -103,63 +103,63 @@ struct ParseUrlTests {
|
||||
};
|
||||
|
||||
ParseUrlTests parse_url_tests[] = {
|
||||
{
|
||||
"https://code.google.com/p/googletest/wiki/Primer", // url
|
||||
"https", // scheme
|
||||
true, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
443, // port
|
||||
"/p/googletest/wiki/Primer", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com/p/googletest/wiki/Primer/", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/p/googletest/wiki/Primer/", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com/", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"http://10.11.12.13:8888/drm", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/drm", // path
|
||||
},
|
||||
{
|
||||
"http://10.11.12.13:8888", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"https://10.11.12.13:8888", // url
|
||||
"https", // scheme
|
||||
true, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/", // path
|
||||
},
|
||||
{ NULL } // list terminator
|
||||
{
|
||||
"https://code.google.com/p/googletest/wiki/Primer", // url
|
||||
"https", // scheme
|
||||
true, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
443, // port
|
||||
"/p/googletest/wiki/Primer", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com/p/googletest/wiki/Primer/", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/p/googletest/wiki/Primer/", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com/", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"http://10.11.12.13:8888/drm", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/drm", // path
|
||||
},
|
||||
{
|
||||
"http://10.11.12.13:8888", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"https://10.11.12.13:8888", // url
|
||||
"https", // scheme
|
||||
true, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/", // path
|
||||
},
|
||||
{NULL} // list terminator
|
||||
};
|
||||
|
||||
TEST_F(HttpSocketTest, ParseUrlTest) {
|
||||
|
||||
@@ -9,13 +9,13 @@ static const std::string kTwoBlankLines("\r\n\r\n");
|
||||
|
||||
size_t LicenseRequest::FindHeaderEndPosition(
|
||||
const std::string& response) const {
|
||||
return(response.find(kTwoBlankLines));
|
||||
return response.find(kTwoBlankLines);
|
||||
}
|
||||
|
||||
// This routine parses the license server's response message and
|
||||
// extracts the drm message from the response header.
|
||||
void LicenseRequest::GetDrmMessage(const std::string& response,
|
||||
std::string& drm_msg) {
|
||||
std::string& drm_msg) {
|
||||
if (response.empty()) {
|
||||
drm_msg.clear();
|
||||
return;
|
||||
@@ -27,23 +27,25 @@ void LicenseRequest::GetDrmMessage(const std::string& response,
|
||||
// the drm message length as below instead of using Content-Length
|
||||
size_t header_end_pos = FindHeaderEndPosition(response);
|
||||
if (header_end_pos != std::string::npos) {
|
||||
header_end_pos += kTwoBlankLines.size(); // points to response body
|
||||
header_end_pos += kTwoBlankLines.size(); // points to response body
|
||||
|
||||
drm_msg.clear();
|
||||
size_t drm_msg_pos = response.find(kTwoBlankLines, header_end_pos);
|
||||
if (drm_msg_pos != std::string::npos) {
|
||||
drm_msg_pos += kTwoBlankLines.size(); // points to drm message
|
||||
} else {
|
||||
// For backward compatibility, no blank line after error code
|
||||
drm_msg_pos = response.find("\r\n", header_end_pos);
|
||||
// Messages from Google Play server add a GLS wrapper. These start
|
||||
// with "GLS/1.0 <status>".
|
||||
if (response.find("GLS/1.", header_end_pos) == header_end_pos) {
|
||||
// For GLS messages, we should skip past the next blank line, and use
|
||||
// what's left of the message.
|
||||
size_t drm_msg_pos = response.find(kTwoBlankLines, header_end_pos);
|
||||
if (drm_msg_pos != std::string::npos) {
|
||||
drm_msg_pos += 2; // points to drm message
|
||||
drm_msg_pos += kTwoBlankLines.size(); // points to drm message
|
||||
drm_msg = response.substr(drm_msg_pos);
|
||||
} else {
|
||||
LOGE("Message had GLS marker, but did not have extra blank line.");
|
||||
drm_msg = response.substr(header_end_pos);
|
||||
}
|
||||
}
|
||||
|
||||
if (drm_msg_pos != std::string::npos) {
|
||||
drm_msg = response.substr(drm_msg_pos);
|
||||
} else {
|
||||
// For servers that do not use the GLS wrapper, we should just strip of
|
||||
// the headers, and use the rest of the message.
|
||||
drm_msg = response.substr(header_end_pos);
|
||||
}
|
||||
} else {
|
||||
@@ -62,11 +64,10 @@ void LicenseRequest::GetHeartbeatUrl(const std::string& response,
|
||||
|
||||
size_t header_end_pos = FindHeaderEndPosition(response);
|
||||
if (header_end_pos != std::string::npos) {
|
||||
header_end_pos += kTwoBlankLines.size(); // points to response body
|
||||
header_end_pos += kTwoBlankLines.size(); // points to response body
|
||||
|
||||
heartbeat_url.clear();
|
||||
size_t heartbeat_url_pos = response.find("Heartbeat-Url: ",
|
||||
header_end_pos);
|
||||
size_t heartbeat_url_pos = response.find("Heartbeat-Url: ", header_end_pos);
|
||||
if (heartbeat_url_pos != std::string::npos) {
|
||||
heartbeat_url_pos += sizeof("Heartbeat-Url: ");
|
||||
heartbeat_url.assign(response.substr(heartbeat_url_pos));
|
||||
@@ -78,4 +79,4 @@ void LicenseRequest::GetHeartbeatUrl(const std::string& response,
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -1,150 +1,282 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "license.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "initialization_data.h"
|
||||
#include "policy_engine.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The test data is based on key box Eureka-Dev-G1-0001520
|
||||
// This unit test should run on oemcrypto mock with the same key box
|
||||
static const char* kInitData = "0801121093789920E8D6520098577DF8F2DD5546";
|
||||
static const char* kSignedRequest =
|
||||
"080112790A4C0800124800000002000001241F344DB9DFF087F01D917910F39B"
|
||||
"60DC7797CD97789EE82516DC07A478CB70B8C08C299293150AA8E01D2DC9808C"
|
||||
"98DAA16E40A0E55DFE3618C7584DD3C7BE4212250A230A140801121093789920"
|
||||
"E8D6520098577DF8F2DD554610011A09393837363534333231180120001A20FA"
|
||||
"E2DDCD7F1ACA4B728EC957FEE802F8A5541557ACA784EE0D05BFCC0E65FEA1";
|
||||
static const char* kValidResponse =
|
||||
"080212D9020A190A093938373635343332311208C434AB9240A9EF2420012800"
|
||||
"120E0801180120809A9E0128809A9E011A461210B72EEBF582B04BDB15C2E0E3"
|
||||
"20B21C351A30E51FC1D27F70DB8E0DDF8C051BD6E251A44599DBCE4E1BE663FD"
|
||||
"3AFAB191A7DD5736841FB04CE558E7F17BD9812A2DBA20011A6E0A1093789920"
|
||||
"E8D6520098577DF8F2DD55461210367E8714B6F10087AFDE542EDC5C91541A20"
|
||||
"ED51D4E84D81C8CBD8E2046EE079F8A2016268A2F192B902FDA241FEEB10C014"
|
||||
"200242240A109209D46191B8752147C9F6A1CE2BEE6E12107910F39B60DC7797"
|
||||
"CD97789EE82516DC1A6E0A107B1328EB61B554E293F75B1E3E94CC3B1210676F"
|
||||
"69BBDA35EE972B77BC1328A087391A20D2B9FA92B164F5F6362CAD9200A11661"
|
||||
"B8F71E9CE671A3A252D34586526B68FA200242240A109D7B13420FD6217666CC"
|
||||
"CD43860FAA3A1210DBCE4E1BE663FD3AFAB191A7DD57368420E9FDCE86051A20"
|
||||
"C6279E32FD2CB9067229E87AFF4B2DE14A077CDF8F061DAEE2CC2D1BCDEF62D0";
|
||||
static const char* kInvalidResponse =
|
||||
"0802128D020A190A093938373635343332311208BA68C949396C438C20012800"
|
||||
"120E0801180120809A9E0128809A9E011A4612105021EB9AEDC1F73E96DE7DCC"
|
||||
"6D7D72401A300A82E118C0BF0DB230FCADE3F49A9777DDD392322240FEF32C97"
|
||||
"F85428E2F6CCFA638B5481464ADBCF199CEC2FCF3AFB20011A480A1093789920"
|
||||
"E8D6520098577DF8F2DD55461210EE52C59B99050A36E10569AFB34D1DA41A20"
|
||||
"C61FCB8019AC9ADE99FF8FCA99ED35E2331B6488A35102F9379AA42C87A22DC7"
|
||||
"20021A480A107B1328EB61B554E293F75B1E3E94CC3B12101BBF5286B859E349"
|
||||
"2E4A47A24C06AC1B1A2061F21836A04E558BEE0244EF41C165F60CF23C580275"
|
||||
"3175D48BAF1C6CA5759F200220A2BCCA86051A203FD4671075D9DEC6486A9317"
|
||||
"70669993306831EDD57D77F34EFEB467470BA364";
|
||||
|
||||
const std::string kCencMimeType = "video/mp4";
|
||||
const std::string kWebmMimeType = "video/webm";
|
||||
|
||||
}
|
||||
const uint32_t kAesBlockSize = 16;
|
||||
const std::string kAesKey = wvcdm::a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kAesIv = wvcdm::a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kCencInitDataHdr = wvcdm::a2bs_hex(
|
||||
"00000042" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000022"); // pssh data size
|
||||
const std::string kCencPssh = wvcdm::a2bs_hex(
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
const std::string kCdmSessionId = "sid2";
|
||||
const std::string kCryptoSessionId = "id2";
|
||||
const std::string kCryptoRequestId = wvcdm::a2bs_hex(
|
||||
"4341444542353737444337393044394330313030303030303030303030303030");
|
||||
const uint32_t kNonce = 0x49e81305;
|
||||
const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013
|
||||
const std::string kToken = wvcdm::a2bs_hex(
|
||||
"0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02"
|
||||
"82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77"
|
||||
"93BDA057E6BC3E0CA2348BC6831E03609445CA4D418CB98EAC98FFC87AB2364CE76BA26BEE"
|
||||
"CDB0C45BD2A6FE9FD38CC5A1C26303AEEB7E9C3CAFAB0D10E46C07E50BEDAB42BF21F40BD2"
|
||||
"E055DB0B455191D6B4CEEB11B3F1AFA42B5C0CE4C96B75A5283C0E3AE049AA7CF86D1C4EF6"
|
||||
"6A9088B53BCF320ABC9B98A22C219DC109014EFEA72DA5FF2ED5D655DE7AE06EAC6C6B4191"
|
||||
"523B2CD2DC1EBFF5F08B11CFE056F2826C1323F12704EC7EBBC1AF935129E5543804492AF9"
|
||||
"23B848F4AF47B4BFB131C39DDDC99DBAEEE0F30AD2ADBBC63E60793D0876E37391008BB4DB"
|
||||
"F7020301000128DD22128002A9E571776EA9D22A1BD14248DA88E12FD859989F613360B8D2"
|
||||
"DA40AF31CC071C7A138466F0EB745E3FD664C0E1A5E4F01098B8D56C34A0158DF9916D192F"
|
||||
"841ADCA17FD630E1C0CBE652CAC6A52B6A1581BE4029CE6FAE0E04D2D2C7861187AF8299D8"
|
||||
"3E008DB9A2789672CA1DED903773D7E82B234CE2C799EB73CF80600C08F17EEDDDF369D2B8"
|
||||
"4A08292F22D1F18FE89521905E713BA674F2217881DBD7711B8C48D5FDCE6FAB51F935E293"
|
||||
"CB29191AB012B115FD2F5F23164B063D0A929C3E254BF0F4FA60051EB6B3498ED99FF77C19"
|
||||
"68E8CD83A35CEB054D05433FD0EA6AAE43C87DDA377591D1DCC1831EE130BFFF6D139A5ADA"
|
||||
"738B0F257CCE2649E71AB4050AAE020801121017DCBC27D11341D497135442A188DAA6188F"
|
||||
"89809105228E023082010A0282010100D21ADD7549D2748B3494526A9C3FB86C79376BBE8C"
|
||||
"8856F601B8D10461F77ACC7331B10DEBF365120056CDB5662D25907B74F12382F0F4A0CA47"
|
||||
"5EEA9562815C6228F6F698ADA27879D8890F2A2D96A746DDEF5316301C003519C2A2250354"
|
||||
"674169FDDA41CE14D3C52BEA7A20384515012D5952B38AA19E15E8563CC7AAA81C2122880A"
|
||||
"A370A64FEA23C53FB83AC3DB5753214730A349E07F64BF32BE7EAD30D02612AF110BB44FB0"
|
||||
"8E1D308173B327EF64D40C41639542B2D1A73C98A6607EC6C683B513A58470514106EF87AE"
|
||||
"1E7B9C695B93A104DF7437BFC4167789748A43ED208F2C1FA710793C688885EAE732A8BFDF"
|
||||
"5B423B23D75B88FC0ADC8FBDB5020301000128DD2212800372D2FB88098BA3B85B6B4354E0"
|
||||
"3767DBE2D7724663FB0A62ABF7704EA910E01F221349EE16D0152C769384050CE78520668C"
|
||||
"06CCFD3D789AF3EB69FF163615CD609169FDBE2E15A029D34AD2605625BC81844C9D1E2CE0"
|
||||
"519039F3799ADAEF86641E20B033DC16DF2E5B9A1A2A417B8BB3B7A4D9AD1A99367448587D"
|
||||
"A13DDE05A3ED9D62FA42078973B4AA40263D7BFA23F1072E94CDF323FA45F78408823E55C4"
|
||||
"F4C5C723819CF44CE6D98E50C04EC24D93B1AAB8877B9108B9CA391308E1A3645EBB0E7CAC"
|
||||
"BB40B5451560ED799421873BFB5ABB917FA60DB9C77CB8606AF7E3142626F5EA40E5CB8AA0"
|
||||
"89D8E7D6A9361935C426A4450EA8BC2E57290D3BF0A0962991D2A91B752FC80C3E7E4E5503"
|
||||
"3D71C94B325307A68815F026448F56A2741CEBEFC18E8C142F5F62BFAA67A291517DDE982D"
|
||||
"8CD5A9DF6E3D3A99B806F6D60991358C5BE77117D4F3168F3348E9A048539F892F4D783152"
|
||||
"C7A8095224AA56B78C5CF7BD1AB1B179C0C0D11E3C3BAC84C141A00191321E3ACC17242E68"
|
||||
"3C");
|
||||
const std::string kLicenseRequestSignature = wvcdm::a2bs_hex(
|
||||
"4A560ACFED04787BE0D29D7396234FA2E11D6DD0B22F87FD77AEAEDAA6C8FE54AD9859AE4E"
|
||||
"C9F12BCB947892D906DAEC1AD78CABD6F9D479CCF91AF5587DB6FC29CBEBF9C338BAF17790"
|
||||
"90980B1F3333BC901CDBF877490C7B85DB2BF9BC559C98450C6F1E8B2E192959F59CC53BD4"
|
||||
"85F2CC87D87C324750D5A8E28B821A3C55ABF27305AE4C58474D16E4FEE499D87A7D10AC84"
|
||||
"8D24103EB15C63C227A0D57A9D90F5A409D2D55147EE10A35AE291D2D725C7F161FF827221"
|
||||
"9AE18B91516E0CDD0B581590DDDEA2A2527E2C9ABA273629B586A9D22D451A827E332CFC3E"
|
||||
"9BEDB6CF3D8713F9E11675DF1F5DB9038DBBECAB9D1683F8722CAF6E18EC8C04AEE5");
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class LicenseTest : public ::testing::Test {
|
||||
// Protobuf generated classes
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::LicenseRequest;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
|
||||
// gmock methods
|
||||
using ::testing::_;
|
||||
using ::testing::Eq;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD0(IsOpen, bool());
|
||||
MOCK_METHOD1(GenerateRequestId, bool(std::string*));
|
||||
MOCK_METHOD1(UsageInformationSupport, bool(bool*));
|
||||
MOCK_METHOD2(GetHdcpCapabilities,
|
||||
bool(OemCryptoHdcpVersion*, OemCryptoHdcpVersion*));
|
||||
MOCK_METHOD1(GetApiVersion, bool(uint32_t*));
|
||||
MOCK_METHOD1(GenerateNonce, bool(uint32_t*));
|
||||
MOCK_METHOD3(PrepareRequest, bool(const std::string&, bool, std::string*));
|
||||
};
|
||||
|
||||
class MockPolicyEngine : public PolicyEngine {
|
||||
public:
|
||||
};
|
||||
|
||||
class MockClock : public Clock {
|
||||
public:
|
||||
MOCK_METHOD0(GetCurrentTime, int64_t());
|
||||
};
|
||||
|
||||
class MockInitializationData : public InitializationData {
|
||||
public:
|
||||
MockInitializationData(const std::string& type, const CdmInitData& data)
|
||||
: InitializationData(type, data) {}
|
||||
MOCK_METHOD0(is_supported, bool());
|
||||
MOCK_METHOD0(is_cenc, bool());
|
||||
};
|
||||
|
||||
class CdmLicenseTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
session_ = new CryptoSession();
|
||||
EXPECT_TRUE(session_ != NULL);
|
||||
|
||||
std::string token;
|
||||
EXPECT_TRUE(session_->GetToken(&token));
|
||||
|
||||
EXPECT_TRUE(session_->Open());
|
||||
EXPECT_TRUE(license_.Init(token, session_, &policy_engine_));
|
||||
clock_ = new MockClock();
|
||||
crypto_session_ = new MockCryptoSession();
|
||||
init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT,
|
||||
kCencInitDataHdr + kCencPssh);
|
||||
policy_engine_ = new MockPolicyEngine();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
session_->Close();
|
||||
delete session_;
|
||||
if (cdm_license_) delete cdm_license_;
|
||||
if (policy_engine_) delete policy_engine_;
|
||||
if (init_data_) delete init_data_;
|
||||
if (crypto_session_) delete crypto_session_;
|
||||
if (clock_) delete clock_;
|
||||
}
|
||||
|
||||
CryptoSession* session_;
|
||||
CdmLicense license_;
|
||||
PolicyEngine policy_engine_;
|
||||
void CreateCdmLicense() {
|
||||
cdm_license_ = new CdmLicense(clock_);
|
||||
clock_ = NULL;
|
||||
}
|
||||
|
||||
CdmLicense* cdm_license_;
|
||||
MockClock* clock_;
|
||||
MockCryptoSession* crypto_session_;
|
||||
MockInitializationData* init_data_;
|
||||
MockPolicyEngine* policy_engine_;
|
||||
};
|
||||
|
||||
TEST(LicenseTestSession, InitNullSession) {
|
||||
CdmLicense license;
|
||||
EXPECT_FALSE(license.Init("Dummy", NULL, NULL));
|
||||
TEST_F(CdmLicenseTest, InitSuccess) {
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
|
||||
CreateCdmLicense();
|
||||
ASSERT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
|
||||
}
|
||||
|
||||
// TODO(rfrias): Fix or remove test.
|
||||
TEST_F(LicenseTest, DISABLED_PrepareIsoBmffKeyRequest) {
|
||||
std::string signed_request;
|
||||
TEST_F(CdmLicenseTest, InitFail_EmptyToken) {
|
||||
CreateCdmLicense();
|
||||
EXPECT_FALSE(cdm_license_->Init("", crypto_session_, policy_engine_));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) {
|
||||
CreateCdmLicense();
|
||||
EXPECT_FALSE(cdm_license_->Init(kToken, 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));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
bool usage_information_support = true;
|
||||
CryptoSession::OemCryptoHdcpVersion current_hdcp_version =
|
||||
CryptoSession::kOemCryptoNoHdcpDeviceAttached;
|
||||
CryptoSession::OemCryptoHdcpVersion max_hdcp_version =
|
||||
CryptoSession::kOemCryptoHdcpVersion2_1;
|
||||
uint32_t crypto_session_api_version = 9;
|
||||
|
||||
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())).WillOnce(
|
||||
DoAll(SetArgPointee<0>(usage_information_support), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version),
|
||||
SetArgPointee<1>(max_hdcp_version), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())).WillOnce(
|
||||
DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true)));
|
||||
EXPECT_CALL(*clock_, GetCurrentTime()).WillOnce(Return(kLicenseStartTime));
|
||||
EXPECT_CALL(*crypto_session_, GenerateNonce(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kNonce), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, PrepareRequest(_, Eq(false), NotNull()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true)));
|
||||
|
||||
CreateCdmLicense();
|
||||
EXPECT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
|
||||
|
||||
CdmAppParameterMap app_parameters;
|
||||
CdmKeyMessage signed_request;
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
std::string server_url;
|
||||
CdmSessionId session_id;
|
||||
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
|
||||
license_.PrepareKeyRequest(init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
session_id,
|
||||
&signed_request,
|
||||
&server_url);
|
||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||
}
|
||||
EXPECT_TRUE(cdm_license_->PrepareKeyRequest(
|
||||
*init_data_, kLicenseTypeStreaming, app_parameters, kCdmSessionId,
|
||||
&signed_request, &server_url));
|
||||
|
||||
// TODO(rfrias): Fix or remove test.
|
||||
TEST_F(LicenseTest, DISABLED_PrepareWebmKeyRequest) {
|
||||
std::string signed_request;
|
||||
CdmAppParameterMap app_parameters;
|
||||
std::string server_url;
|
||||
CdmSessionId session_id;
|
||||
InitializationData init_data(kWebmMimeType, a2bs_hex(kInitData));
|
||||
license_.PrepareKeyRequest(init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
session_id,
|
||||
&signed_request,
|
||||
&server_url);
|
||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||
}
|
||||
EXPECT_TRUE(!signed_request.empty());
|
||||
|
||||
// TODO(rfrias): Fix or remove test.
|
||||
TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) {
|
||||
std::string signed_request;
|
||||
CdmAppParameterMap app_parameters;
|
||||
CdmSessionId session_id;
|
||||
std::string server_url;
|
||||
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
|
||||
license_.PrepareKeyRequest(init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
session_id,
|
||||
&signed_request,
|
||||
&server_url);
|
||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||
EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse)));
|
||||
}
|
||||
// Verify signed message
|
||||
SignedMessage signed_message;
|
||||
EXPECT_TRUE(signed_message.ParseFromString(signed_request));
|
||||
EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
|
||||
EXPECT_TRUE(signed_message.has_signature());
|
||||
EXPECT_TRUE(std::equal(signed_message.signature().begin(),
|
||||
signed_message.signature().end(),
|
||||
kLicenseRequestSignature.begin()));
|
||||
EXPECT_TRUE(!signed_message.msg().empty());
|
||||
|
||||
// TODO(rfrias): Fix or remove test.
|
||||
TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) {
|
||||
std::string signed_request;
|
||||
CdmAppParameterMap app_parameters;
|
||||
CdmSessionId session_id;
|
||||
std::string server_url;
|
||||
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
|
||||
license_.PrepareKeyRequest(init_data,
|
||||
kLicenseTypeStreaming,
|
||||
app_parameters,
|
||||
session_id,
|
||||
&signed_request,
|
||||
&server_url);
|
||||
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
|
||||
EXPECT_FALSE(license_.HandleKeyResponse(a2bs_hex(kInvalidResponse)));
|
||||
}
|
||||
// Verify license request
|
||||
LicenseRequest license_request;
|
||||
EXPECT_TRUE(license_request.ParseFromString(signed_message.msg()));
|
||||
|
||||
// TODO(kqyang): add unit test cases for PrepareKeyRenewalRequest
|
||||
// and HandleRenewalKeyResponse
|
||||
// Verify Client Identification
|
||||
const ClientIdentification& client_id = license_request.client_id();
|
||||
EXPECT_EQ(video_widevine_server::sdk::
|
||||
ClientIdentification_TokenType_DEVICE_CERTIFICATE,
|
||||
client_id.type());
|
||||
EXPECT_TRUE(std::equal(client_id.token().begin(), client_id.token().end(),
|
||||
kToken.begin()));
|
||||
|
||||
EXPECT_LT(0, client_id.client_info_size());
|
||||
for (int i = 0; i < client_id.client_info_size(); ++i) {
|
||||
const ::video_widevine_server::sdk::ClientIdentification_NameValue&
|
||||
name_value = client_id.client_info(i);
|
||||
EXPECT_TRUE(!name_value.name().empty());
|
||||
EXPECT_TRUE(!name_value.value().empty());
|
||||
}
|
||||
|
||||
EXPECT_FALSE(client_id.has_provider_client_token());
|
||||
EXPECT_FALSE(client_id.has_license_counter());
|
||||
|
||||
const ::video_widevine_server::sdk::ClientIdentification_ClientCapabilities&
|
||||
client_capabilities = client_id.client_capabilities();
|
||||
EXPECT_FALSE(client_capabilities.has_client_token());
|
||||
EXPECT_TRUE(client_capabilities.has_session_token());
|
||||
EXPECT_FALSE(client_capabilities.video_resolution_constraints());
|
||||
EXPECT_EQ(video_widevine_server::sdk::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1,
|
||||
client_capabilities.max_hdcp_version());
|
||||
EXPECT_EQ(crypto_session_api_version,
|
||||
client_capabilities.oem_crypto_api_version());
|
||||
|
||||
// Verify Content Identification
|
||||
const LicenseRequest_ContentIdentification& content_id =
|
||||
license_request.content_id();
|
||||
EXPECT_TRUE(content_id.has_cenc_id());
|
||||
EXPECT_FALSE(content_id.has_webm_id());
|
||||
EXPECT_FALSE(content_id.has_license());
|
||||
|
||||
const ::video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC&
|
||||
cenc_id = content_id.cenc_id();
|
||||
EXPECT_TRUE(std::equal(cenc_id.pssh(0).begin(), cenc_id.pssh(0).end(),
|
||||
kCencPssh.begin()));
|
||||
EXPECT_EQ(video_widevine_server::sdk::STREAMING, cenc_id.license_type());
|
||||
EXPECT_TRUE(std::equal(cenc_id.request_id().begin(),
|
||||
cenc_id.request_id().end(), kCryptoRequestId.begin()));
|
||||
|
||||
// Verify other license request fields
|
||||
EXPECT_EQ(::video_widevine_server::sdk::LicenseRequest_RequestType_NEW,
|
||||
license_request.type());
|
||||
EXPECT_EQ(kLicenseStartTime, license_request.request_time());
|
||||
EXPECT_EQ(video_widevine_server::sdk::VERSION_2_1,
|
||||
license_request.protocol_version());
|
||||
EXPECT_EQ(kNonce, license_request.key_control_nonce());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "license.h"
|
||||
#include "policy_engine.h"
|
||||
#include "test_printers.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
|
||||
93
core/test/test_printers.cpp
Normal file
93
core/test/test_printers.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
// This file adds some print methods so that when unit tests fail, the
|
||||
// will print the name of an enumeration instead of the numeric value.
|
||||
|
||||
#include "test_printers.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case NO_ERROR: *os << "NO_ERROR";
|
||||
break;
|
||||
case UNKNOWN_ERROR: *os << "UNKNOWN_ERROR";
|
||||
break;
|
||||
case KEY_ADDED: *os << "KEY_ADDED";
|
||||
break;
|
||||
case KEY_ERROR: *os << "KEY_ERROR";
|
||||
break;
|
||||
case KEY_MESSAGE: *os << "KEY_MESSAGE";
|
||||
break;
|
||||
case NEED_KEY: *os << "NEED_KEY";
|
||||
break;
|
||||
case KEY_CANCELED: *os << "KEY_CANCELED";
|
||||
break;
|
||||
case NEED_PROVISIONING: *os << "NEED_PROVISIONING";
|
||||
break;
|
||||
case DEVICE_REVOKED: *os << "DEVICE_REVOKED";
|
||||
break;
|
||||
case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmResponseType";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintTo(const enum CdmEventType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case LICENSE_EXPIRED_EVENT: *os << "LICENSE_EXPIRED_EVENT";
|
||||
break;
|
||||
case LICENSE_RENEWAL_NEEDED_EVENT: *os << "LICENSE_RENEWAL_NEEDED_EVENT";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmEventType";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case kLicenseTypeOffline: *os << "kLicenseTypeOffline";
|
||||
break;
|
||||
case kLicenseTypeStreaming: *os << "kLicenseTypeStreaming";
|
||||
break;
|
||||
case kLicenseTypeRelease: *os << "kLicenseTypeRelease";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmLicenseType";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
|
||||
break;
|
||||
case kSecurityLevelL1: *os << "kSecurityLevelL1";
|
||||
break;
|
||||
case kSecurityLevelL2: *os << "kSecurityLevelL2";
|
||||
break;
|
||||
case kSecurityLevelL3: *os << "kSecurityLevelL3";
|
||||
break;
|
||||
case kSecurityLevelUnknown: *os << "kSecurityLevelUnknown";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmSecurityLevel";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case kCertificateWidevine: *os << "kCertificateWidevine";
|
||||
break;
|
||||
case kCertificateX509: *os << "kCertificateX509";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmCertificateType";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
19
core/test/test_printers.h
Normal file
19
core/test/test_printers.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
// This file adds some print methods so that when unit tests fail, the
|
||||
// will print the name of an enumeration instead of the numeric value.
|
||||
|
||||
#ifndef CDM_TEST_PRINTERS_H_
|
||||
#define CDM_TEST_PRINTERS_H_
|
||||
|
||||
#include <iostream>
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
void PrintTo(const enum CdmResponseType& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmEventType& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os);
|
||||
}; // namespace wvcdm
|
||||
|
||||
#endif // CDM_TEST_PRINTERS_H_
|
||||
@@ -10,9 +10,7 @@ class TestTimerHandler : public TimerHandler {
|
||||
TestTimerHandler() : timer_events_(0) {};
|
||||
virtual ~TestTimerHandler() {};
|
||||
|
||||
virtual void OnTimerEvent() {
|
||||
timer_events_++;
|
||||
}
|
||||
virtual void OnTimerEvent() { timer_events_++; }
|
||||
|
||||
uint32_t timer_events() { return timer_events_; }
|
||||
void ResetTimerEvents() { timer_events_ = 0; }
|
||||
@@ -41,14 +39,13 @@ TEST(TimerTest, TimerCheck) {
|
||||
EXPECT_TRUE(timer.IsRunning());
|
||||
sleep(duration);
|
||||
|
||||
EXPECT_LE(duration-1, handler.timer_events());
|
||||
EXPECT_LE(handler.timer_events(), duration+1);
|
||||
EXPECT_LE(duration - 1, handler.timer_events());
|
||||
EXPECT_LE(handler.timer_events(), duration + 1);
|
||||
timer.Stop();
|
||||
EXPECT_FALSE(timer.IsRunning());
|
||||
sleep(duration);
|
||||
|
||||
EXPECT_LE(duration-1, handler.timer_events());
|
||||
EXPECT_LE(handler.timer_events(), duration+1);
|
||||
EXPECT_LE(duration - 1, handler.timer_events());
|
||||
EXPECT_LE(handler.timer_events(), duration + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
namespace {
|
||||
|
||||
const int kReadBufferSize = 1024;
|
||||
const int kConnectTimeoutMs = 5000;
|
||||
const int kWriteTimeoutMs = 3000;
|
||||
const int kReadTimeoutMs = 3000;
|
||||
const int kConnectTimeoutMs = 15000;
|
||||
const int kWriteTimeoutMs = 12000;
|
||||
const int kReadTimeoutMs = 12000;
|
||||
|
||||
// Concatenate all chunks into one blob and returns the response with
|
||||
// header information.
|
||||
@@ -69,12 +69,17 @@ void ConcatenateChunkedResponse(const std::string http_response,
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
UrlRequest::UrlRequest(const std::string& url)
|
||||
: is_connected_(false),
|
||||
socket_(url) {
|
||||
: is_connected_(false), socket_(url) {
|
||||
Reconnect();
|
||||
}
|
||||
|
||||
UrlRequest::~UrlRequest() {}
|
||||
|
||||
bool UrlRequest::Reconnect() {
|
||||
socket_.CloseSocket();
|
||||
if (socket_.Connect(kConnectTimeoutMs)) {
|
||||
is_connected_ = true;
|
||||
} else {
|
||||
@@ -83,8 +88,6 @@ UrlRequest::UrlRequest(const std::string& url)
|
||||
}
|
||||
}
|
||||
|
||||
UrlRequest::~UrlRequest() {}
|
||||
|
||||
bool UrlRequest::GetResponse(std::string* message) {
|
||||
std::string response;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ class UrlRequest {
|
||||
~UrlRequest();
|
||||
|
||||
bool is_connected() const { return is_connected_; }
|
||||
bool Reconnect();
|
||||
|
||||
bool PostRequest(const std::string& data);
|
||||
bool PostCertRequestInQueryString(const std::string& data);
|
||||
|
||||
Reference in New Issue
Block a user