Source release v3.1.0
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "certificate_provisioning.h"
|
||||
#include "crypto_session.h"
|
||||
#include "file_store.h"
|
||||
#include "initialization_data.h"
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
@@ -26,22 +27,24 @@ typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap;
|
||||
|
||||
class CdmEngine {
|
||||
public:
|
||||
CdmEngine();
|
||||
CdmEngine(FileSystem* file_system);
|
||||
virtual ~CdmEngine();
|
||||
|
||||
// Session related methods
|
||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
const std::string& origin,
|
||||
const CdmSessionId& forced_session_id,
|
||||
WvCdmEventListener* event_listener);
|
||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id);
|
||||
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||
virtual bool IsOpenSession(const CdmSessionId& session_id);
|
||||
|
||||
virtual CdmResponseType OpenKeySetSession(
|
||||
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener);
|
||||
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener);
|
||||
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
|
||||
// License related methods
|
||||
@@ -58,27 +61,17 @@ class CdmEngine {
|
||||
// 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.
|
||||
// key_request_type: May be null. If it is non-null, it will be filled with
|
||||
// key request type, whether it is an initial request,
|
||||
// renewal request or release request etc.
|
||||
// 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.
|
||||
// TODO(kqyang): Consider refactor GenerateKeyRequest to reduce the number of
|
||||
// parameters.
|
||||
// key_request: This must be non-null and point to a CdmKeyRequest. The
|
||||
// message field will be filled with the key request, the
|
||||
// type field will be filled with the key request type,
|
||||
// whether it is an initial request, renewal request,
|
||||
// release request, etc. The url field will be filled with
|
||||
// the default URL (if one is known) to send this key
|
||||
// request to.
|
||||
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,
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id_out);
|
||||
|
||||
CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
|
||||
// Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data,
|
||||
@@ -90,9 +83,8 @@ class CdmEngine {
|
||||
virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id);
|
||||
|
||||
// Construct valid renewal request for the current session keys.
|
||||
virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType GenerateRenewalRequest(
|
||||
const CdmSessionId& session_id, CdmKeyRequest* key_request);
|
||||
|
||||
// Accept renewal response and update key info.
|
||||
virtual CdmResponseType RenewKey(const CdmSessionId& session_id,
|
||||
@@ -100,38 +92,48 @@ class CdmEngine {
|
||||
|
||||
// Query system information
|
||||
virtual CdmResponseType QueryStatus(SecurityLevel security_level,
|
||||
const std::string& key,
|
||||
std::string* value);
|
||||
const std::string& query_token,
|
||||
std::string* query_response);
|
||||
|
||||
// Query session information
|
||||
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
CdmQueryMap* query_response);
|
||||
virtual bool IsReleaseSession(const CdmSessionId& session_id);
|
||||
virtual bool IsOfflineSession(const CdmSessionId& session_id);
|
||||
|
||||
// Query license information
|
||||
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
CdmQueryMap* query_response);
|
||||
|
||||
// Query session control information
|
||||
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
// Query the types of usage permitted for the specified key.
|
||||
virtual CdmResponseType QueryKeyAllowedUsage(const CdmSessionId& session_id,
|
||||
const std::string& key_id,
|
||||
CdmKeyAllowedUsage* key_usage);
|
||||
|
||||
// Query the types of usage permitted for the specified key.
|
||||
// Apply the query across all sessions. If the key is found in more than
|
||||
// one session, return the allowed usage settings only if the usage settings
|
||||
// are identical for each instance of the key. Otherwise, clear the settings
|
||||
// and return KEY_CONFLICT_1.
|
||||
virtual CdmResponseType QueryKeyAllowedUsage(const std::string& key_id,
|
||||
CdmKeyAllowedUsage* key_usage);
|
||||
|
||||
// Query OEMCrypto session ID
|
||||
virtual CdmResponseType QueryOemCryptoSessionId(
|
||||
const CdmSessionId& session_id, CdmQueryMap* query_response);
|
||||
|
||||
// Provisioning related methods
|
||||
virtual CdmResponseType GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
const std::string& origin, CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
CdmProvisioningRequest* request, std::string* default_url);
|
||||
|
||||
virtual CdmResponseType HandleProvisioningResponse(
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key);
|
||||
const CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
|
||||
virtual bool IsProvisioned(CdmSecurityLevel security_level,
|
||||
const std::string& origin);
|
||||
virtual bool IsProvisioned(CdmSecurityLevel security_level);
|
||||
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level,
|
||||
const std::string& origin);
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
|
||||
|
||||
// Usage related methods for streaming licenses
|
||||
// Retrieve a random usage info from the list of all usage infos for this app
|
||||
@@ -154,6 +156,35 @@ class CdmEngine {
|
||||
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
|
||||
const CdmDecryptionParameters& parameters);
|
||||
|
||||
// Generic crypto operations - provides basic crypto operations that an
|
||||
// application can use outside of content stream processing
|
||||
|
||||
// Encrypts a buffer of app-level data.
|
||||
virtual CdmResponseType GenericEncrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm, std::string* out_buffer);
|
||||
|
||||
// Decrypts a buffer of app-level data.
|
||||
virtual CdmResponseType GenericDecrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm, std::string* out_buffer);
|
||||
|
||||
// Computes the signature for a message.
|
||||
virtual CdmResponseType GenericSign(const std::string& session_id,
|
||||
const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
std::string* signature);
|
||||
|
||||
// Verifies the signature on a buffer of app-level data.
|
||||
virtual CdmResponseType GenericVerify(const std::string& session_id,
|
||||
const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature);
|
||||
|
||||
virtual size_t SessionSize() const { return sessions_.size(); }
|
||||
|
||||
// Is the key known to any session?
|
||||
@@ -175,6 +206,12 @@ class CdmEngine {
|
||||
|
||||
private:
|
||||
// private methods
|
||||
CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id);
|
||||
|
||||
void DeleteAllUsageReportsUponFactoryReset();
|
||||
bool ValidateKeySystem(const CdmKeySystem& key_system);
|
||||
CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
@@ -190,6 +227,7 @@ class CdmEngine {
|
||||
CdmReleaseKeySetMap release_key_sets_;
|
||||
scoped_ptr<CertificateProvisioning> cert_provisioning_;
|
||||
SecurityLevel cert_provisioning_requested_security_level_;
|
||||
FileSystem* file_system_;
|
||||
|
||||
static bool seeded_;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
#include "initialization_data.h"
|
||||
#include "license.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
@@ -22,12 +23,13 @@ class WvCdmEventListener;
|
||||
|
||||
class CdmSession {
|
||||
public:
|
||||
CdmSession(CdmClientPropertySet* cdm_client_property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id);
|
||||
CdmSession(FileSystem* file_system);
|
||||
virtual ~CdmSession();
|
||||
|
||||
virtual CdmResponseType Init();
|
||||
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set);
|
||||
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id,
|
||||
WvCdmEventListener* event_listener);
|
||||
|
||||
virtual CdmResponseType RestoreOfflineSession(
|
||||
const CdmKeySetId& key_set_id, const CdmLicenseType license_type);
|
||||
@@ -35,25 +37,27 @@ class CdmSession {
|
||||
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response);
|
||||
|
||||
virtual const CdmSessionId& session_id() { return session_id_; }
|
||||
virtual const CdmKeySetId& key_set_id() { return key_set_id_; }
|
||||
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id);
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id);
|
||||
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response);
|
||||
|
||||
// Query session status
|
||||
virtual CdmResponseType QueryStatus(CdmQueryMap* key_info);
|
||||
virtual CdmResponseType QueryStatus(CdmQueryMap* query_response);
|
||||
|
||||
// Query license information
|
||||
virtual CdmResponseType QueryKeyStatus(CdmQueryMap* key_info);
|
||||
virtual CdmResponseType QueryKeyStatus(CdmQueryMap* query_response);
|
||||
|
||||
// Query session control info
|
||||
virtual CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info);
|
||||
// Query allowed usages for key
|
||||
virtual CdmResponseType QueryKeyAllowedUsage(const std::string& key_id,
|
||||
CdmKeyAllowedUsage* key_usage);
|
||||
|
||||
// Query OEMCrypto session ID
|
||||
virtual CdmResponseType QueryOemCryptoSessionId(CdmQueryMap* query_response);
|
||||
|
||||
// Decrypt() - Accept encrypted buffer and return decrypted data.
|
||||
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
@@ -61,8 +65,7 @@ class CdmSession {
|
||||
// License renewal
|
||||
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
||||
// session keys.
|
||||
virtual CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType GenerateRenewalRequest(CdmKeyRequest* key_request);
|
||||
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
virtual CdmResponseType RenewKey(const CdmKeyResponse& key_response);
|
||||
@@ -70,13 +73,13 @@ class CdmSession {
|
||||
// License release
|
||||
// GenerateReleaseRequest() - Construct valid release request for the current
|
||||
// session keys.
|
||||
virtual CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType GenerateReleaseRequest(CdmKeyRequest* key_request);
|
||||
|
||||
// ReleaseKey() - Accept response and release key.
|
||||
virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
|
||||
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
virtual int64_t GetDurationRemaining();
|
||||
|
||||
// Used for notifying the Policy Engine of resolution changes
|
||||
virtual void NotifyResolution(uint32_t width, uint32_t height);
|
||||
@@ -116,11 +119,41 @@ class CdmSession {
|
||||
|
||||
bool DeleteLicense();
|
||||
|
||||
// Generate unique ID for each new session.
|
||||
CdmSessionId GenerateSessionId();
|
||||
|
||||
// Generic crypto operations - provides basic crypto operations that an
|
||||
// application can use outside of content stream processing
|
||||
|
||||
// Encrypts a buffer of app-level data.
|
||||
virtual CdmResponseType GenericEncrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer);
|
||||
|
||||
// Decrypts a buffer of app-level data.
|
||||
virtual CdmResponseType GenericDecrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer);
|
||||
|
||||
// Computes the signature for a message.
|
||||
virtual CdmResponseType GenericSign(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
std::string* signature);
|
||||
|
||||
// Verifies the signature on a buffer of app-level data.
|
||||
virtual CdmResponseType GenericVerify(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature);
|
||||
|
||||
private:
|
||||
friend class CdmSessionTest;
|
||||
|
||||
// Generate unique ID for each new session.
|
||||
CdmSessionId GenerateSessionId();
|
||||
bool GenerateKeySetId(CdmKeySetId* key_set_id);
|
||||
|
||||
CdmResponseType StoreLicense();
|
||||
@@ -135,7 +168,6 @@ class CdmSession {
|
||||
// instance variables
|
||||
bool initialized_;
|
||||
CdmSessionId session_id_;
|
||||
const std::string origin_;
|
||||
scoped_ptr<CdmLicense> license_parser_;
|
||||
scoped_ptr<CryptoSession> crypto_session_;
|
||||
scoped_ptr<PolicyEngine> policy_engine_;
|
||||
@@ -167,6 +199,9 @@ class CdmSession {
|
||||
// license type release and offline related information
|
||||
CdmKeySetId key_set_id_;
|
||||
|
||||
bool mock_license_parser_in_use_;
|
||||
bool mock_policy_engine_in_use_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace wvcdm {
|
||||
|
||||
class CdmSession;
|
||||
class FileSystem;
|
||||
|
||||
class CertificateProvisioning {
|
||||
public:
|
||||
@@ -26,7 +27,7 @@ class CertificateProvisioning {
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
CdmResponseType HandleProvisioningResponse(
|
||||
const std::string& origin,
|
||||
FileSystem* file_system,
|
||||
const CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
|
||||
@@ -17,6 +17,7 @@ class CryptoKey {
|
||||
const std::string& key_data_iv() const { return key_data_iv_; }
|
||||
const std::string& key_control() const { return key_control_; }
|
||||
const std::string& key_control_iv() const { return key_control_iv_; }
|
||||
CdmCipherMode cipher_mode() const { return cipher_mode_; }
|
||||
void set_key_id(const std::string& key_id) { key_id_ = key_id; }
|
||||
void set_key_data(const std::string& key_data) { key_data_ = key_data; }
|
||||
void set_key_data_iv(const std::string& iv) { key_data_iv_ = iv; }
|
||||
@@ -24,6 +25,9 @@ class CryptoKey {
|
||||
void set_key_control_iv(const std::string& ctl_iv) {
|
||||
key_control_iv_ = ctl_iv;
|
||||
}
|
||||
void set_cipher_mode(CdmCipherMode cipher_mode) {
|
||||
cipher_mode_ = cipher_mode;
|
||||
}
|
||||
|
||||
bool HasKeyControl() const { return key_control_.size() >= 16; }
|
||||
|
||||
@@ -33,6 +37,7 @@ class CryptoKey {
|
||||
std::string key_data_;
|
||||
std::string key_control_;
|
||||
std::string key_control_iv_;
|
||||
CdmCipherMode cipher_mode_;
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -35,6 +35,7 @@ class CryptoSession {
|
||||
virtual bool GetApiVersion(uint32_t* version);
|
||||
virtual bool GetSystemId(uint32_t* system_id);
|
||||
virtual bool GetProvisioningId(std::string* provisioning_id);
|
||||
virtual uint8_t GetSecurityPatchLevel();
|
||||
|
||||
virtual CdmResponseType Open() { return Open(kLevelDefault); }
|
||||
virtual CdmResponseType Open(SecurityLevel requested_security_level);
|
||||
@@ -102,6 +103,25 @@ class CryptoSession {
|
||||
virtual bool GetNumberOfOpenSessions(size_t* count);
|
||||
virtual bool GetMaxNumberOfSessions(size_t* max);
|
||||
|
||||
virtual CdmResponseType GenericEncrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer);
|
||||
virtual CdmResponseType GenericDecrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer);
|
||||
virtual CdmResponseType GenericSign(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
std::string* signature);
|
||||
virtual CdmResponseType GenericVerify(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature);
|
||||
|
||||
private:
|
||||
void Init();
|
||||
void Terminate();
|
||||
@@ -113,9 +133,17 @@ class CryptoSession {
|
||||
bool GenerateRsaSignature(const std::string& message, std::string* signature);
|
||||
size_t GetOffset(std::string message, std::string field);
|
||||
bool SetDestinationBufferType();
|
||||
|
||||
bool SelectKey(const std::string& key_id);
|
||||
|
||||
static const OEMCrypto_Algorithm kInvalidAlgorithm =
|
||||
static_cast<OEMCrypto_Algorithm>(-1);
|
||||
|
||||
OEMCrypto_Algorithm GenericSigningAlgorithm(CdmSigningAlgorithm algorithm);
|
||||
OEMCrypto_Algorithm GenericEncryptionAlgorithm(
|
||||
CdmEncryptionAlgorithm algorithm);
|
||||
size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm);
|
||||
|
||||
static const size_t kAes128BlockSize = 16; // Block size for AES_CBC_128
|
||||
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
|
||||
static Lock crypto_lock_;
|
||||
static bool initialized_;
|
||||
@@ -129,11 +157,13 @@ class CryptoSession {
|
||||
bool is_destination_buffer_type_valid_;
|
||||
SecurityLevel requested_security_level_;
|
||||
|
||||
KeyId key_id_;
|
||||
KeyId cached_key_id_;
|
||||
|
||||
uint64_t request_id_base_;
|
||||
static uint64_t request_id_index_;
|
||||
|
||||
CdmCipherMode cipher_mode_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class File;
|
||||
class FileSystem;
|
||||
|
||||
class DeviceFiles {
|
||||
public:
|
||||
@@ -28,7 +28,7 @@ class DeviceFiles {
|
||||
kLicenseStateUnknown,
|
||||
} LicenseState;
|
||||
|
||||
DeviceFiles();
|
||||
DeviceFiles(FileSystem*);
|
||||
virtual ~DeviceFiles();
|
||||
|
||||
virtual bool Init(CdmSecurityLevel security_level);
|
||||
@@ -36,14 +36,12 @@ class DeviceFiles {
|
||||
return Init(security_level);
|
||||
}
|
||||
|
||||
virtual bool StoreCertificate(const std::string& origin,
|
||||
const std::string& certificate,
|
||||
virtual bool StoreCertificate(const std::string& certificate,
|
||||
const std::string& wrapped_private_key);
|
||||
virtual bool RetrieveCertificate(const std::string& origin,
|
||||
std::string* certificate,
|
||||
virtual bool RetrieveCertificate(std::string* certificate,
|
||||
std::string* wrapped_private_key);
|
||||
virtual bool HasCertificate(const std::string& origin);
|
||||
virtual bool RemoveCertificate(const std::string& origin);
|
||||
virtual bool HasCertificate();
|
||||
virtual bool RemoveCertificate();
|
||||
|
||||
virtual bool StoreLicense(const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
@@ -100,6 +98,13 @@ class DeviceFiles {
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_response);
|
||||
|
||||
virtual bool StoreHlsAttributes(const std::string& key_set_id,
|
||||
const CdmHlsMethod method,
|
||||
const std::vector<uint8_t>& media_segment_iv);
|
||||
virtual bool RetrieveHlsAttributes(const std::string& key_set_id,
|
||||
CdmHlsMethod* method,
|
||||
std::vector<uint8_t>* media_segment_iv);
|
||||
virtual bool DeleteHlsAttributes(const std::string& key_set_id);
|
||||
private:
|
||||
// Helpers that wrap the File interface and automatically handle hashing, as
|
||||
// well as adding the device files base path to to the file name.
|
||||
@@ -113,29 +118,25 @@ class DeviceFiles {
|
||||
bool RemoveFile(const std::string& name);
|
||||
ssize_t GetFileSize(const std::string& name);
|
||||
|
||||
// Certificate and offline licenses are now stored in security
|
||||
// level specific directories. In an earlier version they were
|
||||
// stored in a common directory and need to be copied over.
|
||||
virtual void SecurityLevelPathBackwardCompatibility();
|
||||
|
||||
static std::string GetCertificateFileName(const std::string& origin);
|
||||
static std::string GetCertificateFileName();
|
||||
static std::string GetHlsAttributesFileNameExtension();
|
||||
static std::string GetLicenseFileNameExtension();
|
||||
static std::string GetUsageInfoFileName(const std::string& app_id);
|
||||
static std::string GetFileNameSafeHash(const std::string& input);
|
||||
|
||||
// For testing only:
|
||||
void SetTestFile(File* file);
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
|
||||
FRIEND_TEST(DeviceCertificateStoreTest, StoreCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, HasCertificate);
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
|
||||
FRIEND_TEST(DeviceFilesHlsAttributesTest, Delete);
|
||||
FRIEND_TEST(DeviceFilesHlsAttributesTest, Read);
|
||||
FRIEND_TEST(DeviceFilesHlsAttributesTest, Store);
|
||||
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, AppParametersBackwardCompatibility);
|
||||
FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility);
|
||||
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, UpdateLicenseState);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Delete);
|
||||
@@ -153,12 +154,10 @@ class DeviceFiles {
|
||||
|
||||
static std::set<std::string> reserved_license_ids_;
|
||||
|
||||
scoped_ptr<File> file_;
|
||||
FileSystem* file_system_;
|
||||
CdmSecurityLevel security_level_;
|
||||
bool initialized_;
|
||||
|
||||
bool test_file_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(DeviceFiles);
|
||||
};
|
||||
|
||||
|
||||
@@ -15,39 +15,54 @@ namespace wvcdm {
|
||||
|
||||
// File class. The implementation is platform dependent.
|
||||
class File {
|
||||
public:
|
||||
virtual ssize_t Read(char* buffer, size_t bytes);
|
||||
virtual ssize_t Write(const char* buffer, size_t bytes);
|
||||
virtual void Close();
|
||||
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
File(Impl*);
|
||||
virtual ~File();
|
||||
|
||||
private:
|
||||
Impl* impl_;
|
||||
|
||||
friend class FileSystem;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(File);
|
||||
};
|
||||
|
||||
class FileSystem {
|
||||
public:
|
||||
class Impl;
|
||||
|
||||
// defines as bit flag
|
||||
enum OpenFlags {
|
||||
kNoFlags = 0,
|
||||
kBinary = 1,
|
||||
kCreate = 2,
|
||||
kReadOnly = 4, // defaults to read and write access
|
||||
kTruncate = 8
|
||||
kCreate = 1,
|
||||
kReadOnly = 2, // defaults to read and write access
|
||||
kTruncate = 4
|
||||
};
|
||||
|
||||
File();
|
||||
virtual ~File();
|
||||
FileSystem();
|
||||
FileSystem(const std::string& origin, void* extra_data);
|
||||
virtual ~FileSystem();
|
||||
|
||||
virtual bool Open(const std::string& file_path, int flags);
|
||||
virtual ssize_t Read(char* buffer, size_t bytes);
|
||||
virtual ssize_t Write(const char* buffer, size_t bytes);
|
||||
virtual void Close();
|
||||
virtual File* Open(const std::string& file_path, int flags);
|
||||
|
||||
virtual bool Exists(const std::string& file_path);
|
||||
virtual bool Remove(const std::string& file_path);
|
||||
virtual bool Copy(const std::string& old_path, const std::string& new_path);
|
||||
virtual bool List(const std::string& path, std::vector<std::string>* files);
|
||||
virtual bool CreateDirectory(const std::string dir_path);
|
||||
virtual bool IsDirectory(const std::string& dir_path);
|
||||
virtual bool IsRegularFile(const std::string& file_path);
|
||||
virtual ssize_t FileSize(const std::string& file_path);
|
||||
|
||||
const std::string& origin() const { return origin_; }
|
||||
void SetOrigin(const std::string& origin);
|
||||
|
||||
private:
|
||||
Impl* impl_;
|
||||
std::string origin_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(File);
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(FileSystem);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -16,24 +16,62 @@ class InitializationData {
|
||||
InitializationData(const std::string& type = std::string(),
|
||||
const CdmInitData& data = CdmInitData());
|
||||
|
||||
bool is_supported() const { return is_cenc_ || is_webm_; }
|
||||
bool is_supported() const { return is_cenc_ || is_webm_ || is_hls_; }
|
||||
bool is_cenc() const { return is_cenc_; }
|
||||
bool is_hls() const { return is_hls_; }
|
||||
bool is_webm() const { return is_webm_; }
|
||||
|
||||
bool IsEmpty() const { return data_.empty(); }
|
||||
|
||||
const std::string& type() const { return type_; }
|
||||
const CdmInitData& data() const { return data_; }
|
||||
std::vector<uint8_t> hls_iv() const { return hls_iv_; }
|
||||
CdmHlsMethod hls_method() const { return hls_method_; }
|
||||
|
||||
private:
|
||||
// Parse a blob of multiple concatenated PSSH atoms to extract the first
|
||||
// Widevine PSSH.
|
||||
bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output);
|
||||
|
||||
bool ExtractHlsAttributes(const std::string& attribute_list,
|
||||
CdmHlsMethod* method, std::vector<uint8_t>* iv,
|
||||
std::string* uri);
|
||||
static bool ConstructWidevineInitData(CdmHlsMethod method,
|
||||
const std::string& uri,
|
||||
CdmInitData* output);
|
||||
static bool ExtractQuotedAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::string* value);
|
||||
static bool ExtractHexAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::vector<uint8_t>* value);
|
||||
static bool ExtractAttribute(const std::string& attribute_list,
|
||||
const std::string& key, std::string* value);
|
||||
|
||||
static std::vector<std::string> ExtractKeyFormatVersions(
|
||||
const std::string& key_format_versions);
|
||||
|
||||
// For testing only:
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(HlsAttributeExtractionTest, ExtractAttribute);
|
||||
FRIEND_TEST(HlsConstructionTest, InitData);
|
||||
FRIEND_TEST(HlsInitDataConstructionTest, InvalidUriDataFormat);
|
||||
FRIEND_TEST(HlsInitDataConstructionTest, InvalidUriBase64Encode);
|
||||
FRIEND_TEST(HlsHexAttributeExtractionTest, ExtractHexAttribute);
|
||||
FRIEND_TEST(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions);
|
||||
FRIEND_TEST(HlsParseTest, Parse);
|
||||
FRIEND_TEST(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute);
|
||||
FRIEND_TEST(HlsTest, ExtractHlsAttributes);
|
||||
#endif
|
||||
|
||||
std::string type_;
|
||||
CdmInitData data_;
|
||||
bool is_cenc_;
|
||||
bool is_hls_;
|
||||
bool is_webm_;
|
||||
|
||||
std::vector<uint8_t> hls_iv_;
|
||||
CdmHlsMethod hls_method_;
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
139
core/include/license_key_status.h
Normal file
139
core/include/license_key_status.h
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CORE_LICENSE_KEY_STATUS_H_
|
||||
#define WVCDM_CORE_LICENSE_KEY_STATUS_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class LicenseKeyStatus;
|
||||
|
||||
// Holds all content and operator session keys for a session.
|
||||
class LicenseKeys {
|
||||
public:
|
||||
LicenseKeys() {}
|
||||
virtual ~LicenseKeys() { Clear(); }
|
||||
|
||||
virtual bool Empty() { return keys_.empty(); }
|
||||
|
||||
// Returns true if the key is a content key (not an operator session key)
|
||||
virtual bool IsContentKey(const KeyId& key_id);
|
||||
|
||||
// Returns true if the key is currently usable for content decryption.
|
||||
virtual bool CanDecryptContent(const KeyId& key_id);
|
||||
|
||||
// Returns the allowed usages for a key.
|
||||
virtual bool GetAllowedUsage(const KeyId& key_id,
|
||||
CdmKeyAllowedUsage* allowed_usage);
|
||||
|
||||
// Applies a new status to each content key.
|
||||
// Returns true if any statuses changed, and sets new_usable_keys to
|
||||
// true if the status changes resulted in keys becoming usable.
|
||||
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
|
||||
bool* new_usable_keys);
|
||||
|
||||
// Populates the CdmKeyStatusMap with the current content keys.
|
||||
virtual void ExtractKeyStatuses(CdmKeyStatusMap* content_keys);
|
||||
|
||||
// Determines whether the specified key can be used under the current
|
||||
// resolution and/or hdcp constraints. If no constraints have been applied
|
||||
// to the key, returns true.
|
||||
virtual bool MeetsConstraints(const KeyId& key_id);
|
||||
|
||||
// Applies a resolution and/or hdcp change to each key, updating their
|
||||
// useability under their constraints.
|
||||
virtual void ApplyConstraints(uint32_t new_resolution,
|
||||
CryptoSession::HdcpCapability new_hdcp_level);
|
||||
|
||||
// Extracts the keys from a license and makes them available for
|
||||
// querying usage and constraint settings.
|
||||
virtual void SetFromLicense(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
|
||||
private:
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef std::map<wvcdm::KeyId, LicenseKeyStatus*>::const_iterator
|
||||
LicenseKeyStatusIterator;
|
||||
|
||||
void Clear();
|
||||
|
||||
bool is_initialized_;
|
||||
std::map<KeyId, LicenseKeyStatus*> keys_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeys);
|
||||
};
|
||||
|
||||
// Holds the current license status of a key.
|
||||
class LicenseKeyStatus {
|
||||
friend class LicenseKeys;
|
||||
|
||||
public:
|
||||
// Returns true if the key is a content key (not an operator session key)
|
||||
virtual bool IsContentKey() { return is_content_key_; }
|
||||
|
||||
// Returns true if the key is currently usable for content decryption
|
||||
virtual bool CanDecryptContent();
|
||||
|
||||
// Returns the usages allowed for this key.
|
||||
virtual bool GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage);
|
||||
|
||||
// Returns the current status of the key.
|
||||
virtual CdmKeyStatus GetKeyStatus() const { return key_status_; }
|
||||
|
||||
// Applies a new status to this key.
|
||||
// Returns true if the status changed, and sets new_usable_keys to
|
||||
// true if the status changes resulted in the key becoming usable.
|
||||
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
|
||||
bool* new_usable_keys);
|
||||
|
||||
// Returns the current constraint status of this key. The result
|
||||
// may change due to calls to ApplyConstraints().
|
||||
// Note: this will return true until the first call to ApplyConstraints().
|
||||
virtual bool MeetsConstraints() const { return meets_constraints_; }
|
||||
|
||||
// Applies the given changes in resolution or HDCP settings.
|
||||
virtual void ApplyConstraints(
|
||||
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level);
|
||||
|
||||
protected:
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef KeyContainer::OperatorSessionKeyPermissions
|
||||
OperatorSessionKeyPermissions;
|
||||
typedef KeyContainer::OutputProtection OutputProtection;
|
||||
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
ConstraintList;
|
||||
|
||||
LicenseKeyStatus(const KeyContainer& key);
|
||||
|
||||
virtual ~LicenseKeyStatus() {}
|
||||
|
||||
private:
|
||||
|
||||
void ParseContentKey(const KeyContainer& key);
|
||||
void ParseOperatorSessionKey(const KeyContainer& key);
|
||||
|
||||
bool HasConstraints() {
|
||||
return is_content_key_ && constraints_.size() != 0;
|
||||
}
|
||||
|
||||
void SetConstraints(const ConstraintList& constraints);
|
||||
|
||||
bool is_content_key_;
|
||||
CdmKeyStatus key_status_;
|
||||
bool meets_constraints_;
|
||||
CdmKeyAllowedUsage allowed_usage_;
|
||||
CryptoSession::HdcpCapability default_hdcp_level_;
|
||||
ConstraintList constraints_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeyStatus);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_LICENSE_KEY_STATUS_H_
|
||||
@@ -1,105 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVCDM_CORE_MAX_RES_ENGINE_H_
|
||||
#define WVCDM_CORE_MAX_RES_ENGINE_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "lock.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Clock;
|
||||
class MaxResEngineTest;
|
||||
|
||||
// Similar to the Policy Engine, this acts as an oracle that basically says
|
||||
// "Yes(true) you may still decrypt or no(false) you may not decrypt this data
|
||||
// anymore."
|
||||
class MaxResEngine {
|
||||
public:
|
||||
explicit MaxResEngine(CryptoSession* crypto_session);
|
||||
virtual ~MaxResEngine();
|
||||
|
||||
// The value returned is computed during the last call to SetLicense/
|
||||
// SetResolution/OnTimerEvent and may be out of sync depending on the amount
|
||||
// of time elapsed. The current decryption status is not calculated when this
|
||||
// function is called to avoid overhead in the decryption path.
|
||||
virtual bool CanDecrypt(const KeyId& key_id);
|
||||
|
||||
// SetLicense is used in handling the initial license response. It stores
|
||||
// an exact copy of the key constraints from the license.
|
||||
virtual void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
|
||||
// SetResolution is called when the current output resolution is updated by
|
||||
// the decoder. The max-res engine will recalculate the current resolution
|
||||
// constraints, (if any) which may affect the results for CanDecrypt().
|
||||
virtual void SetResolution(uint32_t width, uint32_t height);
|
||||
|
||||
// OnTimerEvent is called when a timer fires. The max-res engine may check the
|
||||
// current HDCP level using the crypto session, which may affect the results
|
||||
// for CanDecrypt().
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
private:
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
|
||||
OutputProtection;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::
|
||||
VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
ConstraintList;
|
||||
|
||||
class KeyStatus {
|
||||
public:
|
||||
explicit KeyStatus(const ConstraintList& constraints);
|
||||
KeyStatus(const ConstraintList& constraints,
|
||||
const OutputProtection::HDCP& default_hdcp_level);
|
||||
|
||||
bool can_decrypt() const { return can_decrypt_; }
|
||||
|
||||
void Update(uint32_t res,
|
||||
CryptoSession::HdcpCapability current_hdcp_level);
|
||||
|
||||
private:
|
||||
void Init(const ConstraintList& constraints);
|
||||
|
||||
VideoResolutionConstraint* GetConstraintForRes(uint32_t res);
|
||||
|
||||
static CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp(
|
||||
const OutputProtection::HDCP& input);
|
||||
|
||||
bool can_decrypt_;
|
||||
|
||||
CryptoSession::HdcpCapability default_hdcp_level_;
|
||||
ConstraintList constraints_;
|
||||
};
|
||||
|
||||
typedef std::map<wvcdm::KeyId,
|
||||
wvcdm::MaxResEngine::KeyStatus*>::const_iterator KeyIterator;
|
||||
|
||||
void Init(CryptoSession* crypto_session, Clock* clock);
|
||||
|
||||
void DeleteAllKeys();
|
||||
|
||||
Lock status_lock_;
|
||||
std::map<KeyId, KeyStatus*> keys_;
|
||||
uint32_t current_resolution_;
|
||||
int64_t next_check_time_;
|
||||
|
||||
scoped_ptr<Clock> clock_;
|
||||
CryptoSession* crypto_session_;
|
||||
|
||||
// For testing
|
||||
friend class MaxResEngineTest;
|
||||
MaxResEngine(CryptoSession* crypto_session, Clock* clock);
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(MaxResEngine);
|
||||
};
|
||||
|
||||
} // wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_MAX_RES_ENGINE_H_
|
||||
@@ -34,6 +34,7 @@ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
|
||||
size_t* count);
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
|
||||
size_t* maximum);
|
||||
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level);
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "license_key_status.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "max_res_engine.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
@@ -67,7 +67,10 @@ class PolicyEngine {
|
||||
|
||||
virtual void NotifySessionExpiration();
|
||||
|
||||
virtual CdmResponseType Query(CdmQueryMap* key_info);
|
||||
virtual CdmResponseType Query(CdmQueryMap* query_response);
|
||||
|
||||
virtual CdmResponseType QueryKeyAllowedUsage(const KeyId& key_id,
|
||||
CdmKeyAllowedUsage* key_usage);
|
||||
|
||||
virtual const LicenseIdentification& license_id() { return license_id_; }
|
||||
|
||||
@@ -84,11 +87,20 @@ class PolicyEngine {
|
||||
bool IsPlaybackStarted() { return playback_start_time_ > 0; }
|
||||
|
||||
bool IsLicenseOrPlaybackDurationExpired(int64_t current_time);
|
||||
int64_t GetLicenseOrPlaybackDurationRemaining();
|
||||
|
||||
bool CanRenew() { return policy_.can_renew(); }
|
||||
|
||||
private:
|
||||
friend class PolicyEngineTest;
|
||||
friend class PolicyEngineConstraintsTest;
|
||||
|
||||
void InitDevice(CryptoSession* crypto_session);
|
||||
void CheckDevice(int64_t current_time);
|
||||
|
||||
void SetDeviceResolution(uint32_t width, uint32_t height) {
|
||||
current_resolution_ = width * height;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
kLicenseStateInitial,
|
||||
@@ -119,9 +131,9 @@ class PolicyEngine {
|
||||
// expiry time changes.
|
||||
void NotifyExpirationUpdate();
|
||||
|
||||
// These setters are for testing only. Takes ownership of the pointers.
|
||||
// set_clock() is for testing only. It alters ownership of the
|
||||
// passed-in pointer.
|
||||
void set_clock(Clock* clock);
|
||||
void set_max_res_engine(MaxResEngine* max_res_engine);
|
||||
|
||||
LicenseState license_state_;
|
||||
|
||||
@@ -153,9 +165,14 @@ class PolicyEngine {
|
||||
CdmSessionId session_id_;
|
||||
WvCdmEventListener* event_listener_;
|
||||
|
||||
scoped_ptr<MaxResEngine> max_res_engine_;
|
||||
// Keys associated with license - holds allowed usage, usage constraints,
|
||||
// and current status (CdmKeyStatus)
|
||||
scoped_ptr<LicenseKeys> license_keys_;
|
||||
|
||||
std::map<KeyId, CdmKeyStatus> keys_status_;
|
||||
// Device checks
|
||||
int64_t next_device_check_;
|
||||
uint32_t current_resolution_;
|
||||
CryptoSession* crypto_session_;
|
||||
|
||||
scoped_ptr<Clock> clock_;
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@ std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& b);
|
||||
std::string a2bs_hex(const std::string& b);
|
||||
std::string b2a_hex(const std::vector<uint8_t>& b);
|
||||
std::string b2a_hex(const std::string& b);
|
||||
std::string Base64Encode(const std::vector<uint8_t>& bin_input);
|
||||
std::vector<uint8_t> Base64Decode(const std::string& bin_input);
|
||||
std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input);
|
||||
std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input);
|
||||
std::vector<uint8_t> Base64SafeDecode(const std::string& bin_input);
|
||||
std::string HexEncode(const uint8_t* bytes, unsigned size);
|
||||
std::string IntToString(int value);
|
||||
std::string UintToString(unsigned int value);
|
||||
int64_t htonll64(int64_t x);
|
||||
inline int64_t ntohll64(int64_t x) { return htonll64(x); }
|
||||
|
||||
|
||||
@@ -80,8 +80,19 @@ static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4";
|
||||
static const std::string WEBM_VIDEO_MIME_TYPE = "video/webm";
|
||||
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
|
||||
static const std::string CENC_INIT_DATA_FORMAT = "cenc";
|
||||
static const std::string HLS_INIT_DATA_FORMAT = "hls";
|
||||
static const std::string WEBM_INIT_DATA_FORMAT = "webm";
|
||||
|
||||
static const std::string HLS_KEYFORMAT_ATTRIBUTE = "KEYFORMAT";
|
||||
static const std::string HLS_KEYFORMAT_VERSIONS_ATTRIBUTE = "KEYFORMATVERSIONS";
|
||||
static const std::string HLS_KEYFORMAT_VERSION_VALUE_1 = "1";
|
||||
static const std::string HLS_METHOD_ATTRIBUTE = "METHOD";
|
||||
static const std::string HLS_METHOD_AES_128 = "AES-128";
|
||||
static const std::string HLS_METHOD_NONE = "NONE";
|
||||
static const std::string HLS_METHOD_SAMPLE_AES = "SAMPLE-AES";
|
||||
static const std::string HLS_IV_ATTRIBUTE = "IV";
|
||||
static const std::string HLS_URI_ATTRIBUTE = "URI";
|
||||
|
||||
static const char EMPTY_ORIGIN[] = "";
|
||||
} // namespace wvcdm
|
||||
|
||||
|
||||
@@ -149,9 +149,9 @@ enum CdmResponseType {
|
||||
RENEW_KEY_ERROR_1,
|
||||
RENEW_KEY_ERROR_2,
|
||||
LICENSE_RENEWAL_SIGNING_ERROR,
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_1,
|
||||
UNUSED_4, /* previously RESTORE_OFFLINE_LICENSE_ERROR_1 */
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_2,
|
||||
SESSION_INIT_ERROR_1,
|
||||
UNUSED_5, /* SESSION_INIT_ERROR_1 */
|
||||
SESSION_INIT_ERROR_2,
|
||||
SESSION_INIT_GET_KEYBOX_ERROR,
|
||||
SESSION_NOT_FOUND_1,
|
||||
@@ -169,7 +169,7 @@ enum CdmResponseType {
|
||||
SIGNATURE_NOT_FOUND,
|
||||
STORE_LICENSE_ERROR_1,
|
||||
STORE_LICENSE_ERROR_2,
|
||||
STORE_LICENSE_ERROR_3,
|
||||
UNUSED_6, /* previously STORE_LICENSE_ERROR_3 */
|
||||
STORE_USAGE_INFO_ERROR,
|
||||
UNPROVISION_ERROR_1,
|
||||
UNPROVISION_ERROR_2,
|
||||
@@ -213,6 +213,38 @@ enum CdmResponseType {
|
||||
SESSION_NOT_FOUND_11,
|
||||
LOAD_USAGE_INFO_FILE_ERROR,
|
||||
LOAD_USAGE_INFO_MISSING,
|
||||
SESSION_FILE_HANDLE_INIT_ERROR,
|
||||
INCORRECT_CRYPTO_MODE,
|
||||
INVALID_PARAMETERS_ENG_5,
|
||||
DECRYPT_ERROR,
|
||||
INSUFFICIENT_OUTPUT_PROTECTION,
|
||||
SESSION_NOT_FOUND_12,
|
||||
KEY_NOT_FOUND_1,
|
||||
KEY_NOT_FOUND_2,
|
||||
KEY_CONFLICT_1,
|
||||
INVALID_PARAMETERS_ENG_6,
|
||||
INVALID_PARAMETERS_ENG_7,
|
||||
INVALID_PARAMETERS_ENG_8,
|
||||
INVALID_PARAMETERS_ENG_9,
|
||||
INVALID_PARAMETERS_ENG_10,
|
||||
INVALID_PARAMETERS_ENG_11,
|
||||
INVALID_PARAMETERS_ENG_12,
|
||||
SESSION_NOT_FOUND_13,
|
||||
SESSION_NOT_FOUND_14,
|
||||
SESSION_NOT_FOUND_15,
|
||||
SESSION_NOT_FOUND_16,
|
||||
KEY_NOT_FOUND_3,
|
||||
KEY_NOT_FOUND_4,
|
||||
KEY_NOT_FOUND_5,
|
||||
KEY_NOT_FOUND_6,
|
||||
KEY_ERROR_1,
|
||||
KEY_ERROR_2,
|
||||
KEY_ERROR_3,
|
||||
KEY_ERROR_4,
|
||||
INVALID_PARAMETERS_ENG_13,
|
||||
INVALID_PARAMETERS_ENG_14,
|
||||
INVALID_PARAMETERS_ENG_15,
|
||||
INVALID_PARAMETERS_ENG_16,
|
||||
};
|
||||
|
||||
enum CdmKeyStatus {
|
||||
@@ -258,9 +290,89 @@ enum CdmCertificateType {
|
||||
kCertificateX509,
|
||||
};
|
||||
|
||||
enum CdmHlsMethod {
|
||||
kHlsMethodNone,
|
||||
kHlsMethodAes128,
|
||||
kHlsMethodSampleAes,
|
||||
};
|
||||
|
||||
enum CdmCipherMode {
|
||||
kCipherModeCtr,
|
||||
kCipherModeCbc,
|
||||
};
|
||||
|
||||
enum CdmEncryptionAlgorithm {
|
||||
kEncryptionAlgorithmUnknown,
|
||||
kEncryptionAlgorithmAesCbc128
|
||||
};
|
||||
|
||||
enum CdmSigningAlgorithm {
|
||||
kSigningAlgorithmUnknown,
|
||||
kSigningAlgorithmHmacSha256
|
||||
};
|
||||
|
||||
class CdmKeyAllowedUsage {
|
||||
public:
|
||||
CdmKeyAllowedUsage() {
|
||||
Clear();
|
||||
}
|
||||
|
||||
bool Valid() const { return valid_; }
|
||||
void SetValid() { valid_ = true; }
|
||||
|
||||
void Clear() {
|
||||
decrypt_to_clear_buffer = false;
|
||||
decrypt_to_secure_buffer = false;
|
||||
generic_encrypt = false;
|
||||
generic_decrypt = false;
|
||||
generic_sign = false;
|
||||
generic_verify = false;
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
bool Equals(const CdmKeyAllowedUsage& other) {
|
||||
if (!valid_ || !other.Valid() ||
|
||||
decrypt_to_clear_buffer != other.decrypt_to_clear_buffer ||
|
||||
decrypt_to_secure_buffer != other.decrypt_to_secure_buffer ||
|
||||
generic_encrypt != other.generic_encrypt ||
|
||||
generic_decrypt != other.generic_decrypt ||
|
||||
generic_sign != other.generic_sign ||
|
||||
generic_verify != other.generic_verify) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool decrypt_to_clear_buffer;
|
||||
bool decrypt_to_secure_buffer;
|
||||
bool generic_encrypt;
|
||||
bool generic_decrypt;
|
||||
bool generic_sign;
|
||||
bool generic_verify;
|
||||
|
||||
private:
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
// For schemes that do not use pattern encryption (cenc and cbc1), encrypt
|
||||
// and skip should be set to 0. For those that do (cens and cbcs), it is
|
||||
// recommended that encrypt+skip bytes sum to 10 and for cbcs that a 1:9
|
||||
// encrypt:skip ratio be used. See ISO/IEC DIS 23001-7, section 10.4.2 for
|
||||
// more information.
|
||||
struct CdmCencPatternEncryptionDescriptor {
|
||||
size_t encrypt_blocks; // number of 16 byte blocks to decrypt
|
||||
size_t skip_blocks; // number of 16 byte blocks to leave in clear
|
||||
size_t offset_blocks; // offset into the pattern for this call, in blocks
|
||||
CdmCencPatternEncryptionDescriptor()
|
||||
: encrypt_blocks(0),
|
||||
skip_blocks(0),
|
||||
offset_blocks(0) {}
|
||||
};
|
||||
|
||||
struct CdmDecryptionParameters {
|
||||
bool is_encrypted;
|
||||
bool is_secure;
|
||||
CdmCipherMode cipher_mode;
|
||||
const KeyId* key_id;
|
||||
const uint8_t* encrypt_buffer;
|
||||
size_t encrypt_length;
|
||||
@@ -271,9 +383,11 @@ struct CdmDecryptionParameters {
|
||||
size_t decrypt_buffer_offset;
|
||||
uint8_t subsample_flags;
|
||||
bool is_video;
|
||||
CdmCencPatternEncryptionDescriptor pattern_descriptor;
|
||||
CdmDecryptionParameters()
|
||||
: is_encrypted(true),
|
||||
is_secure(true),
|
||||
cipher_mode(kCipherModeCtr),
|
||||
key_id(NULL),
|
||||
encrypt_buffer(NULL),
|
||||
encrypt_length(0),
|
||||
@@ -290,6 +404,7 @@ struct CdmDecryptionParameters {
|
||||
size_t offset, void* decrypted_buffer)
|
||||
: is_encrypted(true),
|
||||
is_secure(true),
|
||||
cipher_mode(kCipherModeCtr),
|
||||
key_id(key),
|
||||
encrypt_buffer(encrypted_buffer),
|
||||
encrypt_length(encrypted_length),
|
||||
@@ -302,6 +417,12 @@ struct CdmDecryptionParameters {
|
||||
is_video(true) {}
|
||||
};
|
||||
|
||||
struct CdmKeyRequest {
|
||||
CdmKeyMessage message;
|
||||
CdmKeyRequestType type;
|
||||
std::string url;
|
||||
};
|
||||
|
||||
// forward class references
|
||||
class KeyMessage;
|
||||
class Request;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
@@ -55,11 +54,13 @@ class UsagePropertySet : public CdmClientPropertySet {
|
||||
|
||||
bool CdmEngine::seeded_ = false;
|
||||
|
||||
CdmEngine::CdmEngine()
|
||||
CdmEngine::CdmEngine(FileSystem* file_system)
|
||||
: cert_provisioning_(NULL),
|
||||
cert_provisioning_requested_security_level_(kLevelDefault),
|
||||
file_system_(file_system),
|
||||
usage_session_(NULL),
|
||||
last_usage_information_update_time_(0) {
|
||||
assert(file_system);
|
||||
Properties::Init();
|
||||
if (!seeded_) {
|
||||
Clock clock;
|
||||
@@ -79,7 +80,22 @@ CdmEngine::~CdmEngine() {
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
const std::string& origin,
|
||||
const CdmSessionId& forced_session_id,
|
||||
WvCdmEventListener* event_listener) {
|
||||
return OpenSession(key_system, property_set, event_listener,
|
||||
&forced_session_id, NULL);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
CdmSessionId* session_id) {
|
||||
return OpenSession(key_system, property_set, event_listener, NULL,
|
||||
session_id);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id) {
|
||||
@@ -90,8 +106,8 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
return INVALID_KEY_SYSTEM;
|
||||
}
|
||||
|
||||
if (!session_id) {
|
||||
LOGE("CdmEngine::OpenSession: no session ID destination provided");
|
||||
if (!session_id && !forced_session_id) {
|
||||
LOGE("CdmEngine::OpenSession: no (forced/)session ID destination provided");
|
||||
return INVALID_PARAMETERS_ENG_1;
|
||||
}
|
||||
|
||||
@@ -101,32 +117,33 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
}
|
||||
}
|
||||
|
||||
scoped_ptr<CdmSession> new_session(
|
||||
new CdmSession(property_set, origin, event_listener, forced_session_id));
|
||||
if (new_session->session_id().empty()) {
|
||||
LOGE("CdmEngine::OpenSession: failure to generate session ID");
|
||||
return EMPTY_SESSION_ID;
|
||||
}
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
|
||||
|
||||
CdmResponseType sts = new_session->Init();
|
||||
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
|
||||
event_listener);
|
||||
if (sts != NO_ERROR) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
new_session->GetRequestedSecurityLevel();
|
||||
// Reserve a session ID so the CDM can return success.
|
||||
if (session_id)
|
||||
*session_id = new_session->GenerateSessionId();
|
||||
} else {
|
||||
LOGE("CdmEngine::OpenSession: bad session init: %d", sts);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
*session_id = new_session->session_id();
|
||||
CdmSessionId id = new_session->session_id();
|
||||
|
||||
AutoLock lock(session_list_lock_);
|
||||
sessions_[*session_id] = new_session.release();
|
||||
sessions_[id] = new_session.release();
|
||||
if (session_id) *session_id = id;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener) {
|
||||
WvCdmEventListener* event_listener) {
|
||||
LOGI("CdmEngine::OpenKeySetSession");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
@@ -135,9 +152,8 @@ CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
}
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmResponseType sts =
|
||||
OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
|
||||
NULL /* forced_session_id */, &session_id);
|
||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, property_set, event_listener,
|
||||
NULL /* forced_session_id */, &session_id);
|
||||
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
@@ -182,9 +198,7 @@ bool CdmEngine::IsOpenSession(const CdmSessionId& session_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,
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id_out) {
|
||||
CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSessionId id = session_id;
|
||||
@@ -223,11 +237,11 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: no key request destination provided");
|
||||
LOGE("CdmEngine::GenerateKeyRequest: output destination provided");
|
||||
return INVALID_PARAMETERS_ENG_2;
|
||||
}
|
||||
|
||||
key_request->clear();
|
||||
key_request->message.clear();
|
||||
|
||||
if (license_type == kLicenseTypeRelease &&
|
||||
!iter->second->license_received()) {
|
||||
@@ -240,8 +254,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
}
|
||||
|
||||
sts = iter->second->GenerateKeyRequest(
|
||||
init_data, license_type, app_parameters, key_request, key_request_type,
|
||||
server_url, key_set_id_out);
|
||||
init_data, license_type, app_parameters, key_request);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
@@ -300,7 +313,10 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
return EMPTY_KEY_DATA_1;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->AddKey(key_data, key_set_id);
|
||||
CdmResponseType sts = iter->second->AddKey(key_data);
|
||||
if (key_set_id) {
|
||||
*key_set_id = iter->second->key_set_id();
|
||||
}
|
||||
|
||||
switch (sts) {
|
||||
case KEY_ADDED:
|
||||
@@ -359,8 +375,7 @@ CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
const CdmSessionId& session_id, CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
const CdmSessionId& session_id, CdmKeyRequest* key_request) {
|
||||
LOGI("CdmEngine::GenerateRenewalRequest");
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
@@ -371,14 +386,13 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination");
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: no request destination");
|
||||
return INVALID_PARAMETERS_ENG_4;
|
||||
}
|
||||
|
||||
key_request->clear();
|
||||
key_request->message.clear();
|
||||
|
||||
CdmResponseType sts =
|
||||
iter->second->GenerateRenewalRequest(key_request, server_url);
|
||||
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
|
||||
@@ -414,8 +428,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
const std::string& key,
|
||||
std::string* value) {
|
||||
const std::string& query_token,
|
||||
std::string* query_response) {
|
||||
LOGI("CdmEngine::QueryStatus");
|
||||
CryptoSession crypto_session;
|
||||
if (security_level == kLevel3) {
|
||||
@@ -423,36 +437,41 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
if (NO_ERROR != status) return INVALID_QUERY_STATUS;
|
||||
}
|
||||
|
||||
if (key == QUERY_KEY_SECURITY_LEVEL) {
|
||||
if (!query_response) {
|
||||
LOGE("CdmEngine::QueryStatus: no query response destination");
|
||||
return INVALID_PARAMETERS_ENG_6;
|
||||
}
|
||||
|
||||
if (query_token == QUERY_KEY_SECURITY_LEVEL) {
|
||||
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
|
||||
switch (security_level) {
|
||||
case kSecurityLevelL1:
|
||||
*value = QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
*query_response = QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
break;
|
||||
case kSecurityLevelL2:
|
||||
*value = QUERY_VALUE_SECURITY_LEVEL_L2;
|
||||
*query_response = QUERY_VALUE_SECURITY_LEVEL_L2;
|
||||
break;
|
||||
case kSecurityLevelL3:
|
||||
*value = QUERY_VALUE_SECURITY_LEVEL_L3;
|
||||
*query_response = QUERY_VALUE_SECURITY_LEVEL_L3;
|
||||
break;
|
||||
case kSecurityLevelUninitialized:
|
||||
case kSecurityLevelUnknown:
|
||||
*value = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
*query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
LOGW("CdmEngine::QueryStatus: Unknown security level: %d",
|
||||
security_level);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
} else if (key == QUERY_KEY_DEVICE_ID) {
|
||||
} else if (query_token == QUERY_KEY_DEVICE_ID) {
|
||||
std::string deviceId;
|
||||
if (!crypto_session.GetDeviceUniqueId(&deviceId)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetDeviceUniqueId failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
*value = deviceId;
|
||||
} else if (key == QUERY_KEY_SYSTEM_ID) {
|
||||
*query_response = deviceId;
|
||||
} else if (query_token == QUERY_KEY_SYSTEM_ID) {
|
||||
uint32_t system_id;
|
||||
if (!crypto_session.GetSystemId(&system_id)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetSystemId failed");
|
||||
@@ -461,35 +480,36 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
|
||||
std::ostringstream system_id_stream;
|
||||
system_id_stream << system_id;
|
||||
*value = system_id_stream.str();
|
||||
} else if (key == QUERY_KEY_PROVISIONING_ID) {
|
||||
*query_response = system_id_stream.str();
|
||||
} else if (query_token == QUERY_KEY_PROVISIONING_ID) {
|
||||
std::string provisioning_id;
|
||||
if (!crypto_session.GetProvisioningId(&provisioning_id)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetProvisioningId failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
*value = provisioning_id;
|
||||
} else if (key == QUERY_KEY_CURRENT_HDCP_LEVEL ||
|
||||
key == QUERY_KEY_MAX_HDCP_LEVEL) {
|
||||
*query_response = provisioning_id;
|
||||
} else if (query_token == QUERY_KEY_CURRENT_HDCP_LEVEL ||
|
||||
query_token == QUERY_KEY_MAX_HDCP_LEVEL) {
|
||||
CryptoSession::HdcpCapability current_hdcp;
|
||||
CryptoSession::HdcpCapability max_hdcp;
|
||||
if (!crypto_session.GetHdcpCapabilities(¤t_hdcp, &max_hdcp)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetHdcpCapabilities failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
*value = MapHdcpVersion(key == QUERY_KEY_CURRENT_HDCP_LEVEL ? current_hdcp
|
||||
: max_hdcp);
|
||||
} else if (key == QUERY_KEY_USAGE_SUPPORT) {
|
||||
*query_response =
|
||||
MapHdcpVersion(query_token == QUERY_KEY_CURRENT_HDCP_LEVEL ?
|
||||
current_hdcp : max_hdcp);
|
||||
} else if (query_token == QUERY_KEY_USAGE_SUPPORT) {
|
||||
bool supports_usage_reporting;
|
||||
if (!crypto_session.UsageInformationSupport(&supports_usage_reporting)) {
|
||||
LOGW("CdmEngine::QueryStatus: UsageInformationSupport failed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
*value = supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
} else if (key == QUERY_KEY_NUMBER_OF_OPEN_SESSIONS) {
|
||||
*query_response =
|
||||
supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
} else if (query_token == QUERY_KEY_NUMBER_OF_OPEN_SESSIONS) {
|
||||
size_t number_of_open_sessions;
|
||||
if (!crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetNumberOfOpenSessions failed");
|
||||
@@ -498,8 +518,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
|
||||
std::ostringstream open_sessions_stream;
|
||||
open_sessions_stream << number_of_open_sessions;
|
||||
*value = open_sessions_stream.str();
|
||||
} else if (key == QUERY_KEY_MAX_NUMBER_OF_SESSIONS) {
|
||||
*query_response = open_sessions_stream.str();
|
||||
} else if (query_token == QUERY_KEY_MAX_NUMBER_OF_SESSIONS) {
|
||||
size_t maximum_number_of_sessions;
|
||||
if (!crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetMaxNumberOfOpenSessions failed");
|
||||
@@ -508,8 +528,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
|
||||
std::ostringstream max_sessions_stream;
|
||||
max_sessions_stream << maximum_number_of_sessions;
|
||||
*value = max_sessions_stream.str();
|
||||
} else if (key == QUERY_KEY_OEMCRYPTO_API_VERSION) {
|
||||
*query_response = max_sessions_stream.str();
|
||||
} else if (query_token == QUERY_KEY_OEMCRYPTO_API_VERSION) {
|
||||
uint32_t api_version;
|
||||
if (!crypto_session.GetApiVersion(&api_version)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetApiVersion failed");
|
||||
@@ -518,10 +538,10 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
|
||||
std::ostringstream api_version_stream;
|
||||
api_version_stream << api_version;
|
||||
*value = api_version_stream.str();
|
||||
*query_response = api_version_stream.str();
|
||||
} else {
|
||||
LOGW("CdmEngine::QueryStatus: Unknown status requested, key = %s",
|
||||
key.c_str());
|
||||
LOGW("CdmEngine::QueryStatus: Unknown status requested, token = %s",
|
||||
query_token.c_str());
|
||||
return INVALID_QUERY_KEY;
|
||||
}
|
||||
|
||||
@@ -529,7 +549,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
CdmQueryMap* query_response) {
|
||||
LOGI("CdmEngine::QuerySessionStatus");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
@@ -537,7 +557,7 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_8;
|
||||
}
|
||||
return iter->second->QueryStatus(key_info);
|
||||
return iter->second->QueryStatus(query_response);
|
||||
}
|
||||
|
||||
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
|
||||
@@ -563,7 +583,7 @@ bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
CdmQueryMap* query_response) {
|
||||
LOGI("CdmEngine::QueryKeyStatus");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
@@ -571,19 +591,72 @@ CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_9;
|
||||
}
|
||||
return iter->second->QueryKeyStatus(key_info);
|
||||
return iter->second->QueryKeyStatus(query_response);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyControlInfo");
|
||||
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
|
||||
const std::string& key_id,
|
||||
CdmKeyAllowedUsage* key_usage) {
|
||||
LOGI("CdmEngine::QueryKeyAllowedUsage");
|
||||
if (!key_usage) {
|
||||
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
|
||||
return INVALID_PARAMETERS_ENG_12;
|
||||
}
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s",
|
||||
LOGE("CdmEngine::QueryKeyAllowedUsage: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_12;
|
||||
}
|
||||
return iter->second->QueryKeyAllowedUsage(key_id, key_usage);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
|
||||
CdmKeyAllowedUsage* key_usage) {
|
||||
LOGI("CdmEngine::QueryKeyAllowedUsage (all sessions)");
|
||||
CdmResponseType session_sts;
|
||||
CdmKeyAllowedUsage found_in_this_session;
|
||||
bool found = false;
|
||||
if (!key_usage) {
|
||||
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
|
||||
return INVALID_PARAMETERS_ENG_7;
|
||||
}
|
||||
key_usage->Clear();
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
session_sts = iter->second->QueryKeyAllowedUsage(key_id,
|
||||
&found_in_this_session);
|
||||
if (session_sts == NO_ERROR) {
|
||||
if (found) {
|
||||
// Found another key. If usage settings do not match, fail.
|
||||
if (!key_usage->Equals(found_in_this_session)) {
|
||||
key_usage->Clear();
|
||||
return KEY_CONFLICT_1;
|
||||
}
|
||||
} else {
|
||||
*key_usage = found_in_this_session;
|
||||
found = true;
|
||||
}
|
||||
} else if (session_sts != KEY_NOT_FOUND_1) {
|
||||
LOGE("CdmEngine::QueryKeyAllowedUsage (all sessions) FAILED = %d",
|
||||
session_sts);
|
||||
key_usage->Clear();
|
||||
return session_sts;
|
||||
}
|
||||
}
|
||||
return (found) ? NO_ERROR : KEY_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryOemCryptoSessionId(
|
||||
const CdmSessionId& session_id, CdmQueryMap* query_response) {
|
||||
LOGI("CdmEngine::QueryOemCryptoSessionId");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryOemCryptoSessionId: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_10;
|
||||
}
|
||||
return iter->second->QueryKeyControlInfo(key_info);
|
||||
return iter->second->QueryOemCryptoSessionId(query_response);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -595,8 +668,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
*/
|
||||
CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
const std::string& origin, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
if (!request) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
|
||||
return INVALID_PROVISIONING_REQUEST_PARAM_1;
|
||||
@@ -613,7 +685,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
}
|
||||
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
|
||||
cert_provisioning_requested_security_level_, cert_type, cert_authority,
|
||||
origin, request, default_url);
|
||||
file_system_->origin(), request, default_url);
|
||||
if (ret != NO_ERROR) {
|
||||
cert_provisioning_.reset(NULL); // Release resources.
|
||||
}
|
||||
@@ -628,8 +700,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
* Returns NO_ERROR for success and CdmResponseType error code if fails.
|
||||
*/
|
||||
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key) {
|
||||
const CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
if (response.empty()) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
||||
cert_provisioning_.reset(NULL);
|
||||
@@ -661,7 +733,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
return EMPTY_PROVISIONING_CERTIFICATE_2;
|
||||
}
|
||||
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
|
||||
if (!IsProvisioned(security_level, origin)) {
|
||||
if (!IsProvisioned(security_level)) {
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: provisioning object "
|
||||
"missing.");
|
||||
@@ -671,7 +743,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
}
|
||||
|
||||
CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
|
||||
origin, response, cert, wrapped_key);
|
||||
file_system_, response, cert, wrapped_key);
|
||||
// Release resources only on success. It is possible that a provisioning
|
||||
// attempt was made after this one was requested but before the response was
|
||||
// received, which will cause this attempt to fail. Not releasing will
|
||||
@@ -680,28 +752,25 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level,
|
||||
const std::string& origin) {
|
||||
DeviceFiles handle;
|
||||
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level) {
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::IsProvisioned: unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
return handle.HasCertificate(origin);
|
||||
return handle.HasCertificate();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level,
|
||||
const std::string& origin) {
|
||||
DeviceFiles handle;
|
||||
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::Unprovision: unable to initialize device files");
|
||||
return UNPROVISION_ERROR_1;
|
||||
}
|
||||
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
if (!handle.RemoveCertificate(origin)) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete certificate for origin %s",
|
||||
origin.c_str());
|
||||
if (!file_system_->origin().empty()) {
|
||||
if (!handle.RemoveCertificate()) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete certificate");
|
||||
return UNPROVISION_ERROR_2;
|
||||
}
|
||||
return NO_ERROR;
|
||||
@@ -732,16 +801,19 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
if (NULL == usage_property_set_.get()) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
if (!usage_info) {
|
||||
LOGE("CdmEngine::GetUsageInfo: no usage info destination");
|
||||
return INVALID_PARAMETERS_ENG_8;
|
||||
}
|
||||
usage_property_set_->set_security_level(kLevelDefault);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
usage_session_.reset(
|
||||
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
|
||||
CdmResponseType status = usage_session_->Init();
|
||||
usage_session_.reset(new CdmSession(file_system_));
|
||||
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
return status;
|
||||
}
|
||||
DeviceFiles handle;
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: device file init error");
|
||||
return GET_USAGE_INFO_ERROR_1;
|
||||
@@ -753,9 +825,8 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
&license_response)) {
|
||||
usage_property_set_->set_security_level(kLevel3);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
usage_session_.reset(
|
||||
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
|
||||
status = usage_session_->Init();
|
||||
usage_session_.reset(new CdmSession(file_system_));
|
||||
status = usage_session_->Init(usage_property_set_.get());
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
return status;
|
||||
@@ -771,18 +842,20 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
}
|
||||
}
|
||||
|
||||
std::string server_url;
|
||||
usage_info->resize(1);
|
||||
status =
|
||||
usage_session_->RestoreUsageSession(license_request, license_response);
|
||||
usage_session_->RestoreUsageSession(license_request,license_response);
|
||||
|
||||
if (KEY_ADDED != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
status =
|
||||
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
CdmKeyRequest request;
|
||||
status = usage_session_->GenerateReleaseRequest(&request);
|
||||
|
||||
usage_info->clear();
|
||||
usage_info->push_back(request.message);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d", status);
|
||||
@@ -797,6 +870,10 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
// Return a random usage report from a random security level
|
||||
SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3;
|
||||
CdmResponseType status = UNKNOWN_ERROR;
|
||||
if (!usage_info) {
|
||||
LOGE("CdmEngine::GetUsageInfo: no usage info destination");
|
||||
return INVALID_PARAMETERS_ENG_9;
|
||||
}
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, usage_info);
|
||||
|
||||
@@ -822,16 +899,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
usage_property_set_->set_security_level(requested_security_level);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
|
||||
usage_session_.reset(
|
||||
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
|
||||
usage_session_.reset(new CdmSession(file_system_));
|
||||
|
||||
CdmResponseType status = usage_session_->Init();
|
||||
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
return status;
|
||||
}
|
||||
|
||||
DeviceFiles handle;
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: unable to initialize device files");
|
||||
return GET_USAGE_INFO_ERROR_3;
|
||||
@@ -843,12 +919,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
return GET_USAGE_INFO_ERROR_4;
|
||||
}
|
||||
|
||||
if (!usage_info) {
|
||||
LOGE("CdmEngine::GetUsageInfo: no usage info destination");
|
||||
return INVALID_PARAMETERS_ENG_10;
|
||||
}
|
||||
if (0 == license_info.size()) {
|
||||
usage_info->resize(0);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
std::string server_url;
|
||||
usage_info->resize(kUsageReportsPerRequest);
|
||||
|
||||
uint32_t index = rand() % license_info.size();
|
||||
@@ -861,8 +940,11 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
return status;
|
||||
}
|
||||
|
||||
status =
|
||||
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
CdmKeyRequest request;
|
||||
status = usage_session_->GenerateReleaseRequest(&request);
|
||||
|
||||
usage_info->clear();
|
||||
usage_info->push_back(request.message);
|
||||
|
||||
switch (status) {
|
||||
case KEY_MESSAGE:
|
||||
@@ -888,7 +970,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
|
||||
|
||||
CdmResponseType status = NO_ERROR;
|
||||
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
|
||||
DeviceFiles handle;
|
||||
DeviceFiles handle(file_system_);
|
||||
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
|
||||
@@ -901,9 +983,8 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
|
||||
? kLevel3
|
||||
: kLevelDefault;
|
||||
usage_property_set_->set_security_level(security_level);
|
||||
usage_session_.reset(
|
||||
new CdmSession(usage_property_set_.get(),
|
||||
EMPTY_ORIGIN, NULL, NULL));
|
||||
usage_session_.reset(new CdmSession(file_system_));
|
||||
usage_session_->Init(usage_property_set_.get());
|
||||
CdmResponseType status2 = usage_session_->
|
||||
DeleteMultipleUsageInformation(provider_session_tokens);
|
||||
if (status2 != NO_ERROR) {
|
||||
@@ -954,7 +1035,12 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
|
||||
return SESSION_NOT_FOUND_11;
|
||||
}
|
||||
|
||||
DeviceFiles handle;
|
||||
if (!release_message) {
|
||||
LOGE("CdmEngine::LoadUsageSession: no release message destination");
|
||||
return INVALID_PARAMETERS_ENG_11;
|
||||
}
|
||||
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(iter->second->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::LoadUsageSession: unable to initialize device files");
|
||||
return LOAD_USAGE_INFO_FILE_ERROR;
|
||||
@@ -978,8 +1064,9 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
|
||||
return status;
|
||||
}
|
||||
|
||||
std::string server_url;
|
||||
status = iter->second->GenerateReleaseRequest(release_message, &server_url);
|
||||
CdmKeyRequest request;
|
||||
status = iter->second->GenerateReleaseRequest(&request);
|
||||
*release_message = request.message;
|
||||
|
||||
switch (status) {
|
||||
case KEY_MESSAGE:
|
||||
@@ -1021,24 +1108,86 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
|
||||
// else we must be level 1 direct and we don't need to return a buffer.
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter;
|
||||
CdmSessionMap::iterator session_iter = sessions_.end();
|
||||
if (session_id.empty()) {
|
||||
// Loop through the sessions to find the session containing the key_id.
|
||||
for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
|
||||
// Loop through the sessions to find the session containing the key_id
|
||||
// with the longest remaining license validity.
|
||||
int64_t seconds_remaining = 0;
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
if (iter->second->IsKeyLoaded(*parameters.key_id)) {
|
||||
break;
|
||||
int64_t duration = iter->second->GetDurationRemaining();
|
||||
if (duration > seconds_remaining) {
|
||||
session_iter = iter;
|
||||
seconds_remaining = duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iter = sessions_.find(session_id);
|
||||
session_iter = sessions_.find(session_id);
|
||||
}
|
||||
if (iter == sessions_.end()) {
|
||||
if (session_iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
|
||||
session_id.c_str(), session_id.size());
|
||||
return SESSION_NOT_FOUND_FOR_DECRYPT;
|
||||
}
|
||||
|
||||
return iter->second->Decrypt(parameters);
|
||||
return session_iter->second->Decrypt(parameters);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericEncrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm, std::string* out_buffer) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenericEncrypt: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_13;
|
||||
}
|
||||
return iter->second->GenericEncrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericDecrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenericDecrypt: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_14;
|
||||
}
|
||||
return iter->second->GenericDecrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericSign(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, CdmSigningAlgorithm algorithm,
|
||||
std::string* signature) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenericSign: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_15;
|
||||
}
|
||||
return iter->second->GenericSign(message, key_id, algorithm, signature);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenericVerify(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenericVerify: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return SESSION_NOT_FOUND_16;
|
||||
}
|
||||
return iter->second->GenericVerify(message, key_id, algorithm, signature);
|
||||
}
|
||||
|
||||
bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
|
||||
@@ -1058,25 +1207,30 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(*session_id);
|
||||
if (iter != sessions_.end()) {
|
||||
if (iter->second->IsKeyLoaded(key_id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id);
|
||||
|
||||
for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
|
||||
CdmSessionMap::iterator session_iter = sessions_.end();
|
||||
int64_t seconds_remaining = 0;
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
CdmSessionId local_session_id = iter->second->session_id();
|
||||
if (Properties::GetSessionSharingId(local_session_id) ==
|
||||
session_sharing_id) {
|
||||
if (iter->second->IsKeyLoaded(key_id)) {
|
||||
*session_id = local_session_id;
|
||||
return true;
|
||||
int64_t duration = iter->second->GetDurationRemaining();
|
||||
if (duration > seconds_remaining) {
|
||||
session_iter = iter;
|
||||
seconds_remaining = duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (session_iter != sessions_.end()) {
|
||||
*session_id = session_iter->second->session_id();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1172,9 +1326,8 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
|
||||
Properties::GetDeviceFilesBasePath(kSecurityLevelL3,
|
||||
&device_base_path_level3);
|
||||
|
||||
File file;
|
||||
if (!file.Exists(device_base_path_level1) &&
|
||||
!file.Exists(device_base_path_level3)) {
|
||||
if (!file_system_->Exists(device_base_path_level1) &&
|
||||
!file_system_->Exists(device_base_path_level3)) {
|
||||
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
|
||||
CdmResponseType status = crypto_session->Open(
|
||||
cert_provisioning_requested_security_level_);
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
#include "cdm_engine.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
@@ -25,47 +23,22 @@ const size_t kKeySetIdLength = 14;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
CdmSession::CdmSession(CdmClientPropertySet* cdm_client_property_set,
|
||||
const std::string& origin,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id)
|
||||
: initialized_(false),
|
||||
session_id_(GenerateSessionId()),
|
||||
origin_(origin),
|
||||
crypto_session_(new CryptoSession),
|
||||
file_handle_(new DeviceFiles),
|
||||
license_received_(false),
|
||||
is_offline_(false),
|
||||
is_release_(false),
|
||||
is_temporary_(false),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
requested_security_level_(kLevelDefault),
|
||||
is_initial_decryption_(true),
|
||||
has_decrypted_since_last_report_(false),
|
||||
is_initial_usage_update_(true),
|
||||
is_usage_update_needed_(false) {
|
||||
if (Properties::AlwaysUseKeySetIds()) {
|
||||
if (forced_session_id) {
|
||||
key_set_id_ = *forced_session_id;
|
||||
} else {
|
||||
bool ok = GenerateKeySetId(&key_set_id_);
|
||||
(void)ok; // ok is now used when assertions are turned off.
|
||||
assert(ok);
|
||||
}
|
||||
session_id_ = key_set_id_;
|
||||
}
|
||||
license_parser_.reset(new CdmLicense(session_id_));
|
||||
policy_engine_.reset(new PolicyEngine(
|
||||
session_id_, event_listener, crypto_session_.get()));
|
||||
if (cdm_client_property_set) {
|
||||
if (cdm_client_property_set->security_level() ==
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
requested_security_level_ = kLevel3;
|
||||
security_level_ = kSecurityLevelL3;
|
||||
}
|
||||
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
|
||||
}
|
||||
}
|
||||
CdmSession::CdmSession(FileSystem* file_system) :
|
||||
initialized_(false),
|
||||
crypto_session_(new CryptoSession),
|
||||
file_handle_(new DeviceFiles(file_system)),
|
||||
license_received_(false),
|
||||
is_offline_(false),
|
||||
is_release_(false),
|
||||
is_temporary_(false),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
requested_security_level_(kLevelDefault),
|
||||
is_initial_decryption_(true),
|
||||
has_decrypted_since_last_report_(false),
|
||||
is_initial_usage_update_(true),
|
||||
is_usage_update_needed_(false),
|
||||
mock_license_parser_in_use_(false),
|
||||
mock_policy_engine_in_use_(false) {}
|
||||
|
||||
CdmSession::~CdmSession() {
|
||||
if (!key_set_id_.empty()) {
|
||||
@@ -75,24 +48,38 @@ CdmSession::~CdmSession() {
|
||||
Properties::RemoveSessionPropertySet(session_id_);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::Init() {
|
||||
if (session_id_.empty()) {
|
||||
LOGE("CdmSession::Init: Failed, session not properly constructed");
|
||||
return SESSION_INIT_ERROR_1;
|
||||
}
|
||||
CdmResponseType CdmSession::Init(
|
||||
CdmClientPropertySet* cdm_client_property_set) {
|
||||
return Init(cdm_client_property_set, NULL, NULL);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id,
|
||||
WvCdmEventListener* event_listener) {
|
||||
if (initialized_) {
|
||||
LOGE("CdmSession::Init: Failed due to previous initialization");
|
||||
return SESSION_INIT_ERROR_2;
|
||||
}
|
||||
|
||||
if (cdm_client_property_set &&
|
||||
cdm_client_property_set->security_level() ==
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
requested_security_level_ = kLevel3;
|
||||
security_level_ = kSecurityLevelL3;
|
||||
}
|
||||
CdmResponseType sts = crypto_session_->Open(requested_security_level_);
|
||||
if (NO_ERROR != sts) return sts;
|
||||
security_level_ = crypto_session_->GetSecurityLevel();
|
||||
|
||||
if (!file_handle_->Init(security_level_)) {
|
||||
LOGE("CdmSession::Init: Unable to initialize file handle");
|
||||
return SESSION_FILE_HANDLE_INIT_ERROR;
|
||||
}
|
||||
|
||||
std::string token;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
std::string wrapped_key;
|
||||
if (!file_handle_->Init(security_level_) ||
|
||||
!file_handle_->RetrieveCertificate(origin_, &token, &wrapped_key) ||
|
||||
if (!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
|
||||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
@@ -101,6 +88,30 @@ CdmResponseType CdmSession::Init() {
|
||||
return SESSION_INIT_GET_KEYBOX_ERROR;
|
||||
}
|
||||
|
||||
if (forced_session_id) {
|
||||
key_set_id_ = *forced_session_id;
|
||||
} else {
|
||||
bool ok = GenerateKeySetId(&key_set_id_);
|
||||
(void)ok; // ok is now used when assertions are turned off.
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
session_id_ =
|
||||
Properties::AlwaysUseKeySetIds() ? key_set_id_ : GenerateSessionId();
|
||||
|
||||
if (session_id_.empty()) {
|
||||
LOGE("CdmSession::Init: empty session ID");
|
||||
return EMPTY_SESSION_ID;
|
||||
}
|
||||
if (cdm_client_property_set)
|
||||
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
|
||||
|
||||
if (!mock_license_parser_in_use_)
|
||||
license_parser_.reset(new CdmLicense(session_id_));
|
||||
if (!mock_policy_engine_in_use_)
|
||||
policy_engine_.reset(new PolicyEngine(
|
||||
session_id_, event_listener, crypto_session_.get()));
|
||||
|
||||
if (!license_parser_->Init(token, crypto_session_.get(),
|
||||
policy_engine_.get()))
|
||||
return LICENSE_PARSER_INIT_ERROR;
|
||||
@@ -115,10 +126,6 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
const CdmKeySetId& key_set_id, const CdmLicenseType license_type) {
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return RESTORE_OFFLINE_LICENSE_ERROR_1;
|
||||
|
||||
DeviceFiles::LicenseState license_state;
|
||||
int64_t playback_start_time;
|
||||
int64_t last_playback_time;
|
||||
@@ -177,9 +184,9 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id) {
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyRequest* key_request) {
|
||||
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return INVALID_CRYPTO_SESSION_1;
|
||||
@@ -190,6 +197,11 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
return CRYPTO_SESSION_OPEN_ERROR_1;
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmSession::GenerateKeyRequest: No output destination provided");
|
||||
return INVALID_PARAMETERS_ENG_5;
|
||||
}
|
||||
|
||||
switch (license_type) {
|
||||
case kLicenseTypeTemporary:
|
||||
is_temporary_ = true;
|
||||
@@ -230,13 +242,12 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
}
|
||||
|
||||
if (is_release_) {
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeRelease;
|
||||
return GenerateReleaseRequest(key_request, server_url);
|
||||
return GenerateReleaseRequest(key_request);
|
||||
} else if (license_received_) { // renewal
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeRenewal;
|
||||
return GenerateRenewalRequest(key_request, server_url);
|
||||
return GenerateRenewalRequest(key_request);
|
||||
} else {
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeInitial;
|
||||
key_request->type = kKeyRequestTypeInitial;
|
||||
|
||||
if (!license_parser_->HasInitData()) {
|
||||
if (!init_data.is_supported()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
|
||||
@@ -248,8 +259,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
return INIT_DATA_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if (is_offline_ && key_set_id_.empty() &&
|
||||
!GenerateKeySetId(&key_set_id_)) {
|
||||
if (is_offline_ && key_set_id_.empty()) {
|
||||
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
|
||||
return KEY_REQUEST_ERROR_1;
|
||||
}
|
||||
@@ -257,24 +267,24 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
app_parameters_ = app_parameters;
|
||||
CdmResponseType status = license_parser_->PrepareKeyRequest(
|
||||
init_data, license_type,
|
||||
app_parameters, key_request, server_url);
|
||||
app_parameters, &key_request->message,
|
||||
&key_request->url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
key_request_ = *key_request;
|
||||
key_request_ = key_request->message;
|
||||
if (is_offline_) {
|
||||
offline_init_data_ = init_data.data();
|
||||
offline_release_server_url_ = *server_url;
|
||||
offline_release_server_url_ = key_request->url;
|
||||
|
||||
}
|
||||
|
||||
if (key_set_id) *key_set_id = key_set_id_;
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id) {
|
||||
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::AddKey: Invalid crypto session");
|
||||
return INVALID_CRYPTO_SESSION_2;
|
||||
@@ -303,12 +313,11 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
if (sts != NO_ERROR) return sts;
|
||||
}
|
||||
|
||||
if (key_set_id) *key_set_id = key_set_id_;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* query_response) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGE("CdmSession::QueryStatus: Invalid crypto session");
|
||||
return INVALID_CRYPTO_SESSION_3;
|
||||
@@ -321,17 +330,20 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
|
||||
switch (security_level_) {
|
||||
case kSecurityLevelL1:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
|
||||
QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
break;
|
||||
case kSecurityLevelL2:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2;
|
||||
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
|
||||
QUERY_VALUE_SECURITY_LEVEL_L2;
|
||||
break;
|
||||
case kSecurityLevelL3:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
|
||||
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3;
|
||||
break;
|
||||
case kSecurityLevelUninitialized:
|
||||
case kSecurityLevelUnknown:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] =
|
||||
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
|
||||
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
@@ -340,24 +352,30 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
|
||||
return policy_engine_->Query(key_info);
|
||||
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* query_response) {
|
||||
return policy_engine_->Query(query_response);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmSession::QueryKeyAllowedUsage(
|
||||
const std::string& key_id, CdmKeyAllowedUsage* key_usage) {
|
||||
return policy_engine_->QueryKeyAllowedUsage(key_id, key_usage);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryOemCryptoSessionId(
|
||||
CdmQueryMap* query_response) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::QueryKeyControlInfo: Invalid crypto session");
|
||||
LOGW("CdmSession::QueryOemCryptoSessionId: Invalid crypto session");
|
||||
return INVALID_CRYPTO_SESSION_4;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::QueryKeyControlInfo: Crypto session not open");
|
||||
LOGW("CdmSession::QueryOemCryptoSessionId: Crypto session not open");
|
||||
return CRYPTO_SESSION_OPEN_ERROR_4;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << crypto_session_->oec_session_id();
|
||||
(*key_info)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str();
|
||||
(*query_response)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -405,15 +423,17 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
// License renewal
|
||||
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(
|
||||
CdmKeyRequest* key_request) {
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
true, app_parameters_, key_request, server_url);
|
||||
true, app_parameters_, &key_request->message, &key_request->url);
|
||||
|
||||
key_request->type = kKeyRequestTypeRenewal;
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
if (is_offline_) {
|
||||
offline_key_renewal_request_ = *key_request;
|
||||
offline_key_renewal_request_ = key_request->message;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
@@ -432,11 +452,14 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(
|
||||
CdmKeyRequest* key_request) {
|
||||
is_release_ = true;
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
false, app_parameters_, key_request, server_url);
|
||||
false, app_parameters_, &key_request->message,
|
||||
&key_request->url);
|
||||
|
||||
key_request->type = kKeyRequestTypeRelease;
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
@@ -463,6 +486,11 @@ bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
|
||||
return license_parser_->IsKeyLoaded(key_id);
|
||||
}
|
||||
|
||||
int64_t CdmSession::GetDurationRemaining() {
|
||||
if (policy_engine_->IsLicenseForFuture()) return 0;
|
||||
return policy_engine_->GetLicenseOrPlaybackDurationRemaining();
|
||||
}
|
||||
|
||||
CdmSessionId CdmSession::GenerateSessionId() {
|
||||
static int session_num = 1;
|
||||
return SESSION_ID_PREFIX + IntToString(++session_num);
|
||||
@@ -477,8 +505,6 @@ 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;
|
||||
|
||||
while (key_set_id->empty()) {
|
||||
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0]))
|
||||
return false;
|
||||
@@ -514,13 +540,6 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to store license");
|
||||
CdmResponseType sts = Init();
|
||||
if (sts != NO_ERROR) {
|
||||
LOGW("CdmSession::StoreLicense: Reinitialization failed");
|
||||
return sts;
|
||||
}
|
||||
|
||||
key_set_id_.clear();
|
||||
return STORE_LICENSE_ERROR_1;
|
||||
}
|
||||
return NO_ERROR;
|
||||
@@ -533,11 +552,6 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
return STORE_LICENSE_ERROR_2;
|
||||
}
|
||||
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
|
||||
return STORE_LICENSE_ERROR_3;
|
||||
}
|
||||
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
|
||||
@@ -549,8 +563,6 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
}
|
||||
|
||||
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
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_,
|
||||
@@ -558,14 +570,15 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
policy_engine_->GetLastPlaybackTime(), app_parameters_);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::ReleaseCrypto() {
|
||||
crypto_session_->Close();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool CdmSession::DeleteLicense() {
|
||||
if (!is_offline_ && license_parser_->provider_session_token().empty())
|
||||
return false;
|
||||
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
if (is_offline_) {
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
} else {
|
||||
@@ -613,13 +626,55 @@ CdmResponseType CdmSession::UpdateUsageInformation() {
|
||||
return crypto_session_->UpdateUsageInformation();
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::ReleaseCrypto() {
|
||||
crypto_session_->Close();
|
||||
return NO_ERROR;
|
||||
CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer) {
|
||||
if (!out_buffer) {
|
||||
LOGE("CdmSession::GenericEncrypt: No output destination provided");
|
||||
return INVALID_PARAMETERS_ENG_6;
|
||||
}
|
||||
return crypto_session_->GenericEncrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer) {
|
||||
if (!out_buffer) {
|
||||
LOGE("CdmSession::GenericDecrypt: No output destination provided");
|
||||
return INVALID_PARAMETERS_ENG_7;
|
||||
}
|
||||
return crypto_session_->GenericDecrypt(in_buffer, key_id, iv, algorithm,
|
||||
out_buffer);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenericSign(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
std::string* signature) {
|
||||
if (!signature) {
|
||||
LOGE("CdmSession::GenericSign: No output destination provided");
|
||||
return INVALID_PARAMETERS_ENG_8;
|
||||
}
|
||||
return crypto_session_->GenericSign(message, key_id, algorithm, signature);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenericVerify(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature) {
|
||||
return crypto_session_->GenericVerify(message, key_id, algorithm, signature);
|
||||
}
|
||||
|
||||
// For testing only - takes ownership of pointers
|
||||
|
||||
void CdmSession::set_license_parser(CdmLicense* license_parser) {
|
||||
license_parser_.reset(license_parser);
|
||||
mock_license_parser_in_use_ = true;
|
||||
}
|
||||
|
||||
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
|
||||
@@ -628,6 +683,7 @@ void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
|
||||
|
||||
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {
|
||||
policy_engine_.reset(policy_engine);
|
||||
mock_policy_engine_in_use_ = true;
|
||||
}
|
||||
|
||||
void CdmSession::set_file_handle(DeviceFiles* file_handle) {
|
||||
|
||||
@@ -189,7 +189,7 @@ bool CertificateProvisioning::ParseJsonResponse(
|
||||
* Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
FileSystem* file_system, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key) {
|
||||
// Extracts signed response from JSON string, decodes base64 signed response
|
||||
const std::string kMessageStart = "\"signedResponse\": \"";
|
||||
@@ -259,12 +259,12 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
const std::string& device_certificate =
|
||||
provisioning_response.device_certificate();
|
||||
|
||||
DeviceFiles handle;
|
||||
DeviceFiles handle(file_system);
|
||||
if (!handle.Init(crypto_session_.GetSecurityLevel())) {
|
||||
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_7;
|
||||
}
|
||||
if (!handle.StoreCertificate(origin, device_certificate, wrapped_rsa_key)) {
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_8;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ CryptoSession::CryptoSession()
|
||||
update_usage_table_after_close_session_(false),
|
||||
is_destination_buffer_type_valid_(false),
|
||||
requested_security_level_(kLevelDefault),
|
||||
request_id_base_(0) {
|
||||
request_id_base_(0),
|
||||
cipher_mode_(kCipherModeCtr) {
|
||||
Init();
|
||||
}
|
||||
|
||||
@@ -55,17 +56,19 @@ void CryptoSession::Init() {
|
||||
LOGV("CryptoSession::Init");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
session_count_ += 1;
|
||||
if (initialized_) return;
|
||||
OEMCryptoResult sts = OEMCrypto_Initialize();
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("OEMCrypto_Initialize failed: %d", sts);
|
||||
return;
|
||||
if (!initialized_) {
|
||||
OEMCryptoResult sts = OEMCrypto_Initialize();
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("OEMCrypto_Initialize failed: %d", sts);
|
||||
return;
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void CryptoSession::Terminate() {
|
||||
LOGV("CryptoSession::Terminate");
|
||||
LOGE("CryptoSession::Terminate: initialized_=%d, session_count_=%d",
|
||||
initialized_, session_count_);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (session_count_ > 0) {
|
||||
session_count_ -= 1;
|
||||
@@ -233,6 +236,10 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t CryptoSession::GetSecurityPatchLevel() {
|
||||
return OEMCrypto_Security_Patch_Level(requested_security_level_);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
|
||||
LOGV("CryptoSession::Open: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
@@ -247,16 +254,15 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
|
||||
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
|
||||
open_ = true;
|
||||
} else if (OEMCrypto_ERROR_TOO_MANY_SESSIONS == sts) {
|
||||
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
|
||||
session_count_, (int)initialized_);
|
||||
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d",
|
||||
sts, session_count_, (int)initialized_);
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
}
|
||||
if (!open_) {
|
||||
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
|
||||
session_count_, (int)initialized_);
|
||||
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d",
|
||||
sts, session_count_, (int)initialized_);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
OEMCrypto_GetRandom(reinterpret_cast<uint8_t*>(&request_id_base_),
|
||||
sizeof(request_id_base_));
|
||||
++request_id_index_;
|
||||
@@ -412,6 +418,10 @@ CdmResponseType CryptoSession::LoadKeys(
|
||||
ko->key_control_iv = NULL;
|
||||
ko->key_control = NULL;
|
||||
}
|
||||
ko->cipher_mode = ki->cipher_mode() == kCipherModeCbc
|
||||
? OEMCrypto_CipherMode_CBC
|
||||
: OEMCrypto_CipherMode_CTR;
|
||||
cipher_mode_ = ki->cipher_mode();
|
||||
}
|
||||
uint8_t* pst = NULL;
|
||||
if (!provider_session_token.empty()) {
|
||||
@@ -498,12 +508,22 @@ bool CryptoSession::RefreshKeys(const std::string& message,
|
||||
}
|
||||
|
||||
bool CryptoSession::SelectKey(const std::string& key_id) {
|
||||
// Crypto session lock already locked.
|
||||
if (!cached_key_id_.empty() && cached_key_id_ == key_id) {
|
||||
// Already using the desired key.
|
||||
return true;
|
||||
}
|
||||
|
||||
cached_key_id_ = key_id;
|
||||
|
||||
const uint8_t* key_id_string =
|
||||
reinterpret_cast<const uint8_t*>(key_id.data());
|
||||
reinterpret_cast<const uint8_t*>(cached_key_id_.data());
|
||||
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_SelectKey(oec_session_id_, key_id_string, key_id.size());
|
||||
OEMCrypto_SelectKey(oec_session_id_, key_id_string,
|
||||
cached_key_id_.size());
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
cached_key_id_.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -558,78 +578,76 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
|
||||
bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
std::string* signature) {
|
||||
LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) return false;
|
||||
if (!signature) {
|
||||
LOGE("GenerateSignature: null signature string");
|
||||
return false;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts;
|
||||
size_t length = signature->size();
|
||||
OEMCryptoResult sts = OEMCrypto_GenerateSignature(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(),
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
|
||||
&length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retry with proper-sized signature buffer
|
||||
signature->resize(length);
|
||||
// At most two attempts.
|
||||
// The first attempt may fail due to buffer too short
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
sts = OEMCrypto_GenerateSignature(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(),
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
|
||||
&length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
if (OEMCrypto_SUCCESS == sts) {
|
||||
// Trim signature buffer and done
|
||||
signature->resize(length);
|
||||
return true;
|
||||
}
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Retry with proper-sized signature buffer
|
||||
signature->resize(length);
|
||||
}
|
||||
|
||||
// Trim signature buffer
|
||||
signature->resize(length);
|
||||
|
||||
return true;
|
||||
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
std::string* signature) {
|
||||
LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) return false;
|
||||
if (!signature) {
|
||||
LOGE("GenerateRsaSignature: null signature string");
|
||||
return false;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts;
|
||||
signature->resize(kRsaSignatureLength);
|
||||
size_t length = signature->size();
|
||||
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);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retry with proper-sized signature buffer
|
||||
signature->resize(length);
|
||||
// At most two attempts.
|
||||
// The first attempt may fail due to buffer too short
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
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);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
if (OEMCrypto_SUCCESS == sts) {
|
||||
// Trim signature buffer and done
|
||||
signature->resize(length);
|
||||
return true;
|
||||
}
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Retry with proper-sized signature buffer
|
||||
signature->resize(length);
|
||||
}
|
||||
|
||||
// Trim signature buffer
|
||||
signature->resize(length);
|
||||
|
||||
return true;
|
||||
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
@@ -666,27 +684,32 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (!params.is_encrypted) {
|
||||
if (!params.is_encrypted &&
|
||||
params.subsample_flags ==
|
||||
(OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)) {
|
||||
sts = OEMCrypto_CopyBuffer(requested_security_level_,
|
||||
params.encrypt_buffer, params.encrypt_length,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
}
|
||||
if (params.is_encrypted && params.cipher_mode != cipher_mode_) {
|
||||
return INCORRECT_CRYPTO_MODE;
|
||||
}
|
||||
if (params.is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern_descriptor;
|
||||
pattern_descriptor.encrypt = params.pattern_descriptor.encrypt_blocks;
|
||||
pattern_descriptor.skip = params.pattern_descriptor.skip_blocks;
|
||||
pattern_descriptor.offset = params.pattern_descriptor.offset_blocks;
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
// Check if key needs to be selected
|
||||
if (params.is_encrypted) {
|
||||
if (key_id_ != *params.key_id) {
|
||||
if (SelectKey(*params.key_id)) {
|
||||
key_id_ = *params.key_id;
|
||||
} else {
|
||||
return NEED_KEY;
|
||||
}
|
||||
if (!SelectKey(*params.key_id)) {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
sts = OEMCrypto_DecryptCTR(
|
||||
sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
&buffer_descriptor, &pattern_descriptor, params.subsample_flags);
|
||||
}
|
||||
|
||||
switch (sts) {
|
||||
@@ -696,6 +719,13 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
case OEMCrypto_ERROR_KEY_EXPIRED:
|
||||
return NEED_KEY;
|
||||
case OEMCrypto_ERROR_INVALID_SESSION:
|
||||
return SESSION_NOT_FOUND_FOR_DECRYPT;
|
||||
case OEMCrypto_ERROR_DECRYPT_FAILED:
|
||||
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
|
||||
return DECRYPT_ERROR;
|
||||
case OEMCrypto_ERROR_INSUFFICIENT_HDCP:
|
||||
return INSUFFICIENT_OUTPUT_PROTECTION;
|
||||
default:
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -1076,4 +1106,200 @@ bool CryptoSession::GetMaxNumberOfSessions(size_t* max) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenericEncrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer) {
|
||||
LOGV("GenericEncrypt: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!out_buffer) return INVALID_PARAMETERS_ENG_9;
|
||||
|
||||
OEMCrypto_Algorithm oec_algorithm = GenericEncryptionAlgorithm(algorithm);
|
||||
if (iv.size() != GenericEncryptionBlockSize(algorithm) ||
|
||||
oec_algorithm == kInvalidAlgorithm) {
|
||||
return INVALID_PARAMETERS_ENG_13;
|
||||
}
|
||||
|
||||
if (out_buffer->size() < in_buffer.size()) {
|
||||
out_buffer->resize(in_buffer.size());
|
||||
}
|
||||
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!SelectKey(key_id)) {
|
||||
return KEY_ERROR_1;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_Generic_Encrypt(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(in_buffer.data()),
|
||||
in_buffer.size(), reinterpret_cast<const uint8_t*>(iv.data()),
|
||||
oec_algorithm,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(out_buffer->data())));
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("GenericEncrypt: OEMCrypto_Generic_Encrypt err=%d", sts);
|
||||
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
|
||||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
|
||||
return KEY_NOT_FOUND_3;
|
||||
} else {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenericDecrypt(const std::string& in_buffer,
|
||||
const std::string& key_id,
|
||||
const std::string& iv,
|
||||
CdmEncryptionAlgorithm algorithm,
|
||||
std::string* out_buffer) {
|
||||
LOGV("GenericDecrypt: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!out_buffer) return INVALID_PARAMETERS_ENG_10;
|
||||
|
||||
OEMCrypto_Algorithm oec_algorithm = GenericEncryptionAlgorithm(algorithm);
|
||||
if (iv.size() != GenericEncryptionBlockSize(algorithm) ||
|
||||
oec_algorithm == kInvalidAlgorithm) {
|
||||
return INVALID_PARAMETERS_ENG_14;
|
||||
}
|
||||
|
||||
if (out_buffer->size() < in_buffer.size()) {
|
||||
out_buffer->resize(in_buffer.size());
|
||||
}
|
||||
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!SelectKey(key_id)) {
|
||||
return KEY_ERROR_2;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_Generic_Decrypt(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(in_buffer.data()),
|
||||
in_buffer.size(), reinterpret_cast<const uint8_t*>(iv.data()),
|
||||
oec_algorithm,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(out_buffer->data())));
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("GenericDecrypt: OEMCrypto_Generic_Decrypt err=%d", sts);
|
||||
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
|
||||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
|
||||
return KEY_NOT_FOUND_4;
|
||||
} else {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenericSign(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
std::string* signature) {
|
||||
LOGV("GenericSign: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) {
|
||||
LOGE("GenerateSign: null signature string");
|
||||
return INVALID_PARAMETERS_ENG_11;
|
||||
}
|
||||
|
||||
OEMCrypto_Algorithm oec_algorithm = GenericSigningAlgorithm(algorithm);
|
||||
if (oec_algorithm == kInvalidAlgorithm) {
|
||||
return INVALID_PARAMETERS_ENG_15;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts;
|
||||
size_t length = signature->size();
|
||||
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!SelectKey(key_id)) {
|
||||
return KEY_ERROR_3;
|
||||
}
|
||||
|
||||
// At most two attempts.
|
||||
// The first attempt may fail due to buffer too short
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
sts = OEMCrypto_Generic_Sign(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(), oec_algorithm,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
|
||||
&length);
|
||||
|
||||
if (OEMCrypto_SUCCESS == sts) {
|
||||
// Trim signature buffer and done
|
||||
signature->resize(length);
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Retry with proper-sized return buffer
|
||||
signature->resize(length);
|
||||
}
|
||||
|
||||
LOGE("GenericSign: OEMCrypto_Generic_Sign err=%d", sts);
|
||||
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
|
||||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
|
||||
return KEY_NOT_FOUND_5;
|
||||
} else {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenericVerify(const std::string& message,
|
||||
const std::string& key_id,
|
||||
CdmSigningAlgorithm algorithm,
|
||||
const std::string& signature) {
|
||||
LOGV("GenericVerify: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
OEMCrypto_Algorithm oec_algorithm = GenericSigningAlgorithm(algorithm);
|
||||
if (oec_algorithm == kInvalidAlgorithm) {
|
||||
return INVALID_PARAMETERS_ENG_16;
|
||||
}
|
||||
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!SelectKey(key_id)) {
|
||||
return KEY_ERROR_4;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_Generic_Verify(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(), oec_algorithm,
|
||||
reinterpret_cast<const uint8_t*>(signature.data()), signature.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGE("GenericVerify: OEMCrypto_Generic_Verify err=%d", sts);
|
||||
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
|
||||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
|
||||
return KEY_NOT_FOUND_6;
|
||||
} else {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
OEMCrypto_Algorithm CryptoSession::GenericSigningAlgorithm(
|
||||
CdmSigningAlgorithm algorithm) {
|
||||
if (kSigningAlgorithmHmacSha256 == algorithm) {
|
||||
return OEMCrypto_HMAC_SHA256;
|
||||
} else {
|
||||
return kInvalidAlgorithm;
|
||||
}
|
||||
}
|
||||
|
||||
OEMCrypto_Algorithm CryptoSession::GenericEncryptionAlgorithm(
|
||||
CdmEncryptionAlgorithm algorithm) {
|
||||
if (kEncryptionAlgorithmAesCbc128 == algorithm) {
|
||||
return OEMCrypto_AES_CBC_128_NO_PADDING;
|
||||
} else {
|
||||
return kInvalidAlgorithm;
|
||||
}
|
||||
}
|
||||
|
||||
size_t CryptoSession::GenericEncryptionBlockSize(
|
||||
CdmEncryptionAlgorithm algorithm) {
|
||||
if (kEncryptionAlgorithmAesCbc128 == algorithm) {
|
||||
return kAes128BlockSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -18,13 +18,16 @@
|
||||
#define MD5 CC_MD5
|
||||
#define MD5_DIGEST_LENGTH CC_MD5_DIGEST_LENGTH
|
||||
#else
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
using video_widevine_client::sdk::HashedFile;
|
||||
using video_widevine_client::sdk::HlsAttributes;
|
||||
using video_widevine_client::sdk::HlsAttributes_Method_AES_128;
|
||||
using video_widevine_client::sdk::HlsAttributes_Method_SAMPLE_AES;
|
||||
using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
@@ -34,19 +37,13 @@ using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kCertificateFileNamePrefix[] = "cert";
|
||||
const char kCertificateFileNameExt[] = ".bin";
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kHlsAttributesFileNameExt[] = ".hal";
|
||||
const char kUsageInfoFileNamePrefix[] = "usage";
|
||||
const char kUsageInfoFileNameExt[] = ".bin";
|
||||
const char kLicenseFileNameExt[] = ".lic";
|
||||
const char kEmptyFileName[] = "";
|
||||
const char kWildcard[] = "*";
|
||||
const char kDirectoryDelimiter = '/';
|
||||
const char* kSecurityLevelPathCompatibilityExclusionList[] = {
|
||||
"ay64.dat", "ay64.dat2", "ay64.dat3"};
|
||||
size_t kSecurityLevelPathCompatibilityExclusionListSize =
|
||||
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
|
||||
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
|
||||
|
||||
bool Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash) return false;
|
||||
@@ -66,30 +63,30 @@ namespace wvcdm {
|
||||
// static
|
||||
std::set<std::string> DeviceFiles::reserved_license_ids_;
|
||||
|
||||
DeviceFiles::DeviceFiles()
|
||||
: file_(NULL),
|
||||
DeviceFiles::DeviceFiles(FileSystem* file_system)
|
||||
: file_system_(file_system),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
initialized_(false),
|
||||
test_file_(false) {}
|
||||
initialized_(false) {}
|
||||
|
||||
DeviceFiles::~DeviceFiles() {
|
||||
if (test_file_) file_.release();
|
||||
}
|
||||
DeviceFiles::~DeviceFiles() {}
|
||||
|
||||
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
||||
if (!file_system_) {
|
||||
LOGD("DeviceFiles::Init: Invalid FileSystem given.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level, &path)) {
|
||||
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
|
||||
return false;
|
||||
}
|
||||
if (!test_file_) file_.reset(new File());
|
||||
security_level_ = security_level;
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreCertificate(const std::string& origin,
|
||||
const std::string& certificate,
|
||||
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
const std::string& wrapped_private_key) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreCertificate: not initialized");
|
||||
@@ -109,23 +106,18 @@ bool DeviceFiles::StoreCertificate(const std::string& origin,
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
return StoreFileWithHash(GetCertificateFileName(origin), serialized_file);
|
||||
return StoreFileWithHash(GetCertificateFileName(), serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(const std::string& origin,
|
||||
std::string* certificate,
|
||||
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
std::string* wrapped_private_key) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Properties::security_level_path_backward_compatibility_support()) {
|
||||
SecurityLevelPathBackwardCompatibility();
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveHashedFile(GetCertificateFileName(origin), &file)) {
|
||||
if (!RetrieveHashedFile(GetCertificateFileName(), &file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -151,22 +143,22 @@ bool DeviceFiles::RetrieveCertificate(const std::string& origin,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::HasCertificate(const std::string& origin) {
|
||||
bool DeviceFiles::HasCertificate() {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::HasCertificate: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return FileExists(GetCertificateFileName(origin));
|
||||
return FileExists(GetCertificateFileName());
|
||||
}
|
||||
|
||||
bool DeviceFiles::RemoveCertificate(const std::string& origin) {
|
||||
bool DeviceFiles::RemoveCertificate() {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RemoveCertificate: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return RemoveFile(GetCertificateFileName(origin));
|
||||
return RemoveFile(GetCertificateFileName());
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(
|
||||
@@ -534,13 +526,105 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
|
||||
bool DeviceFiles::StoreHlsAttributes(
|
||||
const std::string& key_set_id, const CdmHlsMethod method,
|
||||
const std::vector<uint8_t>& media_segment_iv) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreHlsAttributes: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill in file information
|
||||
video_widevine_client::sdk::File file;
|
||||
|
||||
file.set_type(video_widevine_client::sdk::File::HLS_ATTRIBUTES);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
|
||||
HlsAttributes* hls_attributes = file.mutable_hls_attributes();
|
||||
switch (method) {
|
||||
case kHlsMethodAes128:
|
||||
hls_attributes->set_method(HlsAttributes_Method_AES_128);
|
||||
break;
|
||||
case kHlsMethodSampleAes:
|
||||
hls_attributes->set_method(HlsAttributes_Method_SAMPLE_AES);
|
||||
break;
|
||||
case kHlsMethodNone:
|
||||
default:
|
||||
LOGW("DeviceFiles::StoreHlsAttributeInfo: Unknown HLS method: %u",
|
||||
method);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
hls_attributes->set_media_segment_iv(&media_segment_iv[0],
|
||||
media_segment_iv.size());
|
||||
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
return StoreFileWithHash(key_set_id + kHlsAttributesFileNameExt,
|
||||
serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveHlsAttributes(
|
||||
const std::string& key_set_id, CdmHlsMethod* method,
|
||||
std::vector<uint8_t>* media_segment_iv) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveHlsAttributes: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveHashedFile(key_set_id + kHlsAttributesFileNameExt, &file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.type() != video_widevine_client::sdk::File::HLS_ATTRIBUTES) {
|
||||
LOGW("DeviceFiles::RetrieveHlsAttributes: Incorrect file type: %u",
|
||||
file.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
|
||||
LOGW("DeviceFiles::RetrieveHlsAttributes: Incorrect file version: %u",
|
||||
file.version());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file.has_hls_attributes()) {
|
||||
LOGW("DeviceFiles::RetrieveHlsAttributes: HLS attributes not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
HlsAttributes attributes = file.hls_attributes();
|
||||
|
||||
switch (attributes.method()) {
|
||||
case HlsAttributes_Method_AES_128:
|
||||
*method = kHlsMethodAes128;
|
||||
break;
|
||||
case HlsAttributes_Method_SAMPLE_AES:
|
||||
*method = kHlsMethodSampleAes;
|
||||
break;
|
||||
default:
|
||||
LOGW("DeviceFiles::RetrieveHlsAttributes: Unrecognized HLS method: %u",
|
||||
attributes.method());
|
||||
*method = kHlsMethodNone;
|
||||
break;
|
||||
}
|
||||
media_segment_iv->assign(attributes.media_segment_iv().begin(),
|
||||
attributes.media_segment_iv().end());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteHlsAttributes: not initialized");
|
||||
return false;
|
||||
}
|
||||
return RemoveFile(key_set_id + kHlsAttributesFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
// calculate SHA hash
|
||||
std::string hash;
|
||||
if (!Hash(serialized_file, &hash)) {
|
||||
@@ -561,30 +645,23 @@ bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
||||
|
||||
bool DeviceFiles::StoreFileRaw(const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_->IsDirectory(path)) {
|
||||
if (!file_->CreateDirectory(path)) return false;
|
||||
}
|
||||
|
||||
path += name;
|
||||
|
||||
if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) {
|
||||
File* file =
|
||||
file_system_->Open(path, FileSystem::kCreate | FileSystem::kTruncate);
|
||||
if (!file) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: File open failed: %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t bytes = file_->Write(serialized_file.data(), serialized_file.size());
|
||||
file_->Close();
|
||||
ssize_t bytes = file->Write(serialized_file.data(), serialized_file.size());
|
||||
file->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(serialized_file.size())) {
|
||||
LOGW(
|
||||
@@ -599,16 +676,12 @@ bool DeviceFiles::StoreFileRaw(const std::string& name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveHashedFile(const std::string& name,
|
||||
video_widevine_client::sdk::File* file) {
|
||||
bool DeviceFiles::RetrieveHashedFile(
|
||||
const std::string& name,
|
||||
video_widevine_client::sdk::File* deserialized_file) {
|
||||
std::string serialized_file;
|
||||
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
if (!deserialized_file) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file parameter");
|
||||
return false;
|
||||
}
|
||||
@@ -621,29 +694,30 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name,
|
||||
|
||||
path += name;
|
||||
|
||||
if (!file_->Exists(path)) {
|
||||
if (!file_system_->Exists(path)) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: %s does not exist", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t bytes = file_->FileSize(path);
|
||||
ssize_t bytes = file_system_->FileSize(path);
|
||||
if (bytes <= 0) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s",
|
||||
path.c_str());
|
||||
// Remove the corrupted file so the caller will not get the same error
|
||||
// when trying to access the file repeatedly, causing the system to stall.
|
||||
file_->Remove(path);
|
||||
file_system_->Remove(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_->Open(path, File::kReadOnly | File::kBinary)) {
|
||||
File* file = file_system_->Open(path, FileSystem::kReadOnly);
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_hash_file;
|
||||
serialized_hash_file.resize(bytes);
|
||||
bytes = file_->Read(&serialized_hash_file[0], serialized_hash_file.size());
|
||||
file_->Close();
|
||||
bytes = file->Read(&serialized_hash_file[0], serialized_hash_file.size());
|
||||
file->Close();
|
||||
|
||||
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: read failed");
|
||||
@@ -669,11 +743,11 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name,
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
|
||||
// Remove the corrupted file so the caller will not get the same error
|
||||
// when trying to access the file repeatedly, causing the system to stall.
|
||||
file_->Remove(path);
|
||||
file_system_->Remove(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file->ParseFromString(hash_file.file())) {
|
||||
if (!deserialized_file->ParseFromString(hash_file.file())) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
@@ -681,11 +755,6 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name,
|
||||
}
|
||||
|
||||
bool DeviceFiles::FileExists(const std::string& name) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::FileExists: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::FileExists: Unable to get base path");
|
||||
@@ -693,15 +762,10 @@ bool DeviceFiles::FileExists(const std::string& name) {
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->Exists(path);
|
||||
return file_system_->Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RemoveFile(const std::string& name) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::RemoveFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
|
||||
@@ -709,15 +773,10 @@ bool DeviceFiles::RemoveFile(const std::string& name) {
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->Remove(path);
|
||||
return file_system_->Remove(path);
|
||||
}
|
||||
|
||||
ssize_t DeviceFiles::GetFileSize(const std::string& name) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::GetFileSize: Invalid file handle");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::GetFileSize: Unable to get base path");
|
||||
@@ -725,79 +784,15 @@ ssize_t DeviceFiles::GetFileSize(const std::string& name) {
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->FileSize(path);
|
||||
return file_system_->FileSize(path);
|
||||
}
|
||||
|
||||
void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW(
|
||||
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
|
||||
"get base path");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> security_dirs;
|
||||
if (!Properties::GetSecurityLevelDirectories(&security_dirs)) {
|
||||
LOGW(
|
||||
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
|
||||
"get security directories");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t pos = std::string::npos;
|
||||
for (size_t i = 0; i < security_dirs.size(); ++i) {
|
||||
pos = path.find(security_dirs[i]);
|
||||
if (pos != std::string::npos && pos > 0 &&
|
||||
pos == path.size() - security_dirs[i].size() &&
|
||||
path[pos - 1] == kDirectoryDelimiter) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
LOGV(
|
||||
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level "
|
||||
"specific path not found. Check properties?");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string from_dir(path, 0, pos);
|
||||
|
||||
std::vector<std::string> files;
|
||||
if (!file_->List(from_dir, &files)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
std::string from = from_dir + files[i];
|
||||
bool exclude = false;
|
||||
for (size_t j = 0; j < kSecurityLevelPathCompatibilityExclusionListSize;
|
||||
++j) {
|
||||
if (files[i] == kSecurityLevelPathCompatibilityExclusionList[j]) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exclude) continue;
|
||||
if (!file_->IsRegularFile(from)) continue;
|
||||
|
||||
for (size_t j = 0; j < security_dirs.size(); ++j) {
|
||||
std::string to_dir = from_dir + security_dirs[j];
|
||||
if (!file_->Exists(to_dir)) file_->CreateDirectory(to_dir);
|
||||
std::string to = to_dir + files[i];
|
||||
file_->Copy(from, to);
|
||||
}
|
||||
file_->Remove(from);
|
||||
}
|
||||
std::string DeviceFiles::GetCertificateFileName() {
|
||||
return kCertificateFileName;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetCertificateFileName(const std::string& origin) {
|
||||
std::string hash;
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
hash = GetFileNameSafeHash(origin);
|
||||
}
|
||||
return kCertificateFileNamePrefix + hash + kCertificateFileNameExt;
|
||||
std::string DeviceFiles::GetHlsAttributesFileNameExtension() {
|
||||
return kHlsAttributesFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetLicenseFileNameExtension() {
|
||||
@@ -820,9 +815,4 @@ std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
|
||||
return wvcdm::Base64SafeEncode(hash);
|
||||
}
|
||||
|
||||
void DeviceFiles::SetTestFile(File* file) {
|
||||
file_.reset(file);
|
||||
test_file_ = true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -52,11 +52,21 @@ message UsageInfo {
|
||||
repeated ProviderSession sessions = 1;
|
||||
}
|
||||
|
||||
message HlsAttributes {
|
||||
enum Method {
|
||||
AES_128 = 1;
|
||||
SAMPLE_AES = 2;
|
||||
}
|
||||
optional Method method = 1;
|
||||
optional bytes media_segment_iv = 2;
|
||||
}
|
||||
|
||||
message File {
|
||||
enum FileType {
|
||||
DEVICE_CERTIFICATE = 1;
|
||||
LICENSE = 2;
|
||||
USAGE_INFO = 3;
|
||||
HLS_ATTRIBUTES = 4;
|
||||
}
|
||||
|
||||
enum FileVersion {
|
||||
@@ -68,6 +78,7 @@ message File {
|
||||
optional DeviceCertificate device_certificate = 3;
|
||||
optional License license = 4;
|
||||
optional UsageInfo usage_info = 5;
|
||||
optional HlsAttributes hls_attributes = 6;
|
||||
}
|
||||
|
||||
message HashedFile {
|
||||
|
||||
@@ -2,31 +2,70 @@
|
||||
|
||||
#include "initialization_data.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "buffer_reader.h"
|
||||
#include "jsmn.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
const char kKeyFormatVersionsSeparator = '/';
|
||||
const char kColon = ':';
|
||||
const char kDoubleQuote = '\"';
|
||||
const char kLeftBracket = '[';
|
||||
const char kRightBracket = ']';
|
||||
const std::string kBase64String = "base64,";
|
||||
const uint32_t kFourCcCbc1 = 0x63626331;
|
||||
const uint32_t kFourCcCbcs = 0x63626373;
|
||||
|
||||
// json init data key values
|
||||
const std::string kProvider = "provider";
|
||||
const std::string kContentId = "content_id";
|
||||
const std::string kKeyIds = "key_ids";
|
||||
|
||||
// Being conservative, usually we expect 6 + number of Key Ids
|
||||
const int kDefaultNumJsonTokens = 128;
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::WidevineCencHeader;
|
||||
using video_widevine_server::sdk::WidevineCencHeader_Algorithm;
|
||||
using video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR;
|
||||
|
||||
InitializationData::InitializationData(const std::string& type,
|
||||
const CdmInitData& data)
|
||||
: type_(type), is_cenc_(false), is_webm_(false) {
|
||||
: type_(type),
|
||||
is_cenc_(false),
|
||||
is_hls_(false),
|
||||
is_webm_(false),
|
||||
hls_method_(kHlsMethodNone) {
|
||||
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 ||
|
||||
type == WEBM_INIT_DATA_FORMAT) {
|
||||
is_webm_ = true;
|
||||
} else if (type == HLS_INIT_DATA_FORMAT) {
|
||||
is_hls_ = true;
|
||||
}
|
||||
|
||||
if (is_supported()) {
|
||||
if (is_cenc()) {
|
||||
ExtractWidevinePssh(data, &data_);
|
||||
} else {
|
||||
} else if (is_webm()) {
|
||||
data_ = data;
|
||||
} else if (is_hls()) {
|
||||
std::string uri;
|
||||
if (ExtractHlsAttributes(data, &hls_method_, &hls_iv_, &uri)) {
|
||||
ConstructWidevineInitData(hls_method_, uri, &data_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,19 +100,22 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// atom size, used for skipping.
|
||||
uint64_t size;
|
||||
if (!reader.Read4Into8(&size)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read atom size.");
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> atom_type;
|
||||
if (!reader.ReadVec(&atom_type, 4)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read atom type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
if (!reader.Read8(&size)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
|
||||
"size.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read 64-bit "
|
||||
"atom size.");
|
||||
return false;
|
||||
}
|
||||
} else if (size == 0) {
|
||||
@@ -82,10 +124,12 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
|
||||
// "pssh"
|
||||
if (memcmp(&atom_type[0], "pssh", 4)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: PSSH literal not present.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: PSSH literal not present.");
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
||||
"the atom.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -94,15 +138,18 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// version
|
||||
uint8_t version;
|
||||
if (!reader.Read1(&version)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH "
|
||||
"version.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version > 1) {
|
||||
// unrecognized version - skip.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
||||
"the atom.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -110,25 +157,31 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
|
||||
// flags
|
||||
if (!reader.SkipBytes(3)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the PSSH "
|
||||
"flags.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// system id
|
||||
std::vector<uint8_t> system_id;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read system ID.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read system ID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
|
||||
// skip non-Widevine PSSH boxes.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
|
||||
"the atom.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
|
||||
"of the atom.");
|
||||
return false;
|
||||
}
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Skipping non-Widevine PSSH.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Skipping non-Widevine "
|
||||
"PSSH.");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -136,11 +189,14 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// v1 has additional fields for key IDs. We can skip them.
|
||||
uint32_t num_key_ids;
|
||||
if (!reader.Read4(&num_key_ids)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read num key IDs.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read num key "
|
||||
"IDs.");
|
||||
return false;
|
||||
}
|
||||
if (!reader.SkipBytes(num_key_ids * 16)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip key IDs.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to skip key IDs.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -148,13 +204,16 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
// size of PSSH data
|
||||
uint32_t data_length;
|
||||
if (!reader.Read4(&data_length)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data size.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data "
|
||||
"size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, data_length)) {
|
||||
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||
LOGV(
|
||||
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -165,4 +224,357 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse an EXT-X-KEY tag attribute list. Verify that Widevine supports it
|
||||
// by validating KEYFORMAT and KEYFORMATVERSION attributes. Extract out
|
||||
// method, IV, URI and WV init data.
|
||||
//
|
||||
// An example of a widevine supported attribute list from an HLS media playlist
|
||||
// is,
|
||||
// "EXT-X-KEY: METHOD=SAMPLE-AES, \"
|
||||
// "URI=”data:text/plain;base64,eyANCiAgICJwcm92aWRlciI6Im1sYmFtaGJvIiwNCiAg"
|
||||
// "ICJjb250ZW50X2lkIjoiMjAxNV9UZWFycyIsDQogICAia2V5X2lkcyI6DQogICBbDQo"
|
||||
// "gICAgICAiMzcxZTEzNWUxYTk4NWQ3NWQxOThhN2Y0MTAyMGRjMjMiDQogICBdDQp9DQ"
|
||||
// "o=, \"
|
||||
// "IV=0x6df49213a781e338628d0e9c812d328e, \"
|
||||
// "KEYFORMAT=”com.widevine”, \"
|
||||
// "KEYFORMATVERSIONS=”1”"
|
||||
bool InitializationData::ExtractHlsAttributes(const std::string& attribute_list,
|
||||
CdmHlsMethod* method,
|
||||
std::vector<uint8_t>* iv,
|
||||
std::string* uri) {
|
||||
std::string value;
|
||||
if (!ExtractQuotedAttribute(attribute_list, HLS_KEYFORMAT_ATTRIBUTE,
|
||||
&value)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: Unable to read HLS "
|
||||
"keyformat value");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.compare(0, sizeof(KEY_SYSTEM) - 1, KEY_SYSTEM) != 0) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: unrecognized HLS "
|
||||
"keyformat value: %s",
|
||||
value.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// KEYFORMATVERSIONS is an optional parameter. If absent its
|
||||
// value defaults to "1"
|
||||
if (ExtractQuotedAttribute(attribute_list, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
|
||||
&value)) {
|
||||
std::vector<std::string> versions = ExtractKeyFormatVersions(value);
|
||||
bool supported = false;
|
||||
for (size_t i = 0; i < versions.size(); ++i) {
|
||||
if (versions[i].compare(HLS_KEYFORMAT_VERSION_VALUE_1) == 0) {
|
||||
supported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!supported) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS keyformat "
|
||||
"version is not supported: %s",
|
||||
value.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ExtractAttribute(attribute_list, HLS_METHOD_ATTRIBUTE, &value)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: Unable to read HLS "
|
||||
"method");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.compare(HLS_METHOD_AES_128) == 0) {
|
||||
*method = kHlsMethodAes128;
|
||||
} else if (value.compare(HLS_METHOD_SAMPLE_AES) == 0) {
|
||||
*method = kHlsMethodSampleAes;
|
||||
} else if (value.compare(HLS_METHOD_NONE) == 0) {
|
||||
*method = kHlsMethodNone;
|
||||
} else {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS method "
|
||||
"unrecognized: %s",
|
||||
value.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExtractHexAttribute(attribute_list, HLS_IV_ATTRIBUTE, iv)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS IV attribute "
|
||||
"not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExtractQuotedAttribute(attribute_list, HLS_URI_ATTRIBUTE, uri)) {
|
||||
LOGV(
|
||||
"InitializationData::ExtractHlsInitDataAtttribute: HLS URI attribute "
|
||||
"not present");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extracts a base64 encoded string from URI data. This is then base64 decoded
|
||||
// and the Json formatted init data is then parsed. The information is used
|
||||
// to generate a Widevine init data protobuf (WidevineCencHeader).
|
||||
//
|
||||
// An example of a widevine supported json formatted init data string is,
|
||||
//
|
||||
// {
|
||||
// "provider":"mlbamhbo",
|
||||
// "content_id":"MjAxNV9UZWFycw==",
|
||||
// "key_ids":
|
||||
// [
|
||||
// "371e135e1a985d75d198a7f41020dc23"
|
||||
// ]
|
||||
// }
|
||||
bool InitializationData::ConstructWidevineInitData(
|
||||
CdmHlsMethod method, const std::string& uri, CdmInitData* init_data_proto) {
|
||||
if (!init_data_proto) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: Invalid parameter");
|
||||
return false;
|
||||
}
|
||||
if (method != kHlsMethodAes128 && method != kHlsMethodSampleAes) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: Invalid method"
|
||||
" parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t pos = uri.find(kBase64String);
|
||||
if (pos == std::string::npos) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: URI attribute "
|
||||
"unexpected format: %s",
|
||||
uri.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> json_init_data =
|
||||
Base64Decode(uri.substr(pos + kBase64String.size()));
|
||||
if (json_init_data.size() == 0) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Base64 decode of json "
|
||||
"data failed");
|
||||
return false;
|
||||
}
|
||||
std::string json_string((const char*)(&json_init_data[0]),
|
||||
json_init_data.size());
|
||||
|
||||
// Parse the Json string using jsmn
|
||||
jsmn_parser parser;
|
||||
jsmntok_t tokens[kDefaultNumJsonTokens];
|
||||
jsmn_init(&parser);
|
||||
int num_of_tokens =
|
||||
jsmn_parse(&parser, json_string.c_str(), json_string.size(), tokens,
|
||||
kDefaultNumJsonTokens);
|
||||
|
||||
if (num_of_tokens <= 0) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Json parsing failed: "
|
||||
"%d",
|
||||
num_of_tokens);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string provider;
|
||||
std::string content_id;
|
||||
std::vector<std::string> key_ids;
|
||||
|
||||
enum JsmnParserState {
|
||||
kParseState,
|
||||
kProviderState,
|
||||
kContentIdState,
|
||||
kKeyIdsState,
|
||||
} state = kParseState;
|
||||
|
||||
int number_of_key_ids = 0;
|
||||
|
||||
// Extract the provider, content_id and key_ids
|
||||
for (int i = 0; i < num_of_tokens; ++i) {
|
||||
if (tokens[i].start < 0 || tokens[i].end < 0) {
|
||||
LOGV(
|
||||
"InitializationData::ConstructWidevineInitData: Invalid start or end "
|
||||
"of token");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case kParseState:
|
||||
if (tokens[i].type == JSMN_STRING) {
|
||||
std::string token(json_string, tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
if (token == kProvider) {
|
||||
state = kProviderState;
|
||||
} else if (token == kContentId) {
|
||||
state = kContentIdState;
|
||||
} else if (token == kKeyIds) {
|
||||
state = kKeyIdsState;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kProviderState:
|
||||
if (tokens[i].type == JSMN_STRING) {
|
||||
provider.assign(json_string, tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
}
|
||||
state = kParseState;
|
||||
break;
|
||||
case kContentIdState:
|
||||
if (tokens[i].type == JSMN_STRING) {
|
||||
std::string base64_content_id(json_string, tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
std::vector<uint8_t> content_id_data =
|
||||
Base64Decode(base64_content_id);
|
||||
content_id.assign(reinterpret_cast<const char*>(&content_id_data[0]),
|
||||
content_id_data.size());
|
||||
}
|
||||
state = kParseState;
|
||||
break;
|
||||
case kKeyIdsState:
|
||||
if (tokens[i].type == JSMN_ARRAY) {
|
||||
number_of_key_ids = tokens[i].size;
|
||||
} else if (tokens[i].type == JSMN_STRING) {
|
||||
std::string key_id(a2bs_hex(json_string.substr(
|
||||
tokens[i].start, tokens[i].end - tokens[i].start)));
|
||||
if (key_id.size() == 16) key_ids.push_back(key_id);
|
||||
--number_of_key_ids;
|
||||
} else {
|
||||
state = kParseState;
|
||||
}
|
||||
if (number_of_key_ids <= 0) state = kParseState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (provider.size() == 0) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: Invalid provider");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (content_id.size() == 0) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: Invalid content_id");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key_ids.size() == 0) {
|
||||
LOGV("InitializationData::ConstructWidevineInitData: No key_ids present");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now format as Widevine init data protobuf
|
||||
WidevineCencHeader cenc_header;
|
||||
// TODO(rfrias): The algorithm is a deprecated field, but proto changes
|
||||
// have not yet been pushed to production. Set until then.
|
||||
cenc_header.set_algorithm(WidevineCencHeader_Algorithm_AESCTR);
|
||||
for (size_t i = 0; i < key_ids.size(); ++i) {
|
||||
cenc_header.add_key_id(key_ids[i]);
|
||||
}
|
||||
cenc_header.set_provider(provider);
|
||||
cenc_header.set_content_id(content_id);
|
||||
if (method == kHlsMethodAes128)
|
||||
cenc_header.set_protection_scheme(htonl(kFourCcCbc1));
|
||||
else
|
||||
cenc_header.set_protection_scheme(htonl(kFourCcCbcs));
|
||||
cenc_header.SerializeToString(init_data_proto);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitializationData::ExtractQuotedAttribute(
|
||||
const std::string& attribute_list, const std::string& key,
|
||||
std::string* value) {
|
||||
bool result = ExtractAttribute(attribute_list, key, value);
|
||||
if (value->size() < 2 || value->at(0) != '\"' ||
|
||||
value->at(value->size() - 1) != '\"')
|
||||
return false;
|
||||
*value = value->substr(1, value->size() - 2);
|
||||
if (value->find('\"') != std::string::npos) return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InitializationData::ExtractHexAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::vector<uint8_t>* value) {
|
||||
std::string val;
|
||||
bool result = ExtractAttribute(attribute_list, key, &val);
|
||||
if (!result || val.size() <= 2 || val.size() % 2 != 0 || val[0] != '0' ||
|
||||
((val[1] != 'x') && (val[1] != 'X')))
|
||||
return false;
|
||||
for (size_t i = 2; i < val.size(); ++i) {
|
||||
if (!isxdigit(val[i])) return false;
|
||||
}
|
||||
*value = a2b_hex(val.substr(2, val.size() - 2));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InitializationData::ExtractAttribute(const std::string& attribute_list,
|
||||
const std::string& key,
|
||||
std::string* value) {
|
||||
// validate the key
|
||||
for (size_t i = 0; i < key.size(); ++i)
|
||||
if (!isupper(key[i]) && !isdigit(key[i]) && key[i] != '-') return false;
|
||||
|
||||
bool found = false;
|
||||
size_t pos = 0;
|
||||
// Find the key followed by '='
|
||||
while (!found) {
|
||||
pos = attribute_list.find(key, pos);
|
||||
if (pos == std::string::npos) return false;
|
||||
pos += key.size();
|
||||
if (attribute_list[pos] != '=') continue;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (attribute_list.size() <= ++pos) return false;
|
||||
|
||||
size_t end_pos = pos;
|
||||
found = false;
|
||||
// Find the next comma outside the quote or end of string
|
||||
while (!found) {
|
||||
end_pos = attribute_list.find(',', end_pos);
|
||||
if (end_pos != std::string::npos && attribute_list[pos] == '\"' &&
|
||||
attribute_list[end_pos - 1] != '\"') {
|
||||
++end_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (end_pos == std::string::npos)
|
||||
end_pos = attribute_list.size() - 1;
|
||||
else
|
||||
--end_pos;
|
||||
found = true;
|
||||
}
|
||||
|
||||
*value = attribute_list.substr(pos, end_pos - pos + 1);
|
||||
|
||||
// validate the value
|
||||
for (size_t i = 0; i < value->size(); ++i)
|
||||
if (!isgraph(value->at(i))) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Key format versions are individual values or multiple versions
|
||||
// separated by '/'. "1" or "1/2/5"
|
||||
std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
|
||||
const std::string& key_format_versions) {
|
||||
std::vector<std::string> versions;
|
||||
size_t pos = 0;
|
||||
while (pos < key_format_versions.size()) {
|
||||
size_t next_pos =
|
||||
key_format_versions.find(kKeyFormatVersionsSeparator, pos);
|
||||
if (next_pos == std::string::npos) {
|
||||
versions.push_back(key_format_versions.substr(pos));
|
||||
break;
|
||||
} else {
|
||||
versions.push_back(key_format_versions.substr(pos, next_pos - pos));
|
||||
pos = next_pos + 1;
|
||||
}
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "license.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "clock.h"
|
||||
@@ -24,6 +26,7 @@ std::string kProductNameKey = "product_name";
|
||||
std::string kBuildInfoKey = "build_info";
|
||||
std::string kDeviceIdKey = "device_id";
|
||||
std::string kWVCdmVersionKey = "widevine_cdm_version";
|
||||
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
|
||||
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,
|
||||
@@ -60,6 +63,10 @@ const unsigned char kServiceCertificateCAPublicKey[] = {
|
||||
0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01,
|
||||
0x00, 0x01};
|
||||
}
|
||||
const uint32_t kFourCcCbc1 = 0x63626331;
|
||||
const uint32_t kFourCcCbcs = 0x63626373;
|
||||
const uint32_t kFourCcCenc = 0x63656e63;
|
||||
const uint32_t kFourCcCens = 0x63656e73;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
@@ -91,7 +98,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
size_t length;
|
||||
switch (license.key(i).type()) {
|
||||
case License_KeyContainer::CONTENT:
|
||||
case License_KeyContainer::OPERATOR_SESSION:
|
||||
case License_KeyContainer::OPERATOR_SESSION: {
|
||||
key.set_key_id(license.key(i).id());
|
||||
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
|
||||
// the padding will always be 16 bytes.
|
||||
@@ -106,8 +113,17 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
key.set_key_control(license.key(i).key_control().key_control_block());
|
||||
key.set_key_control_iv(license.key(i).key_control().iv());
|
||||
}
|
||||
uint32_t four_cc = kFourCcCenc;
|
||||
if (license.has_protection_scheme()) {
|
||||
four_cc = ntohl(license.protection_scheme());
|
||||
}
|
||||
if (four_cc == kFourCcCbc1 || four_cc == kFourCcCbcs)
|
||||
key.set_cipher_mode(kCipherModeCbc);
|
||||
else
|
||||
key.set_cipher_mode(kCipherModeCtr);
|
||||
key_array.push_back(key);
|
||||
break;
|
||||
}
|
||||
case License_KeyContainer::KEY_CONTROL:
|
||||
if (license.key(i).has_key_control()) {
|
||||
key.set_key_control(license.key(i).key_control().key_control_block());
|
||||
@@ -232,7 +248,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
LicenseRequest_ContentIdentification* content_id =
|
||||
license_request.mutable_content_id();
|
||||
|
||||
if (init_data.is_cenc()) {
|
||||
if (init_data.is_cenc() || init_data.is_hls()) {
|
||||
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
|
||||
content_id->mutable_cenc_id();
|
||||
|
||||
@@ -535,8 +551,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
|
||||
policy_engine_->SetLicense(license);
|
||||
|
||||
if (license.policy().has_renew_with_client_id()) {
|
||||
renew_with_client_id_ = license.policy().renew_with_client_id();
|
||||
}
|
||||
@@ -551,6 +565,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
it != key_array.end(); ++it) {
|
||||
loaded_keys_.insert(it->key_id());
|
||||
}
|
||||
policy_engine_->SetLicense(license);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
@@ -612,8 +627,6 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
return LICENSE_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
if (license.policy().has_renew_with_client_id()) {
|
||||
renew_with_client_id_ = license.policy().renew_with_client_id();
|
||||
}
|
||||
@@ -637,6 +650,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
|
||||
if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(),
|
||||
key_array.size(), &key_array[0])) {
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
return KEY_ADDED;
|
||||
} else {
|
||||
return REFRESH_KEYS_ERROR;
|
||||
@@ -981,6 +996,11 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
client_info->set_name(kWVCdmVersionKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kOemCryptoSecurityPatchLevelKey);
|
||||
std::stringstream ss;
|
||||
ss << (uint32_t)session_->GetSecurityPatchLevel();
|
||||
client_info->set_value(ss.str());
|
||||
|
||||
ClientIdentification_ClientCapabilities* client_capabilities =
|
||||
client_id->mutable_client_capabilities();
|
||||
|
||||
285
core/src/license_key_status.cpp
Normal file
285
core/src/license_key_status.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "license_key_status.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace {
|
||||
// License protocol aliases
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef KeyContainer::OutputProtection OutputProtection;
|
||||
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
ConstraintList;
|
||||
|
||||
// Map the HDCP protection associated with a key in the license to
|
||||
// an equivalent OEMCrypto HDCP protection level
|
||||
wvcdm::CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp(
|
||||
const OutputProtection::HDCP& input) {
|
||||
switch (input) {
|
||||
case OutputProtection::HDCP_NONE:
|
||||
return HDCP_NONE;
|
||||
case OutputProtection::HDCP_V1:
|
||||
return HDCP_V1;
|
||||
case OutputProtection::HDCP_V2:
|
||||
return HDCP_V2;
|
||||
case OutputProtection::HDCP_V2_1:
|
||||
return HDCP_V2_1;
|
||||
case OutputProtection::HDCP_V2_2:
|
||||
return HDCP_V2_2;
|
||||
case OutputProtection::HDCP_NO_DIGITAL_OUTPUT:
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
default:
|
||||
LOGE("ContentKeyStatus::ProtobufHdcpToOemCryptoHdcp: "
|
||||
"Unknown HDCP Level: input=%d, returning HDCP_NO_DIGITAL_OUTPUT",
|
||||
input);
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the constraint from a set of constraints that matches the
|
||||
// specified resolution, or null if none match
|
||||
VideoResolutionConstraint* GetConstraintForRes(
|
||||
uint32_t res, ConstraintList& constraints_from_key) {
|
||||
typedef ConstraintList::pointer_iterator Iterator;
|
||||
for (Iterator i = constraints_from_key.pointer_begin();
|
||||
i != constraints_from_key.pointer_end(); ++i) {
|
||||
VideoResolutionConstraint* constraint = *i;
|
||||
if (constraint->has_min_resolution_pixels() &&
|
||||
constraint->has_max_resolution_pixels() &&
|
||||
res >= constraint->min_resolution_pixels() &&
|
||||
res <= constraint->max_resolution_pixels()) {
|
||||
return constraint;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
bool LicenseKeys::IsContentKey(const std::string& key_id) {
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->IsContentKey();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LicenseKeys::CanDecryptContent(const std::string& key_id) {
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->CanDecryptContent();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LicenseKeys::GetAllowedUsage(const KeyId& key_id,
|
||||
CdmKeyAllowedUsage* allowed_usage) {
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->GetAllowedUsage(allowed_usage);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status,
|
||||
bool* new_usable_keys) {
|
||||
bool keys_changed = false;
|
||||
bool newly_usable = false;
|
||||
*new_usable_keys = false;
|
||||
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
|
||||
bool usable;
|
||||
if (it->second->ApplyStatusChange(new_status, &usable)) {
|
||||
newly_usable |= usable;
|
||||
keys_changed = true;
|
||||
}
|
||||
}
|
||||
*new_usable_keys = newly_usable;
|
||||
return keys_changed;
|
||||
}
|
||||
|
||||
void LicenseKeys::ExtractKeyStatuses(CdmKeyStatusMap* content_keys) {
|
||||
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
|
||||
if (it->second->IsContentKey()) {
|
||||
const KeyId key_id = it->first;
|
||||
CdmKeyStatus key_status = it->second->GetKeyStatus();
|
||||
(*content_keys)[key_id] = key_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LicenseKeys::MeetsConstraints(const KeyId& key_id) {
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->MeetsConstraints();
|
||||
} else {
|
||||
// If a Key ID is unknown to us, we don't know of any constraints for it,
|
||||
// so never block decryption.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeys::ApplyConstraints(
|
||||
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
|
||||
for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) {
|
||||
i->second->ApplyConstraints(new_resolution, new_hdcp_level);
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeys::SetFromLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
this->Clear();
|
||||
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
|
||||
const KeyContainer& key = license.key(key_index);
|
||||
if (key.has_id() && (key.type() == KeyContainer::CONTENT ||
|
||||
key.type() == KeyContainer::OPERATOR_SESSION)) {
|
||||
const KeyId& key_id = key.id();
|
||||
keys_[key_id] = new LicenseKeyStatus(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) :
|
||||
is_content_key_(false),
|
||||
key_status_(kKeyStatusInternalError),
|
||||
meets_constraints_(true),
|
||||
default_hdcp_level_(HDCP_NONE) {
|
||||
|
||||
allowed_usage_.Clear();
|
||||
constraints_.Clear();
|
||||
|
||||
if (key.type() == KeyContainer::CONTENT) {
|
||||
ParseContentKey(key);
|
||||
} else if (key.type() == KeyContainer::OPERATOR_SESSION) {
|
||||
ParseOperatorSessionKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeyStatus::ParseContentKey(const KeyContainer& key) {
|
||||
is_content_key_ = true;
|
||||
if (key.has_level() &&
|
||||
((key.level() == KeyContainer::HW_SECURE_DECODE) ||
|
||||
(key.level() == KeyContainer::HW_SECURE_ALL))) {
|
||||
allowed_usage_.decrypt_to_clear_buffer = false;
|
||||
allowed_usage_.decrypt_to_secure_buffer = true;
|
||||
} else {
|
||||
allowed_usage_.decrypt_to_clear_buffer = true;
|
||||
allowed_usage_.decrypt_to_secure_buffer = true;
|
||||
}
|
||||
allowed_usage_.SetValid();
|
||||
|
||||
if (key.video_resolution_constraints_size() > 0) {
|
||||
SetConstraints(key.video_resolution_constraints());
|
||||
}
|
||||
|
||||
if (key.has_required_protection()) {
|
||||
default_hdcp_level_ =
|
||||
ProtobufHdcpToOemCryptoHdcp(key.required_protection().hdcp());
|
||||
}
|
||||
}
|
||||
|
||||
void LicenseKeyStatus::ParseOperatorSessionKey(const KeyContainer& key) {
|
||||
is_content_key_ = false;
|
||||
if (key.has_operator_session_key_permissions()) {
|
||||
OperatorSessionKeyPermissions permissions =
|
||||
key.operator_session_key_permissions();
|
||||
if (permissions.has_allow_encrypt())
|
||||
allowed_usage_.generic_encrypt = permissions.allow_encrypt();
|
||||
if (permissions.has_allow_decrypt())
|
||||
allowed_usage_.generic_decrypt = permissions.allow_decrypt();
|
||||
if (permissions.has_allow_sign())
|
||||
allowed_usage_.generic_sign = permissions.allow_sign();
|
||||
if (permissions.has_allow_signature_verify())
|
||||
allowed_usage_.generic_verify = permissions.allow_signature_verify();
|
||||
} else {
|
||||
allowed_usage_.generic_encrypt = false;
|
||||
allowed_usage_.generic_decrypt = false;
|
||||
allowed_usage_.generic_sign = false;
|
||||
allowed_usage_.generic_verify = false;
|
||||
}
|
||||
allowed_usage_.SetValid();
|
||||
}
|
||||
|
||||
void LicenseKeys::Clear() {
|
||||
for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) {
|
||||
delete i->second;
|
||||
}
|
||||
keys_.clear();
|
||||
}
|
||||
|
||||
bool LicenseKeyStatus::CanDecryptContent() {
|
||||
return is_content_key_ && key_status_ == kKeyStatusUsable;
|
||||
}
|
||||
|
||||
bool LicenseKeyStatus::GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage) {
|
||||
if (NULL == allowed_usage)
|
||||
return false;
|
||||
*allowed_usage = allowed_usage_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
|
||||
bool* new_usable_key) {
|
||||
*new_usable_key = false;
|
||||
if (!is_content_key_) {
|
||||
return false;
|
||||
}
|
||||
CdmKeyStatus updated_status = new_status;
|
||||
if (updated_status == kKeyStatusUsable) {
|
||||
if (!MeetsConstraints()) {
|
||||
updated_status = kKeyStatusOutputNotAllowed;
|
||||
}
|
||||
}
|
||||
if (key_status_ != updated_status) {
|
||||
key_status_ = updated_status;
|
||||
if (updated_status == kKeyStatusUsable) {
|
||||
*new_usable_key = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the key has constraints, find the constraint that applies.
|
||||
// If none found, then the constraint test fails.
|
||||
// If a constraint is found, verify that the device's current HDCP
|
||||
// level is sufficient. If the constraint has an HDCP setting, use it,
|
||||
// If the key has no constraints, or if the constraint has no HDCP
|
||||
// requirement, use the key's default HDCP setting to check against the
|
||||
// device's current HDCP level.
|
||||
void LicenseKeyStatus::ApplyConstraints(
|
||||
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
|
||||
|
||||
VideoResolutionConstraint* current_constraint = NULL;
|
||||
if (HasConstraints()) {
|
||||
current_constraint = GetConstraintForRes(new_resolution, constraints_);
|
||||
if (NULL == current_constraint) {
|
||||
meets_constraints_ = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability desired_hdcp_level;
|
||||
if (current_constraint && current_constraint->has_required_protection()) {
|
||||
desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp(
|
||||
current_constraint->required_protection().hdcp());
|
||||
} else {
|
||||
desired_hdcp_level = default_hdcp_level_;
|
||||
}
|
||||
|
||||
meets_constraints_ = (new_hdcp_level >= desired_hdcp_level);
|
||||
}
|
||||
|
||||
void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) {
|
||||
if (!is_content_key_) {
|
||||
return;
|
||||
}
|
||||
constraints_.Clear();
|
||||
constraints_.MergeFrom(constraints);
|
||||
meets_constraints_ = true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -196,6 +196,10 @@ message License {
|
||||
optional bool remote_attestation_verified = 5 [default = false];
|
||||
// Client token generated by the content provider. Optional.
|
||||
optional bytes provider_client_token = 6;
|
||||
// Protection scheme identifying the encryption algorithm. Represented as one
|
||||
// of the following 4CC values: 'cenc' (AES-CTR), 'cbc1' (AES-CBC),
|
||||
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
|
||||
optional uint32 protection_scheme = 7;
|
||||
}
|
||||
|
||||
enum ProtocolVersion {
|
||||
@@ -268,6 +272,28 @@ message LicenseError {
|
||||
optional Error error_code = 1;
|
||||
}
|
||||
|
||||
message MetricData {
|
||||
enum MetricType {
|
||||
// The time spent in the 'stage', specified in microseconds.
|
||||
LATENCY = 1;
|
||||
// The UNIX epoch timestamp at which the 'stage' was first accessed in
|
||||
// microseconds.
|
||||
TIMESTAMP = 2;
|
||||
}
|
||||
|
||||
message TypeValue {
|
||||
optional MetricType type = 1;
|
||||
// The value associated with 'type'. For example if type == LATENCY, the
|
||||
// value would be the time in microseconds spent in this 'stage'.
|
||||
optional int64 value = 2 [default = 0];
|
||||
}
|
||||
|
||||
// 'stage' that is currently processing the SignedMessage. Required.
|
||||
optional string stage_name = 1;
|
||||
// metric and associated value.
|
||||
repeated TypeValue metric_data = 2;
|
||||
}
|
||||
|
||||
message RemoteAttestation {
|
||||
// Encrypted ClientIdentification message containing the device remote
|
||||
// attestation certificate. Required.
|
||||
@@ -296,6 +322,14 @@ message SignedMessage {
|
||||
// request for ChromeOS client devices operating in verified mode. Remote
|
||||
// attestation challenge data is |msg| field above. Optional.
|
||||
optional RemoteAttestation remote_attestation = 5;
|
||||
repeated MetricData metric_data = 6;
|
||||
}
|
||||
|
||||
message GroupKeys {
|
||||
repeated License.KeyContainer key = 1;
|
||||
// Byte string that identifies the group to which this license material
|
||||
// belongs.
|
||||
optional bytes group_id = 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -427,7 +461,7 @@ message EncryptedClientIdentification {
|
||||
optional bytes encrypted_client_id = 3;
|
||||
// Initialization vector needed to decrypt encrypted_client_id.
|
||||
optional bytes encrypted_client_id_iv = 4;
|
||||
// AES-128 privacy key, encrytped with the service public public key using
|
||||
// AES-128 privacy key, encrypted with the service public public key using
|
||||
// RSA-OAEP.
|
||||
optional bytes encrypted_privacy_key = 5;
|
||||
}
|
||||
@@ -547,3 +581,48 @@ message SignedCertificateStatusList {
|
||||
// key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// widevine_header.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Public protocol buffer definitions for Widevine Cenc Header
|
||||
// protocol.
|
||||
message WidevineCencHeader {
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
AESCTR = 1;
|
||||
};
|
||||
// Replaced with protection_scheme.
|
||||
optional Algorithm algorithm = 1 [deprecated=true];
|
||||
repeated bytes key_id = 2;
|
||||
|
||||
// Content provider name.
|
||||
optional string provider = 3;
|
||||
|
||||
// A content identifier, specified by content provider.
|
||||
optional bytes content_id = 4;
|
||||
|
||||
// Track type. Acceptable values are SD, HD and AUDIO. Used to differentiate
|
||||
// content keys used by an asset.
|
||||
// No longer adding track_type to the PSSH since the Widevine license server
|
||||
// will return keys for all allowed track types in a single license.
|
||||
optional string track_type_deprecated = 5;
|
||||
|
||||
// The name of a registered policy to be used for this asset.
|
||||
optional string policy = 6 [deprecated=true];
|
||||
|
||||
// Crypto period index, for media using key rotation.
|
||||
optional uint32 crypto_period_index = 7;
|
||||
|
||||
// Optional protected context for group content. The grouped_license is a
|
||||
// serialized SignedMessage.
|
||||
optional bytes grouped_license = 8;
|
||||
|
||||
// Protection scheme identifying the encryption algorithm. Represented as one
|
||||
// of the following 4CC values: 'cenc' (AES-CTR), 'cbc1' (AES-CBC),
|
||||
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
|
||||
optional uint32 protection_scheme = 9;
|
||||
}
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "max_res_engine.h"
|
||||
|
||||
#include "clock.h"
|
||||
#include "log.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const int64_t kHdcpCheckInterval = 10;
|
||||
const uint32_t kNoResolution = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
MaxResEngine::MaxResEngine(CryptoSession* crypto_session) {
|
||||
Init(crypto_session, new Clock());
|
||||
}
|
||||
|
||||
MaxResEngine::MaxResEngine(CryptoSession* crypto_session, Clock* clock) {
|
||||
Init(crypto_session, clock);
|
||||
}
|
||||
|
||||
MaxResEngine::~MaxResEngine() {
|
||||
AutoLock lock(status_lock_);
|
||||
DeleteAllKeys();
|
||||
}
|
||||
|
||||
bool MaxResEngine::CanDecrypt(const KeyId& key_id) {
|
||||
AutoLock lock(status_lock_);
|
||||
if (keys_.count(key_id) > 0) {
|
||||
return keys_[key_id]->can_decrypt();
|
||||
} else {
|
||||
// If a Key ID is unknown to us, we don't know of any constraints for it,
|
||||
// so never block decryption.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void MaxResEngine::Init(CryptoSession* crypto_session, Clock* clock) {
|
||||
AutoLock lock(status_lock_);
|
||||
current_resolution_ = kNoResolution;
|
||||
clock_.reset(clock);
|
||||
next_check_time_ = clock_->GetCurrentTime();
|
||||
crypto_session_ = crypto_session;
|
||||
}
|
||||
|
||||
void MaxResEngine::SetLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
AutoLock lock(status_lock_);
|
||||
DeleteAllKeys();
|
||||
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
|
||||
const KeyContainer& key = license.key(key_index);
|
||||
if (key.type() == KeyContainer::CONTENT && key.has_id() &&
|
||||
key.video_resolution_constraints_size() > 0) {
|
||||
const ConstraintList& constraints = key.video_resolution_constraints();
|
||||
const KeyId& key_id = key.id();
|
||||
if (key.has_required_protection()) {
|
||||
keys_[key_id] =
|
||||
new KeyStatus(constraints, key.required_protection().hdcp());
|
||||
} else {
|
||||
keys_[key_id] = new KeyStatus(constraints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaxResEngine::SetResolution(uint32_t width, uint32_t height) {
|
||||
AutoLock lock(status_lock_);
|
||||
current_resolution_ = width * height;
|
||||
}
|
||||
|
||||
void MaxResEngine::OnTimerEvent() {
|
||||
AutoLock lock(status_lock_);
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
if (!keys_.empty() && current_resolution_ != kNoResolution &&
|
||||
current_time >= next_check_time_) {
|
||||
CryptoSession::HdcpCapability current_hdcp_level;
|
||||
CryptoSession::HdcpCapability ignored;
|
||||
if (!crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored)) {
|
||||
current_hdcp_level = HDCP_NONE;
|
||||
}
|
||||
for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) {
|
||||
i->second->Update(current_resolution_, current_hdcp_level);
|
||||
}
|
||||
next_check_time_ = current_time + kHdcpCheckInterval;
|
||||
}
|
||||
}
|
||||
|
||||
void MaxResEngine::DeleteAllKeys() {
|
||||
// This helper method assumes that status_lock_ is already held.
|
||||
for (KeyIterator i = keys_.begin(); i != keys_.end(); ++i) delete i->second;
|
||||
keys_.clear();
|
||||
}
|
||||
|
||||
MaxResEngine::KeyStatus::KeyStatus(const ConstraintList& constraints)
|
||||
: default_hdcp_level_(HDCP_NONE) {
|
||||
Init(constraints);
|
||||
}
|
||||
|
||||
MaxResEngine::KeyStatus::KeyStatus(
|
||||
const ConstraintList& constraints,
|
||||
const OutputProtection::HDCP& default_hdcp_level)
|
||||
: default_hdcp_level_(ProtobufHdcpToOemCryptoHdcp(default_hdcp_level)) {
|
||||
Init(constraints);
|
||||
}
|
||||
|
||||
void MaxResEngine::KeyStatus::Init(const ConstraintList& constraints) {
|
||||
constraints_.Clear();
|
||||
constraints_.MergeFrom(constraints);
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
|
||||
void MaxResEngine::KeyStatus::Update(
|
||||
uint32_t res, CryptoSession::HdcpCapability current_hdcp_level) {
|
||||
VideoResolutionConstraint* current_constraint = GetConstraintForRes(res);
|
||||
|
||||
if (current_constraint == NULL) {
|
||||
can_decrypt_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability desired_hdcp_level;
|
||||
if (current_constraint->has_required_protection()) {
|
||||
desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp(
|
||||
current_constraint->required_protection().hdcp());
|
||||
} else {
|
||||
desired_hdcp_level = default_hdcp_level_;
|
||||
}
|
||||
can_decrypt_ = (current_hdcp_level >= desired_hdcp_level);
|
||||
}
|
||||
|
||||
MaxResEngine::VideoResolutionConstraint*
|
||||
MaxResEngine::KeyStatus::GetConstraintForRes(uint32_t res) {
|
||||
typedef ConstraintList::pointer_iterator Iterator;
|
||||
for (Iterator i = constraints_.pointer_begin();
|
||||
i != constraints_.pointer_end(); ++i) {
|
||||
VideoResolutionConstraint* constraint = *i;
|
||||
if (constraint->has_min_resolution_pixels() &&
|
||||
constraint->has_max_resolution_pixels() &&
|
||||
res >= constraint->min_resolution_pixels() &&
|
||||
res <= constraint->max_resolution_pixels()) {
|
||||
return constraint;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability
|
||||
MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp(
|
||||
const OutputProtection::HDCP& input) {
|
||||
switch (input) {
|
||||
case OutputProtection::HDCP_NONE:
|
||||
return HDCP_NONE;
|
||||
case OutputProtection::HDCP_V1:
|
||||
return HDCP_V1;
|
||||
case OutputProtection::HDCP_V2:
|
||||
return HDCP_V2;
|
||||
case OutputProtection::HDCP_V2_1:
|
||||
return HDCP_V2_1;
|
||||
case OutputProtection::HDCP_V2_2:
|
||||
return HDCP_V2_2;
|
||||
case OutputProtection::HDCP_NO_DIGITAL_OUTPUT:
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
default:
|
||||
LOGE("MaxResEngine::KeyStatus::ProtobufHdcpToOemCryptoHdcp: "
|
||||
"Unknown HDCP Level");
|
||||
return HDCP_NO_DIGITAL_OUTPUT;
|
||||
}
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
@@ -38,6 +38,10 @@ uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
|
||||
return ::OEMCrypto_APIVersion();
|
||||
}
|
||||
|
||||
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level) {
|
||||
return ::OEMCrypto_Security_Patch_Level();
|
||||
}
|
||||
|
||||
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
67
core/src/oemcrypto_adapter_static_v11.cpp
Normal file
67
core/src/oemcrypto_adapter_static_v11.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
|
||||
// This should be used when liboemcrypto.so is linked with the CDM code at
|
||||
// compile time.
|
||||
//
|
||||
// Defines APIs introduced in newer version (v11) which is not available in v10
|
||||
// to allow an older oemcrypto implementation to be linked with CDM.
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_DecryptCTR_V10(
|
||||
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
|
||||
bool is_encrypted, const uint8_t* iv, size_t block_offset,
|
||||
const OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags);
|
||||
|
||||
typedef struct {
|
||||
const uint8_t* key_id;
|
||||
size_t key_id_length;
|
||||
const uint8_t* key_data_iv;
|
||||
const uint8_t* key_data;
|
||||
size_t key_data_length;
|
||||
const uint8_t* key_control_iv;
|
||||
const uint8_t* key_control;
|
||||
} OEMCrypto_KeyObject_V9;
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V9_or_V10(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,
|
||||
size_t num_keys, const OEMCrypto_KeyObject_V9* key_array,
|
||||
const uint8_t* pst, size_t pst_length);
|
||||
|
||||
extern "C" uint8_t OEMCrypto_Security_Patch_Level() { return 0; }
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_DecryptCENC(
|
||||
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
|
||||
bool is_encrypted, const uint8_t* iv, size_t block_offset,
|
||||
OEMCrypto_DestBufferDesc* out_buffer,
|
||||
const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags) {
|
||||
return OEMCrypto_DecryptCTR_V10(session, data_addr, data_length, is_encrypted,
|
||||
iv, block_offset, out_buffer,
|
||||
subsample_flags);
|
||||
}
|
||||
|
||||
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_keys_iv, const uint8_t* enc_mac_keys,
|
||||
size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
OEMCrypto_KeyObject_V9 key_array_v9[num_keys];
|
||||
for (size_t key_index = 0; key_index < num_keys; key_index++) {
|
||||
key_array_v9[key_index].key_id = key_array[key_index].key_id;
|
||||
key_array_v9[key_index].key_id_length = key_array[key_index].key_id_length;
|
||||
key_array_v9[key_index].key_data_iv = key_array[key_index].key_data_iv;
|
||||
key_array_v9[key_index].key_data = key_array[key_index].key_data;
|
||||
key_array_v9[key_index].key_data_length =
|
||||
key_array[key_index].key_data_length;
|
||||
key_array_v9[key_index].key_control_iv =
|
||||
key_array[key_index].key_control_iv;
|
||||
key_array_v9[key_index].key_control = key_array[key_index].key_control;
|
||||
}
|
||||
return OEMCrypto_LoadKeys_V9_or_V10(
|
||||
session, message, message_length, signature, signature_length,
|
||||
enc_mac_keys_iv, enc_mac_keys, num_keys, key_array_v9, pst, pst_length);
|
||||
}
|
||||
@@ -3,10 +3,7 @@
|
||||
#include "policy_engine.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "clock.h"
|
||||
#include "log.h"
|
||||
@@ -17,6 +14,13 @@
|
||||
|
||||
using video_widevine_server::sdk::License;
|
||||
|
||||
namespace {
|
||||
|
||||
const int64_t kHdcpCheckInterval = 10;
|
||||
const uint32_t kNoResolution = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
@@ -32,18 +36,43 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
policy_max_duration_seconds_(0),
|
||||
session_id_(session_id),
|
||||
event_listener_(event_listener),
|
||||
max_res_engine_(new MaxResEngine(crypto_session)),
|
||||
clock_(new Clock) {}
|
||||
license_keys_(new LicenseKeys),
|
||||
clock_(new Clock) {
|
||||
InitDevice(crypto_session);
|
||||
}
|
||||
|
||||
PolicyEngine::~PolicyEngine() {}
|
||||
|
||||
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
|
||||
if (keys_status_.find(key_id) == keys_status_.end()) {
|
||||
if (license_keys_->IsContentKey(key_id)) {
|
||||
return license_keys_->CanDecryptContent(key_id);
|
||||
} else {
|
||||
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
|
||||
b2a_hex(key_id).c_str());
|
||||
return false;
|
||||
}
|
||||
return keys_status_[key_id] == kKeyStatusUsable;
|
||||
}
|
||||
|
||||
void PolicyEngine::InitDevice(CryptoSession* crypto_session) {
|
||||
current_resolution_ = kNoResolution;
|
||||
next_device_check_ = 0;
|
||||
crypto_session_ = crypto_session;
|
||||
}
|
||||
|
||||
void PolicyEngine::CheckDevice(int64_t current_time) {
|
||||
if (current_time < next_device_check_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!license_keys_->Empty() && current_resolution_ != kNoResolution) {
|
||||
CryptoSession::HdcpCapability current_hdcp_level;
|
||||
CryptoSession::HdcpCapability ignored;
|
||||
if (!crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored)) {
|
||||
current_hdcp_level = HDCP_NONE;
|
||||
}
|
||||
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
|
||||
next_device_check_ = current_time + kHdcpCheckInterval;
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::OnTimerEvent() {
|
||||
@@ -57,7 +86,8 @@ void PolicyEngine::OnTimerEvent() {
|
||||
return;
|
||||
}
|
||||
|
||||
max_res_engine_->OnTimerEvent();
|
||||
// Check device conditions that affect playability (HDCP, resolution)
|
||||
CheckDevice(current_time);
|
||||
|
||||
bool renewal_needed = false;
|
||||
|
||||
@@ -110,17 +140,8 @@ void PolicyEngine::SetLicense(const License& license) {
|
||||
license_id_.Clear();
|
||||
license_id_.CopyFrom(license.id());
|
||||
policy_.Clear();
|
||||
|
||||
// Extract content key ids.
|
||||
keys_status_.clear();
|
||||
for (int key_index = 0; key_index < license.key_size(); ++key_index) {
|
||||
const License::KeyContainer& key = license.key(key_index);
|
||||
if (key.type() == License::KeyContainer::CONTENT && key.has_id())
|
||||
keys_status_[key.id()] = kKeyStatusInternalError;
|
||||
}
|
||||
|
||||
license_keys_->SetFromLicense(license);
|
||||
UpdateLicense(license);
|
||||
max_res_engine_->SetLicense(license);
|
||||
}
|
||||
|
||||
void PolicyEngine::SetLicenseForRelease(const License& license) {
|
||||
@@ -130,7 +151,6 @@ void PolicyEngine::SetLicenseForRelease(const License& license) {
|
||||
|
||||
// Expire any old keys.
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
|
||||
UpdateLicense(license);
|
||||
}
|
||||
|
||||
@@ -220,7 +240,7 @@ void PolicyEngine::DecryptionEvent() {
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
max_res_engine_->SetResolution(width, height);
|
||||
SetDeviceResolution(width, height);
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifySessionExpiration() {
|
||||
@@ -228,35 +248,46 @@ void PolicyEngine::NotifySessionExpiration() {
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
}
|
||||
|
||||
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
|
||||
std::stringstream ss;
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
key_info->clear();
|
||||
query_response->clear();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
|
||||
(*query_response)[QUERY_KEY_LICENSE_TYPE] =
|
||||
license_id_.type() == video_widevine_server::sdk::STREAMING
|
||||
? QUERY_VALUE_STREAMING
|
||||
: QUERY_VALUE_OFFLINE;
|
||||
(*key_info)[QUERY_KEY_PLAY_ALLOWED] =
|
||||
(*query_response)[QUERY_KEY_PLAY_ALLOWED] =
|
||||
policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_PERSIST_ALLOWED] =
|
||||
(*query_response)[QUERY_KEY_PERSIST_ALLOWED] =
|
||||
policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_RENEW_ALLOWED] =
|
||||
(*query_response)[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();
|
||||
(*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
|
||||
ss.str("");
|
||||
ss << GetPlaybackDurationRemaining(current_time);
|
||||
(*key_info)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
|
||||
(*key_info)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
|
||||
(*query_response)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
|
||||
(*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType PolicyEngine::QueryKeyAllowedUsage(
|
||||
const KeyId& key_id, CdmKeyAllowedUsage* key_usage) {
|
||||
if (NULL == key_usage) {
|
||||
return INVALID_PARAMETERS_ENG_12;
|
||||
}
|
||||
if (license_keys_->GetAllowedUsage(key_id, key_usage)) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
return KEY_NOT_FOUND_1;
|
||||
}
|
||||
|
||||
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
|
||||
if (playback_start_time_ == 0) return false;
|
||||
|
||||
@@ -272,6 +303,12 @@ bool PolicyEngine::GetSecondsSinceLastPlayed(
|
||||
return (*seconds_since_last_played >= 0) ? true : false;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() {
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
return std::min(GetLicenseDurationRemaining(current_time),
|
||||
GetPlaybackDurationRemaining(current_time));
|
||||
}
|
||||
|
||||
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time) {
|
||||
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
|
||||
@@ -345,25 +382,14 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
|
||||
bool keys_changed = false;
|
||||
bool keys_changed;
|
||||
bool has_new_usable_key = false;
|
||||
for (std::map<KeyId, CdmKeyStatus>::iterator it = keys_status_.begin();
|
||||
it != keys_status_.end(); ++it) {
|
||||
const KeyId key_id = it->first;
|
||||
CdmKeyStatus& key_status = it->second;
|
||||
CdmKeyStatus updated_status = new_status;
|
||||
if (updated_status == kKeyStatusUsable) {
|
||||
if (!max_res_engine_->CanDecrypt(key_id))
|
||||
updated_status = kKeyStatusOutputNotAllowed;
|
||||
}
|
||||
if (key_status != updated_status) {
|
||||
key_status = updated_status;
|
||||
if (updated_status == kKeyStatusUsable) has_new_usable_key = true;
|
||||
keys_changed = true;
|
||||
}
|
||||
}
|
||||
if (keys_changed && event_listener_) {
|
||||
event_listener_->OnSessionKeysChange(session_id_, keys_status_,
|
||||
keys_changed = license_keys_->ApplyStatusChange(new_status,
|
||||
&has_new_usable_key);
|
||||
if (event_listener_ && keys_changed) {
|
||||
CdmKeyStatusMap content_keys;
|
||||
license_keys_->ExtractKeyStatuses(&content_keys);
|
||||
event_listener_->OnSessionKeysChange(session_id_, content_keys,
|
||||
has_new_usable_key);
|
||||
}
|
||||
}
|
||||
@@ -381,8 +407,4 @@ void PolicyEngine::NotifyExpirationUpdate() {
|
||||
|
||||
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
|
||||
|
||||
void PolicyEngine::set_max_res_engine(MaxResEngine* max_res_engine) {
|
||||
max_res_engine_.reset(max_res_engine);
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <modp_b64.h>
|
||||
#include <modp_b64w.h>
|
||||
|
||||
#include "log.h"
|
||||
@@ -78,6 +79,26 @@ std::string b2a_hex(const std::string& byte) {
|
||||
byte.length());
|
||||
}
|
||||
|
||||
// Encode for standard base64 encoding (RFC4648).
|
||||
std::string Base64Encode(const std::vector<uint8_t>& bin_input) {
|
||||
if (bin_input.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
int in_size = bin_input.size();
|
||||
std::string b64_output(modp_b64_encode_len(in_size), 0);
|
||||
|
||||
int out_size = modp_b64_encode(
|
||||
&b64_output[0], reinterpret_cast<const char*>(&bin_input[0]), in_size);
|
||||
if (out_size == -1) {
|
||||
LOGE("Base64Encode failed");
|
||||
return std::string();
|
||||
}
|
||||
|
||||
b64_output.resize(out_size);
|
||||
return b64_output;
|
||||
}
|
||||
|
||||
// Filename-friendly base64 encoding (RFC4648), commonly referred to
|
||||
// as Base64WebSafeEncode.
|
||||
//
|
||||
@@ -111,6 +132,25 @@ std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input) {
|
||||
return b64_output;
|
||||
}
|
||||
|
||||
// Decode for standard base64 encoding (RFC4648).
|
||||
std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
|
||||
if (b64_input.empty()) {
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
int in_size = b64_input.size();
|
||||
std::vector<uint8_t> bin_output(modp_b64_decode_len(in_size), 0);
|
||||
int out_size = modp_b64_decode(reinterpret_cast<char*>(&bin_output[0]),
|
||||
b64_input.data(), in_size);
|
||||
if (out_size == -1) {
|
||||
LOGE("Base64Decode failed");
|
||||
return std::vector<uint8_t>(0);
|
||||
}
|
||||
|
||||
bin_output.resize(out_size);
|
||||
return bin_output;
|
||||
}
|
||||
|
||||
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
|
||||
// as Base64WebSafeDecode.
|
||||
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
|
||||
@@ -157,18 +197,6 @@ std::string IntToString(int value) {
|
||||
return out_string;
|
||||
}
|
||||
|
||||
std::string UintToString(unsigned int value) {
|
||||
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
|
||||
// So round up to allocate 3 output characters per byte.
|
||||
const int kOutputBufSize = 3 * sizeof(unsigned int);
|
||||
char buffer[kOutputBufSize];
|
||||
memset(buffer, 0, kOutputBufSize);
|
||||
snprintf(buffer, kOutputBufSize, "%u", value);
|
||||
|
||||
std::string out_string(buffer);
|
||||
return out_string;
|
||||
}
|
||||
|
||||
int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order)
|
||||
union {
|
||||
uint32_t array[2];
|
||||
|
||||
@@ -38,34 +38,55 @@ const std::string kTestData =
|
||||
const std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh");
|
||||
const std::string kOneByteOverB64Data("SGVsbG8gRnJpZW5kIQ==");
|
||||
const std::string kTwoBytesOverB64Data("SGVsbG8gRnJpZW5kISE=");
|
||||
const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
|
||||
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)};
|
||||
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)};
|
||||
|
||||
std::string ConvertToBase64WebSafe(const std::string &std_base64_string) {
|
||||
std::string str(std_base64_string);
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
if (str[i] == '+')
|
||||
str[i] = '-';
|
||||
else if (str[i] == '/')
|
||||
str[i] = '_';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class Base64EncodeDecodeTest
|
||||
: public ::testing::TestWithParam<
|
||||
std::pair<const std::string*, const std::string*> > {};
|
||||
std::pair<const std::string *, const std::string *> > {};
|
||||
|
||||
TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
|
||||
std::pair<const std::string *, const std::string *> values = GetParam();
|
||||
std::vector<uint8_t> decoded_vector = Base64SafeDecode(values.second->data());
|
||||
std::vector<uint8_t> decoded_vector = Base64Decode(values.second->data());
|
||||
std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
|
||||
EXPECT_STREQ(values.first->data(), decoded_string.data());
|
||||
std::string b64_string = Base64Encode(decoded_vector);
|
||||
EXPECT_STREQ(values.second->data(), b64_string.data());
|
||||
}
|
||||
|
||||
TEST_P(Base64EncodeDecodeTest, WebSafeEncodeDecodeTest) {
|
||||
std::pair<const std::string *, const std::string *> values = GetParam();
|
||||
std::string encoded_string = ConvertToBase64WebSafe(*(values.second));
|
||||
std::vector<uint8_t> decoded_vector = Base64SafeDecode(encoded_string);
|
||||
std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
|
||||
EXPECT_STREQ(values.first->data(), decoded_string.data());
|
||||
std::string b64_string = Base64SafeEncode(decoded_vector);
|
||||
EXPECT_STREQ(values.second->data(), b64_string.data());
|
||||
EXPECT_STREQ(encoded_string.data(), b64_string.data());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// 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 <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
@@ -43,6 +44,8 @@ const std::string kWebmMimeType = "video/webm";
|
||||
|
||||
class WvCdmEngineTest : public testing::Test {
|
||||
public:
|
||||
WvCdmEngineTest() : cdm_engine_(&file_system_) {}
|
||||
|
||||
static void SetUpTestCase() {
|
||||
ConfigTestEnv config(kContentProtectionUatServer);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
@@ -52,20 +55,16 @@ class WvCdmEngineTest : public testing::Test {
|
||||
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
|
||||
|
||||
// Extract the key ID from the PSSH box.
|
||||
InitializationData extractor(CENC_INIT_DATA_FORMAT,
|
||||
g_key_id_pssh);
|
||||
InitializationData extractor(CENC_INIT_DATA_FORMAT, g_key_id_pssh);
|
||||
g_key_id_unwrapped = extractor.data();
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
CdmResponseType status =
|
||||
cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
|
||||
NULL /* forced_session_id */, &session_id_);
|
||||
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
|
||||
if (status == NEED_PROVISIONING) {
|
||||
Provision();
|
||||
status = cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
|
||||
NULL /* forced_session_id */,
|
||||
&session_id_);
|
||||
status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
|
||||
}
|
||||
ASSERT_EQ(NO_ERROR, status);
|
||||
ASSERT_NE("", session_id_) << "Could not open CDM session.";
|
||||
@@ -82,37 +81,43 @@ class WvCdmEngineTest : public testing::Test {
|
||||
std::string cert_authority;
|
||||
std::string cert, wrapped_key;
|
||||
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
|
||||
cert_type, cert_authority, EMPTY_ORIGIN,
|
||||
&prov_request, &provisioning_server_url));
|
||||
cert_type, cert_authority, &prov_request,
|
||||
&provisioning_server_url));
|
||||
UrlRequest url_request(provisioning_server_url);
|
||||
EXPECT_TRUE(url_request.is_connected());
|
||||
url_request.PostCertRequestInQueryString(prov_request);
|
||||
std::string message;
|
||||
bool ok = url_request.GetResponse(&message);
|
||||
EXPECT_TRUE(ok);
|
||||
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(EMPTY_ORIGIN,
|
||||
message, &cert,
|
||||
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;
|
||||
std::string server_url;
|
||||
CdmKeySetId key_set_id;
|
||||
|
||||
InitializationData init_data(init_data_type_string, key_id);
|
||||
|
||||
CdmKeyRequestType key_request_type;
|
||||
CdmKeyRequest key_request;
|
||||
|
||||
EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateKeyRequest(
|
||||
session_id_, key_set_id, init_data,
|
||||
kLicenseTypeStreaming, app_parameters, &key_msg_,
|
||||
&key_request_type, &server_url, NULL));
|
||||
EXPECT_EQ(kKeyRequestTypeInitial, key_request_type);
|
||||
kLicenseTypeStreaming, app_parameters,
|
||||
&key_request));
|
||||
|
||||
key_msg_ = key_request.message;
|
||||
EXPECT_EQ(kKeyRequestTypeInitial, key_request.type);
|
||||
}
|
||||
|
||||
void GenerateRenewalRequest() {
|
||||
EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateRenewalRequest(
|
||||
session_id_, &key_msg_, &server_url_));
|
||||
CdmKeyRequest request;
|
||||
EXPECT_EQ(KEY_MESSAGE,
|
||||
cdm_engine_.GenerateRenewalRequest(session_id_, &request));
|
||||
|
||||
key_msg_ = request.message;
|
||||
server_url_ = request.url;
|
||||
}
|
||||
|
||||
std::string GetKeyRequestResponse(const std::string& server_url,
|
||||
@@ -171,6 +176,7 @@ class WvCdmEngineTest : public testing::Test {
|
||||
EXPECT_EQ(KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
|
||||
}
|
||||
|
||||
FileSystem file_system_;
|
||||
CdmEngine cdm_engine_;
|
||||
std::string key_msg_;
|
||||
std::string session_id_;
|
||||
|
||||
@@ -88,13 +88,12 @@ const std::string kWrappedKey = a2bs_hex(
|
||||
"E74C92B44F9205D22262FB47948654229DE1920F8EDF96A19A88A1CA1552F8856FB4CBF83B"
|
||||
"AA3348419159D207F65FCE9C1A500C6818");
|
||||
|
||||
const std::string kTestOrigin = "com.google";
|
||||
|
||||
class MockDeviceFiles : public DeviceFiles {
|
||||
public:
|
||||
MockDeviceFiles() : DeviceFiles(NULL) {}
|
||||
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD3(RetrieveCertificate, bool(const std::string&, std::string*,
|
||||
std::string*));
|
||||
MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*));
|
||||
};
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
@@ -136,7 +135,7 @@ using ::testing::StrEq;
|
||||
class CdmSessionTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
cdm_session_.reset(new CdmSession(NULL, kTestOrigin, NULL, NULL));
|
||||
cdm_session_.reset(new CdmSession(NULL));
|
||||
// Inject testing mocks.
|
||||
license_parser_ = new MockCdmLicense(cdm_session_->session_id());
|
||||
cdm_session_->set_license_parser(license_parser_);
|
||||
@@ -165,9 +164,8 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
@@ -178,7 +176,7 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
|
||||
}
|
||||
|
||||
TEST_F(CdmSessionTest, InitWithKeybox) {
|
||||
@@ -193,13 +191,14 @@ TEST_F(CdmSessionTest, InitWithKeybox) {
|
||||
EXPECT_CALL(*crypto_session_, GetToken(NotNull()))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), Return(true)));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*license_parser_,
|
||||
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
Properties::set_use_certificates_as_identification(false);
|
||||
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
|
||||
}
|
||||
|
||||
TEST_F(CdmSessionTest, ReInitFail) {
|
||||
@@ -212,9 +211,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
@@ -225,8 +223,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
ASSERT_NE(NO_ERROR, cdm_session_->Init());
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
|
||||
ASSERT_NE(NO_ERROR, cdm_session_->Init(NULL));
|
||||
}
|
||||
|
||||
TEST_F(CdmSessionTest, InitFailCryptoError) {
|
||||
@@ -235,7 +233,7 @@ TEST_F(CdmSessionTest, InitFailCryptoError) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init());
|
||||
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init(NULL));
|
||||
}
|
||||
|
||||
TEST_F(CdmSessionTest, InitNeedsProvisioning) {
|
||||
@@ -248,13 +246,12 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init());
|
||||
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init(NULL));
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -1408,23 +1408,49 @@ UsageInfo kUsageInfoTestData[] = {
|
||||
"0E12202FF1FBA9926A24A1F79970EC427DDF87B4421488F7952499BC33CEB282D9E48"
|
||||
"A")}};
|
||||
|
||||
const std::string kTestOrigin = "com.google";
|
||||
struct HlsAttributesInfo {
|
||||
std::string key_set_id;
|
||||
CdmHlsMethod method;
|
||||
std::string media_segment_iv;
|
||||
std::string file_data;
|
||||
};
|
||||
|
||||
HlsAttributesInfo kHlsAttributesTestData[] = {
|
||||
{
|
||||
"ksidC8EAA2579A282EB0", kHlsMethodAes128, // hls attributes 0
|
||||
a2bs_hex("F7C4D15BD466BF285E241A4E58638543"),
|
||||
a2bs_hex("0A1A08041001321408011210F7C4D15BD466BF285E241A4E5863854312201"
|
||||
"39114B0372FF80FADF92614106E27BE8BD1588B4CAE6E1AEFB7F9C34EA52E"
|
||||
"CC"),
|
||||
},
|
||||
{
|
||||
"ksidE8C37662C88DC673", kHlsMethodSampleAes, // hls attributes 1
|
||||
a2bs_hex("16413F038088438B5D4CD99F03EBB3D8"),
|
||||
a2bs_hex("0A1A0804100132140802121016413F038088438B5D4CD99F03EBB3D812205"
|
||||
"9EA13188B75C55D1EB78B3A65DB3EA3F43BD1B16642266D988E3543943C5F"
|
||||
"41"),
|
||||
}};
|
||||
|
||||
class MockFile : public File {
|
||||
public:
|
||||
MOCK_METHOD2(Open, bool(const std::string&, int flags));
|
||||
MOCK_METHOD0(Close, void());
|
||||
MockFile() : File(NULL) {}
|
||||
~MockFile() {}
|
||||
|
||||
MOCK_METHOD2(Read, ssize_t(char*, size_t));
|
||||
MOCK_METHOD2(Write, ssize_t(const char*, size_t));
|
||||
MOCK_METHOD0(Close, void());
|
||||
};
|
||||
|
||||
class MockFileSystem : public FileSystem {
|
||||
public:
|
||||
MockFileSystem() {}
|
||||
~MockFileSystem() {}
|
||||
|
||||
MOCK_METHOD2(Open, File*(const std::string&, int flags));
|
||||
MOCK_METHOD0(IsFactoryReset, bool());
|
||||
|
||||
MOCK_METHOD1(Exists, bool(const std::string&));
|
||||
MOCK_METHOD1(Remove, bool(const std::string&));
|
||||
MOCK_METHOD2(Copy, bool(const std::string&, const std::string&));
|
||||
MOCK_METHOD2(List, bool(const std::string&, std::vector<std::string>*));
|
||||
MOCK_METHOD1(CreateDirectory, bool(const std::string));
|
||||
MOCK_METHOD1(IsDirectory, bool(const std::string&));
|
||||
MOCK_METHOD1(IsRegularFile, bool(const std::string&));
|
||||
MOCK_METHOD1(FileSize, ssize_t(const std::string&));
|
||||
};
|
||||
|
||||
@@ -1490,21 +1516,9 @@ class DeviceFilesTest : public ::testing::Test {
|
||||
class DeviceFilesStoreTest : public DeviceFilesTest,
|
||||
public ::testing::WithParamInterface<bool> {};
|
||||
|
||||
struct CertStorageVariant {
|
||||
CertStorageVariant(bool dir_exists_value, const std::string& origin_value)
|
||||
: dir_exists(dir_exists_value), origin(origin_value) {}
|
||||
class DeviceCertificateStoreTest : public DeviceFilesTest {};
|
||||
|
||||
const bool dir_exists;
|
||||
const std::string origin;
|
||||
};
|
||||
|
||||
class DeviceCertificateStoreTest
|
||||
: public DeviceFilesTest,
|
||||
public ::testing::WithParamInterface<CertStorageVariant> {};
|
||||
|
||||
class DeviceCertificateTest
|
||||
: public DeviceFilesTest,
|
||||
public ::testing::WithParamInterface<std::string> {};
|
||||
class DeviceCertificateTest : public DeviceFilesTest {};
|
||||
|
||||
class DeviceFilesSecurityLevelTest
|
||||
: public DeviceFilesTest,
|
||||
@@ -1513,13 +1527,22 @@ class DeviceFilesSecurityLevelTest
|
||||
class DeviceFilesUsageInfoTest : public DeviceFilesTest,
|
||||
public ::testing::WithParamInterface<int> {};
|
||||
|
||||
MATCHER(IsCreateFileFlagSet, "") { return File::kCreate & arg; }
|
||||
MATCHER(IsBinaryFileFlagSet, "") { return File::kBinary & arg; }
|
||||
class DeviceFilesHlsAttributesTest
|
||||
: public DeviceFilesTest,
|
||||
public ::testing::WithParamInterface<HlsAttributesInfo*> {};
|
||||
|
||||
MATCHER(IsCreateFileFlagSet, "") { return FileSystem::kCreate & arg; }
|
||||
MATCHER_P(IsStrEq, str, "") {
|
||||
// Estimating the length of data. We can have gmock provide length
|
||||
// as well as pointer to data but that will introduce a dependency on tr1
|
||||
return memcmp(arg, str.c_str(), str.size()) == 0;
|
||||
}
|
||||
MATCHER_P2(Contains, str1, size, "") {
|
||||
// Estimating the length of data. We can have gmock provide length
|
||||
// as well as pointer to data but that will introduce a dependency on tr1
|
||||
std::string data(arg, size + str1.size() + kProtobufEstimatedOverhead);
|
||||
return (data.find(str1) != std::string::npos);
|
||||
}
|
||||
MATCHER_P3(Contains, str1, str2, size, "") {
|
||||
// Estimating the length of data. We can have gmock provide length
|
||||
// as well as pointer to data but that will introduce a dependency on tr1
|
||||
@@ -1586,105 +1609,78 @@ MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") {
|
||||
data.find(str6) != std::string::npos && map7_entries_present);
|
||||
}
|
||||
|
||||
TEST_P(DeviceCertificateStoreTest, StoreCertificate) {
|
||||
MockFile file;
|
||||
CertStorageVariant params = GetParam();
|
||||
TEST_F(DeviceCertificateStoreTest, StoreCertificate) {
|
||||
MockFileSystem file_system;
|
||||
std::string certificate(GenerateRandomData(kCertificateLen));
|
||||
std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen));
|
||||
std::string device_certificate_path =
|
||||
device_base_path_ + DeviceFiles::GetCertificateFileName(params.origin);
|
||||
device_base_path_ + DeviceFiles::GetCertificateFileName();
|
||||
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
.WillOnce(Return(params.dir_exists));
|
||||
if (params.dir_exists) {
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
} else {
|
||||
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_)))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
|
||||
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
|
||||
.WillOnce(Return(true));
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system,
|
||||
Open(StrEq(device_certificate_path), IsCreateFileFlagSet()))
|
||||
.WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
|
||||
Gt(certificate.size() + wrapped_private_key.size())))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
EXPECT_TRUE(device_files.StoreCertificate(params.origin, certificate,
|
||||
wrapped_private_key));
|
||||
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
StoreCertificate, DeviceCertificateStoreTest,
|
||||
::testing::Values(CertStorageVariant(true, EMPTY_ORIGIN),
|
||||
CertStorageVariant(true, kTestOrigin),
|
||||
CertStorageVariant(false, EMPTY_ORIGIN),
|
||||
CertStorageVariant(false, kTestOrigin)));
|
||||
|
||||
TEST_P(DeviceCertificateTest, ReadCertificate) {
|
||||
MockFile file;
|
||||
std::string origin = GetParam();
|
||||
TEST_F(DeviceCertificateTest, ReadCertificate) {
|
||||
MockFileSystem file_system;
|
||||
std::string device_certificate_path =
|
||||
device_base_path_ + DeviceFiles::GetCertificateFileName(origin);
|
||||
device_base_path_ + DeviceFiles::GetCertificateFileName();
|
||||
std::string data = a2bs_hex(kTestCertificateFileData);
|
||||
|
||||
EXPECT_CALL(file, Exists(StrEq(device_certificate_path)))
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system, Exists(StrEq(device_certificate_path)))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file, FileSize(StrEq(device_certificate_path)))
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(device_certificate_path)))
|
||||
.WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file, Open(StrEq(device_certificate_path), IsBinaryFileFlagSet()))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, Open(StrEq(device_certificate_path), _))
|
||||
.WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
|
||||
Return(data.size())));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
if (Properties::security_level_path_backward_compatibility_support()) {
|
||||
EXPECT_CALL(file, List(_, NotNull())).WillOnce(Return(false));
|
||||
}
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
std::string certificate, wrapped_private_key;
|
||||
ASSERT_TRUE(device_files.RetrieveCertificate(origin, &certificate,
|
||||
&wrapped_private_key));
|
||||
ASSERT_TRUE(
|
||||
device_files.RetrieveCertificate(&certificate, &wrapped_private_key));
|
||||
EXPECT_EQ(kTestCertificate, b2a_hex(certificate));
|
||||
EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key));
|
||||
}
|
||||
|
||||
TEST_P(DeviceCertificateTest, HasCertificate) {
|
||||
MockFile file;
|
||||
std::string origin = GetParam();
|
||||
TEST_F(DeviceCertificateTest, HasCertificate) {
|
||||
MockFileSystem file_system;
|
||||
std::string device_certificate_path =
|
||||
device_base_path_ + DeviceFiles::GetCertificateFileName(origin);
|
||||
device_base_path_ + DeviceFiles::GetCertificateFileName();
|
||||
|
||||
EXPECT_CALL(file, Exists(StrEq(device_certificate_path)))
|
||||
EXPECT_CALL(file_system, Exists(StrEq(device_certificate_path)))
|
||||
.WillOnce(Return(false))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Open(_, _)).Times(0);
|
||||
EXPECT_CALL(file_system, Open(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
ASSERT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
// MockFile returns false.
|
||||
EXPECT_FALSE(device_files.HasCertificate(origin));
|
||||
EXPECT_FALSE(device_files.HasCertificate());
|
||||
// MockFile returns true.
|
||||
EXPECT_TRUE(device_files.HasCertificate(origin));
|
||||
EXPECT_TRUE(device_files.HasCertificate());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CertificateUseTests, DeviceCertificateTest,
|
||||
::testing::Values(EMPTY_ORIGIN, kTestOrigin));
|
||||
|
||||
TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
|
||||
MockFile file;
|
||||
MockFileSystem file_system;
|
||||
std::string certificate(GenerateRandomData(kCertificateLen));
|
||||
std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen));
|
||||
|
||||
@@ -1693,55 +1689,39 @@ TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
|
||||
ASSERT_TRUE(
|
||||
Properties::GetDeviceFilesBasePath(security_level, &device_base_path));
|
||||
std::string device_certificate_path =
|
||||
device_base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
|
||||
device_base_path + DeviceFiles::GetCertificateFileName();
|
||||
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path)))
|
||||
.WillOnce(Return(false));
|
||||
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path)))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
|
||||
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
|
||||
.WillOnce(Return(true));
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system,
|
||||
Open(StrEq(device_certificate_path), IsCreateFileFlagSet()))
|
||||
.WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
|
||||
Gt(certificate.size() + wrapped_private_key.size())))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(security_level));
|
||||
device_files.SetTestFile(&file);
|
||||
EXPECT_TRUE(device_files.StoreCertificate(EMPTY_ORIGIN, certificate,
|
||||
wrapped_private_key));
|
||||
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(SecurityLevel, DeviceFilesSecurityLevelTest,
|
||||
::testing::Values(kSecurityLevelL1, kSecurityLevelL3));
|
||||
|
||||
TEST_P(DeviceFilesStoreTest, StoreLicense) {
|
||||
MockFile file;
|
||||
MockFileSystem file_system;
|
||||
size_t license_num = 0;
|
||||
std::string license_path = device_base_path_ +
|
||||
license_test_data[license_num].key_set_id +
|
||||
DeviceFiles::GetLicenseFileNameExtension();
|
||||
|
||||
bool dir_exists = GetParam();
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
.WillOnce(Return(dir_exists));
|
||||
if (dir_exists) {
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
} else {
|
||||
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_)))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
CdmAppParameterMap app_parameters =
|
||||
GetAppParameters(license_test_data[license_num].app_parameters);
|
||||
|
||||
EXPECT_CALL(file, Open(StrEq(license_path),
|
||||
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
|
||||
.WillOnce(Return(true));
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet()))
|
||||
.WillOnce(Return(&file));
|
||||
EXPECT_CALL(
|
||||
file, Write(Contains(license_test_data[license_num].pssh_data,
|
||||
license_test_data[license_num].key_request,
|
||||
@@ -1755,9 +1735,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
EXPECT_TRUE(device_files.StoreLicense(
|
||||
license_test_data[license_num].key_set_id,
|
||||
license_test_data[license_num].license_state,
|
||||
@@ -1774,11 +1753,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
|
||||
INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, ::testing::Bool());
|
||||
|
||||
TEST_F(DeviceFilesTest, StoreLicenses) {
|
||||
MockFileSystem file_system;
|
||||
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_ +
|
||||
@@ -1788,9 +1764,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
|
||||
CdmAppParameterMap app_parameters =
|
||||
GetAppParameters(license_test_data[i].app_parameters);
|
||||
|
||||
EXPECT_CALL(file, Open(StrEq(license_path),
|
||||
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet()))
|
||||
.WillOnce(Return(&file));
|
||||
|
||||
EXPECT_CALL(file, Write(Contains(license_test_data[i].pssh_data,
|
||||
license_test_data[i].key_request,
|
||||
@@ -1805,9 +1780,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
|
||||
EXPECT_CALL(file, Close()).Times(kNumberOfLicenses);
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
for (size_t i = 0; i < kNumberOfLicenses; i++) {
|
||||
CdmAppParameterMap app_parameters =
|
||||
GetAppParameters(license_test_data[i].app_parameters);
|
||||
@@ -1825,6 +1799,7 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
|
||||
}
|
||||
|
||||
TEST_F(DeviceFilesTest, RetrieveLicenses) {
|
||||
MockFileSystem file_system;
|
||||
MockFile file;
|
||||
|
||||
for (size_t i = 0; i < kNumberOfLicenses; ++i) {
|
||||
@@ -1834,10 +1809,12 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
|
||||
|
||||
size_t size = license_test_data[i].file_data.size();
|
||||
|
||||
EXPECT_CALL(file, Exists(StrEq(license_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
|
||||
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
|
||||
EXPECT_CALL(file_system, Exists(StrEq(license_path)))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(license_path)))
|
||||
.WillOnce(Return(size));
|
||||
EXPECT_CALL(file_system, Open(StrEq(license_path), _))
|
||||
.WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(size)))
|
||||
.WillOnce(
|
||||
DoAll(SetArrayArgument<0>(license_test_data[i].file_data.begin(),
|
||||
@@ -1847,9 +1824,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
|
||||
EXPECT_CALL(file, Close()).Times(kNumberOfLicenses);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
CdmInitData pssh_data;
|
||||
CdmKeyMessage key_request;
|
||||
CdmKeyResponse key_response;
|
||||
@@ -1886,7 +1862,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
|
||||
}
|
||||
|
||||
TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
|
||||
MockFile file;
|
||||
MockFileSystem file_system;
|
||||
LicenseInfo* test_data =
|
||||
&license_app_parameters_backwards_compatibility_test_data;
|
||||
|
||||
@@ -1895,10 +1871,12 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
|
||||
|
||||
size_t size = test_data->file_data.size();
|
||||
|
||||
EXPECT_CALL(file, Exists(StrEq(license_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
|
||||
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
|
||||
.WillOnce(Return(true));
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system, Exists(StrEq(license_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(license_path)))
|
||||
.WillOnce(Return(size));
|
||||
EXPECT_CALL(file_system, Open(StrEq(license_path), _))
|
||||
.WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(size)))
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(test_data->file_data.begin(),
|
||||
test_data->file_data.end()),
|
||||
@@ -1907,9 +1885,8 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
DeviceFiles::LicenseState license_state;
|
||||
CdmInitData pssh_data;
|
||||
CdmKeyMessage key_request;
|
||||
@@ -1936,102 +1913,16 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
|
||||
EXPECT_EQ(0u, app_parameters.size());
|
||||
}
|
||||
|
||||
TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
|
||||
if (!Properties::security_level_path_backward_compatibility_support()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MockFile file;
|
||||
std::vector<std::string> security_dirs;
|
||||
EXPECT_TRUE(Properties::GetSecurityLevelDirectories(&security_dirs));
|
||||
|
||||
size_t pos = std::string::npos;
|
||||
for (size_t i = 0; i < security_dirs.size(); ++i) {
|
||||
pos = device_base_path_.rfind(security_dirs[i]);
|
||||
if (std::string::npos != pos) break;
|
||||
}
|
||||
|
||||
EXPECT_NE(std::string::npos, pos);
|
||||
|
||||
std::string base_path(device_base_path_, 0, pos);
|
||||
std::vector<std::string> old_files;
|
||||
std::string new_path;
|
||||
for (size_t i = 0; i < security_dirs.size(); ++i) {
|
||||
old_files.push_back(security_dirs[i]);
|
||||
new_path = base_path + security_dirs[i];
|
||||
EXPECT_CALL(file, IsRegularFile(StrEq(new_path))).WillOnce(Return(false));
|
||||
EXPECT_CALL(file, Exists(StrEq(new_path)))
|
||||
.WillOnce(Return(false))
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, CreateDirectory(StrEq(new_path))).WillOnce(Return(true));
|
||||
}
|
||||
|
||||
std::string old_path =
|
||||
base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
|
||||
old_files.push_back(DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN));
|
||||
EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
|
||||
for (size_t i = 0; i < security_dirs.size(); ++i) {
|
||||
new_path = base_path + security_dirs[i] +
|
||||
DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
|
||||
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < kNumberOfLicenses; ++j) {
|
||||
std::string file_name = license_test_data[j].key_set_id +
|
||||
DeviceFiles::GetLicenseFileNameExtension();
|
||||
old_path = base_path + file_name;
|
||||
old_files.push_back(file_name);
|
||||
EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
|
||||
for (size_t i = 0; i < security_dirs.size(); ++i) {
|
||||
new_path = base_path + security_dirs[i] + file_name;
|
||||
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_CALL(file, List(StrEq(base_path), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(old_files), Return(true)));
|
||||
|
||||
std::string data = a2bs_hex(kTestCertificateFileData);
|
||||
|
||||
new_path =
|
||||
device_base_path_ + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
|
||||
EXPECT_CALL(file, Exists(StrEq(new_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, FileSize(_)).WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file, Open(_, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
|
||||
Return(data.size())));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
Properties::Init();
|
||||
std::string certificate, wrapped_private_key;
|
||||
ASSERT_TRUE(device_files.RetrieveCertificate(EMPTY_ORIGIN, &certificate,
|
||||
&wrapped_private_key));
|
||||
}
|
||||
|
||||
TEST_F(DeviceFilesTest, UpdateLicenseState) {
|
||||
MockFile file;
|
||||
MockFileSystem file_system;
|
||||
std::string license_path = device_base_path_ +
|
||||
license_update_test_data[0].key_set_id +
|
||||
DeviceFiles::GetLicenseFileNameExtension();
|
||||
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet()))
|
||||
.Times(2)
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
EXPECT_CALL(file, Open(StrEq(license_path),
|
||||
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
|
||||
.Times(2)
|
||||
.WillRepeatedly(Return(true));
|
||||
.WillRepeatedly(Return(&file));
|
||||
EXPECT_CALL(file, Write(IsStrEq(license_update_test_data[0].file_data),
|
||||
Eq(license_update_test_data[0].file_data.size())))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
@@ -2041,9 +1932,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
|
||||
EXPECT_CALL(file, Close()).Times(2);
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
EXPECT_TRUE(device_files.StoreLicense(
|
||||
license_update_test_data[0].key_set_id,
|
||||
license_update_test_data[0].license_state,
|
||||
@@ -2072,32 +1962,33 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
|
||||
}
|
||||
|
||||
TEST_F(DeviceFilesTest, DeleteLicense) {
|
||||
MockFile file;
|
||||
MockFileSystem file_system;
|
||||
std::string license_path = device_base_path_ +
|
||||
license_test_data[0].key_set_id +
|
||||
DeviceFiles::GetLicenseFileNameExtension();
|
||||
|
||||
size_t size = license_test_data[0].file_data.size();
|
||||
|
||||
EXPECT_CALL(file, Exists(StrEq(license_path)))
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system, Exists(StrEq(license_path)))
|
||||
.Times(2)
|
||||
.WillOnce(Return(true))
|
||||
.WillOnce(Return(false));
|
||||
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
|
||||
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(license_path)))
|
||||
.WillOnce(Return(size));
|
||||
EXPECT_CALL(file_system, Open(StrEq(license_path), _))
|
||||
.WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(size)))
|
||||
.WillOnce(
|
||||
DoAll(SetArrayArgument<0>(license_test_data[0].file_data.begin(),
|
||||
license_test_data[0].file_data.end()),
|
||||
Return(size)));
|
||||
EXPECT_CALL(file, Remove(StrEq(license_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, Remove(StrEq(license_path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
DeviceFiles::LicenseState license_state;
|
||||
CdmInitData pssh_data;
|
||||
CdmKeyMessage key_request;
|
||||
@@ -2135,17 +2026,11 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
|
||||
|
||||
TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) {
|
||||
// Validate that ReserveLicenseIds does not touch the file system.
|
||||
MockFile file;
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))).Times(0);
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
EXPECT_CALL(file, Open(_, _)).Times(0);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
EXPECT_CALL(file, Close()).Times(0);
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
MockFileSystem file_system;
|
||||
EXPECT_CALL(file_system, Open(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
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));
|
||||
// Validate that the license IDs are actually reserved.
|
||||
@@ -2156,6 +2041,7 @@ TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) {
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesUsageInfoTest, Read) {
|
||||
MockFileSystem file_system;
|
||||
MockFile file;
|
||||
std::string app_id; // TODO(fredgc): add tests with multiple app_ids.
|
||||
std::string path =
|
||||
@@ -2167,28 +2053,24 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
|
||||
data = kUsageInfoTestData[index].file_data;
|
||||
}
|
||||
if (index >= 0) {
|
||||
EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, FileSize(StrEq(path)))
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(path)))
|
||||
.WillRepeatedly(Return(data.size()));
|
||||
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
|
||||
Return(data.size())));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
} else {
|
||||
EXPECT_CALL(file, Exists(StrEq(path)))
|
||||
.WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(file, FileSize(_)).Times(0);
|
||||
EXPECT_CALL(file, Open(_, _)).Times(0);
|
||||
EXPECT_CALL(file, Close()).Times(0);
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(file_system, FileSize(_)).Times(0);
|
||||
EXPECT_CALL(file_system, Open(_, _)).Times(0);
|
||||
}
|
||||
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
|
||||
ASSERT_TRUE(device_files.RetrieveUsageInfo(app_id, &license_info));
|
||||
@@ -2212,7 +2094,7 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesUsageInfoTest, Store) {
|
||||
MockFile file;
|
||||
MockFileSystem file_system;
|
||||
std::string app_id; // TODO(fredgc): multiple app ids.
|
||||
std::string pst(GenerateRandomData(kProviderSessionTokenLen));
|
||||
std::string license_request(GenerateRandomData(kLicenseRequestLen));
|
||||
@@ -2226,24 +2108,24 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
|
||||
if (index >= 0) {
|
||||
data = kUsageInfoTestData[index].file_data;
|
||||
}
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
|
||||
EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(index >= 0));
|
||||
MockFile file;
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path)))
|
||||
.WillRepeatedly(Return(index >= 0));
|
||||
if (index >= 0) {
|
||||
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(path)))
|
||||
.WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file_system, Open(StrEq(path), _))
|
||||
.Times(2)
|
||||
.WillRepeatedly(Return(true));
|
||||
.WillRepeatedly(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
|
||||
Return(data.size())));
|
||||
EXPECT_CALL(file, Close()).Times(2);
|
||||
} else {
|
||||
EXPECT_CALL(file, FileSize(_)).Times(0);
|
||||
EXPECT_CALL(file, Open(_, _)).Times(1).WillOnce(Return(true));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file_system, FileSize(_)).Times(0);
|
||||
EXPECT_CALL(file_system, Open(_, _)).Times(1).WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Close());
|
||||
}
|
||||
|
||||
EXPECT_CALL(file,
|
||||
@@ -2253,9 +2135,8 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
|
||||
key_set_id.size())))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
ASSERT_TRUE(
|
||||
device_files.StoreUsageInfo(pst, license_request, license, app_id,
|
||||
@@ -2263,6 +2144,7 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesUsageInfoTest, Delete) {
|
||||
MockFileSystem file_system;
|
||||
MockFile file;
|
||||
std::string app_id; // TODO(fredgc): expand tests.
|
||||
std::string path =
|
||||
@@ -2282,24 +2164,19 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(index >= 0));
|
||||
|
||||
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(index >= 0));
|
||||
|
||||
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(path))).WillOnce(Return(data.size()));
|
||||
if (index >= 1) {
|
||||
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
|
||||
EXPECT_CALL(file_system, Open(StrEq(path), _))
|
||||
.Times(2)
|
||||
.WillRepeatedly(Return(true));
|
||||
.WillRepeatedly(Return(&file));
|
||||
EXPECT_CALL(file, Write(Contains(prev_pst, prev_license, prev_data.size()),
|
||||
Gt(prev_pst.size() + prev_license.size())))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
EXPECT_CALL(file, Close()).Times(2);
|
||||
} else {
|
||||
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
}
|
||||
@@ -2307,9 +2184,8 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
|
||||
Return(data.size())));
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
if (index >= 1) {
|
||||
ASSERT_TRUE(device_files.DeleteUsageInfo(app_id, pst));
|
||||
@@ -2319,36 +2195,33 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesUsageInfoTest, DeleteAll) {
|
||||
MockFileSystem file_system;
|
||||
MockFile file;
|
||||
std::string app_id; // TODO(fredgc): expand tests.
|
||||
std::string path =
|
||||
device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
|
||||
|
||||
int index = GetParam();
|
||||
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
|
||||
.WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
std::string data;
|
||||
if (index < 0) {
|
||||
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(false));
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(false));
|
||||
} else {
|
||||
data = kUsageInfoTestData[index].file_data;
|
||||
EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(path)))
|
||||
.WillOnce(Return(data.size()));
|
||||
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
|
||||
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
|
||||
Return(data.size())));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
EXPECT_CALL(file, Remove(StrEq(path))).WillOnce(Return(true));
|
||||
EXPECT_CALL(file_system, Remove(StrEq(path))).WillOnce(Return(true));
|
||||
}
|
||||
|
||||
DeviceFiles device_files;
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
device_files.SetTestFile(&file);
|
||||
|
||||
std::vector<std::string> psts;
|
||||
ASSERT_TRUE(device_files.DeleteAllUsageInfoForApp(app_id, &psts));
|
||||
@@ -2367,4 +2240,75 @@ TEST_P(DeviceFilesUsageInfoTest, DeleteAll) {
|
||||
INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageInfoTest,
|
||||
::testing::Range(-1, 4));
|
||||
|
||||
TEST_P(DeviceFilesHlsAttributesTest, Read) {
|
||||
MockFileSystem file_system;
|
||||
MockFile file;
|
||||
HlsAttributesInfo* param = GetParam();
|
||||
std::string path = device_base_path_ + param->key_set_id +
|
||||
DeviceFiles::GetHlsAttributesFileNameExtension();
|
||||
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file_system, FileSize(StrEq(path)))
|
||||
.WillRepeatedly(Return(param->file_data.size()));
|
||||
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Read(NotNull(), Eq(param->file_data.size())))
|
||||
.WillOnce(DoAll(
|
||||
SetArrayArgument<0>(param->file_data.begin(), param->file_data.end()),
|
||||
Return(param->file_data.size())));
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
|
||||
EXPECT_CALL(file, Write(_, _)).Times(0);
|
||||
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
|
||||
CdmHlsMethod method;
|
||||
std::vector<uint8_t> media_segment_iv;
|
||||
ASSERT_TRUE(device_files.RetrieveHlsAttributes(param->key_set_id, &method,
|
||||
&media_segment_iv));
|
||||
EXPECT_EQ(param->method, method);
|
||||
EXPECT_EQ(b2a_hex(param->media_segment_iv), b2a_hex(media_segment_iv));
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesHlsAttributesTest, Store) {
|
||||
MockFileSystem file_system;
|
||||
MockFile file;
|
||||
HlsAttributesInfo* param = GetParam();
|
||||
std::string path = device_base_path_ + param->key_set_id +
|
||||
DeviceFiles::GetHlsAttributesFileNameExtension();
|
||||
|
||||
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
|
||||
EXPECT_CALL(file, Write(Contains(param->media_segment_iv, 0),
|
||||
Gt(param->media_segment_iv.size())))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
EXPECT_CALL(file, Read(_, _)).Times(0);
|
||||
EXPECT_CALL(file, Close()).Times(1);
|
||||
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
std::vector<uint8_t> iv(param->media_segment_iv.begin(),
|
||||
param->media_segment_iv.end());
|
||||
ASSERT_TRUE(
|
||||
device_files.StoreHlsAttributes(param->key_set_id, param->method, iv));
|
||||
}
|
||||
|
||||
TEST_P(DeviceFilesHlsAttributesTest, Delete) {
|
||||
MockFileSystem file_system;
|
||||
MockFile file;
|
||||
HlsAttributesInfo* param = GetParam();
|
||||
std::string path = device_base_path_ + param->key_set_id +
|
||||
DeviceFiles::GetHlsAttributesFileNameExtension();
|
||||
|
||||
EXPECT_CALL(file_system, Remove(StrEq(path))).WillOnce(Return(true));
|
||||
|
||||
DeviceFiles device_files(&file_system);
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
ASSERT_TRUE(device_files.DeleteHlsAttributes(param->key_set_id));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(HlsAttributes, DeviceFilesHlsAttributesTest,
|
||||
::testing::Range(&kHlsAttributesTestData[0],
|
||||
&kHlsAttributesTestData[2]));
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "device_files.h"
|
||||
|
||||
#include "file_store.h"
|
||||
#include "properties.h"
|
||||
#include "test_vectors.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -18,20 +17,12 @@ const std::string kWildcard = "*";
|
||||
|
||||
class FileTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() { CreateTestDir(); }
|
||||
FileTest() {}
|
||||
|
||||
virtual void TearDown() { RemoveTestDir(); }
|
||||
|
||||
void CreateTestDir() {
|
||||
File file;
|
||||
if (!file.Exists(test_vectors::kTestDir)) {
|
||||
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
|
||||
}
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
|
||||
}
|
||||
|
||||
void RemoveTestDir() {
|
||||
File file;
|
||||
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
|
||||
EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir));
|
||||
}
|
||||
|
||||
std::string GenerateRandomData(uint32_t len) {
|
||||
@@ -41,64 +32,52 @@ class FileTest : public testing::Test {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
FileSystem file_system;
|
||||
};
|
||||
|
||||
TEST_F(FileTest, FileExists) {
|
||||
File file;
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kExistentFile));
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kExistentDir));
|
||||
EXPECT_FALSE(file.Exists(test_vectors::kNonExistentFile));
|
||||
EXPECT_FALSE(file.Exists(test_vectors::kNonExistentDir));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, CreateDirectory) {
|
||||
File file;
|
||||
std::string dir_wo_delimiter =
|
||||
test_vectors::kTestDir.substr(0, test_vectors::kTestDir.size() - 1);
|
||||
if (file.Exists(dir_wo_delimiter)) EXPECT_TRUE(file.Remove(dir_wo_delimiter));
|
||||
EXPECT_FALSE(file.Exists(dir_wo_delimiter));
|
||||
EXPECT_TRUE(file.CreateDirectory(dir_wo_delimiter));
|
||||
EXPECT_TRUE(file.Exists(dir_wo_delimiter));
|
||||
EXPECT_TRUE(file.Remove(dir_wo_delimiter));
|
||||
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
|
||||
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
|
||||
EXPECT_TRUE(file_system.Exists(test_vectors::kExistentFile));
|
||||
EXPECT_TRUE(file_system.Exists(test_vectors::kExistentDir));
|
||||
EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentFile));
|
||||
EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentDir));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, RemoveDir) {
|
||||
File file;
|
||||
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
|
||||
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
|
||||
EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir));
|
||||
EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, OpenFile) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
File handle;
|
||||
EXPECT_TRUE(handle.Remove(path));
|
||||
EXPECT_TRUE(file_system.Remove(path));
|
||||
|
||||
File file;
|
||||
EXPECT_TRUE(file.Open(path, File::kCreate));
|
||||
file.Close();
|
||||
File* file = file_system.Open(path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
|
||||
EXPECT_TRUE(handle.Exists(path));
|
||||
EXPECT_TRUE(file_system.Exists(path));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, RemoveDirAndFile) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
|
||||
File file;
|
||||
EXPECT_TRUE(file.Open(path, File::kCreate));
|
||||
file.Close();
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
EXPECT_TRUE(file.Remove(path));
|
||||
EXPECT_FALSE(file.Exists(path));
|
||||
File* file = file_system.Open(path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
|
||||
EXPECT_TRUE(file.Open(path, File::kCreate));
|
||||
file.Close();
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
EXPECT_TRUE(file_system.Exists(path));
|
||||
EXPECT_TRUE(file_system.Remove(path));
|
||||
EXPECT_FALSE(file_system.Exists(path));
|
||||
|
||||
file = file_system.Open(path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
|
||||
EXPECT_TRUE(file_system.Exists(path));
|
||||
RemoveTestDir();
|
||||
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
|
||||
EXPECT_FALSE(file.Exists(path));
|
||||
EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir));
|
||||
EXPECT_FALSE(file_system.Exists(path));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, RemoveWildcardFiles) {
|
||||
@@ -107,159 +86,53 @@ TEST_F(FileTest, RemoveWildcardFiles) {
|
||||
std::string wildcard_path =
|
||||
test_vectors::kTestDir + kWildcard + kTestFileNameExt;
|
||||
|
||||
File file;
|
||||
EXPECT_TRUE(file.Open(path1, File::kCreate));
|
||||
file.Close();
|
||||
EXPECT_TRUE(file.Open(path2, File::kCreate));
|
||||
file.Close();
|
||||
EXPECT_TRUE(file.Exists(path1));
|
||||
EXPECT_TRUE(file.Exists(path2));
|
||||
EXPECT_TRUE(file.Remove(wildcard_path));
|
||||
EXPECT_FALSE(file.Exists(path1));
|
||||
EXPECT_FALSE(file.Exists(path2));
|
||||
}
|
||||
File* file = file_system.Open(path1, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
file = file_system.Open(path2, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
|
||||
TEST_F(FileTest, IsDir) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
File file;
|
||||
EXPECT_TRUE(file.Open(path, File::kCreate));
|
||||
file.Close();
|
||||
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
|
||||
EXPECT_FALSE(file.IsDirectory(path));
|
||||
EXPECT_TRUE(file.IsDirectory(test_vectors::kTestDir));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, IsRegularFile) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
File file;
|
||||
EXPECT_TRUE(file.Open(path, File::kCreate));
|
||||
file.Close();
|
||||
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
|
||||
EXPECT_TRUE(file.IsRegularFile(path));
|
||||
EXPECT_FALSE(file.IsRegularFile(test_vectors::kTestDir));
|
||||
EXPECT_TRUE(file_system.Exists(path1));
|
||||
EXPECT_TRUE(file_system.Exists(path2));
|
||||
EXPECT_TRUE(file_system.Remove(wildcard_path));
|
||||
EXPECT_FALSE(file_system.Exists(path1));
|
||||
EXPECT_FALSE(file_system.Exists(path2));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, FileSize) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
File file;
|
||||
file.Remove(path);
|
||||
file_system.Remove(path);
|
||||
|
||||
std::string write_data = GenerateRandomData(600);
|
||||
File wr_file;
|
||||
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
|
||||
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
|
||||
wr_file.Close();
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
File* file = file_system.Open(path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(file->Write(write_data.data(), write_data.size()));
|
||||
file->Close();
|
||||
EXPECT_TRUE(file_system.Exists(path));
|
||||
|
||||
EXPECT_EQ(static_cast<ssize_t>(write_data.size()), file.FileSize(path));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, WriteReadTextFile) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
File file;
|
||||
file.Remove(path);
|
||||
|
||||
std::string write_data = "This is a test";
|
||||
File wr_file;
|
||||
EXPECT_TRUE(wr_file.Open(path, File::kCreate));
|
||||
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
|
||||
wr_file.Close();
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
|
||||
std::string read_data;
|
||||
read_data.resize(file.FileSize(path));
|
||||
File rd_file;
|
||||
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
|
||||
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
|
||||
rd_file.Close();
|
||||
EXPECT_EQ(write_data, read_data);
|
||||
EXPECT_EQ(static_cast<ssize_t>(write_data.size()),
|
||||
file_system.FileSize(path));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, WriteReadBinaryFile) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
File file;
|
||||
file.Remove(path);
|
||||
file_system.Remove(path);
|
||||
|
||||
std::string write_data = GenerateRandomData(600);
|
||||
File wr_file;
|
||||
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
|
||||
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
|
||||
wr_file.Close();
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
File* file = file_system.Open(path, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(file->Write(write_data.data(), write_data.size()));
|
||||
file->Close();
|
||||
EXPECT_TRUE(file_system.Exists(path));
|
||||
|
||||
std::string read_data;
|
||||
read_data.resize(file.FileSize(path));
|
||||
File rd_file;
|
||||
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
|
||||
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
|
||||
rd_file.Close();
|
||||
read_data.resize(file_system.FileSize(path));
|
||||
file = file_system.Open(path, FileSystem::kReadOnly);
|
||||
ASSERT_TRUE(file);
|
||||
EXPECT_TRUE(file->Read(&read_data[0], read_data.size()));
|
||||
file->Close();
|
||||
EXPECT_EQ(write_data, read_data);
|
||||
}
|
||||
|
||||
TEST_F(FileTest, CopyFile) {
|
||||
std::string path = test_vectors::kTestDir + kTestFileName;
|
||||
File file;
|
||||
file.Remove(path);
|
||||
|
||||
std::string write_data = GenerateRandomData(600);
|
||||
File wr_file;
|
||||
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
|
||||
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
|
||||
wr_file.Close();
|
||||
ASSERT_TRUE(file.Exists(path));
|
||||
|
||||
std::string path_copy = test_vectors::kTestDir + kTestFileName2;
|
||||
EXPECT_FALSE(file.Exists(path_copy));
|
||||
EXPECT_TRUE(file.Copy(path, path_copy));
|
||||
|
||||
std::string read_data;
|
||||
read_data.resize(file.FileSize(path_copy));
|
||||
File rd_file;
|
||||
EXPECT_TRUE(rd_file.Open(path_copy, File::kReadOnly));
|
||||
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
|
||||
rd_file.Close();
|
||||
EXPECT_EQ(write_data, read_data);
|
||||
EXPECT_EQ(file.FileSize(path), file.FileSize(path_copy));
|
||||
}
|
||||
|
||||
TEST_F(FileTest, ListEmptyDirectory) {
|
||||
std::vector<std::string> files;
|
||||
File file;
|
||||
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
|
||||
EXPECT_EQ(0u, files.size());
|
||||
}
|
||||
|
||||
TEST_F(FileTest, ListFiles) {
|
||||
File file;
|
||||
std::string path = test_vectors::kTestDir + kTestDirName;
|
||||
EXPECT_TRUE(file.CreateDirectory(path));
|
||||
|
||||
path = test_vectors::kTestDir + kTestFileName;
|
||||
std::string write_data = GenerateRandomData(600);
|
||||
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
|
||||
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
|
||||
file.Close();
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
|
||||
path = test_vectors::kTestDir + kTestFileName2;
|
||||
write_data = GenerateRandomData(600);
|
||||
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
|
||||
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
|
||||
file.Close();
|
||||
EXPECT_TRUE(file.Exists(path));
|
||||
|
||||
std::vector<std::string> files;
|
||||
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
|
||||
EXPECT_EQ(3u, files.size());
|
||||
|
||||
for (size_t i = 0; i < files.size(); ++i) {
|
||||
EXPECT_TRUE(files[i] == kTestDirName || files[i] == kTestFileName ||
|
||||
files[i] == kTestFileName2);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
416
core/test/generic_crypto_unittest.cpp
Normal file
416
core/test/generic_crypto_unittest.cpp
Normal file
@@ -0,0 +1,416 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
// These tests are for the generic crypto operations. They call on the
|
||||
// CdmEngine class and exercise the classes below it as well. 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 <arpa/inet.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
|
||||
#include "cdm_engine.h"
|
||||
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
#include "oec_session_util.h"
|
||||
#include "../../oemcrypto/mock/src/oemcrypto_key_mock.h"
|
||||
#include "string_conversions.h"
|
||||
#include "url_request.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kKeySystem = "com.widevine.alpha";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class WvGenericOperationsTest : public testing::Test {
|
||||
public:
|
||||
virtual void SetUp() {
|
||||
::testing::Test::SetUp();
|
||||
|
||||
// Load test keybox. This keybox will be used by any CryptoSession
|
||||
// created by the CDM under test.
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox());
|
||||
|
||||
// Perform CdmEngine setup
|
||||
cdm_engine_ = new CdmEngine(&file_system_);
|
||||
|
||||
CdmResponseType status =
|
||||
cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
|
||||
if (status == NEED_PROVISIONING) {
|
||||
Provision();
|
||||
status = cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
|
||||
}
|
||||
ASSERT_EQ(NO_ERROR, status);
|
||||
ASSERT_NE("", session_id_) << "Could not open CDM session.";
|
||||
ASSERT_TRUE(cdm_engine_->IsOpenSession(session_id_));
|
||||
|
||||
// Get OEMCrypto session ID from the CDM
|
||||
CdmQueryMap query;
|
||||
cdm_engine_->QueryOemCryptoSessionId(session_id_, &query);
|
||||
std::istringstream parse_int;
|
||||
parse_int.str(query[QUERY_KEY_OEMCRYPTO_SESSION_ID]);
|
||||
parse_int >> oec_session_id_;
|
||||
|
||||
// Construct and install keys into the CDM's OEMCrypto session.
|
||||
OecSessionSetup(oec_session_id_);
|
||||
EncryptAndLoadKeys();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
oec_util_session_.close();
|
||||
cdm_engine_->CloseSession(session_id_);
|
||||
// OEMCrypto_Terminate() will be performed during the test class's
|
||||
// destruction (specifically by the CryptoSession destructor)
|
||||
}
|
||||
|
||||
void OecSessionSetup(uint32_t oec_session_id) {
|
||||
buffer_size_ = 160;
|
||||
oec_util_session_.SetSessionId(oec_session_id);
|
||||
oec_util_session_.GenerateTestSessionKeys();
|
||||
MakeFourKeys();
|
||||
}
|
||||
|
||||
enum GenericKeyType {
|
||||
kGenericEncrypt = 0,
|
||||
kGenericDecrypt = 1,
|
||||
kGenericSign = 2,
|
||||
kGenericVerify = 3
|
||||
};
|
||||
|
||||
virtual void MakeFourKeys(
|
||||
uint32_t duration = wvoec::kDuration, uint32_t control = 0,
|
||||
uint32_t nonce = 0, const std::string& pst = "") {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
oec_util_session_.FillSimpleMessage(duration, control, nonce, pst));
|
||||
oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |=
|
||||
htonl(wvoec_mock::kControlAllowEncrypt);
|
||||
oec_util_session_.license().keys[kGenericDecrypt].control.control_bits |=
|
||||
htonl(wvoec_mock::kControlAllowDecrypt);
|
||||
oec_util_session_.license().keys[kGenericSign].control.control_bits |=
|
||||
htonl(wvoec_mock::kControlAllowSign);
|
||||
oec_util_session_.license().keys[kGenericVerify].control.control_bits |=
|
||||
htonl(wvoec_mock::kControlAllowVerify);
|
||||
|
||||
oec_util_session_.license().keys[kGenericSign].key_data_length =
|
||||
wvcdm::MAC_KEY_SIZE;
|
||||
oec_util_session_.license().keys[kGenericVerify].key_data_length =
|
||||
wvcdm::MAC_KEY_SIZE;
|
||||
|
||||
clear_buffer_.assign(buffer_size_, 0);
|
||||
for (size_t i = 0; i < clear_buffer_.size(); i++) {
|
||||
clear_buffer_[i] = 1 + i % 250;
|
||||
}
|
||||
for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) {
|
||||
iv_[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetKeyId(GenericKeyType type) {
|
||||
std::string key_id;
|
||||
size_t key_id_length = oec_util_session_.license().keys[0].key_id_length;
|
||||
key_id.assign(
|
||||
&(oec_util_session_.license().keys[type].key_id[0]),
|
||||
&(oec_util_session_.license().keys[type].key_id[key_id_length]));
|
||||
return key_id;
|
||||
}
|
||||
|
||||
std::string GetClearBuffer() {
|
||||
std::string buffer;
|
||||
size_t buffer_length = clear_buffer_.size();
|
||||
buffer.assign(&clear_buffer_[0], &clear_buffer_[buffer_length]);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string GetEncryptedBuffer() {
|
||||
std::string buffer;
|
||||
size_t buffer_length = encrypted_buffer_.size();
|
||||
buffer.assign(&encrypted_buffer_[0], &encrypted_buffer_[buffer_length]);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string GetIvBlock() {
|
||||
std::string buffer;
|
||||
size_t buffer_length = wvcdm::KEY_IV_SIZE;
|
||||
buffer.assign(&iv_[0], &iv_[buffer_length]);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string GetSignatureBuffer() {
|
||||
std::string buffer;
|
||||
buffer.resize(SHA256_DIGEST_LENGTH);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void EncryptAndLoadKeys() {
|
||||
ASSERT_NO_FATAL_FAILURE(oec_util_session_.EncryptAndSign());
|
||||
oec_util_session_.LoadTestKeys();
|
||||
}
|
||||
|
||||
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);
|
||||
EXPECT_TRUE(url_request.is_connected());
|
||||
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));
|
||||
}
|
||||
|
||||
// This CryptoSession object handles Initialization and Termination
|
||||
// calls on OEMCrypto for the duration of the test. CryptoSessions
|
||||
// created by the CDM will share the OEMCrypto state of this CryptoSession,
|
||||
// including, for example, a test keybox.
|
||||
CryptoSession crypto_session_;
|
||||
|
||||
FileSystem file_system_;
|
||||
CdmEngine* cdm_engine_;
|
||||
std::string key_msg_;
|
||||
std::string session_id_;
|
||||
std::string server_url_;
|
||||
uint32_t oec_session_id_;
|
||||
wvoec::Session oec_util_session_;
|
||||
size_t buffer_size_;
|
||||
vector<uint8_t> clear_buffer_;
|
||||
vector<uint8_t> encrypted_buffer_;
|
||||
uint8_t iv_[wvcdm::KEY_IV_SIZE];
|
||||
};
|
||||
|
||||
TEST_F(WvGenericOperationsTest, NormalSessionOpenClose) {
|
||||
wvoec::Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenerateSessionKeys) {
|
||||
wvoec::Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.GenerateTestSessionKeys());
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericEncryptNoKey) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string out_buffer = GetEncryptedBuffer();
|
||||
std::string iv = GetIvBlock();
|
||||
|
||||
// No key
|
||||
KeyId key_id("xyz");
|
||||
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
|
||||
wvcdm::kEncryptionAlgorithmAesCbc128,
|
||||
&out_buffer);
|
||||
EXPECT_EQ(KEY_ERROR_1, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericEncryptKeyNotAllowed) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string out_buffer = GetEncryptedBuffer();
|
||||
std::string iv = GetIvBlock();
|
||||
|
||||
// Wrong key
|
||||
std::string key_id = GetKeyId(kGenericDecrypt);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
|
||||
wvcdm::kEncryptionAlgorithmAesCbc128,
|
||||
&out_buffer);
|
||||
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericEncryptGood) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string out_buffer = GetEncryptedBuffer();
|
||||
std::string iv = GetIvBlock();
|
||||
|
||||
// Good key
|
||||
std::string key_id = GetKeyId(kGenericEncrypt);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
|
||||
wvcdm::kEncryptionAlgorithmAesCbc128,
|
||||
&out_buffer);
|
||||
EXPECT_EQ(NO_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericDecryptKeyNotAllowed) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string out_buffer = GetEncryptedBuffer();
|
||||
std::string iv = GetIvBlock();
|
||||
|
||||
// Wrong key
|
||||
std::string key_id = GetKeyId(kGenericEncrypt);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv,
|
||||
wvcdm::kEncryptionAlgorithmAesCbc128,
|
||||
&out_buffer);
|
||||
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericDecryptGood) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string out_buffer = GetEncryptedBuffer();
|
||||
std::string iv = GetIvBlock();
|
||||
|
||||
// Good key
|
||||
std::string key_id = GetKeyId(kGenericDecrypt);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv,
|
||||
wvcdm::kEncryptionAlgorithmAesCbc128,
|
||||
&out_buffer);
|
||||
EXPECT_EQ(NO_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericSignKeyNotAllowed) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string signature_buffer;
|
||||
|
||||
// Wrong key
|
||||
std::string key_id = GetKeyId(kGenericVerify);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
|
||||
wvcdm::kSigningAlgorithmHmacSha256,
|
||||
&signature_buffer);
|
||||
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericSignGood) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string signature_buffer;
|
||||
|
||||
// Good key
|
||||
std::string key_id = GetKeyId(kGenericSign);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
|
||||
wvcdm::kSigningAlgorithmHmacSha256,
|
||||
&signature_buffer);
|
||||
EXPECT_EQ(NO_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericVerifyKeyNotAllowed) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string signature_buffer = GetSignatureBuffer();
|
||||
|
||||
// Wrong key
|
||||
std::string key_id = GetKeyId(kGenericSign);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
|
||||
wvcdm::kSigningAlgorithmHmacSha256,
|
||||
signature_buffer);
|
||||
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsTest, GenericVerifyGood) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string signature_buffer = GetSignatureBuffer();
|
||||
|
||||
// Good key - signature not set.
|
||||
std::string key_id = GetKeyId(kGenericVerify);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
|
||||
wvcdm::kSigningAlgorithmHmacSha256,
|
||||
signature_buffer);
|
||||
// OEMCrypto error is OEMCrypto_ERROR_SIGNATURE_FAILURE
|
||||
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
class WvGenericOperationsDataTest : public WvGenericOperationsTest {
|
||||
public:
|
||||
// Construct keys for encrypt/decrypt and for sign/verify
|
||||
virtual void MakeFourKeys(
|
||||
uint32_t duration = wvoec::kDuration, uint32_t control = 0,
|
||||
uint32_t nonce = 0, const std::string& pst = "") {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
oec_util_session_.FillSimpleMessage(duration, control, nonce, pst));
|
||||
oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |=
|
||||
htonl(wvoec_mock::kControlAllowEncrypt |
|
||||
wvoec_mock::kControlAllowDecrypt);
|
||||
oec_util_session_.license().keys[kGenericSign].control.control_bits |=
|
||||
htonl(wvoec_mock::kControlAllowSign | wvoec_mock::kControlAllowVerify);
|
||||
|
||||
oec_util_session_.license().keys[kGenericSign].key_data_length =
|
||||
wvcdm::MAC_KEY_SIZE;
|
||||
|
||||
clear_buffer_.assign(buffer_size_, 0);
|
||||
for (size_t i = 0; i < clear_buffer_.size(); i++) {
|
||||
clear_buffer_[i] = 1 + i % 250;
|
||||
}
|
||||
for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) {
|
||||
iv_[i] = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(WvGenericOperationsDataTest, GenericEncryptDecrypt) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string encrypted_buffer = GetEncryptedBuffer();
|
||||
std::string iv = GetIvBlock();
|
||||
|
||||
// Encrypt
|
||||
std::string key_id = GetKeyId(kGenericEncrypt);
|
||||
|
||||
cdm_sts = cdm_engine_->GenericEncrypt(
|
||||
session_id_, in_buffer, key_id, iv, wvcdm::kEncryptionAlgorithmAesCbc128,
|
||||
&encrypted_buffer);
|
||||
|
||||
EXPECT_EQ(NO_ERROR, cdm_sts);
|
||||
|
||||
// Decrypt, use same key as encrypt.
|
||||
key_id = GetKeyId(kGenericEncrypt);
|
||||
|
||||
std::string final_buffer;
|
||||
final_buffer.resize(in_buffer.size());
|
||||
|
||||
cdm_sts = cdm_engine_->GenericDecrypt(
|
||||
session_id_, encrypted_buffer, key_id, iv,
|
||||
wvcdm::kEncryptionAlgorithmAesCbc128, &final_buffer);
|
||||
|
||||
EXPECT_EQ(NO_ERROR, cdm_sts);
|
||||
EXPECT_EQ(0, in_buffer.compare(final_buffer));
|
||||
}
|
||||
|
||||
TEST_F(WvGenericOperationsDataTest, GenericSignVerify) {
|
||||
CdmResponseType cdm_sts;
|
||||
std::string in_buffer = GetClearBuffer();
|
||||
std::string signature_buffer = GetSignatureBuffer();
|
||||
|
||||
// Signing key
|
||||
std::string key_id = GetKeyId(kGenericSign);
|
||||
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
|
||||
wvcdm::kSigningAlgorithmHmacSha256,
|
||||
&signature_buffer);
|
||||
EXPECT_EQ(NO_ERROR, cdm_sts);
|
||||
|
||||
// Verify signature, use same key as sign.
|
||||
key_id = GetKeyId(kGenericSign);
|
||||
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
|
||||
wvcdm::kSigningAlgorithmHmacSha256,
|
||||
signature_buffer);
|
||||
EXPECT_EQ(NO_ERROR, cdm_sts);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -41,9 +41,12 @@ SSL_CTX* InitSslContext() {
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
method = SSLv3_client_method();
|
||||
method = TLSv1_2_client_method();
|
||||
ctx = SSL_CTX_new(method);
|
||||
if (!ctx) LOGE("failed to create SSL context");
|
||||
int ret = SSL_CTX_set_cipher_list(
|
||||
ctx, "ALL:!RC4-MD5:!RC4-SHA:!ECDHE-ECDSA-RC4-SHA:!ECDHE-RSA-RC4-SHA");
|
||||
if (0 != ret) LOGE("error disabling vulnerable ciphers");
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -294,6 +297,10 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) {
|
||||
int total_read = 0;
|
||||
int to_read = len;
|
||||
|
||||
if (socket_fd_ == -1) {
|
||||
LOGE("Socket to %s not open. Cannot read.", domain_name_.c_str());
|
||||
return -1;
|
||||
}
|
||||
while (to_read > 0) {
|
||||
if (!SocketWait(socket_fd_, /* for_read */ true, timeout_in_ms)) {
|
||||
LOGE("unable to read from %s", domain_name_.c_str());
|
||||
@@ -329,6 +336,10 @@ int HttpSocket::Write(const char* data, int len, int timeout_in_ms) {
|
||||
int total_sent = 0;
|
||||
int to_send = len;
|
||||
|
||||
if (socket_fd_ == -1) {
|
||||
LOGE("Socket to %s not open. Cannot write.", domain_name_.c_str());
|
||||
return -1;
|
||||
}
|
||||
while (to_send > 0) {
|
||||
int sent;
|
||||
if (secure_connect_)
|
||||
|
||||
@@ -1,19 +1,44 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "initialization_data.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
// References:
|
||||
// [1] http://dashif.org/identifiers/content-protection/
|
||||
// [2] http://www.w3.org/TR/encrypted-media/cenc-format.html#common-system
|
||||
// [3] https://tools.ietf.org/html/draft-pantos-http-live-streaming-18
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::WidevineCencHeader;
|
||||
|
||||
namespace {
|
||||
|
||||
// Constants for JSON formatting
|
||||
const std::string kLeftBrace = "{";
|
||||
const std::string kRightBrace = "}";
|
||||
const std::string kLeftBracket = "[";
|
||||
const std::string kRightBracket = "]";
|
||||
const std::string kComma = ",";
|
||||
const std::string kColon = ":";
|
||||
const std::string kDoubleQuote = "\"";
|
||||
const std::string kNewline = "\n";
|
||||
const std::string kFourSpaceIndent = " ";
|
||||
|
||||
const std::string kJsonProvider = "provider";
|
||||
const std::string kJsonContentId = "content_id";
|
||||
const std::string kJsonKeyIds = "key_ids";
|
||||
|
||||
const uint32_t kFourCcCbc1 = 0x63626331;
|
||||
const uint32_t kFourCcCbcs = 0x63626373;
|
||||
|
||||
const std::string kWidevinePssh = a2bs_hex(
|
||||
// Widevine PSSH box
|
||||
"00000042" // atom size
|
||||
@@ -139,8 +164,245 @@ const std::string kZeroSizedPsshBox = a2bs_hex(
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
// HLS test attribute key and values
|
||||
const std::string kHlsIvHexValue = "6DF49213A781E338628D0E9C812D328E";
|
||||
const std::string kHlsIvValue = "0x" + kHlsIvHexValue;
|
||||
const std::string kHlsKeyFormatValue = "com.widevine.alpha";
|
||||
const std::string kHlsKeyFormatValueOther = "com.example";
|
||||
const std::string kHlsTestKey1 = "TESTKEY1";
|
||||
const std::string kHlsTestValue1 = "testvalue1";
|
||||
const std::string kHlsTestKey2 = "TESTKEY2";
|
||||
const std::string kHlsTestValue2 = "testvalue2";
|
||||
const std::string kHlsTestInvalidLowercaseKey = "testkey3";
|
||||
const std::string kHlsTestKeyWithDash = "TEST-KEY4";
|
||||
const std::string kHlsTestInvalidNonAlphanumKey = "TEST;KEY4";
|
||||
const std::string kHlsTestValueWithEmbeddedQuote = "test\"value1";
|
||||
const std::string kHlsTestEmptyHexValue = "";
|
||||
const std::string kHlsTestNoHexValue = "0x";
|
||||
const std::string kHlsTestHexValueWithOddBytes = kHlsIvHexValue + "7";
|
||||
const std::string kHlsTestInvalidHexValue = kHlsIvHexValue + "g7";
|
||||
char kHlsTestKeyFormatVersionsSeparator = '/';
|
||||
const std::string kHlsTestUriDataFormat = "data:text/plain;base64,";
|
||||
const std::string kHlsTestProvider = "youtube";
|
||||
const std::string kHlsTestContentId = "MjAxNV9UZWFycw==";
|
||||
const std::string kHlsTestKeyId1 = "371E135E1A985D75D198A7F41020DC23";
|
||||
const std::string kHlsTestKeyId2 = "E670D9B60AE61583E01BC9253FA19261";
|
||||
const std::string kHlsTestKeyId3 = "78094E72165DF39721B8A354D6A71390";
|
||||
const std::string kHlsTestInvalidKeyId = "B8A354D6A71390";
|
||||
const std::string kHlsTestKeyFormatVersion1 = "1";
|
||||
const std::string kHlsTestKeyFormatVersion3 = "3";
|
||||
const std::string kHlsTestKeyFormatVersion5 = "5";
|
||||
const std::string kHlsTestKeyFormatVersion13 = "13";
|
||||
const std::string kHlsTestKeyFormatVersion21 = "21";
|
||||
const std::string kHlsTestKeyFormatVersion37 = "37";
|
||||
|
||||
// HLS attribute helper functions
|
||||
std::string QuoteString(const std::string& value) {
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
|
||||
std::string GenerateJsonInitData(const std::string& provider,
|
||||
const std::string& content_id,
|
||||
const std::vector<std::string>& key_ids) {
|
||||
std::string json = kLeftBrace + kNewline;
|
||||
if (provider.size() > 0) {
|
||||
json += kFourSpaceIndent + kDoubleQuote + kJsonProvider + kDoubleQuote +
|
||||
kColon + kDoubleQuote + provider + kDoubleQuote + kComma + kNewline;
|
||||
}
|
||||
if (content_id.size() > 0) {
|
||||
json += kFourSpaceIndent + kDoubleQuote + kJsonContentId + kDoubleQuote +
|
||||
kColon + kDoubleQuote + content_id + kDoubleQuote + kComma +
|
||||
kNewline;
|
||||
}
|
||||
if (key_ids.size() > 0) {
|
||||
json += kFourSpaceIndent + kDoubleQuote + kJsonKeyIds + kDoubleQuote +
|
||||
kColon + kNewline;
|
||||
json += kFourSpaceIndent + kLeftBracket + kNewline;
|
||||
for (size_t i = 0; i < key_ids.size(); ++i) {
|
||||
json += kFourSpaceIndent + kFourSpaceIndent + kDoubleQuote + key_ids[i] +
|
||||
kDoubleQuote;
|
||||
if (i != key_ids.size() - 1) {
|
||||
json += kComma;
|
||||
}
|
||||
json += kNewline;
|
||||
}
|
||||
json += kFourSpaceIndent + kRightBracket + kNewline;
|
||||
}
|
||||
json += kRightBrace + kNewline;
|
||||
return json;
|
||||
}
|
||||
|
||||
class VectorOfStrings {
|
||||
public:
|
||||
VectorOfStrings(const std::string& str) { vec_.push_back(str); }
|
||||
VectorOfStrings& Add(const std::string& str) {
|
||||
vec_.push_back(str);
|
||||
return *this;
|
||||
}
|
||||
const std::vector<std::string> Generate() { return vec_; }
|
||||
|
||||
private:
|
||||
std::vector<std::string> vec_;
|
||||
};
|
||||
|
||||
std::string GenerateHlsUriData(const std::string& provider,
|
||||
const std::string& content_id,
|
||||
const std::vector<std::string>& key_ids) {
|
||||
std::string json = GenerateJsonInitData(provider, content_id, key_ids);
|
||||
std::vector<uint8_t> json_init_data(
|
||||
reinterpret_cast<const uint8_t*>(json.data()),
|
||||
reinterpret_cast<const uint8_t*>(json.data() + json.size()));
|
||||
return kHlsTestUriDataFormat + Base64Encode(json_init_data);
|
||||
}
|
||||
|
||||
std::string CreateHlsAttributeList(const std::string& method,
|
||||
const std::string& provider,
|
||||
const std::string& content_id,
|
||||
const std::vector<std::string>& key_ids,
|
||||
const std::string& iv,
|
||||
const std::string& key_format,
|
||||
const std::string& key_format_version) {
|
||||
return "EXT-X-KEY: " + HLS_METHOD_ATTRIBUTE + "=" + method + "," +
|
||||
HLS_URI_ATTRIBUTE + "=" +
|
||||
QuoteString(GenerateHlsUriData(provider, content_id, key_ids)) + "," +
|
||||
HLS_IV_ATTRIBUTE + "=" + iv + "," + HLS_KEYFORMAT_ATTRIBUTE + "=" +
|
||||
QuoteString(key_format) + "," + HLS_KEYFORMAT_VERSIONS_ATTRIBUTE +
|
||||
"=" + QuoteString(key_format_version);
|
||||
}
|
||||
|
||||
// HLS attribute list for testing
|
||||
const std::string kHlsAttributeList = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListKeyFormatUnknown = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue,
|
||||
kHlsKeyFormatValueOther, HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListKeyFormatVersionUnsupported =
|
||||
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider,
|
||||
kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(),
|
||||
kHlsIvValue, kHlsKeyFormatValue, "2");
|
||||
|
||||
const std::string kHlsAttributeListKeyFormatVersionMultiple =
|
||||
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider,
|
||||
kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(),
|
||||
kHlsIvValue, kHlsKeyFormatValue, "1/2/5");
|
||||
|
||||
const std::string kHlsAttributeListMethodAes128 = CreateHlsAttributeList(
|
||||
HLS_METHOD_AES_128, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListMethodNone = CreateHlsAttributeList(
|
||||
HLS_METHOD_NONE, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListMethodInvalid = CreateHlsAttributeList(
|
||||
kHlsTestValue1, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListInvalidUriNoProvider =
|
||||
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, "", kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(),
|
||||
kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListInvalidUriNoContentId =
|
||||
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, "",
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(),
|
||||
kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListInvalidUriNoKeyId = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings("").Generate(), kHlsIvValue, kHlsKeyFormatValue,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListValidUriThreeKeyIds = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1)
|
||||
.Add(kHlsTestKeyId2)
|
||||
.Add(kHlsTestKeyId3)
|
||||
.Generate(),
|
||||
kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListNoIv = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestNoHexValue,
|
||||
kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
const std::string kHlsAttributeListInvalidIv = CreateHlsAttributeList(
|
||||
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestHexValueWithOddBytes,
|
||||
kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1);
|
||||
|
||||
std::string InsertHlsAttributeInList(const std::string key,
|
||||
const std::string& value) {
|
||||
return kHlsAttributeList + "," + key + "=" + value + "," + kHlsTestKey2 +
|
||||
"=" + kHlsTestValue2;
|
||||
}
|
||||
|
||||
struct HlsInitDataVariant {
|
||||
HlsInitDataVariant(CdmHlsMethod method, const std::string& provider,
|
||||
const std::string& content_id, const std::string& key_id,
|
||||
bool success)
|
||||
: method_(method), provider_(provider), content_id_(content_id),
|
||||
success_(success) {
|
||||
if (key_id.size() > 0) key_ids_.push_back(key_id);
|
||||
}
|
||||
HlsInitDataVariant& AddKeyId(const std::string& key_id) {
|
||||
key_ids_.push_back(key_id);
|
||||
return *this;
|
||||
}
|
||||
const CdmHlsMethod method_;
|
||||
const std::string provider_;
|
||||
const std::string content_id_;
|
||||
std::vector<std::string> key_ids_;
|
||||
const bool success_;
|
||||
};
|
||||
|
||||
struct HlsAttributeVariant {
|
||||
HlsAttributeVariant(const std::string& attribute_list, const std::string& key,
|
||||
const std::string& value, bool success)
|
||||
: attribute_list_(attribute_list),
|
||||
key_(key),
|
||||
value_(value),
|
||||
success_(success) {}
|
||||
const std::string attribute_list_;
|
||||
const std::string key_;
|
||||
const std::string value_;
|
||||
const bool success_;
|
||||
};
|
||||
|
||||
class InitializationDataTest : public ::testing::TestWithParam<std::string> {};
|
||||
|
||||
class HlsAttributeExtractionTest
|
||||
: public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsHexAttributeExtractionTest
|
||||
: public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsQuotedAttributeExtractionTest
|
||||
: public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsKeyFormatVersionsExtractionTest
|
||||
: public ::testing::TestWithParam<std::vector<std::string> > {};
|
||||
|
||||
class HlsConstructionTest
|
||||
: public ::testing::TestWithParam<HlsInitDataVariant> {};
|
||||
|
||||
class HlsInitDataConstructionTest : public ::testing::Test {};
|
||||
|
||||
class HlsParseTest : public ::testing::TestWithParam<HlsAttributeVariant> {};
|
||||
|
||||
class HlsTest : public ::testing::Test {};
|
||||
} // namespace
|
||||
|
||||
TEST_P(InitializationDataTest, Parse) {
|
||||
@@ -148,17 +410,343 @@ TEST_P(InitializationDataTest, Parse) {
|
||||
EXPECT_FALSE(init_data.IsEmpty());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ParsePssh, InitializationDataTest,
|
||||
::testing::Values(kWidevinePssh, kWidevinePsshFirst,
|
||||
kWidevinePsshAfterV0Pssh,
|
||||
kWidevinePsshAfterNonZeroFlags,
|
||||
kWidevinePsshAfterV1Pssh,
|
||||
kWidevineV1Pssh, kOtherBoxFirst,
|
||||
kZeroSizedPsshBox));
|
||||
|
||||
TEST_P(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions) {
|
||||
std::vector<std::string> versions = GetParam();
|
||||
std::string key_format_versions;
|
||||
for (size_t i = 0; i < versions.size(); ++i) {
|
||||
key_format_versions += versions[i] + kHlsTestKeyFormatVersionsSeparator;
|
||||
}
|
||||
key_format_versions.resize(key_format_versions.size() -
|
||||
sizeof(kHlsTestKeyFormatVersionsSeparator));
|
||||
std::vector<std::string> extracted_versions =
|
||||
InitializationData::ExtractKeyFormatVersions(key_format_versions);
|
||||
EXPECT_EQ(versions.size(), extracted_versions.size());
|
||||
for (size_t i = 0; i < versions.size(); ++i) {
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < extracted_versions.size(); ++j) {
|
||||
if (versions[i] == extracted_versions[j]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ParsePssh, InitializationDataTest,
|
||||
HlsTest, HlsKeyFormatVersionsExtractionTest,
|
||||
::testing::Values(VectorOfStrings(kHlsTestKeyFormatVersion1).Generate(),
|
||||
VectorOfStrings(kHlsTestKeyFormatVersion21).Generate(),
|
||||
VectorOfStrings(kHlsTestKeyFormatVersion1)
|
||||
.Add(kHlsTestKeyFormatVersion3)
|
||||
.Generate(),
|
||||
VectorOfStrings(kHlsTestKeyFormatVersion1)
|
||||
.Add(kHlsTestKeyFormatVersion3)
|
||||
.Add(kHlsTestKeyFormatVersion13)
|
||||
.Generate(),
|
||||
VectorOfStrings(kHlsTestKeyFormatVersion13)
|
||||
.Add(kHlsTestKeyFormatVersion5)
|
||||
.Add(kHlsTestKeyFormatVersion21)
|
||||
.Add(kHlsTestKeyFormatVersion37)
|
||||
.Generate()));
|
||||
|
||||
TEST_P(HlsAttributeExtractionTest, ExtractAttribute) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
std::string value;
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(InitializationData::ExtractAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
EXPECT_EQ(param.value_, value);
|
||||
} else {
|
||||
EXPECT_FALSE(InitializationData::ExtractAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsAttributeExtractionTest,
|
||||
::testing::Values(
|
||||
kWidevinePssh,
|
||||
kWidevinePsshFirst,
|
||||
kWidevinePsshAfterV0Pssh,
|
||||
kWidevinePsshAfterNonZeroFlags,
|
||||
kWidevinePsshAfterV1Pssh,
|
||||
kWidevineV1Pssh,
|
||||
kOtherBoxFirst,
|
||||
kZeroSizedPsshBox
|
||||
));
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_METHOD_ATTRIBUTE,
|
||||
HLS_METHOD_SAMPLE_AES, true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_URI_ATTRIBUTE,
|
||||
QuoteString(GenerateHlsUriData(
|
||||
kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate())),
|
||||
true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvValue,
|
||||
true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE,
|
||||
QuoteString(kHlsKeyFormatValue), true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
|
||||
QuoteString(HLS_KEYFORMAT_VERSION_VALUE_1), true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey2, kHlsTestValue2, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "\t",
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
" " + kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestValue1 + " "),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "3",
|
||||
kHlsTestValue1),
|
||||
kHlsTestKey1, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, ""),
|
||||
kHlsTestKey1, "", true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(
|
||||
kHlsTestInvalidLowercaseKey, kHlsTestValue1),
|
||||
kHlsTestInvalidLowercaseKey, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKeyWithDash,
|
||||
kHlsTestValue1),
|
||||
kHlsTestKeyWithDash, kHlsTestValue1, true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(
|
||||
kHlsTestInvalidNonAlphanumKey, kHlsTestValue1),
|
||||
kHlsTestInvalidNonAlphanumKey, kHlsTestValue1,
|
||||
false),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)),
|
||||
kHlsTestKey1, QuoteString(kHlsTestValue1), true),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(
|
||||
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)),
|
||||
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote), true)));
|
||||
|
||||
TEST_P(HlsHexAttributeExtractionTest, ExtractHexAttribute) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
std::vector<uint8_t> value;
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(InitializationData::ExtractHexAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
EXPECT_EQ(param.value_, b2a_hex(value));
|
||||
} else {
|
||||
EXPECT_FALSE(InitializationData::ExtractHexAttribute(param.attribute_list_,
|
||||
param.key_, &value));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsHexAttributeExtractionTest,
|
||||
::testing::Values(
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvHexValue,
|
||||
true),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestEmptyHexValue),
|
||||
kHlsTestKey1, kHlsTestEmptyHexValue, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestNoHexValue),
|
||||
kHlsTestKey1, kHlsTestNoHexValue, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(
|
||||
kHlsTestKey1, kHlsTestHexValueWithOddBytes),
|
||||
kHlsTestKey1, kHlsTestHexValueWithOddBytes, false),
|
||||
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
|
||||
kHlsTestInvalidHexValue),
|
||||
kHlsTestKey1, kHlsTestInvalidHexValue, false)));
|
||||
|
||||
TEST_P(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
std::string value;
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(InitializationData::ExtractQuotedAttribute(
|
||||
param.attribute_list_, param.key_, &value));
|
||||
EXPECT_EQ(param.value_, value);
|
||||
} else {
|
||||
EXPECT_FALSE(InitializationData::ExtractQuotedAttribute(
|
||||
param.attribute_list_, param.key_, &value));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsQuotedAttributeExtractionTest,
|
||||
::testing::Values(
|
||||
HlsAttributeVariant(
|
||||
kHlsAttributeList, HLS_URI_ATTRIBUTE,
|
||||
GenerateHlsUriData(kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate()),
|
||||
true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE,
|
||||
kHlsKeyFormatValue, true),
|
||||
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, true),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)),
|
||||
kHlsTestKey1, kHlsTestValue1, true),
|
||||
HlsAttributeVariant(
|
||||
InsertHlsAttributeInList(
|
||||
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)),
|
||||
kHlsTestKey1, kHlsTestValueWithEmbeddedQuote, false)));
|
||||
|
||||
TEST_P(HlsConstructionTest, InitData) {
|
||||
HlsInitDataVariant param = GetParam();
|
||||
|
||||
std::string uri =
|
||||
GenerateHlsUriData(param.provider_, param.content_id_, param.key_ids_);
|
||||
std::string value;
|
||||
EXPECT_EQ(param.success_, InitializationData::ConstructWidevineInitData(
|
||||
param.method_, uri, &value));
|
||||
if (param.success_) {
|
||||
WidevineCencHeader cenc_header;
|
||||
EXPECT_TRUE(cenc_header.ParseFromString(value));
|
||||
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
|
||||
cenc_header.algorithm());
|
||||
for (size_t i = 0; i < param.key_ids_.size(); ++i) {
|
||||
bool key_id_found = false;
|
||||
if (param.key_ids_[i].size() != 32) continue;
|
||||
for (int j = 0; j < cenc_header.key_id_size(); ++j) {
|
||||
if (param.key_ids_[i] == b2a_hex(cenc_header.key_id(j))) {
|
||||
key_id_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(key_id_found);
|
||||
}
|
||||
EXPECT_EQ(param.provider_, cenc_header.provider());
|
||||
std::vector<uint8_t> param_content_id_vec(Base64Decode(param.content_id_));
|
||||
EXPECT_EQ(
|
||||
std::string(param_content_id_vec.begin(), param_content_id_vec.end()),
|
||||
cenc_header.content_id());
|
||||
uint32_t protection_scheme = 0;
|
||||
switch (param.method_) {
|
||||
case kHlsMethodAes128: protection_scheme = kFourCcCbc1; break;
|
||||
case kHlsMethodSampleAes: protection_scheme = kFourCcCbcs; break;
|
||||
default: break;
|
||||
}
|
||||
EXPECT_EQ(protection_scheme, ntohl(cenc_header.protection_scheme()));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsConstructionTest,
|
||||
::testing::Values(
|
||||
HlsInitDataVariant(kHlsMethodAes128, "", kHlsTestContentId,
|
||||
kHlsTestKeyId1, false),
|
||||
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
|
||||
"", kHlsTestKeyId1, false),
|
||||
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
|
||||
kHlsTestContentId, "", false),
|
||||
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
|
||||
kHlsTestContentId, kHlsTestInvalidKeyId, false),
|
||||
HlsInitDataVariant(kHlsMethodNone, kHlsTestProvider, kHlsTestContentId,
|
||||
kHlsTestKeyId1, false),
|
||||
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
|
||||
kHlsTestContentId, kHlsTestKeyId1, true),
|
||||
HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider,
|
||||
kHlsTestContentId, kHlsTestKeyId1, true),
|
||||
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
|
||||
kHlsTestContentId, kHlsTestKeyId1, true)
|
||||
.AddKeyId(kHlsTestKeyId2)
|
||||
.AddKeyId(kHlsTestKeyId3),
|
||||
HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider,
|
||||
kHlsTestContentId, kHlsTestKeyId1, true)
|
||||
.AddKeyId(kHlsTestKeyId2)
|
||||
.AddKeyId(kHlsTestKeyId3),
|
||||
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
|
||||
kHlsTestContentId, kHlsTestInvalidKeyId, true)
|
||||
.AddKeyId(kHlsTestKeyId1),
|
||||
HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider,
|
||||
kHlsTestContentId, kHlsTestInvalidKeyId, true)
|
||||
.AddKeyId(kHlsTestKeyId1)));
|
||||
|
||||
TEST_F(HlsInitDataConstructionTest, InvalidUriDataFormat) {
|
||||
std::string json =
|
||||
GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate());
|
||||
std::vector<uint8_t> json_init_data(
|
||||
reinterpret_cast<const uint8_t*>(json.data()),
|
||||
reinterpret_cast<const uint8_t*>(json.data() + json.size()));
|
||||
std::string value;
|
||||
EXPECT_FALSE(InitializationData::ConstructWidevineInitData(
|
||||
kHlsMethodAes128, Base64Encode(json_init_data), &value));
|
||||
}
|
||||
|
||||
TEST_F(HlsInitDataConstructionTest, InvalidUriBase64Encode) {
|
||||
std::string json =
|
||||
GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId,
|
||||
VectorOfStrings(kHlsTestKeyId1).Generate());
|
||||
std::string value;
|
||||
EXPECT_FALSE(InitializationData::ConstructWidevineInitData(
|
||||
kHlsMethodSampleAes, kHlsTestUriDataFormat + json, &value));
|
||||
}
|
||||
|
||||
TEST_P(HlsParseTest, Parse) {
|
||||
HlsAttributeVariant param = GetParam();
|
||||
InitializationData init_data(HLS_INIT_DATA_FORMAT, param.attribute_list_);
|
||||
if (param.success_) {
|
||||
EXPECT_TRUE(init_data.is_hls());
|
||||
EXPECT_FALSE(init_data.IsEmpty());
|
||||
if (param.key_.compare(HLS_METHOD_ATTRIBUTE) == 0) {
|
||||
if (param.value_.compare(HLS_METHOD_SAMPLE_AES) == 0) {
|
||||
EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method());
|
||||
} else if (param.value_.compare(HLS_METHOD_AES_128) == 0) {
|
||||
EXPECT_EQ(kHlsMethodAes128, init_data.hls_method());
|
||||
} else if (param.value_.compare(HLS_METHOD_NONE) == 0) {
|
||||
EXPECT_EQ(kHlsMethodNone, init_data.hls_method());
|
||||
}
|
||||
} else {
|
||||
EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method());
|
||||
}
|
||||
|
||||
WidevineCencHeader cenc_header;
|
||||
EXPECT_TRUE(cenc_header.ParseFromString(init_data.data()));
|
||||
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
|
||||
cenc_header.algorithm());
|
||||
if (param.key_.compare(kJsonProvider) == 0) {
|
||||
EXPECT_EQ(param.value_, cenc_header.provider());
|
||||
} else if (param.key_.compare(kJsonContentId) == 0) {
|
||||
EXPECT_EQ(param.value_, cenc_header.content_id());
|
||||
} else if (param.key_.compare(kJsonKeyIds) == 0) {
|
||||
EXPECT_EQ(param.value_, b2a_hex(cenc_header.key_id(0)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(kHlsIvHexValue, b2a_hex(init_data.hls_iv()));
|
||||
} else {
|
||||
EXPECT_TRUE(init_data.is_hls());
|
||||
EXPECT_TRUE(init_data.IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
HlsTest, HlsParseTest,
|
||||
::testing::Values(
|
||||
HlsAttributeVariant(kHlsAttributeList, "", "", true),
|
||||
HlsAttributeVariant(kHlsAttributeListKeyFormatUnknown,
|
||||
HLS_KEYFORMAT_ATTRIBUTE, kHlsKeyFormatValueOther,
|
||||
false),
|
||||
HlsAttributeVariant(kHlsAttributeListKeyFormatVersionUnsupported,
|
||||
HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, "2", false),
|
||||
HlsAttributeVariant(kHlsAttributeListMethodAes128, HLS_METHOD_ATTRIBUTE,
|
||||
HLS_METHOD_AES_128, true),
|
||||
HlsAttributeVariant(kHlsAttributeListMethodNone, HLS_METHOD_ATTRIBUTE,
|
||||
HLS_METHOD_NONE, false),
|
||||
HlsAttributeVariant(kHlsAttributeListKeyFormatVersionMultiple,
|
||||
HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
|
||||
HLS_KEYFORMAT_VERSION_VALUE_1, true),
|
||||
HlsAttributeVariant(kHlsAttributeListMethodInvalid,
|
||||
HLS_METHOD_ATTRIBUTE, kHlsTestValue1, false),
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidUriNoProvider,
|
||||
kJsonProvider, kHlsTestProvider, false),
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidUriNoContentId,
|
||||
kJsonContentId, kHlsTestContentId, false),
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidUriNoKeyId, kJsonKeyIds,
|
||||
kHlsTestKeyId1, false),
|
||||
HlsAttributeVariant(kHlsAttributeListValidUriThreeKeyIds, kJsonKeyIds,
|
||||
kHlsTestKeyId1, true),
|
||||
HlsAttributeVariant(kHlsAttributeListNoIv, HLS_IV_ATTRIBUTE,
|
||||
kHlsTestNoHexValue, false),
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidIv, HLS_IV_ATTRIBUTE,
|
||||
kHlsTestHexValueWithOddBytes, false)));
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
888
core/test/license_keys_unittest.cpp
Normal file
888
core/test/license_keys_unittest.cpp
Normal file
@@ -0,0 +1,888 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
#include "license.h"
|
||||
#include "license_key_status.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
static const uint32_t dev_lo_res = 200;
|
||||
static const uint32_t dev_hi_res = 400;
|
||||
static const uint32_t dev_top_res = 800;
|
||||
|
||||
static const uint32_t key_lo_res_min = 151;
|
||||
static const uint32_t key_lo_res_max = 300;
|
||||
static const uint32_t key_hi_res_min = 301;
|
||||
static const uint32_t key_hi_res_max = 450;
|
||||
static const uint32_t key_top_res_min = 451;
|
||||
static const uint32_t key_top_res_max = 650;
|
||||
|
||||
// Content Keys
|
||||
static const KeyId ck_sw_crypto = "c_key_SW_SECURE_CRYPTO";
|
||||
static const KeyId ck_sw_decode = "c_key_SW_SECURE_DECODE";
|
||||
static const KeyId ck_hw_crypto = "c_key_HW_SECURE_CRYPTO";
|
||||
static const KeyId ck_hw_decode = "c_key_HW_SECURE_DECODE";
|
||||
static const KeyId ck_hw_secure = "c_key_HW_SECURE_ALL";
|
||||
|
||||
// Operator Session Keys
|
||||
static const KeyId osk_decrypt = "os_key_generic_decrypt";
|
||||
static const KeyId osk_encrypt = "os_key_generic_encrypt";
|
||||
static const KeyId osk_sign = "os_key_generic_sign";
|
||||
static const KeyId osk_verify = "os_key_generic_verify";
|
||||
static const KeyId osk_encrypt_decrypt = "os_key_generic_encrypt_decrypt";
|
||||
static const KeyId osk_sign_verify = "os_key_generic_sign_verify";
|
||||
static const KeyId osk_all = "os_key_generic_all";
|
||||
|
||||
// HDCP test keys
|
||||
static const KeyId ck_sw_crypto_NO_HDCP = "ck_sw_crypto_NO_HDCP";
|
||||
static const KeyId ck_hw_secure_NO_HDCP = "ck_hw_secure_NO_HDCP";
|
||||
static const KeyId ck_sw_crypto_HDCP_V2_1 = "ck_sw_crypto_HDCP_V2_1";
|
||||
static const KeyId ck_hw_secure_HDCP_V2_1 = "ck_hw_secure_HDCP_V2_1";
|
||||
static const KeyId ck_sw_crypto_HDCP_NO_OUTPUT = "ck_sw_crypto_HDCP_NO_OUT";
|
||||
static const KeyId ck_hw_secure_HDCP_NO_OUTPUT = "ck_hw_secure_HDCP_NO_OUT";
|
||||
|
||||
// Constraint test keys
|
||||
static const KeyId ck_NO_HDCP_lo_res = "ck_NO_HDCP_lo_res";
|
||||
static const KeyId ck_HDCP_NO_OUTPUT_hi_res = "ck_HDCP_NO_OUTPUT_hi_res";
|
||||
static const KeyId ck_HDCP_V2_1_max_res = "ck_HDCP_V2_1_max_res";
|
||||
static const KeyId ck_NO_HDCP_dual_res = "ck_NO_HDCP_dual_res";
|
||||
|
||||
} // namespace
|
||||
|
||||
// protobuf generated classes.
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
using video_widevine_server::sdk::STREAMING;
|
||||
using video_widevine_server::sdk::OFFLINE;
|
||||
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
|
||||
|
||||
class LicenseKeysTest : public ::testing::Test {
|
||||
protected:
|
||||
|
||||
enum KeyFlag {
|
||||
kKeyFlagNull,
|
||||
kKeyFlagFalse,
|
||||
kKeyFlagTrue
|
||||
};
|
||||
|
||||
static const KeyFlag kEncryptNull = kKeyFlagNull;
|
||||
static const KeyFlag kEncryptFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kEncryptTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kDecryptNull = kKeyFlagNull;
|
||||
static const KeyFlag kDecryptFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kDecryptTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kSignNull = kKeyFlagNull;
|
||||
static const KeyFlag kSignFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kSignTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kVerifyNull = kKeyFlagNull;
|
||||
static const KeyFlag kVerifyFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kVerifyTrue = kKeyFlagTrue;
|
||||
|
||||
static const KeyFlag kContentSecureFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kContentSecureTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kContentClearFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kContentClearTrue = kKeyFlagTrue;
|
||||
|
||||
virtual void SetUp() {
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_version(1);
|
||||
id->set_type(STREAMING);
|
||||
}
|
||||
|
||||
virtual void AddContentKey(
|
||||
const KeyId& key_id, bool set_level = false,
|
||||
KeyContainer::SecurityLevel level = KeyContainer::SW_SECURE_CRYPTO,
|
||||
bool set_hdcp = false, KeyContainer::OutputProtection::HDCP hdcp_value =
|
||||
KeyContainer::OutputProtection::HDCP_NONE,
|
||||
bool set_constraints = false,
|
||||
std::vector<VideoResolutionConstraint>* constraints = NULL) {
|
||||
KeyContainer* key = license_.add_key();
|
||||
key->set_type(KeyContainer::CONTENT);
|
||||
if (set_level) {
|
||||
key->set_level(level);
|
||||
}
|
||||
if (set_hdcp) {
|
||||
KeyContainer::OutputProtection* pro = key->mutable_required_protection();
|
||||
pro->set_hdcp(hdcp_value);
|
||||
}
|
||||
if (set_constraints) {
|
||||
for (std::vector<VideoResolutionConstraint>::iterator
|
||||
it = constraints->begin(); it != constraints->end(); ++it) {
|
||||
VideoResolutionConstraint* constraint =
|
||||
key->add_video_resolution_constraints();
|
||||
constraint->set_min_resolution_pixels(it->min_resolution_pixels());
|
||||
constraint->set_max_resolution_pixels(it->max_resolution_pixels());
|
||||
constraint->mutable_required_protection()->
|
||||
set_hdcp(it->required_protection().hdcp());
|
||||
}
|
||||
}
|
||||
key->set_id(key_id);
|
||||
}
|
||||
|
||||
virtual void AddOperatorSessionKey(
|
||||
const KeyId& key_id, bool set_perms = false,
|
||||
KeyFlag encrypt = kKeyFlagNull, KeyFlag decrypt = kKeyFlagNull,
|
||||
KeyFlag sign = kKeyFlagNull, KeyFlag verify = kKeyFlagNull) {
|
||||
KeyContainer* non_content_key = license_.add_key();
|
||||
non_content_key->set_type(KeyContainer::OPERATOR_SESSION);
|
||||
non_content_key->set_id(key_id);
|
||||
if (set_perms) {
|
||||
KeyContainer::OperatorSessionKeyPermissions* permissions =
|
||||
non_content_key->mutable_operator_session_key_permissions();
|
||||
if (encrypt != kKeyFlagNull) {
|
||||
permissions->set_allow_encrypt(encrypt == kKeyFlagTrue);
|
||||
}
|
||||
if (decrypt != kKeyFlagNull) {
|
||||
permissions->set_allow_decrypt(decrypt == kKeyFlagTrue);
|
||||
}
|
||||
if (sign != kKeyFlagNull) {
|
||||
permissions->set_allow_sign(sign == kKeyFlagTrue);
|
||||
}
|
||||
if (verify != kKeyFlagNull) {
|
||||
permissions->set_allow_signature_verify(verify == kKeyFlagTrue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void AddSigningKey(const KeyId& key_id) {
|
||||
KeyContainer* key = license_.add_key();
|
||||
key->set_type(KeyContainer::SIGNING);
|
||||
key->set_id(key_id);
|
||||
}
|
||||
|
||||
virtual void ExpectAllowedUsageContent(
|
||||
const CdmKeyAllowedUsage& key_usage, KeyFlag secure, KeyFlag clear) {
|
||||
EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue);
|
||||
EXPECT_FALSE(key_usage.generic_encrypt);
|
||||
EXPECT_FALSE(key_usage.generic_decrypt);
|
||||
EXPECT_FALSE(key_usage.generic_sign);
|
||||
EXPECT_FALSE(key_usage.generic_verify);
|
||||
}
|
||||
|
||||
virtual void ExpectAllowedUsageOperator(
|
||||
const CdmKeyAllowedUsage& key_usage, KeyFlag encrypt, KeyFlag decrypt,
|
||||
KeyFlag sign, KeyFlag verify) {
|
||||
EXPECT_FALSE(key_usage.decrypt_to_secure_buffer);
|
||||
EXPECT_FALSE(key_usage.decrypt_to_clear_buffer);
|
||||
EXPECT_EQ(key_usage.generic_encrypt, encrypt == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.generic_decrypt, decrypt == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.generic_sign, sign == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue);
|
||||
}
|
||||
|
||||
virtual int NumContentKeys() {
|
||||
return content_key_count_;
|
||||
}
|
||||
|
||||
virtual void StageContentKeys() {
|
||||
content_key_count_ = 0;
|
||||
AddContentKey(ck_sw_crypto, true, KeyContainer::SW_SECURE_CRYPTO);
|
||||
content_key_count_++;
|
||||
AddContentKey(ck_sw_decode, true, KeyContainer::SW_SECURE_DECODE);
|
||||
content_key_count_++;
|
||||
AddContentKey(ck_hw_crypto, true, KeyContainer::HW_SECURE_CRYPTO);
|
||||
content_key_count_++;
|
||||
AddContentKey(ck_hw_decode, true, KeyContainer::HW_SECURE_DECODE);
|
||||
content_key_count_++;
|
||||
AddContentKey(ck_hw_secure, true, KeyContainer::HW_SECURE_ALL);
|
||||
content_key_count_++;
|
||||
license_keys_.SetFromLicense(license_);
|
||||
}
|
||||
|
||||
virtual void StageOperatorSessionKeys() {
|
||||
AddOperatorSessionKey(osk_decrypt, true,
|
||||
kEncryptNull, kDecryptTrue, kSignNull, kVerifyNull);
|
||||
AddOperatorSessionKey(osk_encrypt, true,
|
||||
kEncryptTrue, kDecryptNull, kSignNull, kVerifyNull);
|
||||
AddOperatorSessionKey(osk_sign, true,
|
||||
kEncryptNull, kDecryptNull, kSignTrue, kVerifyNull);
|
||||
AddOperatorSessionKey(osk_verify, true,
|
||||
kEncryptNull, kDecryptNull, kSignNull, kVerifyTrue);
|
||||
AddOperatorSessionKey(osk_encrypt_decrypt, true,
|
||||
kEncryptTrue, kDecryptTrue, kSignNull, kVerifyNull);
|
||||
AddOperatorSessionKey(osk_sign_verify, true,
|
||||
kEncryptNull, kDecryptNull, kSignTrue, kVerifyTrue);
|
||||
AddOperatorSessionKey(osk_all, true,
|
||||
kEncryptTrue, kDecryptTrue, kSignTrue, kVerifyTrue);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
}
|
||||
|
||||
virtual void StageHdcpKeys() {
|
||||
content_key_count_ = 0;
|
||||
AddContentKey(ck_sw_crypto_NO_HDCP, true, KeyContainer::SW_SECURE_CRYPTO,
|
||||
true, KeyContainer::OutputProtection::HDCP_NONE);
|
||||
content_key_count_++;
|
||||
AddContentKey(ck_hw_secure_NO_HDCP, true, KeyContainer::HW_SECURE_ALL, true,
|
||||
KeyContainer::OutputProtection::HDCP_NONE);
|
||||
content_key_count_++;
|
||||
|
||||
AddContentKey(ck_sw_crypto_HDCP_V2_1, true, KeyContainer::SW_SECURE_CRYPTO,
|
||||
true, KeyContainer::OutputProtection::HDCP_V2_1);
|
||||
content_key_count_++;
|
||||
AddContentKey(ck_hw_secure_HDCP_V2_1, true, KeyContainer::HW_SECURE_ALL,
|
||||
true, KeyContainer::OutputProtection::HDCP_V2_1);
|
||||
content_key_count_++;
|
||||
|
||||
AddContentKey(ck_sw_crypto_HDCP_NO_OUTPUT, true,
|
||||
KeyContainer::SW_SECURE_CRYPTO, true,
|
||||
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
|
||||
content_key_count_++;
|
||||
AddContentKey(ck_hw_secure_HDCP_NO_OUTPUT, true,
|
||||
KeyContainer::HW_SECURE_ALL, true,
|
||||
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
|
||||
content_key_count_++;
|
||||
license_keys_.SetFromLicense(license_);
|
||||
}
|
||||
|
||||
virtual void AddConstraint(
|
||||
std::vector<VideoResolutionConstraint>& constraints, uint32_t min_res,
|
||||
uint32_t max_res, bool set_hdcp = false,
|
||||
KeyContainer::OutputProtection::HDCP hdcp =
|
||||
KeyContainer::OutputProtection::HDCP_NONE) {
|
||||
VideoResolutionConstraint constraint;
|
||||
constraint.set_min_resolution_pixels(min_res);
|
||||
constraint.set_max_resolution_pixels(max_res);
|
||||
if (set_hdcp) {
|
||||
constraint.mutable_required_protection()->set_hdcp(hdcp);
|
||||
}
|
||||
constraints.push_back(constraint);
|
||||
}
|
||||
|
||||
virtual void StageConstraintKeys() {
|
||||
content_key_count_ = 0;
|
||||
|
||||
std::vector<VideoResolutionConstraint> constraints;
|
||||
|
||||
AddConstraint(constraints, key_lo_res_min, key_lo_res_max);
|
||||
|
||||
AddContentKey(ck_NO_HDCP_lo_res, true, KeyContainer::SW_SECURE_CRYPTO, true,
|
||||
KeyContainer::OutputProtection::HDCP_NONE, true,
|
||||
&constraints);
|
||||
content_key_count_++;
|
||||
|
||||
constraints.clear();
|
||||
AddConstraint(constraints, key_hi_res_min, key_hi_res_max);
|
||||
|
||||
AddContentKey(
|
||||
ck_HDCP_NO_OUTPUT_hi_res, true, KeyContainer::SW_SECURE_CRYPTO, true,
|
||||
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT, true,
|
||||
&constraints);
|
||||
content_key_count_++;
|
||||
|
||||
constraints.clear();
|
||||
AddConstraint(constraints, key_top_res_min, key_top_res_max);
|
||||
|
||||
AddContentKey(ck_HDCP_V2_1_max_res, true, KeyContainer::SW_SECURE_CRYPTO,
|
||||
true, KeyContainer::OutputProtection::HDCP_V2_1, true,
|
||||
&constraints);
|
||||
content_key_count_++;
|
||||
|
||||
constraints.clear();
|
||||
AddConstraint(constraints, key_lo_res_min, key_lo_res_max);
|
||||
AddConstraint(constraints, key_hi_res_min, key_hi_res_max,
|
||||
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
|
||||
|
||||
AddContentKey(ck_NO_HDCP_dual_res, true, KeyContainer::HW_SECURE_ALL, true,
|
||||
KeyContainer::OutputProtection::HDCP_NONE, true,
|
||||
&constraints);
|
||||
content_key_count_++;
|
||||
|
||||
license_keys_.SetFromLicense(license_);
|
||||
}
|
||||
|
||||
virtual void ExpectKeyStatusesEqual(CdmKeyStatusMap& key_status_map,
|
||||
CdmKeyStatus expected_status) {
|
||||
for (CdmKeyStatusMap::iterator it = key_status_map.begin();
|
||||
it != key_status_map.end(); ++it) {
|
||||
EXPECT_TRUE(it->second == expected_status);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ExpectKeyStatusEqual(CdmKeyStatusMap& key_status_map,
|
||||
const KeyId& key_id,
|
||||
CdmKeyStatus expected_status) {
|
||||
for (CdmKeyStatusMap::iterator it = key_status_map.begin();
|
||||
it != key_status_map.end(); ++it) {
|
||||
if (key_id == it->first) {
|
||||
EXPECT_TRUE(it->second == expected_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int content_key_count_;
|
||||
LicenseKeys license_keys_;
|
||||
License license_;
|
||||
};
|
||||
|
||||
TEST_F(LicenseKeysTest, Empty) {
|
||||
EXPECT_TRUE(license_keys_.Empty());
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, NotEmpty) {
|
||||
const KeyId c_key = "content_key";
|
||||
AddContentKey(c_key);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
EXPECT_FALSE(license_keys_.Empty());
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, BadKeyId) {
|
||||
const KeyId c_key = "content_key";
|
||||
const KeyId os_key = "op_sess_key";
|
||||
const KeyId unk_key = "unknown_key";
|
||||
CdmKeyAllowedUsage allowed_usage;
|
||||
AddContentKey(c_key);
|
||||
AddOperatorSessionKey(os_key);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(unk_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(unk_key));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(unk_key));
|
||||
EXPECT_FALSE(license_keys_.GetAllowedUsage(unk_key, &allowed_usage));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, SigningKey) {
|
||||
const KeyId c_key = "content_key";
|
||||
const KeyId os_key = "op_sess_key";
|
||||
const KeyId sign_key = "signing_key";
|
||||
CdmKeyAllowedUsage allowed_usage;
|
||||
AddSigningKey(sign_key);
|
||||
AddContentKey(c_key);
|
||||
AddOperatorSessionKey(os_key);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(sign_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(sign_key));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(sign_key));
|
||||
EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &allowed_usage));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, ContentKey) {
|
||||
const KeyId c_key = "content_key";
|
||||
AddContentKey(c_key);
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(c_key));
|
||||
|
||||
license_keys_.SetFromLicense(license_);
|
||||
EXPECT_TRUE(license_keys_.IsContentKey(c_key));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, OperatorSessionKey) {
|
||||
const KeyId os_key = "op_sess_key";
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(os_key));
|
||||
AddOperatorSessionKey(os_key);
|
||||
|
||||
license_keys_.SetFromLicense(license_);
|
||||
EXPECT_FALSE(license_keys_.IsContentKey(os_key));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, CanDecrypt) {
|
||||
const KeyId os_key = "op_sess_key";
|
||||
const KeyId c_key = "content_key";
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
|
||||
AddOperatorSessionKey(os_key);
|
||||
AddContentKey(c_key);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
|
||||
bool new_usable_keys = false;
|
||||
bool any_change = false;
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(c_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
|
||||
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired,
|
||||
&new_usable_keys);
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, AllowedUsageNull) {
|
||||
const KeyId os_key = "op_sess_key";
|
||||
const KeyId c_key = "content_key";
|
||||
const KeyId sign_key = "signing_key";
|
||||
AddOperatorSessionKey(os_key);
|
||||
AddContentKey(c_key);
|
||||
AddSigningKey(sign_key);
|
||||
license_keys_.SetFromLicense(license_);
|
||||
CdmKeyAllowedUsage usage_1;
|
||||
EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &usage_1));
|
||||
|
||||
CdmKeyAllowedUsage usage_2;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(c_key, &usage_2));
|
||||
ExpectAllowedUsageContent(usage_2, kContentClearTrue, kContentSecureTrue);
|
||||
|
||||
CdmKeyAllowedUsage usage_3;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_3));
|
||||
ExpectAllowedUsageContent(usage_3, kContentClearFalse, kContentSecureFalse);
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, AllowedUsageContent) {
|
||||
StageContentKeys();
|
||||
CdmKeyAllowedUsage u_sw_crypto;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_crypto, &u_sw_crypto));
|
||||
ExpectAllowedUsageContent(u_sw_crypto, kContentSecureTrue, kContentClearTrue);
|
||||
|
||||
CdmKeyAllowedUsage u_sw_decode;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_decode, &u_sw_decode));
|
||||
ExpectAllowedUsageContent(u_sw_decode, kContentSecureTrue, kContentClearTrue);
|
||||
|
||||
CdmKeyAllowedUsage u_hw_crypto;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_crypto, &u_hw_crypto));
|
||||
ExpectAllowedUsageContent(u_hw_crypto, kContentSecureTrue, kContentClearTrue);
|
||||
|
||||
CdmKeyAllowedUsage u_hw_decode;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_decode, &u_hw_decode));
|
||||
ExpectAllowedUsageContent(u_hw_decode, kContentSecureTrue,
|
||||
kContentClearFalse);
|
||||
|
||||
CdmKeyAllowedUsage u_hw_secure;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_secure, &u_hw_secure));
|
||||
ExpectAllowedUsageContent(u_hw_secure, kContentSecureTrue,
|
||||
kContentClearFalse);
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, AllowedUsageOperatorSession) {
|
||||
StageOperatorSessionKeys();
|
||||
CdmKeyAllowedUsage u_encrypt;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_encrypt, &u_encrypt));
|
||||
ExpectAllowedUsageOperator(u_encrypt, kEncryptTrue, kDecryptFalse, kSignFalse,
|
||||
kVerifyFalse);
|
||||
|
||||
CdmKeyAllowedUsage u_decrypt;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_decrypt, &u_decrypt));
|
||||
ExpectAllowedUsageOperator(u_decrypt, kEncryptFalse, kDecryptTrue, kSignFalse,
|
||||
kVerifyFalse);
|
||||
|
||||
CdmKeyAllowedUsage u_sign;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign, &u_sign));
|
||||
ExpectAllowedUsageOperator(u_sign, kEncryptFalse, kDecryptFalse, kSignTrue,
|
||||
kVerifyFalse);
|
||||
|
||||
CdmKeyAllowedUsage u_verify;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_verify, &u_verify));
|
||||
ExpectAllowedUsageOperator(u_verify, kEncryptFalse, kDecryptFalse, kSignFalse,
|
||||
kVerifyTrue);
|
||||
|
||||
CdmKeyAllowedUsage u_encrypt_decrypt;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_encrypt_decrypt,
|
||||
&u_encrypt_decrypt));
|
||||
ExpectAllowedUsageOperator(u_encrypt_decrypt, kEncryptTrue, kDecryptTrue,
|
||||
kSignFalse, kVerifyFalse);
|
||||
|
||||
CdmKeyAllowedUsage u_sign_verify;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign_verify, &u_sign_verify));
|
||||
ExpectAllowedUsageOperator(u_sign_verify, kEncryptFalse, kDecryptFalse,
|
||||
kSignTrue, kVerifyTrue);
|
||||
|
||||
CdmKeyAllowedUsage u_all;
|
||||
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_all, &u_all));
|
||||
ExpectAllowedUsageOperator(u_all, kEncryptTrue, kDecryptTrue, kSignTrue,
|
||||
kVerifyTrue);
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, ExtractKeyStatuses) {
|
||||
CdmKeyStatusMap key_status_map;
|
||||
StageOperatorSessionKeys();
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(0, key_status_map.size());
|
||||
StageContentKeys();
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(content_key_count_, key_status_map.size());
|
||||
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, KeyStatusChanges) {
|
||||
bool new_usable_keys = false;
|
||||
bool any_change = false;
|
||||
CdmKeyStatusMap key_status_map;
|
||||
StageOperatorSessionKeys();
|
||||
StageContentKeys();
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(content_key_count_, key_status_map.size());
|
||||
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
|
||||
|
||||
// change to pending
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending,
|
||||
&new_usable_keys);
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(content_key_count_, key_status_map.size());
|
||||
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
|
||||
|
||||
// change to pending (again)
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending,
|
||||
&new_usable_keys);
|
||||
EXPECT_FALSE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(content_key_count_, key_status_map.size());
|
||||
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
|
||||
|
||||
// change to usable
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(content_key_count_, key_status_map.size());
|
||||
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
|
||||
|
||||
// change to usable (again)
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
EXPECT_FALSE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(content_key_count_, key_status_map.size());
|
||||
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
|
||||
|
||||
// change to expired
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired,
|
||||
&new_usable_keys);
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
EXPECT_EQ(content_key_count_, key_status_map.size());
|
||||
ExpectKeyStatusesEqual(key_status_map, kKeyStatusExpired);
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, HdcpChanges) {
|
||||
bool new_usable_keys = false;
|
||||
bool any_change = false;
|
||||
CdmKeyStatusMap key_status_map;
|
||||
StageHdcpKeys();
|
||||
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
license_keys_.ApplyConstraints(100, HDCP_NONE);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
|
||||
license_keys_.ApplyConstraints(100, HDCP_V1);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_FALSE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
|
||||
license_keys_.ApplyConstraints(100, HDCP_V2_2);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
|
||||
kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
|
||||
license_keys_.ApplyConstraints(100, HDCP_NO_DIGITAL_OUTPUT);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
|
||||
kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
|
||||
kKeyStatusUsable);
|
||||
|
||||
license_keys_.ApplyConstraints(100, HDCP_NONE);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP,
|
||||
kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
}
|
||||
|
||||
TEST_F(LicenseKeysTest, ConstraintChanges) {
|
||||
bool new_usable_keys = false;
|
||||
bool any_change = false;
|
||||
CdmKeyStatusMap key_status_map;
|
||||
StageConstraintKeys();
|
||||
|
||||
// No constraints set by device
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
|
||||
|
||||
// Low-res device, no HDCP support
|
||||
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
|
||||
// Hi-res device, HDCP_V1 support
|
||||
license_keys_.ApplyConstraints(dev_hi_res, HDCP_V1);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
|
||||
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
|
||||
// Lo-res device, HDCP V2.2 support
|
||||
license_keys_.ApplyConstraints(dev_lo_res, HDCP_V2_2);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
|
||||
kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
|
||||
// Hi-res device, Maximal HDCP support
|
||||
license_keys_.ApplyConstraints(dev_hi_res, HDCP_NO_DIGITAL_OUTPUT);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
|
||||
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
|
||||
kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
|
||||
kKeyStatusUsable);
|
||||
|
||||
// Lo-res device, no HDCP support
|
||||
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_TRUE(new_usable_keys);
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
|
||||
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP,
|
||||
kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
|
||||
// Too-high-res -- all keys rejected
|
||||
license_keys_.ApplyConstraints(dev_top_res, HDCP_NONE);
|
||||
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
|
||||
&new_usable_keys);
|
||||
|
||||
EXPECT_TRUE(any_change);
|
||||
EXPECT_FALSE(new_usable_keys);
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
|
||||
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
|
||||
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
|
||||
|
||||
license_keys_.ExtractKeyStatuses(&key_status_map);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP,
|
||||
kKeyStatusUsable);
|
||||
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
|
||||
kKeyStatusOutputNotAllowed);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -55,30 +55,4 @@ void LicenseRequest::GetDrmMessage(const std::string& response,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns heartbeat url in heartbeat_url.
|
||||
// The heartbeat url is stored as meta data in the response message.
|
||||
void LicenseRequest::GetHeartbeatUrl(const std::string& response,
|
||||
std::string& heartbeat_url) {
|
||||
if (response.empty()) {
|
||||
heartbeat_url.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
size_t header_end_pos = FindHeaderEndPosition(response);
|
||||
if (header_end_pos != std::string::npos) {
|
||||
header_end_pos += kTwoBlankLines.size(); // points to response body
|
||||
|
||||
heartbeat_url.clear();
|
||||
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));
|
||||
} else {
|
||||
LOGE("heartbeat url not found");
|
||||
}
|
||||
} else {
|
||||
LOGE("response body not found");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -17,7 +17,6 @@ class LicenseRequest {
|
||||
~LicenseRequest() {};
|
||||
|
||||
void GetDrmMessage(const std::string& response, std::string& drm_msg);
|
||||
void GetHeartbeatUrl(const std::string& response, std::string& heartbeat_url);
|
||||
|
||||
private:
|
||||
size_t FindHeaderEndPosition(const std::string& response) const;
|
||||
|
||||
@@ -1,323 +0,0 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "crypto_session.h"
|
||||
#include "license.h"
|
||||
#include "max_res_engine.h"
|
||||
#include "mock_clock.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef ::video_widevine_server::sdk::License License;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
|
||||
OutputProtection;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::
|
||||
VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<KeyContainer> KeyList;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
ConstraintList;
|
||||
|
||||
using namespace testing;
|
||||
|
||||
namespace {
|
||||
|
||||
const KeyId kKeyId1 = "357adc89f1673433c36c621f1b5c41ee";
|
||||
const KeyId kKeyId2 = "3d25f819250789ecfc9ed48cc99af164";
|
||||
const KeyId kKeyId3 = "fe3cf6b69e76c9a1c877922e1a661707";
|
||||
const KeyId kKeyId4 = "29a321b9886658078f916fdd41d6f570";
|
||||
const KeyId kKeyId5 = "cc5b031bcde371031c06822d935b9a63";
|
||||
const KeyId kKeyId6 = "90ac1332e4efc8acbaf929c8d321f50c";
|
||||
|
||||
const uint32_t kMinRes1 = 0;
|
||||
const uint32_t kMaxRes1 = 2000;
|
||||
const uint32_t kTargetRes1 = (kMinRes1 + kMaxRes1) / 2;
|
||||
const uint32_t kMinRes2 = kMaxRes1;
|
||||
const uint32_t kMaxRes2 = 4000;
|
||||
const uint32_t kTargetRes2 = (kMinRes2 + kMaxRes2) / 2;
|
||||
const uint32_t kTargetRes3 = kMaxRes2 + 1000;
|
||||
|
||||
const OutputProtection::HDCP kHdcpDefault = OutputProtection::HDCP_V2;
|
||||
const OutputProtection::HDCP kHdcpConstraint = OutputProtection::HDCP_V2_1;
|
||||
|
||||
const int64_t kHdcpInterval = 10;
|
||||
|
||||
class HdcpOnlyMockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ACTION_P2(IncrementAndReturnPointee, p, a) {
|
||||
*p += a;
|
||||
return *p;
|
||||
}
|
||||
|
||||
class MaxResEngineTest : public Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
mock_clock_ = new NiceMock<MockClock>();
|
||||
current_time_ = 0;
|
||||
|
||||
ON_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillByDefault(
|
||||
IncrementAndReturnPointee(¤t_time_, kHdcpInterval));
|
||||
|
||||
max_res_engine_.reset(new MaxResEngine(&crypto_session_, mock_clock_));
|
||||
|
||||
KeyList* keys = license_.mutable_key();
|
||||
|
||||
// Key 1 - Content key w/ ID, no HDCP, no constraints
|
||||
{
|
||||
KeyContainer* key1 = keys->Add();
|
||||
key1->set_type(KeyContainer::CONTENT);
|
||||
key1->set_id(kKeyId1);
|
||||
}
|
||||
|
||||
// Key 2 - Content key w/ ID, HDCP, no constraints
|
||||
{
|
||||
KeyContainer* key2 = keys->Add();
|
||||
key2->set_type(KeyContainer::CONTENT);
|
||||
key2->set_id(kKeyId2);
|
||||
key2->mutable_required_protection()->set_hdcp(kHdcpDefault);
|
||||
}
|
||||
|
||||
// Key 3 - Content key w/ ID, no HDCP, constraints
|
||||
{
|
||||
KeyContainer* key3 = keys->Add();
|
||||
key3->set_type(KeyContainer::CONTENT);
|
||||
key3->set_id(kKeyId3);
|
||||
AddConstraints(key3->mutable_video_resolution_constraints());
|
||||
}
|
||||
|
||||
// Key 4 - Content key w/ ID, HDCP, constraints
|
||||
{
|
||||
KeyContainer* key4 = keys->Add();
|
||||
key4->set_type(KeyContainer::CONTENT);
|
||||
key4->set_id(kKeyId4);
|
||||
key4->mutable_required_protection()->set_hdcp(kHdcpDefault);
|
||||
AddConstraints(key4->mutable_video_resolution_constraints());
|
||||
}
|
||||
|
||||
// Key 5 - Content key w/o ID, HDCP, constraints
|
||||
{
|
||||
KeyContainer* key5 = keys->Add();
|
||||
key5->set_type(KeyContainer::CONTENT);
|
||||
key5->mutable_required_protection()->set_hdcp(kHdcpDefault);
|
||||
AddConstraints(key5->mutable_video_resolution_constraints());
|
||||
}
|
||||
|
||||
// Key 6 - Non-content key
|
||||
{
|
||||
KeyContainer* key6 = keys->Add();
|
||||
key6->set_type(KeyContainer::OPERATOR_SESSION);
|
||||
}
|
||||
}
|
||||
|
||||
void AddConstraints(ConstraintList* constraints) {
|
||||
// Constraint 1 - Low-res and no HDCP
|
||||
{
|
||||
VideoResolutionConstraint* constraint1 = constraints->Add();
|
||||
constraint1->set_min_resolution_pixels(kMinRes1);
|
||||
constraint1->set_max_resolution_pixels(kMaxRes1);
|
||||
}
|
||||
|
||||
// Constraint 2 - High-res and stricter HDCP
|
||||
{
|
||||
VideoResolutionConstraint* constraint2 = constraints->Add();
|
||||
constraint2->set_min_resolution_pixels(kMinRes2);
|
||||
constraint2->set_max_resolution_pixels(kMaxRes2);
|
||||
constraint2->mutable_required_protection()->set_hdcp(kHdcpConstraint);
|
||||
}
|
||||
}
|
||||
|
||||
MockClock* mock_clock_;
|
||||
int64_t current_time_;
|
||||
StrictMock<HdcpOnlyMockCryptoSession> crypto_session_;
|
||||
scoped_ptr<MaxResEngine> max_res_engine_;
|
||||
License license_;
|
||||
};
|
||||
|
||||
TEST_F(MaxResEngineTest, IsPermissiveByDefault) {
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
max_res_engine_->OnTimerEvent();
|
||||
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, IsPermissiveWithoutALicense) {
|
||||
max_res_engine_->SetResolution(1, kTargetRes1);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, IsPermissiveWithoutAResolution) {
|
||||
max_res_engine_->SetLicense(license_);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, HandlesResolutionsBasedOnConstraints) {
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillRepeatedly(
|
||||
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
|
||||
Return(true)));
|
||||
|
||||
max_res_engine_->SetLicense(license_);
|
||||
|
||||
max_res_engine_->SetResolution(1, kTargetRes1);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
max_res_engine_->SetResolution(1, kTargetRes2);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
max_res_engine_->SetResolution(1, kTargetRes3);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, RequestsHdcpImmediatelyAndOnlyAfterInterval) {
|
||||
int64_t start_time = current_time_;
|
||||
|
||||
{
|
||||
InSequence calls;
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime()).WillOnce(Return(start_time));
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(HDCP_V2_2),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(start_time + kHdcpInterval / 2))
|
||||
.WillOnce(Return(start_time + kHdcpInterval));
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(HDCP_V2_2),
|
||||
Return(true)));
|
||||
}
|
||||
|
||||
max_res_engine_->SetLicense(license_);
|
||||
max_res_engine_->SetResolution(1, kTargetRes1);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
max_res_engine_->OnTimerEvent();
|
||||
max_res_engine_->OnTimerEvent();
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, DoesNotRequestHdcpWithoutALicense) {
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
|
||||
|
||||
max_res_engine_->OnTimerEvent();
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, HandlesConstraintOverridingHdcp) {
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillRepeatedly(
|
||||
DoAll(SetArgPointee<0>(HDCP_V2),
|
||||
Return(true)));
|
||||
|
||||
max_res_engine_->SetLicense(license_);
|
||||
|
||||
max_res_engine_->SetResolution(1, kTargetRes1);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
max_res_engine_->SetResolution(1, kTargetRes2);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, HandlesNoHdcp) {
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillRepeatedly(
|
||||
DoAll(SetArgPointee<0>(HDCP_NONE),
|
||||
Return(true)));
|
||||
|
||||
max_res_engine_->SetLicense(license_);
|
||||
|
||||
max_res_engine_->SetResolution(1, kTargetRes1);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
max_res_engine_->SetResolution(1, kTargetRes2);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(MaxResEngineTest, IgnoresHdcpWithoutAResolution) {
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
|
||||
|
||||
max_res_engine_->SetLicense(license_);
|
||||
max_res_engine_->OnTimerEvent();
|
||||
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_TRUE(max_res_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
430
core/test/policy_engine_constraints_unittest.cpp
Normal file
430
core/test/policy_engine_constraints_unittest.cpp
Normal file
@@ -0,0 +1,430 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "crypto_session.h"
|
||||
#include "license.h"
|
||||
#include "policy_engine.h"
|
||||
#include "mock_clock.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
// protobuf generated classes.
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::License_Policy;
|
||||
using video_widevine_server::sdk::STREAMING;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef ::video_widevine_server::sdk::License License;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
|
||||
OutputProtection;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::
|
||||
VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<KeyContainer> KeyList;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
ConstraintList;
|
||||
|
||||
using namespace testing;
|
||||
|
||||
namespace {
|
||||
|
||||
const CdmSessionId kSessionId = "mock_session_id";
|
||||
|
||||
const KeyId kKeyId1 = "1111kKeyId1";
|
||||
const KeyId kKeyId2 = "2222kKeyId2";
|
||||
const KeyId kKeyId3 = "3333kKeyId3";
|
||||
const KeyId kKeyId4 = "4444kKeyId4";
|
||||
const KeyId kKeyId5 = "5555kKeyId5";
|
||||
const KeyId kKeyId6 = "6666kKeyId6";
|
||||
|
||||
const uint32_t kMinRes1 = 0;
|
||||
const uint32_t kMaxRes1 = 2000;
|
||||
const uint32_t kMinRes2 = kMaxRes1;
|
||||
const uint32_t kMaxRes2 = 4000;
|
||||
const uint32_t kTargetRes1 = (kMinRes1 + kMaxRes1) / 2;
|
||||
const uint32_t kTargetRes2 = (kMinRes2 + kMaxRes2) / 2;
|
||||
const uint32_t kTargetRes3 = kMaxRes2 + 1000;
|
||||
|
||||
const int64_t kRentalDuration = 604800; // 7 days
|
||||
const int64_t kPlaybackDuration = 172800; // 48 hours
|
||||
const int64_t kStreamingLicenseDuration = 300;
|
||||
|
||||
const OutputProtection::HDCP kHdcpV2 = OutputProtection::HDCP_V2;
|
||||
const OutputProtection::HDCP kHdcpV2_1 = OutputProtection::HDCP_V2_1;
|
||||
|
||||
// should match kHdcpCheckInterval in policy_engine.cpp
|
||||
const int64_t kHdcpInterval = 10;
|
||||
|
||||
class HdcpOnlyMockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
|
||||
};
|
||||
|
||||
class MockCdmEventListener : public WvCdmEventListener {
|
||||
public:
|
||||
MOCK_METHOD1(OnSessionRenewalNeeded, void(const CdmSessionId& session_id));
|
||||
MOCK_METHOD3(OnSessionKeysChange, void(const CdmSessionId& session_id,
|
||||
const CdmKeyStatusMap& keys_status,
|
||||
bool has_new_usable_key));
|
||||
MOCK_METHOD2(OnExpirationUpdate, void(const CdmSessionId& session_id,
|
||||
int64_t new_expiry_time_seconds));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class PolicyEngineConstraintsTest : public Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
mock_clock_ = new NiceMock<MockClock>();
|
||||
current_time_ = 0;
|
||||
|
||||
policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_,
|
||||
&crypto_session_));
|
||||
InjectMockClock();
|
||||
|
||||
SetupLicense();
|
||||
}
|
||||
|
||||
void AddConstraints(ConstraintList* constraints) {
|
||||
// Constraint 1 - Low-res and no HDCP
|
||||
{
|
||||
VideoResolutionConstraint* c_lo_res_no_hdcp = constraints->Add();
|
||||
c_lo_res_no_hdcp->set_min_resolution_pixels(kMinRes1);
|
||||
c_lo_res_no_hdcp->set_max_resolution_pixels(kMaxRes1);
|
||||
}
|
||||
|
||||
// Constraint 2 - High-res and stricter HDCP
|
||||
{
|
||||
VideoResolutionConstraint* c_hi_res_hdcp_v2 = constraints->Add();
|
||||
c_hi_res_hdcp_v2->set_min_resolution_pixels(kMinRes2);
|
||||
c_hi_res_hdcp_v2->set_max_resolution_pixels(kMaxRes2);
|
||||
c_hi_res_hdcp_v2->mutable_required_protection()->set_hdcp(kHdcpV2_1);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupLicense() {
|
||||
license_.set_license_start_time(current_time_);
|
||||
|
||||
LicenseIdentification* id = license_.mutable_id();
|
||||
id->set_version(1);
|
||||
id->set_type(STREAMING);
|
||||
|
||||
License_Policy* policy = license_.mutable_policy();
|
||||
policy = license_.mutable_policy();
|
||||
policy->set_can_play(true);
|
||||
policy->set_can_persist(false);
|
||||
policy->set_rental_duration_seconds(kRentalDuration);
|
||||
policy->set_playback_duration_seconds(kPlaybackDuration);
|
||||
policy->set_license_duration_seconds(kStreamingLicenseDuration);
|
||||
|
||||
KeyList* keys = license_.mutable_key();
|
||||
|
||||
// Key 1 - Content key w/ ID, no HDCP, no constraints
|
||||
{
|
||||
KeyContainer* key1 = keys->Add();
|
||||
key1->set_type(KeyContainer::CONTENT);
|
||||
key1->set_id(kKeyId1);
|
||||
}
|
||||
|
||||
// Key 2 - Content key w/ ID, HDCP, no constraints
|
||||
{
|
||||
KeyContainer* key2 = keys->Add();
|
||||
key2->set_type(KeyContainer::CONTENT);
|
||||
key2->set_id(kKeyId2);
|
||||
key2->mutable_required_protection()->set_hdcp(kHdcpV2);
|
||||
}
|
||||
|
||||
// Key 3 - Content key w/ ID, no HDCP, constraints
|
||||
{
|
||||
KeyContainer* key3 = keys->Add();
|
||||
key3->set_type(KeyContainer::CONTENT);
|
||||
key3->set_id(kKeyId3);
|
||||
AddConstraints(key3->mutable_video_resolution_constraints());
|
||||
}
|
||||
|
||||
// Key 4 - Content key w/ ID, HDCP, constraints
|
||||
{
|
||||
KeyContainer* key4 = keys->Add();
|
||||
key4->set_type(KeyContainer::CONTENT);
|
||||
key4->set_id(kKeyId4);
|
||||
key4->mutable_required_protection()->set_hdcp(kHdcpV2);
|
||||
AddConstraints(key4->mutable_video_resolution_constraints());
|
||||
}
|
||||
|
||||
// Key 5 - Content key w/o ID, HDCP, constraints
|
||||
{
|
||||
KeyContainer* key5 = keys->Add();
|
||||
key5->set_type(KeyContainer::CONTENT);
|
||||
key5->mutable_required_protection()->set_hdcp(kHdcpV2);
|
||||
AddConstraints(key5->mutable_video_resolution_constraints());
|
||||
}
|
||||
|
||||
// Key 6 - Non-content key
|
||||
{
|
||||
KeyContainer* key6 = keys->Add();
|
||||
key6->set_type(KeyContainer::OPERATOR_SESSION);
|
||||
}
|
||||
}
|
||||
|
||||
void InjectMockClock() {
|
||||
mock_clock_ = new MockClock();
|
||||
policy_engine_->set_clock(mock_clock_);
|
||||
}
|
||||
|
||||
void ExpectSessionKeysChange(CdmKeyStatus expected_key_status,
|
||||
bool expected_has_new_usable_key) {
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
OnSessionKeysChange(
|
||||
kSessionId, UnorderedElementsAre(
|
||||
Pair(kKeyId1, expected_key_status),
|
||||
Pair(kKeyId2, expected_key_status),
|
||||
Pair(kKeyId3, expected_key_status),
|
||||
Pair(kKeyId4, expected_key_status)),
|
||||
expected_has_new_usable_key));
|
||||
}
|
||||
|
||||
void ExpectSessionKeysChanges(const KeyId& k1, CdmKeyStatus expected_1,
|
||||
const KeyId& k2, CdmKeyStatus expected_2,
|
||||
const KeyId& k3, CdmKeyStatus expected_3,
|
||||
const KeyId& k4, CdmKeyStatus expected_4,
|
||||
bool expected_has_new_usable_key) {
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
OnSessionKeysChange(
|
||||
kSessionId, UnorderedElementsAre(
|
||||
Pair(k1, expected_1),
|
||||
Pair(k2, expected_2),
|
||||
Pair(k3, expected_3),
|
||||
Pair(k4, expected_4)),
|
||||
expected_has_new_usable_key));
|
||||
}
|
||||
|
||||
scoped_ptr<PolicyEngine> policy_engine_;
|
||||
MockClock* mock_clock_;
|
||||
int64_t current_time_;
|
||||
StrictMock<HdcpOnlyMockCryptoSession> crypto_session_;
|
||||
StrictMock<MockCdmEventListener> mock_event_listener_;
|
||||
License license_;
|
||||
};
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime()).Times(2);
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->OnTimerEvent();
|
||||
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
|
||||
{
|
||||
Sequence time;
|
||||
for (int i=0; i<4; ++i) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
|
||||
.WillOnce(Return(i * 10));
|
||||
}
|
||||
}
|
||||
{
|
||||
Sequence key_change;
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true);
|
||||
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
|
||||
kKeyId2, kKeyStatusUsable,
|
||||
kKeyId3, kKeyStatusOutputNotAllowed,
|
||||
kKeyId4, kKeyStatusOutputNotAllowed, false);
|
||||
}
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillRepeatedly(
|
||||
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
|
||||
Return(true)));
|
||||
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->NotifyResolution(1, kTargetRes1);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes2);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes3);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest,
|
||||
RequestsHdcpImmediatelyAndOnlyAfterInterval) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(0))
|
||||
.WillOnce(Return(5));
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true);
|
||||
|
||||
int64_t start_time = current_time_ + 5;
|
||||
{
|
||||
InSequence calls;
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(HDCP_V2_2),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(start_time + kHdcpInterval / 2))
|
||||
.WillOnce(Return(start_time + kHdcpInterval));
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(HDCP_V2_2),
|
||||
Return(true)));
|
||||
}
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes1);
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->OnTimerEvent();
|
||||
policy_engine_->OnTimerEvent();
|
||||
policy_engine_->OnTimerEvent();
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(0));
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
|
||||
|
||||
policy_engine_->OnTimerEvent();
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
|
||||
{
|
||||
Sequence time;
|
||||
for (int i=0; i<3; ++i) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
|
||||
.WillOnce(Return(i * 10));
|
||||
}
|
||||
}
|
||||
{
|
||||
Sequence key_change;
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true);
|
||||
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
|
||||
kKeyId2, kKeyStatusUsable,
|
||||
kKeyId3, kKeyStatusOutputNotAllowed,
|
||||
kKeyId4, kKeyStatusOutputNotAllowed, false);
|
||||
}
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillRepeatedly(
|
||||
DoAll(SetArgPointee<0>(HDCP_V2),
|
||||
Return(true)));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->NotifyResolution(1, kTargetRes1);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes2);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
|
||||
{
|
||||
Sequence time;
|
||||
for (int i=0; i<3; ++i) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
|
||||
.WillOnce(Return(i * 10));
|
||||
}
|
||||
}
|
||||
{
|
||||
Sequence key_change;
|
||||
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
|
||||
kKeyId2, kKeyStatusOutputNotAllowed,
|
||||
kKeyId3, kKeyStatusUsable,
|
||||
kKeyId4, kKeyStatusOutputNotAllowed, false);
|
||||
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
|
||||
kKeyId2, kKeyStatusOutputNotAllowed,
|
||||
kKeyId3, kKeyStatusOutputNotAllowed,
|
||||
kKeyId4, kKeyStatusOutputNotAllowed, false);
|
||||
}
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true);
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
|
||||
.WillRepeatedly(
|
||||
DoAll(SetArgPointee<0>(HDCP_NONE),
|
||||
Return(true)));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes1);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes2);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
|
||||
{
|
||||
Sequence time;
|
||||
for (int i=0; i<2; ++i) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
|
||||
.WillOnce(Return(i * 10));
|
||||
}
|
||||
}
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, true);
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
|
||||
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -42,6 +42,7 @@ const char* kRenewalServerUrl =
|
||||
const KeyId kKeyId = "357adc89f1673433c36c621f1b5c41ee";
|
||||
const KeyId kAnotherKeyId = "another_key_id";
|
||||
const KeyId kSomeRandomKeyId = "some_random_key_id";
|
||||
const KeyId kUnknownKeyId = "some_random_unknown_key_id";
|
||||
const CdmSessionId kSessionId = "mock_session_id";
|
||||
|
||||
int64_t GetLicenseRenewalDelay(int64_t license_duration) {
|
||||
@@ -60,13 +61,6 @@ class MockCdmEventListener : public WvCdmEventListener {
|
||||
int64_t new_expiry_time_seconds));
|
||||
};
|
||||
|
||||
class MockMaxResEngine : public MaxResEngine {
|
||||
public:
|
||||
MockMaxResEngine() : MaxResEngine(NULL) {}
|
||||
|
||||
MOCK_METHOD1(CanDecrypt, bool(const KeyId& key_id));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// protobuf generated classes.
|
||||
@@ -138,11 +132,6 @@ class PolicyEngineTest : public ::testing::Test {
|
||||
policy_engine_->set_clock(mock_clock_);
|
||||
}
|
||||
|
||||
void InjectMockMaxResEngine() {
|
||||
mock_max_res_engine_ = new MockMaxResEngine();
|
||||
policy_engine_->set_max_res_engine(mock_max_res_engine_);
|
||||
}
|
||||
|
||||
void ExpectSessionKeysChange(CdmKeyStatus expected_key_status,
|
||||
bool expected_has_new_usable_key) {
|
||||
EXPECT_CALL(mock_event_listener_,
|
||||
@@ -165,7 +154,6 @@ class PolicyEngineTest : public ::testing::Test {
|
||||
|
||||
StrictMock<MockCdmEventListener> mock_event_listener_;
|
||||
MockClock* mock_clock_;
|
||||
MockMaxResEngine* mock_max_res_engine_;
|
||||
scoped_ptr<PolicyEngine> policy_engine_;
|
||||
License license_;
|
||||
MockFunction<void(int i)> check_;
|
||||
@@ -1036,56 +1024,204 @@ TEST_F(PolicyEngineTest, MultipleKeysInLicense) {
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kSomeRandomKeyId));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, KeysOutputNotAllowedByMaxResEngine) {
|
||||
class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest {
|
||||
protected:
|
||||
enum KeyFlag {
|
||||
kKeyFlagNull,
|
||||
kKeyFlagFalse,
|
||||
kKeyFlagTrue
|
||||
};
|
||||
|
||||
static const KeyFlag kEncryptNull = kKeyFlagNull;
|
||||
static const KeyFlag kEncryptFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kEncryptTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kDecryptNull = kKeyFlagNull;
|
||||
static const KeyFlag kDecryptFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kDecryptTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kSignNull = kKeyFlagNull;
|
||||
static const KeyFlag kSignFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kSignTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kVerifyNull = kKeyFlagNull;
|
||||
static const KeyFlag kVerifyFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kVerifyTrue = kKeyFlagTrue;
|
||||
|
||||
static const KeyFlag kContentSecureFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kContentSecureTrue = kKeyFlagTrue;
|
||||
static const KeyFlag kContentClearFalse = kKeyFlagFalse;
|
||||
static const KeyFlag kContentClearTrue = kKeyFlagTrue;
|
||||
|
||||
|
||||
void ExpectAllowedContentKeySettings(const CdmKeyAllowedUsage& key_usage,
|
||||
KeyFlag secure, KeyFlag clear) {
|
||||
EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue);
|
||||
EXPECT_FALSE(key_usage.generic_encrypt);
|
||||
EXPECT_FALSE(key_usage.generic_decrypt);
|
||||
EXPECT_FALSE(key_usage.generic_sign);
|
||||
EXPECT_FALSE(key_usage.generic_verify);
|
||||
}
|
||||
|
||||
void ExpectAllowedOperatorKeySettings(const CdmKeyAllowedUsage& key_usage,
|
||||
KeyFlag encrypt, KeyFlag decrypt,
|
||||
KeyFlag sign, KeyFlag verify) {
|
||||
EXPECT_FALSE(key_usage.decrypt_to_secure_buffer);
|
||||
EXPECT_FALSE(key_usage.decrypt_to_clear_buffer);
|
||||
EXPECT_EQ(key_usage.generic_encrypt, encrypt == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.generic_decrypt, decrypt == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.generic_sign, sign == kKeyFlagTrue);
|
||||
EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue);
|
||||
}
|
||||
|
||||
void ExpectSecureContentKey(const KeyId& key_id) {
|
||||
CdmKeyAllowedUsage key_usage;
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
|
||||
|
||||
ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue,
|
||||
kContentSecureFalse);
|
||||
}
|
||||
|
||||
void ExpectLessSecureContentKey(const KeyId& key_id) {
|
||||
CdmKeyAllowedUsage key_usage;
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
|
||||
|
||||
ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue,
|
||||
kContentSecureTrue);
|
||||
}
|
||||
|
||||
void ExpectOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt,
|
||||
KeyFlag decrypt, KeyFlag sign, KeyFlag verify) {
|
||||
CdmKeyAllowedUsage key_usage;
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
|
||||
|
||||
ExpectAllowedOperatorKeySettings(key_usage, encrypt, decrypt, sign, verify);
|
||||
}
|
||||
|
||||
void AddOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt,
|
||||
KeyFlag decrypt, KeyFlag sign, KeyFlag verify) {
|
||||
License::KeyContainer* non_content_key = license_.add_key();
|
||||
non_content_key->set_type(License::KeyContainer::OPERATOR_SESSION);
|
||||
non_content_key->set_id(key_id);
|
||||
License::KeyContainer::OperatorSessionKeyPermissions* permissions =
|
||||
non_content_key->mutable_operator_session_key_permissions();
|
||||
if (encrypt != kKeyFlagNull) {
|
||||
permissions->set_allow_encrypt(encrypt == kKeyFlagTrue);
|
||||
}
|
||||
if (decrypt != kKeyFlagNull) {
|
||||
permissions->set_allow_decrypt(decrypt == kKeyFlagTrue);
|
||||
}
|
||||
if (sign != kKeyFlagNull) {
|
||||
permissions->set_allow_sign(sign == kKeyFlagTrue);
|
||||
}
|
||||
if (verify != kKeyFlagNull) {
|
||||
permissions->set_allow_signature_verify(verify == kKeyFlagTrue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageBasic) {
|
||||
|
||||
const KeyId kGenericKeyId = "oper_session_key";
|
||||
|
||||
license_.clear_key();
|
||||
|
||||
// most secure
|
||||
License::KeyContainer* content_key = license_.add_key();
|
||||
content_key->set_type(License::KeyContainer::CONTENT);
|
||||
content_key->set_id(kKeyId);
|
||||
content_key->set_level(License::KeyContainer::HW_SECURE_ALL);
|
||||
|
||||
// generic operator session key (sign)
|
||||
AddOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull, kSignTrue,
|
||||
kVerifyNull);
|
||||
|
||||
License::KeyContainer* content_key_without_id = license_.add_key();
|
||||
content_key_without_id->set_type(License::KeyContainer::CONTENT);
|
||||
|
||||
// default level - less secure
|
||||
License::KeyContainer* another_content_key = license_.add_key();
|
||||
another_content_key->set_type(License::KeyContainer::CONTENT);
|
||||
another_content_key->set_id(kAnotherKeyId);
|
||||
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(kLicenseStartTime + 1))
|
||||
.WillOnce(Return(kLicenseStartTime + 5))
|
||||
.WillOnce(Return(kLicenseStartTime + 8))
|
||||
.WillOnce(Return(kLicenseStartTime + 10));
|
||||
.WillOnce(Return(kLicenseStartTime + 1));
|
||||
|
||||
InjectMockMaxResEngine();
|
||||
EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kKeyId))
|
||||
.WillOnce(Return(false))
|
||||
.WillOnce(Return(true))
|
||||
.WillOnce(Return(false))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kAnotherKeyId))
|
||||
.WillOnce(Return(false))
|
||||
.WillOnce(Return(false))
|
||||
.WillOnce(Return(true))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
InSequence s;
|
||||
ExpectSessionKeysChange(kKeyStatusOutputNotAllowed,
|
||||
kKeyStatusOutputNotAllowed, false);
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusOutputNotAllowed, true);
|
||||
ExpectSessionKeysChange(kKeyStatusOutputNotAllowed, kKeyStatusUsable, true);
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId));
|
||||
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId));
|
||||
ExpectSecureContentKey(kKeyId);
|
||||
ExpectLessSecureContentKey(kAnotherKeyId);
|
||||
ExpectOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull,
|
||||
kSignTrue, kVerifyNull);
|
||||
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId));
|
||||
CdmKeyAllowedUsage key_usage;
|
||||
EXPECT_EQ(KEY_NOT_FOUND_1,
|
||||
policy_engine_->QueryKeyAllowedUsage(kUnknownKeyId, &key_usage));
|
||||
}
|
||||
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId));
|
||||
TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) {
|
||||
|
||||
const KeyId kGenericEncryptKeyId = "oper_session_key_1";
|
||||
const KeyId kGenericDecryptKeyId = "oper_session_key_2";
|
||||
const KeyId kGenericSignKeyId = "oper_session_key_3";
|
||||
const KeyId kGenericVerifyKeyId = "oper_session_key_4";
|
||||
const KeyId kGenericFullKeyId = "oper_session_key_5";
|
||||
const KeyId kGenericExplicitKeyId = "oper_session_key_6";
|
||||
|
||||
license_.clear_key();
|
||||
|
||||
// more secure
|
||||
License::KeyContainer* content_key = license_.add_key();
|
||||
content_key->set_type(License::KeyContainer::CONTENT);
|
||||
content_key->set_id(kKeyId);
|
||||
content_key->set_level(License::KeyContainer::HW_SECURE_DECODE);
|
||||
|
||||
// less secure
|
||||
License::KeyContainer* another_content_key = license_.add_key();
|
||||
another_content_key->set_type(License::KeyContainer::CONTENT);
|
||||
another_content_key->set_id(kAnotherKeyId);
|
||||
another_content_key->set_level(License::KeyContainer::HW_SECURE_CRYPTO);
|
||||
|
||||
// generic operator session keys
|
||||
AddOperatorSessionKey(kGenericSignKeyId, kEncryptNull, kDecryptNull,
|
||||
kSignTrue, kVerifyNull);
|
||||
AddOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptNull,
|
||||
kSignNull, kVerifyNull);
|
||||
AddOperatorSessionKey(kGenericDecryptKeyId, kEncryptNull, kDecryptTrue,
|
||||
kSignNull, kVerifyNull);
|
||||
AddOperatorSessionKey(kGenericVerifyKeyId, kEncryptNull, kDecryptNull,
|
||||
kSignNull, kVerifyTrue);
|
||||
AddOperatorSessionKey(kGenericFullKeyId, kEncryptTrue, kDecryptTrue,
|
||||
kSignTrue, kVerifyTrue);
|
||||
AddOperatorSessionKey(kGenericExplicitKeyId, kEncryptFalse, kDecryptTrue,
|
||||
kSignFalse, kVerifyTrue);
|
||||
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(kLicenseStartTime + 1));
|
||||
|
||||
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
|
||||
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
|
||||
ExpectSecureContentKey(kKeyId);
|
||||
ExpectLessSecureContentKey(kAnotherKeyId);
|
||||
ExpectOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptFalse,
|
||||
kSignFalse, kVerifyFalse);
|
||||
ExpectOperatorSessionKey(kGenericDecryptKeyId, kEncryptFalse, kDecryptTrue,
|
||||
kSignFalse, kVerifyFalse);
|
||||
ExpectOperatorSessionKey(kGenericSignKeyId, kEncryptFalse, kDecryptFalse,
|
||||
kSignTrue, kVerifyFalse);
|
||||
ExpectOperatorSessionKey(kGenericVerifyKeyId, kEncryptFalse, kDecryptFalse,
|
||||
kSignFalse, kVerifyTrue);
|
||||
ExpectOperatorSessionKey(kGenericFullKeyId, kEncryptTrue, kDecryptTrue,
|
||||
kSignTrue, kVerifyTrue);
|
||||
ExpectOperatorSessionKey(kGenericExplicitKeyId, kEncryptFalse, kDecryptTrue,
|
||||
kSignFalse, kVerifyTrue);
|
||||
}
|
||||
|
||||
class PolicyEngineQueryTest : public PolicyEngineTest {
|
||||
|
||||
@@ -26,59 +26,74 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case DEVICE_REVOKED: *os << "DEVICE_REVOKED";
|
||||
break;
|
||||
case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES";
|
||||
case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES";
|
||||
break;
|
||||
case ADD_KEY_ERROR: *os << "ADD_KEY_ERROR";
|
||||
break;
|
||||
case CERT_PROVISIONING_GET_KEYBOX_ERROR_1: *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_1";
|
||||
case CERT_PROVISIONING_GET_KEYBOX_ERROR_1:
|
||||
*os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_1";
|
||||
break;
|
||||
case CERT_PROVISIONING_GET_KEYBOX_ERROR_2: *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_2";
|
||||
case CERT_PROVISIONING_GET_KEYBOX_ERROR_2:
|
||||
*os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_2";
|
||||
break;
|
||||
case CERT_PROVISIONING_INVALID_CERT_TYPE: *os << "CERT_PROVISIONING_INVALID_CERT_TYPE";
|
||||
case CERT_PROVISIONING_INVALID_CERT_TYPE:
|
||||
*os << "CERT_PROVISIONING_INVALID_CERT_TYPE";
|
||||
break;
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_1: *os << "CERT_PROVISIONING_REQUEST_ERROR_1";
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_1:
|
||||
*os << "CERT_PROVISIONING_REQUEST_ERROR_1";
|
||||
break;
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_2: *os << "CERT_PROVISIONING_REQUEST_ERROR_2";
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_2:
|
||||
*os << "CERT_PROVISIONING_REQUEST_ERROR_2";
|
||||
break;
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_3: *os << "CERT_PROVISIONING_REQUEST_ERROR_3";
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_3:
|
||||
*os << "CERT_PROVISIONING_REQUEST_ERROR_3";
|
||||
break;
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_4: *os << "CERT_PROVISIONING_REQUEST_ERROR_4";
|
||||
case CERT_PROVISIONING_REQUEST_ERROR_4:
|
||||
*os << "CERT_PROVISIONING_REQUEST_ERROR_4";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_1: *os << "CERT_PROVISIONING_RESPONSE_ERROR_1";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_1:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_1";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_2: *os << "CERT_PROVISIONING_RESPONSE_ERROR_2";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_2:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_2";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_3: *os << "CERT_PROVISIONING_RESPONSE_ERROR_3";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_3:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_3";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_4: *os << "CERT_PROVISIONING_RESPONSE_ERROR_4";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_4:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_4";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_5: *os << "CERT_PROVISIONING_RESPONSE_ERROR_5";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_5:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_5";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_6: *os << "CERT_PROVISIONING_RESPONSE_ERROR_6";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_6:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_6";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_7: *os << "CERT_PROVISIONING_RESPONSE_ERROR_7";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_7:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_7";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_8: *os << "CERT_PROVISIONING_RESPONSE_ERROR_8";
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_8:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_8";
|
||||
break;
|
||||
case CRYPTO_SESSION_OPEN_ERROR_1: *os << "CRYPTO_SESSION_OPEN_ERROR_1";
|
||||
case CRYPTO_SESSION_OPEN_ERROR_1: *os << "CRYPTO_SESSION_OPEN_ERROR_1";
|
||||
break;
|
||||
case CRYPTO_SESSION_OPEN_ERROR_2: *os << "CRYPTO_SESSION_OPEN_ERROR_2";
|
||||
case CRYPTO_SESSION_OPEN_ERROR_2: *os << "CRYPTO_SESSION_OPEN_ERROR_2";
|
||||
break;
|
||||
case CRYPTO_SESSION_OPEN_ERROR_3: *os << "CRYPTO_SESSION_OPEN_ERROR_3";
|
||||
case CRYPTO_SESSION_OPEN_ERROR_3: *os << "CRYPTO_SESSION_OPEN_ERROR_3";
|
||||
break;
|
||||
case CRYPTO_SESSION_OPEN_ERROR_4: *os << "CRYPTO_SESSION_OPEN_ERROR_4";
|
||||
case CRYPTO_SESSION_OPEN_ERROR_4: *os << "CRYPTO_SESSION_OPEN_ERROR_4";
|
||||
break;
|
||||
case CRYPTO_SESSION_OPEN_ERROR_5: *os << "CRYPTO_SESSION_OPEN_ERROR_5";
|
||||
case CRYPTO_SESSION_OPEN_ERROR_5: *os << "CRYPTO_SESSION_OPEN_ERROR_5";
|
||||
break;
|
||||
case DECRYPT_NOT_READY: *os << "DECRYPT_NOT_READY";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_1: *os << "DEVICE_CERTIFICATE_ERROR_1";
|
||||
case DEVICE_CERTIFICATE_ERROR_1: *os << "DEVICE_CERTIFICATE_ERROR_1";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_2: *os << "DEVICE_CERTIFICATE_ERROR_2";
|
||||
case DEVICE_CERTIFICATE_ERROR_2: *os << "DEVICE_CERTIFICATE_ERROR_2";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_3: *os << "DEVICE_CERTIFICATE_ERROR_3";
|
||||
case DEVICE_CERTIFICATE_ERROR_3: *os << "DEVICE_CERTIFICATE_ERROR_3";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4";
|
||||
case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4";
|
||||
break;
|
||||
case EMPTY_KEY_DATA_1: *os << "EMPTY_KEY_DATA_1";
|
||||
break;
|
||||
@@ -96,25 +111,27 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case EMPTY_LICENSE_RENEWAL: *os << "EMPTY_LICENSE_RENEWAL";
|
||||
break;
|
||||
case EMPTY_LICENSE_RESPONSE_1: *os << "EMPTY_LICENSE_RESPONSE_1";
|
||||
case EMPTY_LICENSE_RESPONSE_1: *os << "EMPTY_LICENSE_RESPONSE_1";
|
||||
break;
|
||||
case EMPTY_LICENSE_RESPONSE_2: *os << "EMPTY_LICENSE_RESPONSE_2";
|
||||
case EMPTY_LICENSE_RESPONSE_2: *os << "EMPTY_LICENSE_RESPONSE_2";
|
||||
break;
|
||||
case EMPTY_PROVISIONING_CERTIFICATE_1: *os << "EMPTY_PROVISIONING_CERTIFICATE_1";
|
||||
case EMPTY_PROVISIONING_CERTIFICATE_1:
|
||||
*os << "EMPTY_PROVISIONING_CERTIFICATE_1";
|
||||
break;
|
||||
case EMPTY_PROVISIONING_RESPONSE: *os << "EMPTY_PROVISIONING_RESPONSE";
|
||||
case EMPTY_PROVISIONING_RESPONSE: *os << "EMPTY_PROVISIONING_RESPONSE";
|
||||
break;
|
||||
case EMPTY_SESSION_ID: *os << "EMPTY_SESSION_ID";
|
||||
break;
|
||||
case GENERATE_DERIVED_KEYS_ERROR: *os << "GENERATE_DERIVED_KEYS_ERROR";
|
||||
case GENERATE_DERIVED_KEYS_ERROR: *os << "GENERATE_DERIVED_KEYS_ERROR";
|
||||
break;
|
||||
case LICENSE_RENEWAL_NONCE_GENERATION_ERROR: *os << "LICENSE_RENEWAL_NONCE_GENERATION_ERROR";
|
||||
case LICENSE_RENEWAL_NONCE_GENERATION_ERROR:
|
||||
*os << "LICENSE_RENEWAL_NONCE_GENERATION_ERROR";
|
||||
break;
|
||||
case GENERATE_USAGE_REPORT_ERROR: *os << "GENERATE_USAGE_REPORT_ERROR";
|
||||
case GENERATE_USAGE_REPORT_ERROR: *os << "GENERATE_USAGE_REPORT_ERROR";
|
||||
break;
|
||||
case GET_LICENSE_ERROR: *os << "GET_LICENSE_ERROR";
|
||||
break;
|
||||
case GET_RELEASED_LICENSE_ERROR: *os << "GET_RELEASED_LICENSE_ERROR";
|
||||
case GET_RELEASED_LICENSE_ERROR: *os << "GET_RELEASED_LICENSE_ERROR";
|
||||
break;
|
||||
case GET_USAGE_INFO_ERROR_1: *os << "GET_USAGE_INFO_ERROR_1";
|
||||
break;
|
||||
@@ -126,51 +143,60 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case INIT_DATA_NOT_FOUND: *os << "INIT_DATA_NOT_FOUND";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_1: *os << "INVALID_CRYPTO_SESSION_1";
|
||||
case INVALID_CRYPTO_SESSION_1: *os << "INVALID_CRYPTO_SESSION_1";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_2: *os << "INVALID_CRYPTO_SESSION_2";
|
||||
case INVALID_CRYPTO_SESSION_2: *os << "INVALID_CRYPTO_SESSION_2";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_3: *os << "INVALID_CRYPTO_SESSION_3";
|
||||
case INVALID_CRYPTO_SESSION_3: *os << "INVALID_CRYPTO_SESSION_3";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_4: *os << "INVALID_CRYPTO_SESSION_4";
|
||||
case INVALID_CRYPTO_SESSION_4: *os << "INVALID_CRYPTO_SESSION_4";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_5: *os << "INVALID_CRYPTO_SESSION_5";
|
||||
case INVALID_CRYPTO_SESSION_5: *os << "INVALID_CRYPTO_SESSION_5";
|
||||
break;
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_1: *os << "INVALID_DECRYPT_PARAMETERS_ENG_1";
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_1:
|
||||
*os << "INVALID_DECRYPT_PARAMETERS_ENG_1";
|
||||
break;
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_2: *os << "INVALID_DECRYPT_PARAMETERS_ENG_2";
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_2:
|
||||
*os << "INVALID_DECRYPT_PARAMETERS_ENG_2";
|
||||
break;
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_3: *os << "INVALID_DECRYPT_PARAMETERS_ENG_3";
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_3:
|
||||
*os << "INVALID_DECRYPT_PARAMETERS_ENG_3";
|
||||
break;
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_4: *os << "INVALID_DECRYPT_PARAMETERS_ENG_4";
|
||||
case INVALID_DECRYPT_PARAMETERS_ENG_4:
|
||||
*os << "INVALID_DECRYPT_PARAMETERS_ENG_4";
|
||||
break;
|
||||
case INVALID_DEVICE_CERTIFICATE_TYPE: *os << "INVALID_DEVICE_CERTIFICATE_TYPE";
|
||||
case INVALID_DEVICE_CERTIFICATE_TYPE:
|
||||
*os << "INVALID_DEVICE_CERTIFICATE_TYPE";
|
||||
break;
|
||||
case INVALID_KEY_SYSTEM: *os << "INVALID_KEY_SYSTEM";
|
||||
break;
|
||||
case INVALID_LICENSE_RESPONSE: *os << "INVALID_LICENSE_RESPONSE";
|
||||
case INVALID_LICENSE_RESPONSE: *os << "INVALID_LICENSE_RESPONSE";
|
||||
break;
|
||||
case INVALID_LICENSE_TYPE: *os << "INVALID_LICENSE_TYPE";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_1: *os << "INVALID_PARAMETERS_ENG_1";
|
||||
case INVALID_PARAMETERS_ENG_1: *os << "INVALID_PARAMETERS_ENG_1";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_2: *os << "INVALID_PARAMETERS_ENG_2";
|
||||
case INVALID_PARAMETERS_ENG_2: *os << "INVALID_PARAMETERS_ENG_2";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_3: *os << "INVALID_PARAMETERS_ENG_3";
|
||||
case INVALID_PARAMETERS_ENG_3: *os << "INVALID_PARAMETERS_ENG_3";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_4: *os << "INVALID_PARAMETERS_ENG_4";
|
||||
case INVALID_PARAMETERS_ENG_4: *os << "INVALID_PARAMETERS_ENG_4";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_1: *os << "INVALID_PARAMETERS_LIC_1";
|
||||
case INVALID_PARAMETERS_LIC_1: *os << "INVALID_PARAMETERS_LIC_1";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_2: *os << "INVALID_PARAMETERS_LIC_2";
|
||||
case INVALID_PARAMETERS_LIC_2: *os << "INVALID_PARAMETERS_LIC_2";
|
||||
break;
|
||||
case INVALID_PROVISIONING_PARAMETERS_1: *os << "INVALID_PROVISIONING_PARAMETERS_1";
|
||||
case INVALID_PROVISIONING_PARAMETERS_1:
|
||||
*os << "INVALID_PROVISIONING_PARAMETERS_1";
|
||||
break;
|
||||
case INVALID_PROVISIONING_PARAMETERS_2: *os << "INVALID_PROVISIONING_PARAMETERS_2";
|
||||
case INVALID_PROVISIONING_PARAMETERS_2:
|
||||
*os << "INVALID_PROVISIONING_PARAMETERS_2";
|
||||
break;
|
||||
case INVALID_PROVISIONING_REQUEST_PARAM_1: *os << "INVALID_PROVISIONING_REQUEST_PARAM_1";
|
||||
case INVALID_PROVISIONING_REQUEST_PARAM_1:
|
||||
*os << "INVALID_PROVISIONING_REQUEST_PARAM_1";
|
||||
break;
|
||||
case INVALID_PROVISIONING_REQUEST_PARAM_2: *os << "INVALID_PROVISIONING_REQUEST_PARAM_2";
|
||||
case INVALID_PROVISIONING_REQUEST_PARAM_2:
|
||||
*os << "INVALID_PROVISIONING_REQUEST_PARAM_2";
|
||||
break;
|
||||
case INVALID_QUERY_KEY: *os << "INVALID_QUERY_KEY";
|
||||
break;
|
||||
@@ -188,22 +214,28 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case LICENSE_ID_NOT_FOUND: *os << "LICENSE_ID_NOT_FOUND";
|
||||
break;
|
||||
case LICENSE_PARSER_INIT_ERROR: *os << "LICENSE_PARSER_INIT_ERROR";
|
||||
case LICENSE_PARSER_INIT_ERROR: *os << "LICENSE_PARSER_INIT_ERROR";
|
||||
break;
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_1: *os << "LICENSE_PARSER_NOT_INITIALIZED_1";
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_1:
|
||||
*os << "LICENSE_PARSER_NOT_INITIALIZED_1";
|
||||
break;
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_2: *os << "LICENSE_PARSER_NOT_INITIALIZED_2";
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_2:
|
||||
*os << "LICENSE_PARSER_NOT_INITIALIZED_2";
|
||||
break;
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_3: *os << "LICENSE_PARSER_NOT_INITIALIZED_3";
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_3:
|
||||
*os << "LICENSE_PARSER_NOT_INITIALIZED_3";
|
||||
break;
|
||||
case LICENSE_RESPONSE_NOT_SIGNED: *os << "LICENSE_RESPONSE_NOT_SIGNED";
|
||||
case LICENSE_RESPONSE_NOT_SIGNED: *os << "LICENSE_RESPONSE_NOT_SIGNED";
|
||||
break;
|
||||
break;
|
||||
case LICENSE_RESPONSE_PARSE_ERROR_1: *os << "LICENSE_RESPONSE_PARSE_ERROR_1";
|
||||
case LICENSE_RESPONSE_PARSE_ERROR_1:
|
||||
*os << "LICENSE_RESPONSE_PARSE_ERROR_1";
|
||||
break;
|
||||
case LICENSE_RESPONSE_PARSE_ERROR_2: *os << "LICENSE_RESPONSE_PARSE_ERROR_2";
|
||||
case LICENSE_RESPONSE_PARSE_ERROR_2:
|
||||
*os << "LICENSE_RESPONSE_PARSE_ERROR_2";
|
||||
break;
|
||||
case LICENSE_RESPONSE_PARSE_ERROR_3: *os << "LICENSE_RESPONSE_PARSE_ERROR_3";
|
||||
case LICENSE_RESPONSE_PARSE_ERROR_3:
|
||||
*os << "LICENSE_RESPONSE_PARSE_ERROR_3";
|
||||
break;
|
||||
case LOAD_KEY_ERROR: *os << "LOAD_KEY_ERROR";
|
||||
break;
|
||||
@@ -211,35 +243,34 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case REFRESH_KEYS_ERROR: *os << "REFRESH_KEYS_ERROR";
|
||||
break;
|
||||
case RELEASE_ALL_USAGE_INFO_ERROR_1: *os << "RELEASE_ALL_USAGE_INFO_ERROR_1";
|
||||
case RELEASE_ALL_USAGE_INFO_ERROR_1:
|
||||
*os << "RELEASE_ALL_USAGE_INFO_ERROR_1";
|
||||
break;
|
||||
case RELEASE_ALL_USAGE_INFO_ERROR_2: *os << "RELEASE_ALL_USAGE_INFO_ERROR_2";
|
||||
case RELEASE_ALL_USAGE_INFO_ERROR_2:
|
||||
*os << "RELEASE_ALL_USAGE_INFO_ERROR_2";
|
||||
break;
|
||||
case RELEASE_KEY_ERROR: *os << "RELEASE_KEY_ERROR";
|
||||
break;
|
||||
case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR";
|
||||
case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR";
|
||||
break;
|
||||
case RELEASE_LICENSE_ERROR_1: *os << "RELEASE_LICENSE_ERROR_1";
|
||||
break;
|
||||
case RELEASE_LICENSE_ERROR_2: *os << "RELEASE_LICENSE_ERROR_2";
|
||||
break;
|
||||
case RELEASE_USAGE_INFO_ERROR: *os << "RELEASE_USAGE_INFO_ERROR";
|
||||
case RELEASE_USAGE_INFO_ERROR: *os << "RELEASE_USAGE_INFO_ERROR";
|
||||
break;
|
||||
case RENEW_KEY_ERROR_1: *os << "RENEW_KEY_ERROR_1";
|
||||
break;
|
||||
case RENEW_KEY_ERROR_2: *os << "RENEW_KEY_ERROR_2";
|
||||
break;
|
||||
case LICENSE_RENEWAL_SIGNING_ERROR: *os << "LICENSE_RENEWAL_SIGNING_ERROR";
|
||||
case LICENSE_RENEWAL_SIGNING_ERROR: *os << "LICENSE_RENEWAL_SIGNING_ERROR";
|
||||
break;
|
||||
case RESTORE_OFFLINE_LICENSE_ERROR_1: *os << "RESTORE_OFFLINE_LICENSE_ERROR_1";
|
||||
break;
|
||||
case RESTORE_OFFLINE_LICENSE_ERROR_2: *os << "RESTORE_OFFLINE_LICENSE_ERROR_2";
|
||||
break;
|
||||
case SESSION_INIT_ERROR_1: *os << "SESSION_INIT_ERROR_1";
|
||||
case RESTORE_OFFLINE_LICENSE_ERROR_2:
|
||||
*os << "RESTORE_OFFLINE_LICENSE_ERROR_2";
|
||||
break;
|
||||
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
|
||||
break;
|
||||
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR";
|
||||
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1";
|
||||
break;
|
||||
@@ -261,7 +292,7 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case SESSION_NOT_FOUND_10: *os << "SESSION_NOT_FOUND_10";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_FOR_DECRYPT: *os << "SESSION_NOT_FOUND_FOR_DECRYPT";
|
||||
case SESSION_NOT_FOUND_FOR_DECRYPT: *os << "SESSION_NOT_FOUND_FOR_DECRYPT";
|
||||
break;
|
||||
case SESSION_KEYS_NOT_FOUND: *os << "SESSION_KEYS_NOT_FOUND";
|
||||
break;
|
||||
@@ -271,8 +302,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case STORE_LICENSE_ERROR_2: *os << "STORE_LICENSE_ERROR_2";
|
||||
break;
|
||||
case STORE_LICENSE_ERROR_3: *os << "STORE_LICENSE_ERROR_3";
|
||||
break;
|
||||
case STORE_USAGE_INFO_ERROR: *os << "STORE_USAGE_INFO_ERROR";
|
||||
break;
|
||||
case UNPROVISION_ERROR_1: *os << "UNPROVISION_ERROR_1";
|
||||
@@ -290,58 +319,134 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
case LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR:
|
||||
*os << "LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR";
|
||||
break;
|
||||
case EMPTY_PROVISIONING_CERTIFICATE_2: *os << "EMPTY_PROVISIONING_CERTIFICATE_2";
|
||||
case EMPTY_PROVISIONING_CERTIFICATE_2:
|
||||
*os << "EMPTY_PROVISIONING_CERTIFICATE_2";
|
||||
break;
|
||||
case PARSE_SERVICE_CERTIFICATE_ERROR: *os << "PARSE_SERVICE_CERTIFICATE_ERROR";
|
||||
case PARSE_SERVICE_CERTIFICATE_ERROR:
|
||||
*os << "PARSE_SERVICE_CERTIFICATE_ERROR";
|
||||
break;
|
||||
case SERVICE_CERTIFICATE_TYPE_ERROR: *os << "SERVICE_CERTIFICATE_TYPE_ERROR";
|
||||
case SERVICE_CERTIFICATE_TYPE_ERROR:
|
||||
*os << "SERVICE_CERTIFICATE_TYPE_ERROR";
|
||||
break;
|
||||
case CLIENT_ID_GENERATE_RANDOM_ERROR: *os << "CLIENT_ID_GENERATE_RANDOM_ERROR";
|
||||
case CLIENT_ID_GENERATE_RANDOM_ERROR:
|
||||
*os << "CLIENT_ID_GENERATE_RANDOM_ERROR";
|
||||
break;
|
||||
case CLIENT_ID_AES_INIT_ERROR: *os << "CLIENT_ID_AES_INIT_ERROR";
|
||||
case CLIENT_ID_AES_INIT_ERROR: *os << "CLIENT_ID_AES_INIT_ERROR";
|
||||
break;
|
||||
case CLIENT_ID_AES_ENCRYPT_ERROR: *os << "CLIENT_ID_AES_ENCRYPT_ERROR";
|
||||
case CLIENT_ID_AES_ENCRYPT_ERROR: *os << "CLIENT_ID_AES_ENCRYPT_ERROR";
|
||||
break;
|
||||
case CLIENT_ID_RSA_INIT_ERROR: *os << "CLIENT_ID_RSA_INIT_ERROR";
|
||||
case CLIENT_ID_RSA_INIT_ERROR: *os << "CLIENT_ID_RSA_INIT_ERROR";
|
||||
break;
|
||||
case CLIENT_ID_RSA_ENCRYPT_ERROR: *os << "CLIENT_ID_RSA_ENCRYPT_ERROR";
|
||||
case CLIENT_ID_RSA_ENCRYPT_ERROR: *os << "CLIENT_ID_RSA_ENCRYPT_ERROR";
|
||||
break;
|
||||
case INVALID_QUERY_STATUS: *os << "INVALID_QUERY_STATUS";
|
||||
break;
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_4: *os << "LICENSE_PARSER_NOT_INITIALIZED_4";
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_4:
|
||||
*os << "LICENSE_PARSER_NOT_INITIALIZED_4";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_3: *os << "INVALID_PARAMETERS_LIC_3";
|
||||
case INVALID_PARAMETERS_LIC_3: *os << "INVALID_PARAMETERS_LIC_3";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_4: *os << "INVALID_PARAMETERS_LIC_4";
|
||||
case INVALID_PARAMETERS_LIC_4: *os << "INVALID_PARAMETERS_LIC_4";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_6: *os << "INVALID_PARAMETERS_LIC_6";
|
||||
case INVALID_PARAMETERS_LIC_6: *os << "INVALID_PARAMETERS_LIC_6";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_7: *os << "INVALID_PARAMETERS_LIC_7";
|
||||
case INVALID_PARAMETERS_LIC_7: *os << "INVALID_PARAMETERS_LIC_7";
|
||||
break;
|
||||
case LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR:
|
||||
*os << "LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR";
|
||||
break;
|
||||
case CENC_INIT_DATA_UNAVAILABLE: *os << "CENC_INIT_DATA_UNAVAILABLE";
|
||||
case CENC_INIT_DATA_UNAVAILABLE: *os << "CENC_INIT_DATA_UNAVAILABLE";
|
||||
break;
|
||||
case PREPARE_CENC_CONTENT_ID_FAILED: *os << "PREPARE_CENC_CONTENT_ID_FAILED";
|
||||
case PREPARE_CENC_CONTENT_ID_FAILED:
|
||||
*os << "PREPARE_CENC_CONTENT_ID_FAILED";
|
||||
break;
|
||||
case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE";
|
||||
case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE";
|
||||
break;
|
||||
case PREPARE_WEBM_CONTENT_ID_FAILED: *os << "PREPARE_WEBM_CONTENT_ID_FAILED";
|
||||
case PREPARE_WEBM_CONTENT_ID_FAILED:
|
||||
*os << "PREPARE_WEBM_CONTENT_ID_FAILED";
|
||||
break;
|
||||
case UNSUPPORTED_INIT_DATA_FORMAT: *os << "UNSUPPORTED_INIT_DATA_FORMAT";
|
||||
case UNSUPPORTED_INIT_DATA_FORMAT: *os << "UNSUPPORTED_INIT_DATA_FORMAT";
|
||||
break;
|
||||
case LICENSE_REQUEST_NONCE_GENERATION_ERROR: *os << "LICENSE_REQUEST_NONCE_GENERATION_ERROR";
|
||||
case LICENSE_REQUEST_NONCE_GENERATION_ERROR:
|
||||
*os << "LICENSE_REQUEST_NONCE_GENERATION_ERROR";
|
||||
break;
|
||||
case LICENSE_REQUEST_SIGNING_ERROR: *os << "LICENSE_REQUEST_SIGNING_ERROR";
|
||||
case LICENSE_REQUEST_SIGNING_ERROR: *os << "LICENSE_REQUEST_SIGNING_ERROR";
|
||||
break;
|
||||
case EMPTY_LICENSE_REQUEST: *os << "EMPTY_LICENSE_REQUEST";
|
||||
break;
|
||||
case DUPLICATE_SESSION_ID_SPECIFIED: *os << "DUPLICATE_SESSION_ID_SPECIFIED";
|
||||
case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED";
|
||||
case DUPLICATE_SESSION_ID_SPECIFIED:
|
||||
*os << "DUPLICATE_SESSION_ID_SPECIFIED";
|
||||
break;
|
||||
case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED";
|
||||
break;
|
||||
case SESSION_FILE_HANDLE_INIT_ERROR:
|
||||
*os << "SESSION_FILE_HANDLE_INIT_ERROR";
|
||||
break;
|
||||
case INCORRECT_CRYPTO_MODE: *os << "INCORRECT_CRYPTO_MODE";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_5: *os << "INVALID_PARAMETERS_ENG_5";
|
||||
break;
|
||||
case DECRYPT_ERROR: *os << "DECRYPT_ERROR";
|
||||
break;
|
||||
case INSUFFICIENT_OUTPUT_PROTECTION:
|
||||
*os << "INSUFFICIENT_OUTPUT_PROTECTION";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_12: *os << "SESSION_NOT_FOUND_12";
|
||||
break;
|
||||
case KEY_NOT_FOUND_1: *os << "KEY_NOT_FOUND_1";
|
||||
break;
|
||||
case KEY_NOT_FOUND_2: *os << "KEY_NOT_FOUND_2";
|
||||
break;
|
||||
case KEY_CONFLICT_1: *os << "KEY_CONFLICT_1";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_6: *os << "INVALID_PARAMETERS_ENG_6";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_7: *os << "INVALID_PARAMETERS_ENG_7";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_8: *os << "INVALID_PARAMETERS_ENG_8";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_9: *os << "INVALID_PARAMETERS_ENG_9";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_10: *os << "INVALID_PARAMETERS_ENG_10";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_11: *os << "INVALID_PARAMETERS_ENG_11";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_12: *os << "INVALID_PARAMETERS_ENG_12";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_13: *os << "SESSION_NOT_FOUND_13";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_14: *os << "SESSION_NOT_FOUND_14";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_15: *os << "SESSION_NOT_FOUND_15";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_16: *os << "SESSION_NOT_FOUND_16";
|
||||
break;
|
||||
case KEY_NOT_FOUND_3: *os << "KEY_NOT_FOUND_3";
|
||||
break;
|
||||
case KEY_NOT_FOUND_4: *os << "KEY_NOT_FOUND_4";
|
||||
break;
|
||||
case KEY_NOT_FOUND_5: *os << "KEY_NOT_FOUND_5";
|
||||
break;
|
||||
case KEY_NOT_FOUND_6: *os << "KEY_NOT_FOUND_6";
|
||||
break;
|
||||
case KEY_ERROR_1: *os << "KEY_ERROR_1";
|
||||
break;
|
||||
case KEY_ERROR_2: *os << "KEY_ERROR_2";
|
||||
break;
|
||||
case KEY_ERROR_3: *os << "KEY_ERROR_3";
|
||||
break;
|
||||
case KEY_ERROR_4: *os << "KEY_ERROR_4";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_13: *os << "INVALID_PARAMETERS_ENG_13";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_14: *os << "INVALID_PARAMETERS_ENG_14";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_15: *os << "INVALID_PARAMETERS_ENG_15";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_16: *os << "INVALID_PARAMETERS_ENG_16";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmResponseType";
|
||||
*os << "Unknown CdmResponseType";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -364,7 +469,7 @@ void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) {
|
||||
|
||||
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) {
|
||||
switch (value) {
|
||||
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
|
||||
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
|
||||
break;
|
||||
case kSecurityLevelL1: *os << "kSecurityLevelL1";
|
||||
break;
|
||||
@@ -387,7 +492,7 @@ void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) {
|
||||
case kCertificateX509: *os << "kCertificateX509";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmCertificateType";
|
||||
*os << "Unknown CdmCertificateType";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user