Source release v3.0.0-0-g8d3792b-ce + third_party
Change-Id: I399e71ddfffcd436171d1c60283c63ab4658e0b1
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
#ifndef WVCDM_CORE_CDM_CLIENT_PROPERTY_SET_H_
|
||||
#define WVCDM_CORE_CDM_CLIENT_PROPERTY_SET_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
@@ -16,9 +16,11 @@ class CdmClientPropertySet {
|
||||
virtual const std::string& security_level() const = 0;
|
||||
virtual bool use_privacy_mode() const = 0;
|
||||
virtual const std::string& service_certificate() const = 0;
|
||||
virtual void set_service_certificate(const std::string& cert) = 0;
|
||||
virtual bool is_session_sharing_enabled() const = 0;
|
||||
virtual uint32_t session_sharing_id() const = 0;
|
||||
virtual void set_session_sharing_id(uint32_t id) = 0;
|
||||
virtual const std::string& app_id() const = 0;
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
#ifndef WVCDM_CORE_CDM_ENGINE_H_
|
||||
#define WVCDM_CORE_CDM_ENGINE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "certificate_provisioning.h"
|
||||
#include "crypto_session.h"
|
||||
#include "initialization_data.h"
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
@@ -14,6 +18,7 @@ namespace wvcdm {
|
||||
class CdmClientPropertySet;
|
||||
class CdmSession;
|
||||
class CryptoEngine;
|
||||
class UsagePropertySet;
|
||||
class WvCdmEventListener;
|
||||
|
||||
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
|
||||
@@ -26,11 +31,17 @@ class CdmEngine {
|
||||
|
||||
// Session related methods
|
||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
const CdmClientPropertySet* property_set,
|
||||
CdmClientPropertySet* property_set,
|
||||
const std::string& origin,
|
||||
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);
|
||||
virtual CdmResponseType OpenKeySetSession(
|
||||
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener);
|
||||
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
|
||||
// License related methods
|
||||
@@ -49,6 +60,9 @@ class CdmEngine {
|
||||
// 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.
|
||||
@@ -56,11 +70,14 @@ class CdmEngine {
|
||||
// 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.
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id_out);
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id_out);
|
||||
|
||||
// Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
@@ -82,33 +99,50 @@ class CdmEngine {
|
||||
const CdmKeyResponse& key_data);
|
||||
|
||||
// Query system information
|
||||
virtual CdmResponseType QueryStatus(CdmQueryMap* info);
|
||||
virtual CdmResponseType QueryStatus(SecurityLevel security_level,
|
||||
CdmQueryMap* info);
|
||||
|
||||
// Query session information
|
||||
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
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);
|
||||
|
||||
// Query seesion control information
|
||||
// Query session control information
|
||||
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Provisioning related methods
|
||||
virtual CdmResponseType GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request, std::string* default_url);
|
||||
const std::string& origin, CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
|
||||
virtual CdmResponseType HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key);
|
||||
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
|
||||
virtual bool IsProvisioned(CdmSecurityLevel security_level,
|
||||
const std::string& origin);
|
||||
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level,
|
||||
const std::string& origin);
|
||||
|
||||
// Usage related methods for streaming licenses
|
||||
virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
|
||||
// Retrieve a random usage info from the list of all usage infos for this app
|
||||
// id.
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
CdmUsageInfo* usage_info);
|
||||
// Retrieve the usage info for the specified pst.
|
||||
// Returns UNKNOWN_ERROR if no usage info was found.
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
CdmUsageInfo* usage_info);
|
||||
virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id);
|
||||
virtual CdmResponseType ReleaseUsageInfo(
|
||||
const CdmUsageInfoReleaseMessage& message);
|
||||
|
||||
@@ -123,32 +157,50 @@ class CdmEngine {
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
||||
|
||||
// Event listener related methods
|
||||
virtual bool AttachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener);
|
||||
virtual bool DetachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener);
|
||||
// Used for notifying the Max-Res Engine of resolution changes
|
||||
virtual void NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
uint32_t height);
|
||||
|
||||
// Timer expiration method
|
||||
// Timer expiration method. This method is not re-entrant -- there can be
|
||||
// only one timer.
|
||||
// This method triggers appropriate event callbacks from |event_listener_|,
|
||||
// which is assumed to be asynchronous -- i.e. an event should be dispatched
|
||||
// to another thread which does the actual work. In particular, if a
|
||||
// synchronous listener calls OpenSession or CloseSession, the thread will
|
||||
// dead lock.
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
private:
|
||||
// private methods
|
||||
bool ValidateKeySystem(const CdmKeySystem& key_system);
|
||||
CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
SecurityLevel requested_security_level,
|
||||
CdmUsageInfo* usage_info);
|
||||
|
||||
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
|
||||
std::string MapHdcpVersion(CryptoSession::HdcpCapability version);
|
||||
|
||||
// instance variables
|
||||
CdmSessionMap sessions_;
|
||||
CdmReleaseKeySetMap release_key_sets_;
|
||||
CertificateProvisioning cert_provisioning_;
|
||||
scoped_ptr<CertificateProvisioning> cert_provisioning_;
|
||||
SecurityLevel cert_provisioning_requested_security_level_;
|
||||
|
||||
static bool seeded_;
|
||||
|
||||
// usage related variables
|
||||
scoped_ptr<CdmSession> usage_session_;
|
||||
int64_t last_usage_information_update_time;
|
||||
scoped_ptr<UsagePropertySet> usage_property_set_;
|
||||
int64_t last_usage_information_update_time_;
|
||||
|
||||
// Locks the list of sessions, |sessions_|, for the event timer. It will be
|
||||
// locked in OpenSession, CloseSession. It is also locked in OnTimerEvent and
|
||||
// OnKeyReleaseEvent while the list of event listeners is being generated.
|
||||
// The layer above the CDM implementation is expected to handle thread
|
||||
// synchronization to make sure other functions that access sessions do not
|
||||
// occur simultaneously with OpenSession or CloseSession.
|
||||
Lock session_list_lock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#define WVCDM_CORE_CDM_SESSION_H_
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
@@ -21,7 +22,9 @@ class WvCdmEventListener;
|
||||
|
||||
class CdmSession {
|
||||
public:
|
||||
explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set);
|
||||
CdmSession(CdmClientPropertySet* cdm_client_property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id);
|
||||
virtual ~CdmSession();
|
||||
|
||||
virtual CdmResponseType Init();
|
||||
@@ -34,9 +37,10 @@ class CdmSession {
|
||||
virtual const CdmSessionId& session_id() { return session_id_; }
|
||||
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id);
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id);
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response,
|
||||
@@ -74,24 +78,33 @@ class CdmSession {
|
||||
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
|
||||
virtual bool AttachEventListener(WvCdmEventListener* listener);
|
||||
virtual bool DetachEventListener(WvCdmEventListener* listener);
|
||||
// Used for notifying the Policy Engine of resolution changes
|
||||
virtual void NotifyResolution(uint32_t width, uint32_t height);
|
||||
|
||||
virtual void OnTimerEvent();
|
||||
virtual void OnTimerEvent(bool update_usage);
|
||||
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
|
||||
virtual void GetApplicationId(std::string* app_id);
|
||||
virtual SecurityLevel GetRequestedSecurityLevel() {
|
||||
return requested_security_level_;
|
||||
}
|
||||
virtual CdmSecurityLevel GetSecurityLevel() { return security_level_; }
|
||||
|
||||
// Delete usage information for the list of tokens, |provider_session_tokens|.
|
||||
virtual CdmResponseType DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens);
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
|
||||
virtual bool is_initial_usage_update() { return is_initial_usage_update_; }
|
||||
virtual bool is_usage_update_needed() { return is_usage_update_needed_; }
|
||||
virtual void reset_is_usage_update_needed() {
|
||||
virtual void reset_usage_flags() {
|
||||
is_initial_usage_update_ = false;
|
||||
is_usage_update_needed_ = false;
|
||||
}
|
||||
|
||||
virtual bool is_release() { return is_release_; }
|
||||
virtual bool is_offline() { return is_offline_; }
|
||||
|
||||
// ReleaseCrypto() - Closes the underlying crypto session but leaves this
|
||||
// object alive. It is invalid to call any method that requires a crypto
|
||||
// session after calling this. Since calling this renders this object mostly
|
||||
@@ -99,11 +112,10 @@ class CdmSession {
|
||||
// release the underlying crypto session) rather than call this method.
|
||||
virtual CdmResponseType ReleaseCrypto();
|
||||
|
||||
bool DeleteLicense();
|
||||
|
||||
private:
|
||||
// Internal constructor
|
||||
void Create(CdmLicense* license_parser, CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set);
|
||||
friend class CdmSessionTest;
|
||||
|
||||
// Generate unique ID for each new session.
|
||||
CdmSessionId GenerateSessionId();
|
||||
@@ -111,11 +123,17 @@ class CdmSession {
|
||||
|
||||
CdmResponseType StoreLicense();
|
||||
bool StoreLicense(DeviceFiles::LicenseState state);
|
||||
bool DeleteLicense();
|
||||
|
||||
// These setters are for testing only. Takes ownership of the pointers.
|
||||
void set_license_parser(CdmLicense* license_parser);
|
||||
void set_crypto_session(CryptoSession* crypto_session);
|
||||
void set_policy_engine(PolicyEngine* policy_engine);
|
||||
void set_file_handle(DeviceFiles* file_handle);
|
||||
|
||||
// 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_;
|
||||
@@ -123,11 +141,15 @@ class CdmSession {
|
||||
bool license_received_;
|
||||
bool is_offline_;
|
||||
bool is_release_;
|
||||
bool is_usage_update_needed_;
|
||||
bool is_initial_decryption_;
|
||||
|
||||
CdmSecurityLevel security_level_;
|
||||
SecurityLevel requested_security_level_;
|
||||
CdmAppParameterMap app_parameters_;
|
||||
|
||||
// decryption and usage flags
|
||||
bool is_initial_decryption_;
|
||||
bool has_decrypted_since_last_report_; // ... last report to policy engine.
|
||||
bool is_initial_usage_update_;
|
||||
bool is_usage_update_needed_;
|
||||
|
||||
// information useful for offline and usage scenarios
|
||||
CdmKeyMessage key_request_;
|
||||
@@ -142,18 +164,6 @@ class CdmSession {
|
||||
// license type release and offline related information
|
||||
CdmKeySetId key_set_id_;
|
||||
|
||||
std::set<WvCdmEventListener*> listeners_;
|
||||
|
||||
// For testing only
|
||||
// Takes ownership of license_parser, crypto_session, policy_engine
|
||||
// and device_files
|
||||
CdmSession(CdmLicense* license_parser, CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set);
|
||||
#if defined(UNIT_TEST)
|
||||
friend class CdmSessionTest;
|
||||
#endif
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#ifndef WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
||||
#define WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "wv_cdm_types.h"
|
||||
@@ -20,11 +22,14 @@ class CertificateProvisioning {
|
||||
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
|
||||
CdmCertificateType cert_type,
|
||||
const std::string& cert_authority,
|
||||
const std::string& origin,
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
CdmResponseType HandleProvisioningResponse(CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
CdmResponseType HandleProvisioningResponse(
|
||||
const std::string& origin,
|
||||
const CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
|
||||
private:
|
||||
void ComposeJsonRequestAsQueryString(const std::string& message,
|
||||
|
||||
@@ -19,6 +19,6 @@ class Clock {
|
||||
virtual int64_t GetCurrentTime();
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CLOCK_H_
|
||||
|
||||
@@ -35,6 +35,6 @@ class CryptoKey {
|
||||
std::string key_control_iv_;
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CRYPTO_KEY_H_
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#ifndef WVCDM_CORE_CRYPTO_SESSSION_H_
|
||||
#define WVCDM_CORE_CRYPTO_SESSSION_H_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
@@ -18,17 +18,13 @@ typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
|
||||
|
||||
class CryptoSession {
|
||||
public:
|
||||
// This enum should be kept in sync with the values specified for
|
||||
// HDCP capabilities in OEMCryptoCENC.h. (See comments for
|
||||
// OEMCrypto_GetHDCPCapability)
|
||||
typedef OEMCrypto_HDCP_Capability HdcpCapability;
|
||||
typedef enum {
|
||||
kOemCryptoHdcpNotSupported = 0,
|
||||
kOemCryptoHdcpVersion1 = 1,
|
||||
kOemCryptoHdcpVersion2 = 2,
|
||||
kOemCryptoHdcpVersion2_1 = 3,
|
||||
kOemCryptoHdcpVersion2_2 = 4,
|
||||
kOemCryptoNoHdcpDeviceAttached = 0xff,
|
||||
} OemCryptoHdcpVersion;
|
||||
kUsageDurationsInvalid = 0,
|
||||
kUsageDurationPlaybackNotBegun = 1,
|
||||
kUsageDurationsValid = 2,
|
||||
} UsageDurationStatus;
|
||||
|
||||
CryptoSession();
|
||||
virtual ~CryptoSession();
|
||||
|
||||
@@ -77,17 +73,34 @@ class CryptoSession {
|
||||
// Media data path
|
||||
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
|
||||
// Usage related methods
|
||||
virtual bool UsageInformationSupport(bool* has_support);
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
virtual CdmResponseType DeactivateUsageInformation(
|
||||
const std::string& provider_session_token);
|
||||
virtual CdmResponseType GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report);
|
||||
const std::string& provider_session_token, std::string* usage_report,
|
||||
UsageDurationStatus* usage_duration_status,
|
||||
int64_t* seconds_since_started, int64_t* seconds_since_last_played);
|
||||
virtual CdmResponseType ReleaseUsageInformation(
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& provider_session_token);
|
||||
// Delete a usage information for a single token. This does not require
|
||||
// a signed message from the server.
|
||||
virtual CdmResponseType DeleteUsageInformation(
|
||||
const std::string& provider_session_token);
|
||||
// Delete usage information for a list of tokens. This does not require
|
||||
// a signed message from the server.
|
||||
virtual CdmResponseType DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens);
|
||||
virtual CdmResponseType DeleteAllUsageReports();
|
||||
virtual bool IsAntiRollbackHwPresent();
|
||||
|
||||
virtual bool GetHdcpCapabilities(OemCryptoHdcpVersion* current,
|
||||
OemCryptoHdcpVersion* max);
|
||||
virtual bool GetHdcpCapabilities(HdcpCapability* current,
|
||||
HdcpCapability* max);
|
||||
virtual bool GetRandom(size_t data_length, uint8_t* random_data);
|
||||
virtual bool GetNumberOfOpenSessions(size_t* count);
|
||||
virtual bool GetMaxNumberOfSessions(size_t* max);
|
||||
|
||||
private:
|
||||
void Init();
|
||||
@@ -109,6 +122,7 @@ class CryptoSession {
|
||||
static int session_count_;
|
||||
|
||||
bool open_;
|
||||
bool update_usage_table_after_close_session_;
|
||||
CryptoSessionId oec_session_id_;
|
||||
|
||||
OEMCryptoBufferType destination_buffer_type_;
|
||||
@@ -123,6 +137,6 @@ class CryptoSession {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CRYPTO_SESSSION_H_
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
#ifndef WVCDM_CORE_DEVICE_FILES_H_
|
||||
#define WVCDM_CORE_DEVICE_FILES_H_
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
@@ -30,10 +35,14 @@ class DeviceFiles {
|
||||
return Init(security_level);
|
||||
}
|
||||
|
||||
virtual bool StoreCertificate(const std::string& certificate,
|
||||
virtual bool StoreCertificate(const std::string& origin,
|
||||
const std::string& certificate,
|
||||
const std::string& wrapped_private_key);
|
||||
virtual bool RetrieveCertificate(std::string* certificate,
|
||||
virtual bool RetrieveCertificate(const std::string& origin,
|
||||
std::string* certificate,
|
||||
std::string* wrapped_private_key);
|
||||
virtual bool HasCertificate(const std::string& origin);
|
||||
virtual bool RemoveCertificate(const std::string& origin);
|
||||
|
||||
virtual bool StoreLicense(const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
@@ -42,14 +51,17 @@ class DeviceFiles {
|
||||
const CdmKeyResponse& key_response,
|
||||
const CdmKeyMessage& key_renewal_request,
|
||||
const CdmKeyResponse& key_renewal_response,
|
||||
const std::string& release_server_url);
|
||||
virtual bool RetrieveLicense(const std::string& key_set_id,
|
||||
LicenseState* state, CdmInitData* pssh_data,
|
||||
CdmKeyMessage* key_request,
|
||||
CdmKeyResponse* key_response,
|
||||
CdmKeyMessage* key_renewal_request,
|
||||
CdmKeyResponse* key_renewal_response,
|
||||
std::string* release_server_url);
|
||||
const std::string& release_server_url,
|
||||
int64_t playback_start_time,
|
||||
int64_t last_playback_time,
|
||||
const CdmAppParameterMap& app_parameters);
|
||||
virtual bool RetrieveLicense(
|
||||
const std::string& key_set_id, LicenseState* state,
|
||||
CdmInitData* pssh_data, CdmKeyMessage* key_request,
|
||||
CdmKeyResponse* key_response, CdmKeyMessage* key_renewal_request,
|
||||
CdmKeyResponse* key_renewal_response, std::string* release_server_url,
|
||||
int64_t* playback_start_time, int64_t* last_playback_time,
|
||||
CdmAppParameterMap* app_parameters);
|
||||
virtual bool DeleteLicense(const std::string& key_set_id);
|
||||
virtual bool DeleteAllFiles();
|
||||
virtual bool DeleteAllLicenses();
|
||||
@@ -58,47 +70,78 @@ class DeviceFiles {
|
||||
|
||||
virtual bool StoreUsageInfo(const std::string& provider_session_token,
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response);
|
||||
virtual bool DeleteUsageInfo(const std::string& provider_session_token);
|
||||
virtual bool DeleteUsageInfo();
|
||||
const CdmKeyResponse& key_response,
|
||||
const std::string& app_id);
|
||||
virtual bool DeleteUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token);
|
||||
// Delete usage information from the file system. Puts a list of all the
|
||||
// psts that were deleted from the file into |provider_session_tokens|.
|
||||
virtual bool DeleteAllUsageInfoForApp(
|
||||
const std::string& app_id,
|
||||
std::vector<std::string>* provider_session_tokens);
|
||||
// Retrieve one usage info from the file. Subsequent calls will retrieve
|
||||
// subsequent entries in the table for this app_id.
|
||||
virtual bool RetrieveUsageInfo(
|
||||
const std::string& app_id,
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
|
||||
// Retrieve the usage info entry specified by |provider_session_token|.
|
||||
// Returns false if the entry could not be found.
|
||||
virtual bool RetrieveUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_response);
|
||||
|
||||
private:
|
||||
bool StoreFileWithHash(const char* name, const std::string& serialized_file);
|
||||
bool StoreFileRaw(const char* name, const std::string& serialized_file);
|
||||
bool RetrieveHashedFile(const char* name, std::string* serialized_file);
|
||||
// Helpers that wrap the File interface and automatically handle hashing, as
|
||||
// well as adding the device files base path to to the file name.
|
||||
bool StoreFileWithHash(const std::string& name,
|
||||
const std::string& serialized_file);
|
||||
bool StoreFileRaw(const std::string& name,
|
||||
const std::string& serialized_file);
|
||||
bool RetrieveHashedFile(const std::string& name,
|
||||
std::string* serialized_file);
|
||||
bool FileExists(const std::string& name);
|
||||
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();
|
||||
|
||||
// For testing only:
|
||||
static std::string GetCertificateFileName();
|
||||
static std::string GetCertificateFileName(const std::string& origin);
|
||||
static std::string GetLicenseFileNameExtension();
|
||||
static std::string GetUsageInfoFileName();
|
||||
static std::string GetBlankFileData();
|
||||
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(DeviceFilesStoreTest, StoreCertificate);
|
||||
FRIEND_TEST(DeviceCertificateStoreTest, StoreCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceCertificateTest, HasCertificate);
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIds);
|
||||
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);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, DeleteAll);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Read);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Store);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test);
|
||||
FRIEND_TEST(WvCdmUsageInfoTest, DISABLED_UsageInfo);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UsageInfoRetryTest);
|
||||
FRIEND_TEST(WvCdmUsageInfoTest, UsageInfo);
|
||||
FRIEND_TEST(WvCdmExtendedDurationTest, UsageOverflowTest);
|
||||
#endif
|
||||
|
||||
static std::set<std::string> reserved_license_ids_;
|
||||
|
||||
scoped_ptr<File> file_;
|
||||
CdmSecurityLevel security_level_;
|
||||
bool initialized_;
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
#ifndef WVCDM_CORE_FILE_STORE_H_
|
||||
#define WVCDM_CORE_FILE_STORE_H_
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ class InitializationData {
|
||||
CdmInitData data_;
|
||||
bool is_cenc_;
|
||||
bool is_webm_;
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(InitializationData);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace video_widevine_server {
|
||||
namespace sdk {
|
||||
class SignedMessage;
|
||||
class LicenseRequest;
|
||||
}
|
||||
} // namespace video_widevine_server
|
||||
|
||||
@@ -23,21 +24,19 @@ class PolicyEngine;
|
||||
|
||||
class CdmLicense {
|
||||
public:
|
||||
CdmLicense();
|
||||
CdmLicense(const CdmSessionId& session_id);
|
||||
virtual ~CdmLicense();
|
||||
|
||||
virtual bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
|
||||
virtual bool PrepareKeyRequest(const InitializationData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
const CdmSessionId& session_id,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual bool PrepareKeyUpdateRequest(bool is_renewal,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType PrepareKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType PrepareKeyUpdateRequest(
|
||||
bool is_renewal, const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* signed_request, std::string* server_url);
|
||||
virtual CdmResponseType HandleKeyResponse(
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual CdmResponseType HandleKeyUpdateResponse(
|
||||
@@ -46,38 +45,50 @@ class CdmLicense {
|
||||
virtual bool RestoreOfflineLicense(
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response);
|
||||
virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual bool HasInitData() { return !stored_init_data_.empty(); }
|
||||
const CdmKeyResponse& license_renewal_response,
|
||||
int64_t playback_start_time, int64_t last_playback_time);
|
||||
virtual bool RestoreLicenseForRelease(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response);
|
||||
virtual bool HasInitData() { return stored_init_data_.get(); }
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
|
||||
virtual std::string provider_session_token() {
|
||||
return provider_session_token_;
|
||||
}
|
||||
|
||||
static CdmResponseType VerifySignedServiceCertificate(
|
||||
const std::string& signed_service_certificate);
|
||||
|
||||
private:
|
||||
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
CdmResponseType HandleServiceCertificateResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
|
||||
CdmResponseType HandleKeyErrorResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
|
||||
CdmResponseType PrepareClientId(
|
||||
bool encrypt, const std::string& certificate,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
video_widevine_server::sdk::LicenseRequest* license_request);
|
||||
template <typename T>
|
||||
bool PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id);
|
||||
|
||||
static CdmResponseType VerifyAndExtractSignedServiceCertificate(
|
||||
const std::string& signed_service_certificate,
|
||||
std::string* service_certificate);
|
||||
bool GetServiceCertificate(std::string* service_certificate);
|
||||
|
||||
CryptoSession* session_;
|
||||
PolicyEngine* policy_engine_;
|
||||
std::string server_url_;
|
||||
std::string token_;
|
||||
std::string service_certificate_;
|
||||
std::string stored_init_data_;
|
||||
const CdmSessionId session_id_;
|
||||
scoped_ptr<InitializationData> stored_init_data_;
|
||||
bool initialized_;
|
||||
std::set<KeyId> loaded_keys_;
|
||||
std::string provider_session_token_;
|
||||
bool renew_with_client_id_;
|
||||
|
||||
// Used for certificate based licensing
|
||||
CdmKeyMessage key_request_;
|
||||
@@ -85,7 +96,8 @@ class CdmLicense {
|
||||
scoped_ptr<Clock> clock_;
|
||||
|
||||
// For testing
|
||||
CdmLicense(Clock* clock); // CdmLicense takes ownership of the clock.
|
||||
// CdmLicense takes ownership of the clock.
|
||||
CdmLicense(const CdmSessionId& session_id, Clock* clock);
|
||||
#if defined(UNIT_TEST)
|
||||
friend class CdmLicenseTest;
|
||||
#endif
|
||||
|
||||
@@ -21,10 +21,6 @@ class Lock {
|
||||
void Acquire();
|
||||
void Release();
|
||||
|
||||
// Acquires a lock if not held and returns true.
|
||||
// Returns false if the lock is held by another thread.
|
||||
bool Try();
|
||||
|
||||
friend class AutoLock;
|
||||
|
||||
private:
|
||||
@@ -50,6 +46,6 @@ class AutoLock {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_LOCK_H_
|
||||
|
||||
@@ -23,7 +23,7 @@ extern LogPriority g_cutoff;
|
||||
// This function is supplied for cases where the system layer does not
|
||||
// initialize logging. This is also needed to initialize logging in
|
||||
// unit tests.
|
||||
void InitLogging(int argc, const char* const* argv);
|
||||
void InitLogging();
|
||||
|
||||
void Log(const char* file, int line, LogPriority level, const char* fmt, ...);
|
||||
|
||||
@@ -34,6 +34,6 @@ void Log(const char* file, int line, LogPriority level, const char* fmt, ...);
|
||||
#define LOGD(...) Log(__FILE__, __LINE__, wvcdm::LOG_DEBUG, __VA_ARGS__)
|
||||
#define LOGV(...) Log(__FILE__, __LINE__, wvcdm::LOG_VERBOSE, __VA_ARGS__)
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_LOG_H_
|
||||
|
||||
105
core/include/max_res_engine.h
Normal file
105
core/include/max_res_engine.h
Normal file
@@ -0,0 +1,105 @@
|
||||
// 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_
|
||||
@@ -4,26 +4,36 @@
|
||||
#define WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
enum SecurityLevel { kLevelDefault, kLevel3 };
|
||||
|
||||
/* This attempts to open a session at the desired security level.
|
||||
If one level is not available, the other will be used instead. */
|
||||
// This attempts to open a session at the desired security level.
|
||||
// If one level is not available, the other will be used instead.
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
|
||||
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags);
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
|
||||
SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level);
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel level);
|
||||
const char* OEMCrypto_SecurityLevel(SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(SecurityLevel level,
|
||||
OEMCrypto_HDCP_Capability* current,
|
||||
OEMCrypto_HDCP_Capability* maximum);
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level);
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level);
|
||||
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
|
||||
size_t* count);
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
|
||||
size_t* maximum);
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
#ifndef WVCDM_CORE_POLICY_ENGINE_H_
|
||||
#define WVCDM_CORE_POLICY_ENGINE_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "license_protocol.pb.h"
|
||||
#include "max_res_engine.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -13,13 +16,15 @@ namespace wvcdm {
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
|
||||
class Clock;
|
||||
class PolicyEngineTest;
|
||||
class CryptoSession;
|
||||
class WvCdmEventListener;
|
||||
|
||||
// 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 PolicyEngine {
|
||||
public:
|
||||
PolicyEngine();
|
||||
PolicyEngine(CdmSessionId session_id, WvCdmEventListener* event_listener,
|
||||
CryptoSession* crypto_session);
|
||||
virtual ~PolicyEngine();
|
||||
|
||||
// The value returned should be taken as a hint rather than an absolute
|
||||
@@ -27,14 +32,12 @@ class PolicyEngine {
|
||||
// UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync
|
||||
// depending on the amount of time elapsed. The current decryption
|
||||
// status is not calculated to avoid overhead in the decryption path.
|
||||
virtual bool can_decrypt() { return can_decrypt_; }
|
||||
virtual bool CanDecrypt(const KeyId& key_id);
|
||||
|
||||
// OnTimerEvent is called when a timer fires. It notifies the Policy Engine
|
||||
// that the timer has fired and that it should check whether any events have
|
||||
// occurred since the last timer event. If so, it sets event_occurred to true
|
||||
// and sets event to point to the event that occurred. If not, it sets
|
||||
// event_occurred to false.
|
||||
virtual void OnTimerEvent(bool* event_occurred, CdmEventType* event);
|
||||
// that the timer has fired and dispatches the relevant events through
|
||||
// |event_listener_|.
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
// SetLicense is used in handling the initial license response. It stores
|
||||
// an exact copy of the policy information stored in the license.
|
||||
@@ -42,8 +45,14 @@ class PolicyEngine {
|
||||
// permits playback.
|
||||
virtual void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
|
||||
// SetLicenseForRelease is used when releasing a license. The keys in this
|
||||
// license will be ignored, and any old keys will be expired.
|
||||
virtual void SetLicenseForRelease(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
|
||||
// Call this on first decrypt to set the start of playback.
|
||||
virtual void BeginDecryption(void);
|
||||
virtual void DecryptionEvent(void);
|
||||
|
||||
// UpdateLicense is used in handling a license response for a renewal request.
|
||||
// The response may only contain any policy fields that have changed. In this
|
||||
@@ -53,11 +62,32 @@ class PolicyEngine {
|
||||
virtual void UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
|
||||
// Used for notifying the Policy Engine of resolution changes
|
||||
virtual void NotifyResolution(uint32_t width, uint32_t height);
|
||||
|
||||
virtual void NotifySessionExpiration();
|
||||
|
||||
virtual CdmResponseType Query(CdmQueryMap* key_info);
|
||||
|
||||
virtual const LicenseIdentification& license_id() { return license_id_; }
|
||||
|
||||
bool GetSecondsSinceStarted(int64_t* seconds_since_started);
|
||||
bool GetSecondsSinceLastPlayed(int64_t* seconds_since_started);
|
||||
|
||||
// for offline save and restore
|
||||
int64_t GetPlaybackStartTime() { return playback_start_time_; }
|
||||
int64_t GetLastPlaybackTime() { return last_playback_time_; }
|
||||
void RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time);
|
||||
|
||||
bool IsLicenseForFuture() { return license_state_ == kLicenseStatePending; }
|
||||
bool IsPlaybackStarted() { return playback_start_time_ > 0; }
|
||||
|
||||
bool IsLicenseOrPlaybackDurationExpired(int64_t current_time);
|
||||
|
||||
private:
|
||||
friend class PolicyEngineTest;
|
||||
|
||||
typedef enum {
|
||||
kLicenseStateInitial,
|
||||
kLicenseStatePending, // if license is issued for sometime in the future
|
||||
@@ -67,11 +97,10 @@ class PolicyEngine {
|
||||
kLicenseStateExpired
|
||||
} LicenseState;
|
||||
|
||||
void Init(Clock* clock);
|
||||
int64_t GetLicenseExpiryTime();
|
||||
int64_t GetPlaybackExpiryTime();
|
||||
|
||||
bool IsLicenseDurationExpired(int64_t current_time);
|
||||
int64_t GetLicenseDurationRemaining(int64_t current_time);
|
||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||
int64_t GetPlaybackDurationRemaining(int64_t current_time);
|
||||
|
||||
bool IsRenewalDelayExpired(int64_t current_time);
|
||||
@@ -80,8 +109,19 @@ class PolicyEngine {
|
||||
|
||||
void UpdateRenewalRequest(int64_t current_time);
|
||||
|
||||
// Notifies updates in keys information and fire OnKeysChange event if
|
||||
// key changes.
|
||||
void NotifyKeysChange(CdmKeyStatus new_status);
|
||||
|
||||
// Notifies updates in expiry time and fire OnExpirationUpdate event if
|
||||
// expiry time changes.
|
||||
void NotifyExpirationUpdate();
|
||||
|
||||
// These setters are for testing only. Takes ownership of the pointers.
|
||||
void set_clock(Clock* clock);
|
||||
void set_max_res_engine(MaxResEngine* max_res_engine);
|
||||
|
||||
LicenseState license_state_;
|
||||
bool can_decrypt_;
|
||||
|
||||
// This is the current policy information for this license. This gets updated
|
||||
// as license renewals occur.
|
||||
@@ -97,6 +137,9 @@ class PolicyEngine {
|
||||
// license request/renewal
|
||||
int64_t license_start_time_;
|
||||
int64_t playback_start_time_;
|
||||
int64_t last_playback_time_;
|
||||
int64_t last_expiry_time_;
|
||||
bool last_expiry_time_set_;
|
||||
|
||||
// This is used as a reference point for policy management. This value
|
||||
// represents an offset from license_start_time_. This is used to
|
||||
@@ -104,11 +147,15 @@ class PolicyEngine {
|
||||
int64_t next_renewal_time_;
|
||||
int64_t policy_max_duration_seconds_;
|
||||
|
||||
Clock* clock_;
|
||||
// Used to dispatch CDM events.
|
||||
CdmSessionId session_id_;
|
||||
WvCdmEventListener* event_listener_;
|
||||
|
||||
// For testing
|
||||
friend class PolicyEngineTest;
|
||||
PolicyEngine(Clock* clock);
|
||||
scoped_ptr<MaxResEngine> max_res_engine_;
|
||||
|
||||
std::map<KeyId, CdmKeyStatus> keys_status_;
|
||||
|
||||
scoped_ptr<Clock> clock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine);
|
||||
};
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
# include "gtest/gtest_prod.h"
|
||||
# include <gtest/gtest_prod.h>
|
||||
#endif
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::map<CdmSessionId, const CdmClientPropertySet*>
|
||||
typedef std::map<CdmSessionId, CdmClientPropertySet*>
|
||||
CdmClientPropertySetMap;
|
||||
|
||||
// This class saves information about features and properties enabled
|
||||
@@ -37,9 +37,6 @@ class Properties {
|
||||
static inline bool oem_crypto_use_userspace_buffers() {
|
||||
return oem_crypto_use_userspace_buffers_;
|
||||
}
|
||||
static inline bool oem_crypto_require_usage_tables() {
|
||||
return oem_crypto_require_usage_tables_;
|
||||
}
|
||||
static inline bool use_certificates_as_identification() {
|
||||
return use_certificates_as_identification_;
|
||||
}
|
||||
@@ -52,24 +49,29 @@ class Properties {
|
||||
static bool GetDeviceName(std::string* device_name);
|
||||
static bool GetProductName(std::string* product_name);
|
||||
static bool GetBuildInfo(std::string* build_info);
|
||||
static bool GetWVCdmVersion(std::string* version);
|
||||
static bool GetDeviceFilesBasePath(CdmSecurityLevel security_level,
|
||||
std::string* base_path);
|
||||
static bool GetFactoryKeyboxPath(std::string* keybox);
|
||||
static bool GetOEMCryptoPath(std::string* library_name);
|
||||
static bool AlwaysUseKeySetIds();
|
||||
|
||||
static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs);
|
||||
static bool GetSecurityLevel(const CdmSessionId& session_id,
|
||||
std::string* security_level);
|
||||
static bool GetApplicationId(const CdmSessionId& session_id,
|
||||
std::string* app_id);
|
||||
static bool GetServiceCertificate(const CdmSessionId& session_id,
|
||||
std::string* service_certificate);
|
||||
static bool SetServiceCertificate(const CdmSessionId& session_id,
|
||||
const std::string& service_certificate);
|
||||
static bool UsePrivacyMode(const CdmSessionId& session_id);
|
||||
static uint32_t GetSessionSharingId(const CdmSessionId& session_id);
|
||||
|
||||
static bool AddSessionPropertySet(const CdmSessionId& session_id,
|
||||
const CdmClientPropertySet* property_set);
|
||||
CdmClientPropertySet* property_set);
|
||||
static bool RemoveSessionPropertySet(const CdmSessionId& session_id);
|
||||
|
||||
private:
|
||||
static const CdmClientPropertySet* GetCdmClientPropertySet(
|
||||
static CdmClientPropertySet* GetCdmClientPropertySet(
|
||||
const CdmSessionId& session_id);
|
||||
static void set_oem_crypto_use_secure_buffers(bool flag) {
|
||||
oem_crypto_use_secure_buffers_ = flag;
|
||||
@@ -80,9 +82,6 @@ class Properties {
|
||||
static void set_oem_crypto_use_userspace_buffers(bool flag) {
|
||||
oem_crypto_use_userspace_buffers_ = flag;
|
||||
}
|
||||
static void set_oem_crypto_require_usage_tables(bool flag) {
|
||||
oem_crypto_require_usage_tables_ = flag;
|
||||
}
|
||||
static void set_use_certificates_as_identification(bool flag) {
|
||||
use_certificates_as_identification_ = flag;
|
||||
}
|
||||
@@ -104,7 +103,6 @@ class Properties {
|
||||
static bool oem_crypto_use_secure_buffers_;
|
||||
static bool oem_crypto_use_fifo_;
|
||||
static bool oem_crypto_use_userspace_buffers_;
|
||||
static bool oem_crypto_require_usage_tables_;
|
||||
static bool use_certificates_as_identification_;
|
||||
static bool security_level_path_backward_compatibility_support_;
|
||||
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;
|
||||
|
||||
@@ -60,6 +60,6 @@ class scoped_ptr {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(scoped_ptr);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_SCOPED_PTR_H_
|
||||
|
||||
@@ -22,7 +22,8 @@ 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); }
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_STRING_CONVERSIONS_H_
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Timer - Platform independent interface for a Timer class
|
||||
//
|
||||
#ifndef WVCDM_CORE_TIMER_H_
|
||||
#define WVCDM_CORE_TIMER_H_
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Timer Handler class.
|
||||
//
|
||||
// Derive from this class if you wish to receive events when the timer
|
||||
// expires. Provide the handler when setting up a new Timer.
|
||||
|
||||
class TimerHandler {
|
||||
public:
|
||||
TimerHandler() {};
|
||||
virtual ~TimerHandler() {};
|
||||
|
||||
virtual void OnTimerEvent() = 0;
|
||||
};
|
||||
|
||||
// Timer class. The implementation is platform dependent.
|
||||
//
|
||||
// This class provides a simple recurring timer API. The class receiving
|
||||
// timer expiry events should derive from TimerHandler.
|
||||
// Specify the receiver class and the periodicty of timer events when
|
||||
// the timer is initiated by calling Start.
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
class Impl;
|
||||
|
||||
Timer();
|
||||
~Timer();
|
||||
|
||||
bool Start(TimerHandler *handler, uint32_t time_in_secs);
|
||||
void Stop();
|
||||
bool IsRunning();
|
||||
|
||||
private:
|
||||
Impl *impl_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(Timer);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_TIMER_H_
|
||||
@@ -14,6 +14,10 @@ static const size_t KEY_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
|
||||
|
||||
// Use 0 to represent never expired license as specified in EME spec
|
||||
// (NaN in JS translates to 0 in unix timestamp).
|
||||
static const int64_t NEVER_EXPIRES = 0;
|
||||
|
||||
static const char SESSION_ID_PREFIX[] = "sid";
|
||||
static const char KEY_SET_ID_PREFIX[] = "ksid";
|
||||
static const char KEY_SYSTEM[] = "com.widevine";
|
||||
@@ -43,6 +47,16 @@ static const std::string QUERY_KEY_SYSTEM_ID = "SystemID";
|
||||
// system id
|
||||
static const std::string QUERY_KEY_PROVISIONING_ID = "ProvisioningID";
|
||||
// provisioning unique id
|
||||
static const std::string QUERY_KEY_CURRENT_HDCP_LEVEL = "HdcpLevel";
|
||||
// current HDCP level
|
||||
static const std::string QUERY_KEY_MAX_HDCP_LEVEL = "MaxHdcpLevel";
|
||||
// maximum supported HDCP level
|
||||
static const std::string QUERY_KEY_USAGE_SUPPORT = "UsageSupport";
|
||||
// whether usage reporting is supported
|
||||
static const std::string QUERY_KEY_NUMBER_OF_OPEN_SESSIONS =
|
||||
"NumberOfOpenSessions";
|
||||
static const std::string QUERY_KEY_MAX_NUMBER_OF_SESSIONS =
|
||||
"MaxNumberOfSessions";
|
||||
|
||||
static const std::string QUERY_VALUE_TRUE = "True";
|
||||
static const std::string QUERY_VALUE_FALSE = "False";
|
||||
@@ -52,6 +66,12 @@ static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3";
|
||||
static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown";
|
||||
static const std::string QUERY_VALUE_DISCONNECTED = "Disconnected";
|
||||
static const std::string QUERY_VALUE_UNPROTECTED = "Unprotected";
|
||||
static const std::string QUERY_VALUE_HDCP_V1 = "HDCP-1.x";
|
||||
static const std::string QUERY_VALUE_HDCP_V2_0 = "HDCP-2.0";
|
||||
static const std::string QUERY_VALUE_HDCP_V2_1 = "HDCP-2.1";
|
||||
static const std::string QUERY_VALUE_HDCP_V2_2 = "HDCP-2.2";
|
||||
|
||||
static const std::string ISO_BMFF_VIDEO_MIME_TYPE = "video/mp4";
|
||||
static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4";
|
||||
@@ -59,6 +79,8 @@ 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 WEBM_INIT_DATA_FORMAT = "webm";
|
||||
|
||||
static const char EMPTY_ORIGIN[] = "";
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_WV_CDM_CONSTANTS_H_
|
||||
|
||||
@@ -8,16 +8,19 @@
|
||||
namespace wvcdm {
|
||||
|
||||
// Listener for events from the Content Decryption Module.
|
||||
// The caller of the CDM API must provide an implementation for OnEvent
|
||||
// and signal its intent by using the Attach/DetachEventListener methods
|
||||
// in the WvContentDecryptionModule class.
|
||||
class WvCdmEventListener {
|
||||
public:
|
||||
WvCdmEventListener() {}
|
||||
virtual ~WvCdmEventListener() {}
|
||||
|
||||
virtual void OnEvent(const CdmSessionId& session_id,
|
||||
CdmEventType cdm_event) = 0;
|
||||
virtual void OnSessionRenewalNeeded(const CdmSessionId& session_id) = 0;
|
||||
virtual void OnSessionKeysChange(const CdmSessionId& session_id,
|
||||
const CdmKeyStatusMap& keys_status,
|
||||
bool has_new_usable_key) = 0;
|
||||
// Note that a |new_expiry_time_seconds| of 0 represents never expired
|
||||
// license.
|
||||
virtual void OnExpirationUpdate(const CdmSessionId& session_id,
|
||||
int64_t new_expiry_time_seconds) = 0;
|
||||
|
||||
private:
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(WvCdmEventListener);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#ifndef WVCDM_CORE_WV_CDM_TYPES_H_
|
||||
#define WVCDM_CORE_WV_CDM_TYPES_H_
|
||||
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -15,6 +15,7 @@ typedef std::string CdmInitData;
|
||||
typedef std::string CdmKeyMessage;
|
||||
typedef std::string CdmKeyResponse;
|
||||
typedef std::string KeyId;
|
||||
typedef std::string CdmSecureStopId;
|
||||
typedef std::string CdmSessionId;
|
||||
typedef std::string CdmKeySetId;
|
||||
typedef std::string RequestId;
|
||||
@@ -28,6 +29,13 @@ typedef std::string CdmUsageInfoReleaseMessage;
|
||||
typedef std::string CdmProvisioningRequest;
|
||||
typedef std::string CdmProvisioningResponse;
|
||||
|
||||
enum CdmKeyRequestType {
|
||||
kKeyRequestTypeUnknown,
|
||||
kKeyRequestTypeInitial,
|
||||
kKeyRequestTypeRenewal,
|
||||
kKeyRequestTypeRelease,
|
||||
};
|
||||
|
||||
enum CdmResponseType {
|
||||
NO_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
@@ -39,21 +47,189 @@ enum CdmResponseType {
|
||||
NEED_PROVISIONING,
|
||||
DEVICE_REVOKED,
|
||||
INSUFFICIENT_CRYPTO_RESOURCES,
|
||||
ADD_KEY_ERROR,
|
||||
CERT_PROVISIONING_GET_KEYBOX_ERROR_1,
|
||||
CERT_PROVISIONING_GET_KEYBOX_ERROR_2,
|
||||
CERT_PROVISIONING_INVALID_CERT_TYPE,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_1,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_2,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_3,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_4,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_1,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_2,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_3,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_4,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_5,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_6,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_7,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_8,
|
||||
CRYPTO_SESSION_OPEN_ERROR_1,
|
||||
CRYPTO_SESSION_OPEN_ERROR_2,
|
||||
CRYPTO_SESSION_OPEN_ERROR_3,
|
||||
CRYPTO_SESSION_OPEN_ERROR_4,
|
||||
CRYPTO_SESSION_OPEN_ERROR_5,
|
||||
DECRYPT_NOT_READY,
|
||||
DEVICE_CERTIFICATE_ERROR_1,
|
||||
DEVICE_CERTIFICATE_ERROR_2,
|
||||
DEVICE_CERTIFICATE_ERROR_3,
|
||||
DEVICE_CERTIFICATE_ERROR_4,
|
||||
EMPTY_KEY_DATA_1,
|
||||
EMPTY_KEY_DATA_2,
|
||||
EMPTY_KEYSET_ID,
|
||||
EMPTY_KEYSET_ID_ENG_1,
|
||||
EMPTY_KEYSET_ID_ENG_2,
|
||||
EMPTY_KEYSET_ID_ENG_3,
|
||||
EMPTY_KEYSET_ID_ENG_4,
|
||||
EMPTY_LICENSE_RENEWAL,
|
||||
EMPTY_LICENSE_RESPONSE_1,
|
||||
EMPTY_LICENSE_RESPONSE_2,
|
||||
EMPTY_PROVISIONING_CERTIFICATE,
|
||||
EMPTY_PROVISIONING_RESPONSE,
|
||||
EMPTY_SESSION_ID,
|
||||
GENERATE_DERIVED_KEYS_ERROR,
|
||||
LICENSE_RENEWAL_NONCE_GENERATION_ERROR,
|
||||
GENERATE_USAGE_REPORT_ERROR,
|
||||
GET_LICENSE_ERROR,
|
||||
GET_RELEASED_LICENSE_ERROR,
|
||||
GET_USAGE_INFO_ERROR_1,
|
||||
GET_USAGE_INFO_ERROR_2,
|
||||
GET_USAGE_INFO_ERROR_3,
|
||||
GET_USAGE_INFO_ERROR_4,
|
||||
INIT_DATA_NOT_FOUND,
|
||||
INVALID_CRYPTO_SESSION_1,
|
||||
INVALID_CRYPTO_SESSION_2,
|
||||
INVALID_CRYPTO_SESSION_3,
|
||||
INVALID_CRYPTO_SESSION_4,
|
||||
INVALID_CRYPTO_SESSION_5,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_1,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_2,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_3,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_4,
|
||||
INVALID_DEVICE_CERTIFICATE_TYPE,
|
||||
INVALID_KEY_SYSTEM,
|
||||
INVALID_LICENSE_RESPONSE,
|
||||
INVALID_LICENSE_TYPE,
|
||||
INVALID_PARAMETERS_ENG_1,
|
||||
INVALID_PARAMETERS_ENG_2,
|
||||
INVALID_PARAMETERS_ENG_3,
|
||||
INVALID_PARAMETERS_ENG_4,
|
||||
INVALID_PARAMETERS_LIC_1,
|
||||
INVALID_PARAMETERS_LIC_2,
|
||||
INVALID_PROVISIONING_PARAMETERS_1,
|
||||
INVALID_PROVISIONING_PARAMETERS_2,
|
||||
INVALID_PROVISIONING_REQUEST_PARAM_1,
|
||||
INVALID_PROVISIONING_REQUEST_PARAM_2,
|
||||
INVALID_QUERY_KEY,
|
||||
INVALID_SESSION_ID,
|
||||
KEY_REQUEST_ERROR_1,
|
||||
UNUSED_1, /* previously KEY_REQUEST_ERROR_2 */
|
||||
KEY_SIZE_ERROR,
|
||||
KEYSET_ID_NOT_FOUND_1,
|
||||
KEYSET_ID_NOT_FOUND_2,
|
||||
KEYSET_ID_NOT_FOUND_3,
|
||||
LICENSE_ID_NOT_FOUND,
|
||||
LICENSE_PARSER_INIT_ERROR,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_1,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_2,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_3,
|
||||
LICENSE_RESPONSE_NOT_SIGNED,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_1,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_2,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_3,
|
||||
LOAD_KEY_ERROR,
|
||||
NO_CONTENT_KEY,
|
||||
REFRESH_KEYS_ERROR,
|
||||
RELEASE_ALL_USAGE_INFO_ERROR_1,
|
||||
RELEASE_ALL_USAGE_INFO_ERROR_2,
|
||||
RELEASE_KEY_ERROR,
|
||||
RELEASE_KEY_REQUEST_ERROR,
|
||||
RELEASE_LICENSE_ERROR_1,
|
||||
RELEASE_LICENSE_ERROR_2,
|
||||
RELEASE_USAGE_INFO_ERROR,
|
||||
RENEW_KEY_ERROR_1,
|
||||
RENEW_KEY_ERROR_2,
|
||||
LICENSE_RENEWAL_SIGNING_ERROR,
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_1,
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_2,
|
||||
SESSION_INIT_ERROR_1,
|
||||
SESSION_INIT_ERROR_2,
|
||||
SESSION_INIT_GET_KEYBOX_ERROR,
|
||||
SESSION_NOT_FOUND_1,
|
||||
SESSION_NOT_FOUND_2,
|
||||
SESSION_NOT_FOUND_3,
|
||||
SESSION_NOT_FOUND_4,
|
||||
SESSION_NOT_FOUND_5,
|
||||
SESSION_NOT_FOUND_6,
|
||||
SESSION_NOT_FOUND_7,
|
||||
SESSION_NOT_FOUND_8,
|
||||
SESSION_NOT_FOUND_9,
|
||||
SESSION_NOT_FOUND_10,
|
||||
SESSION_NOT_FOUND_FOR_DECRYPT,
|
||||
SESSION_KEYS_NOT_FOUND,
|
||||
SIGNATURE_NOT_FOUND,
|
||||
STORE_LICENSE_ERROR_1,
|
||||
STORE_LICENSE_ERROR_2,
|
||||
STORE_LICENSE_ERROR_3,
|
||||
STORE_USAGE_INFO_ERROR,
|
||||
UNPROVISION_ERROR_1,
|
||||
UNPROVISION_ERROR_2,
|
||||
UNPROVISION_ERROR_3,
|
||||
UNPROVISION_ERROR_4,
|
||||
UNSUPPORTED_INIT_DATA,
|
||||
USAGE_INFO_NOT_FOUND,
|
||||
LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR,
|
||||
PARSE_SERVICE_CERTIFICATE_ERROR,
|
||||
SERVICE_CERTIFICATE_TYPE_ERROR,
|
||||
CLIENT_ID_GENERATE_RANDOM_ERROR,
|
||||
CLIENT_ID_AES_INIT_ERROR,
|
||||
CLIENT_ID_AES_ENCRYPT_ERROR,
|
||||
CLIENT_ID_RSA_INIT_ERROR,
|
||||
CLIENT_ID_RSA_ENCRYPT_ERROR,
|
||||
INVALID_QUERY_STATUS,
|
||||
DUPLICATE_SESSION_ID_SPECIFIED,
|
||||
EMPTY_PROVISIONING_CERTIFICATE_2,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_4,
|
||||
INVALID_PARAMETERS_LIC_3,
|
||||
INVALID_PARAMETERS_LIC_4,
|
||||
UNUSED_2, /* previously INVALID_PARAMETERS_LIC_5 */
|
||||
INVALID_PARAMETERS_LIC_6,
|
||||
INVALID_PARAMETERS_LIC_7,
|
||||
LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR,
|
||||
CENC_INIT_DATA_UNAVAILABLE,
|
||||
PREPARE_CENC_CONTENT_ID_FAILED,
|
||||
WEBM_INIT_DATA_UNAVAILABLE,
|
||||
PREPARE_WEBM_CONTENT_ID_FAILED,
|
||||
UNSUPPORTED_INIT_DATA_FORMAT,
|
||||
LICENSE_REQUEST_NONCE_GENERATION_ERROR,
|
||||
LICENSE_REQUEST_SIGNING_ERROR,
|
||||
EMPTY_LICENSE_REQUEST,
|
||||
};
|
||||
|
||||
enum CdmKeyStatus {
|
||||
kKeyStatusUsable,
|
||||
kKeyStatusExpired,
|
||||
kKeyStatusOutputNotAllowed,
|
||||
kKeyStatusPending,
|
||||
kKeyStatusInternalError,
|
||||
};
|
||||
typedef std::map<KeyId, CdmKeyStatus> CdmKeyStatusMap;
|
||||
|
||||
#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
|
||||
enum CdmEventType {
|
||||
LICENSE_EXPIRED_EVENT,
|
||||
LICENSE_RENEWAL_NEEDED_EVENT
|
||||
};
|
||||
|
||||
enum CdmLicenseType {
|
||||
kLicenseTypeOffline,
|
||||
kLicenseTypeStreaming,
|
||||
kLicenseTypeRelease
|
||||
kLicenseTypeRelease,
|
||||
// If the original request was saved to make a service certificate request,
|
||||
// use Deferred for the license type in the subsequent request.
|
||||
kLicenseTypeDeferred,
|
||||
};
|
||||
|
||||
enum SecurityLevel {
|
||||
kLevelDefault,
|
||||
kLevel3
|
||||
};
|
||||
|
||||
enum CdmSecurityLevel {
|
||||
|
||||
@@ -21,16 +21,43 @@
|
||||
namespace {
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
} // unnamed namespace
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class UsagePropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
UsagePropertySet() {}
|
||||
virtual ~UsagePropertySet() {}
|
||||
void set_security_level(SecurityLevel security_level) {
|
||||
if (kLevel3 == security_level)
|
||||
security_level_ = QUERY_VALUE_SECURITY_LEVEL_L3;
|
||||
else
|
||||
security_level_.clear();
|
||||
}
|
||||
virtual const std::string& security_level() const { return security_level_; }
|
||||
virtual bool use_privacy_mode() const { return false; }
|
||||
virtual const std::string& service_certificate() const { return empty_; }
|
||||
virtual void set_service_certificate(const std::string&) {}
|
||||
virtual bool is_session_sharing_enabled() const { return false; }
|
||||
virtual uint32_t session_sharing_id() const { return 0; }
|
||||
virtual void set_session_sharing_id(uint32_t /* id */) {}
|
||||
virtual const std::string& app_id() const { return app_id_; }
|
||||
void set_app_id(const std::string& appId) { app_id_ = appId; }
|
||||
|
||||
private:
|
||||
std::string app_id_;
|
||||
std::string security_level_;
|
||||
const std::string empty_;
|
||||
};
|
||||
|
||||
bool CdmEngine::seeded_ = false;
|
||||
|
||||
CdmEngine::CdmEngine()
|
||||
: cert_provisioning_requested_security_level_(kLevelDefault),
|
||||
: cert_provisioning_(NULL),
|
||||
cert_provisioning_requested_security_level_(kLevelDefault),
|
||||
usage_session_(NULL),
|
||||
last_usage_information_update_time(0) {
|
||||
last_usage_information_update_time_(0) {
|
||||
Properties::Init();
|
||||
if (!seeded_) {
|
||||
Clock clock;
|
||||
@@ -40,6 +67,7 @@ CdmEngine::CdmEngine()
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator i(sessions_.begin());
|
||||
for (; i != sessions_.end(); ++i) {
|
||||
delete i->second;
|
||||
@@ -48,24 +76,34 @@ CdmEngine::~CdmEngine() {
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
const CdmClientPropertySet* property_set,
|
||||
CdmClientPropertySet* property_set,
|
||||
const std::string& origin,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id) {
|
||||
LOGI("CdmEngine::OpenSession");
|
||||
|
||||
if (!ValidateKeySystem(key_system)) {
|
||||
LOGI("CdmEngine::OpenSession: invalid key_system = %s", key_system.c_str());
|
||||
return KEY_ERROR;
|
||||
return INVALID_KEY_SYSTEM;
|
||||
}
|
||||
|
||||
if (!session_id) {
|
||||
LOGE("CdmEngine::OpenSession: no session ID destination provided");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_1;
|
||||
}
|
||||
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(property_set));
|
||||
if (forced_session_id) {
|
||||
if (sessions_.find(*forced_session_id) != sessions_.end()) {
|
||||
return DUPLICATE_SESSION_ID_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
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 UNKNOWN_ERROR;
|
||||
return EMPTY_SESSION_ID;
|
||||
}
|
||||
|
||||
CdmResponseType sts = new_session->Init();
|
||||
@@ -74,25 +112,30 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
cert_provisioning_requested_security_level_ =
|
||||
new_session->GetRequestedSecurityLevel();
|
||||
} else {
|
||||
LOGE("CdmEngine::OpenSession: bad session init: %u", sts);
|
||||
LOGE("CdmEngine::OpenSession: bad session init: %d", sts);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
*session_id = new_session->session_id();
|
||||
AutoLock lock(session_list_lock_);
|
||||
sessions_[*session_id] = new_session.release();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
|
||||
const std::string& origin, WvCdmEventListener* event_listener) {
|
||||
LOGI("CdmEngine::OpenKeySetSession");
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGE("CdmEngine::OpenKeySetSession: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_1;
|
||||
}
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id);
|
||||
CdmResponseType sts =
|
||||
OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
|
||||
NULL /* forced_session_id */, &session_id);
|
||||
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
@@ -102,13 +145,12 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
|
||||
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CloseSession");
|
||||
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_1;
|
||||
}
|
||||
|
||||
CdmSession* session = iter->second;
|
||||
sessions_.erase(session_id);
|
||||
delete session;
|
||||
@@ -122,7 +164,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return KEYSET_ID_NOT_FOUND_1;
|
||||
}
|
||||
|
||||
CdmResponseType sts = CloseSession(iter->second);
|
||||
@@ -130,33 +172,42 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
return sts;
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
return iter != sessions_.end();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id_out) {
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id_out) {
|
||||
LOGI("CdmEngine::GenerateKeyRequest");
|
||||
|
||||
CdmSessionId id = session_id;
|
||||
CdmResponseType sts;
|
||||
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
// NOTE: If AlwaysUseKeySetIds() is true, there is no need to consult the
|
||||
// release_key_sets_ map for release licenses.
|
||||
if (license_type == kLicenseTypeRelease &&
|
||||
!Properties::AlwaysUseKeySetIds()) {
|
||||
if (key_set_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_2;
|
||||
}
|
||||
|
||||
if (!session_id.empty()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_SESSION_ID;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return KEYSET_ID_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
@@ -166,12 +217,12 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
|
||||
id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: no key request destination provided");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_2;
|
||||
}
|
||||
|
||||
key_request->clear();
|
||||
@@ -179,27 +230,23 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
|
||||
if (sts != KEY_ADDED) {
|
||||
LOGE(
|
||||
"CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d",
|
||||
(int)sts);
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
|
||||
"sts = %d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
|
||||
sts = iter->second->GenerateKeyRequest(
|
||||
init_data, license_type, app_parameters, key_request, server_url,
|
||||
key_set_id_out);
|
||||
init_data, license_type, app_parameters, key_request, key_request_type,
|
||||
server_url, key_set_id_out);
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
}
|
||||
LOGE(
|
||||
"CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d",
|
||||
(int)sts);
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
|
||||
"sts = %d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -221,18 +268,18 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
if (license_type_release) {
|
||||
if (!key_set_id) {
|
||||
LOGE("CdmEngine::AddKey: no key set id provided");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_3;
|
||||
}
|
||||
|
||||
if (key_set_id->empty()) {
|
||||
LOGE("CdmEngine::AddKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_3;
|
||||
}
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
|
||||
return KEY_ERROR;
|
||||
return KEYSET_ID_NOT_FOUND_3;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
@@ -242,18 +289,18 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_3;
|
||||
}
|
||||
|
||||
if (key_data.empty()) {
|
||||
LOGE("CdmEngine::AddKey: no key_data");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEY_DATA_1;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->AddKey(key_data, key_set_id);
|
||||
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
|
||||
LOGE("CdmEngine::AddKey: keys not added, result = %d", sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -266,14 +313,14 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
|
||||
|
||||
if (key_set_id.empty()) {
|
||||
LOGI("CdmEngine::RestoreKey: invalid key set id");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEYSET_ID_ENG_4;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
|
||||
session_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return SESSION_NOT_FOUND_4;
|
||||
}
|
||||
|
||||
CdmResponseType sts =
|
||||
@@ -282,10 +329,10 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
|
||||
cert_provisioning_requested_security_level_ =
|
||||
iter->second->GetRequestedSecurityLevel();
|
||||
}
|
||||
if (sts != KEY_ADDED) {
|
||||
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
|
||||
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
|
||||
}
|
||||
return sts;
|
||||
return sts; // TODO ewew
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
|
||||
@@ -295,7 +342,7 @@ CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RemoveKeys: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_5;
|
||||
}
|
||||
|
||||
iter->second->ReleaseCrypto();
|
||||
@@ -311,12 +358,12 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_6;
|
||||
}
|
||||
|
||||
if (!key_request) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination");
|
||||
return KEY_ERROR;
|
||||
return INVALID_PARAMETERS_ENG_4;
|
||||
}
|
||||
|
||||
key_request->clear();
|
||||
@@ -326,7 +373,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
|
||||
|
||||
if (KEY_MESSAGE != sts) {
|
||||
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
|
||||
(int)sts);
|
||||
sts);
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -340,26 +387,31 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_7;
|
||||
}
|
||||
|
||||
if (key_data.empty()) {
|
||||
LOGE("CdmEngine::RenewKey: no key_data");
|
||||
return KEY_ERROR;
|
||||
return EMPTY_KEY_DATA_2;
|
||||
}
|
||||
|
||||
CdmResponseType sts = iter->second->RenewKey(key_data);
|
||||
if (KEY_ADDED != sts) {
|
||||
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", (int)sts);
|
||||
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryStatus");
|
||||
CryptoSession crypto_session;
|
||||
if (security_level == kLevel3) {
|
||||
CdmResponseType status = crypto_session.Open(kLevel3);
|
||||
if (NO_ERROR != status) return INVALID_QUERY_STATUS;
|
||||
}
|
||||
switch (crypto_session.GetSecurityLevel()) {
|
||||
case kSecurityLevelL1:
|
||||
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
|
||||
@@ -376,7 +428,7 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
return KEY_ERROR;
|
||||
return INVALID_QUERY_KEY;
|
||||
}
|
||||
|
||||
std::string deviceId;
|
||||
@@ -399,6 +451,39 @@ CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
|
||||
(*key_info)[QUERY_KEY_PROVISIONING_ID] = provisioning_id;
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability current_hdcp;
|
||||
CryptoSession::HdcpCapability max_hdcp;
|
||||
success = crypto_session.GetHdcpCapabilities(¤t_hdcp, &max_hdcp);
|
||||
if (success) {
|
||||
(*key_info)[QUERY_KEY_CURRENT_HDCP_LEVEL] = MapHdcpVersion(current_hdcp);
|
||||
(*key_info)[QUERY_KEY_MAX_HDCP_LEVEL] = MapHdcpVersion(max_hdcp);
|
||||
}
|
||||
|
||||
bool supports_usage_reporting;
|
||||
success = crypto_session.UsageInformationSupport(&supports_usage_reporting);
|
||||
if (success) {
|
||||
(*key_info)[QUERY_KEY_USAGE_SUPPORT] =
|
||||
supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
}
|
||||
|
||||
size_t number_of_open_sessions;
|
||||
success = crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions);
|
||||
if (success) {
|
||||
std::ostringstream open_sessions_stream;
|
||||
open_sessions_stream << number_of_open_sessions;
|
||||
(*key_info)[QUERY_KEY_NUMBER_OF_OPEN_SESSIONS] =
|
||||
open_sessions_stream.str();
|
||||
}
|
||||
|
||||
size_t maximum_number_of_sessions;
|
||||
success = crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions);
|
||||
if (success) {
|
||||
std::ostringstream max_sessions_stream;
|
||||
max_sessions_stream << maximum_number_of_sessions;
|
||||
(*key_info)[QUERY_KEY_MAX_NUMBER_OF_SESSIONS] =
|
||||
max_sessions_stream.str();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -409,11 +494,33 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QuerySessionStatus: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_8;
|
||||
}
|
||||
return iter->second->QueryStatus(key_info);
|
||||
}
|
||||
|
||||
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::IsReleaseSession");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::IsReleaseSession: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return false;
|
||||
}
|
||||
return iter->second->is_release();
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::IsOfflineSession");
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::IsOfflineSession: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return false;
|
||||
}
|
||||
return iter->second->is_offline();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info) {
|
||||
LOGI("CdmEngine::QueryKeyStatus");
|
||||
@@ -421,7 +528,7 @@ CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_9;
|
||||
}
|
||||
return iter->second->QueryKeyStatus(key_info);
|
||||
}
|
||||
@@ -433,7 +540,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s",
|
||||
session_id.c_str());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_10;
|
||||
}
|
||||
return iter->second->QueryKeyControlInfo(key_info);
|
||||
}
|
||||
@@ -443,18 +550,30 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
* in *default_url.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CdmResponseType error code if fails.
|
||||
*/
|
||||
CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
if (!request || !default_url) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
|
||||
return UNKNOWN_ERROR;
|
||||
const std::string& origin, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!request) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
|
||||
return INVALID_PROVISIONING_REQUEST_PARAM_1;
|
||||
}
|
||||
return cert_provisioning_.GetProvisioningRequest(
|
||||
if (!default_url) {
|
||||
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
|
||||
return INVALID_PROVISIONING_REQUEST_PARAM_2;
|
||||
}
|
||||
if (NULL == cert_provisioning_.get()) {
|
||||
cert_provisioning_.reset(new CertificateProvisioning());
|
||||
}
|
||||
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
|
||||
cert_provisioning_requested_security_level_, cert_type, cert_authority,
|
||||
request, default_url);
|
||||
origin, request, default_url);
|
||||
if (ret != NO_ERROR) {
|
||||
cert_provisioning_.reset(NULL); // Release resources.
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -462,47 +581,183 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
* The device RSA key is stored in the T.E.E. The device certificate is stored
|
||||
* in the device.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CdmResponseType error code if fails.
|
||||
*/
|
||||
CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
const std::string& origin, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key) {
|
||||
if (response.empty()) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
|
||||
return UNKNOWN_ERROR;
|
||||
cert_provisioning_.reset(NULL);
|
||||
return EMPTY_PROVISIONING_RESPONSE;
|
||||
}
|
||||
if (NULL == cert) {
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid certificate "
|
||||
"destination");
|
||||
return UNKNOWN_ERROR;
|
||||
cert_provisioning_.reset(NULL);
|
||||
return INVALID_PROVISIONING_PARAMETERS_1;
|
||||
}
|
||||
if (NULL == wrapped_key) {
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
return UNKNOWN_ERROR;
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
cert_provisioning_.reset(NULL);
|
||||
return INVALID_PROVISIONING_PARAMETERS_2;
|
||||
}
|
||||
return cert_provisioning_.HandleProvisioningResponse(response, cert,
|
||||
wrapped_key);
|
||||
if (NULL == cert_provisioning_.get()) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: provisioning object missing.");
|
||||
return EMPTY_PROVISIONING_CERTIFICATE;
|
||||
}
|
||||
CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
|
||||
origin, response, cert, wrapped_key);
|
||||
cert_provisioning_.reset(NULL); // Release resources.
|
||||
return ret;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level,
|
||||
const std::string& origin) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::IsProvisioned: unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
return handle.HasCertificate(origin);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level,
|
||||
const std::string& origin) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::Unprovision: unable to initialize device files");
|
||||
return UNKNOWN_ERROR;
|
||||
return UNPROVISION_ERROR_1;
|
||||
}
|
||||
|
||||
if (!handle.DeleteAllFiles()) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete files");
|
||||
return UNKNOWN_ERROR;
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
if (!handle.RemoveCertificate(origin)) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete certificate for origin %s",
|
||||
origin.c_str());
|
||||
return UNPROVISION_ERROR_2;
|
||||
}
|
||||
return NO_ERROR;
|
||||
} else {
|
||||
if (!handle.DeleteAllFiles()) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete files");
|
||||
return UNPROVISION_ERROR_3;
|
||||
}
|
||||
|
||||
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
|
||||
CdmResponseType status = crypto_session->Open(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status);
|
||||
return UNPROVISION_ERROR_4;
|
||||
}
|
||||
status = crypto_session->DeleteAllUsageReports();
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
usage_session_.reset(new CdmSession(NULL));
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
CdmUsageInfo* usage_info) {
|
||||
if (NULL == usage_property_set_.get()) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
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();
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
return status;
|
||||
}
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: device file init error");
|
||||
return GET_USAGE_INFO_ERROR_1;
|
||||
}
|
||||
|
||||
CdmKeyMessage license_request;
|
||||
CdmKeyResponse license_response;
|
||||
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
|
||||
&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();
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
return status;
|
||||
}
|
||||
if (!handle.Reset(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: device file init error");
|
||||
return GET_USAGE_INFO_ERROR_2;
|
||||
}
|
||||
if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request,
|
||||
&license_response)) {
|
||||
// No entry found for that ssid.
|
||||
return USAGE_INFO_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
std::string server_url;
|
||||
usage_info->resize(1);
|
||||
status =
|
||||
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);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d", status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
CdmUsageInfo* usage_info) {
|
||||
// Return a random usage report from a random security level
|
||||
SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3;
|
||||
CdmResponseType status = UNKNOWN_ERROR;
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, usage_info);
|
||||
|
||||
if (KEY_MESSAGE == status && !usage_info->empty()) return status;
|
||||
} while (KEY_CANCELED == status);
|
||||
|
||||
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, usage_info);
|
||||
if (NEED_PROVISIONING == status)
|
||||
return NO_ERROR; // Valid scenario that one of the security
|
||||
// levels has not been provisioned
|
||||
} while (KEY_CANCELED == status);
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
SecurityLevel requested_security_level,
|
||||
CdmUsageInfo* usage_info) {
|
||||
if (NULL == usage_property_set_.get()) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
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));
|
||||
|
||||
CdmResponseType status = usage_session_->Init();
|
||||
if (NO_ERROR != status) {
|
||||
@@ -513,13 +768,13 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("CdmEngine::GetUsageInfo: unable to initialize device files");
|
||||
return status;
|
||||
return GET_USAGE_INFO_ERROR_3;
|
||||
}
|
||||
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
|
||||
if (!handle.RetrieveUsageInfo(&license_info)) {
|
||||
if (!handle.RetrieveUsageInfo(app_id, &license_info)) {
|
||||
LOGE("CdmEngine::GetUsageInfo: unable to read usage information");
|
||||
return UNKNOWN_ERROR;
|
||||
return GET_USAGE_INFO_ERROR_4;
|
||||
}
|
||||
|
||||
if (0 == license_info.size()) {
|
||||
@@ -543,25 +798,59 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
status =
|
||||
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld",
|
||||
status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
switch (status) {
|
||||
case KEY_MESSAGE:
|
||||
break;
|
||||
case KEY_CANCELED: // usage information not present in
|
||||
usage_session_->DeleteLicense(); // OEMCrypto, delete and try again
|
||||
usage_info->clear();
|
||||
break;
|
||||
default:
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d",
|
||||
status);
|
||||
usage_info->clear();
|
||||
break;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
|
||||
CdmResponseType status = NO_ERROR;
|
||||
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
|
||||
DeviceFiles handle;
|
||||
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure"
|
||||
"stops", j);
|
||||
status = RELEASE_ALL_USAGE_INFO_ERROR_1;
|
||||
} else {
|
||||
CdmResponseType status2 = usage_session_->
|
||||
DeleteMultipleUsageInformation(provider_session_tokens);
|
||||
if (status2 != NO_ERROR) {
|
||||
status = status2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device"
|
||||
"files", j);
|
||||
status = RELEASE_ALL_USAGE_INFO_ERROR_2;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseUsageInfo(
|
||||
const CdmUsageInfoReleaseMessage& message) {
|
||||
if (NULL == usage_session_.get()) {
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: cdm session not initialized");
|
||||
return UNKNOWN_ERROR;
|
||||
return RELEASE_USAGE_INFO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType status = usage_session_->ReleaseKey(message);
|
||||
usage_session_.reset(NULL);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %ld", status);
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %d", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -570,25 +859,26 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
|
||||
const CdmDecryptionParameters& parameters) {
|
||||
if (parameters.key_id == NULL) {
|
||||
LOGE("CdmEngine::Decrypt: no key_id");
|
||||
return KEY_ERROR;
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_1;
|
||||
}
|
||||
|
||||
if (parameters.encrypt_buffer == NULL) {
|
||||
LOGE("CdmEngine::Decrypt: no src encrypt buffer");
|
||||
return KEY_ERROR;
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_2;
|
||||
}
|
||||
|
||||
if (parameters.iv == NULL) {
|
||||
LOGE("CdmEngine::Decrypt: no iv");
|
||||
return KEY_ERROR;
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_3;
|
||||
}
|
||||
|
||||
if (parameters.decrypt_buffer == NULL) {
|
||||
if (!parameters.is_secure &&
|
||||
!Properties::Properties::oem_crypto_use_fifo()) {
|
||||
LOGE("CdmEngine::Decrypt: no dest decrypt buffer");
|
||||
return KEY_ERROR;
|
||||
} // else we must be level 1 direct and we don't need to return a buffer.
|
||||
return INVALID_DECRYPT_PARAMETERS_ENG_4;
|
||||
}
|
||||
// else we must be level 1 direct and we don't need to return a buffer.
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter;
|
||||
@@ -605,7 +895,7 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
|
||||
session_id.c_str(), session_id.size());
|
||||
return KEY_ERROR;
|
||||
return SESSION_NOT_FOUND_FOR_DECRYPT;
|
||||
}
|
||||
|
||||
return iter->second->Decrypt(parameters);
|
||||
@@ -650,24 +940,12 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CdmEngine::AttachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
void CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
uint32_t height) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
if (iter != sessions_.end()) {
|
||||
iter->second->NotifyResolution(width, height);
|
||||
}
|
||||
|
||||
return iter->second->AttachEventListener(listener);
|
||||
}
|
||||
|
||||
bool CdmEngine::DetachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener) {
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return iter->second->DetachEventListener(listener);
|
||||
}
|
||||
|
||||
bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
|
||||
@@ -677,37 +955,73 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
|
||||
void CdmEngine::OnTimerEvent() {
|
||||
Clock clock;
|
||||
uint64_t current_time = clock.GetCurrentTime();
|
||||
bool update_usage_information = false;
|
||||
bool usage_update_period_expired = false;
|
||||
|
||||
if (current_time - last_usage_information_update_time >
|
||||
if (current_time - last_usage_information_update_time_ >
|
||||
kUpdateUsageInformationPeriod) {
|
||||
update_usage_information = true;
|
||||
last_usage_information_update_time = current_time;
|
||||
usage_update_period_expired = true;
|
||||
last_usage_information_update_time_ = current_time;
|
||||
}
|
||||
|
||||
bool is_initial_usage_update = false;
|
||||
bool is_usage_update_needed = false;
|
||||
|
||||
AutoLock lock(session_list_lock_);
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnTimerEvent();
|
||||
is_initial_usage_update =
|
||||
is_initial_usage_update || iter->second->is_initial_usage_update();
|
||||
is_usage_update_needed =
|
||||
is_usage_update_needed || iter->second->is_usage_update_needed();
|
||||
|
||||
if (update_usage_information && iter->second->is_usage_update_needed()) {
|
||||
// usage is updated for all sessions so this needs to be
|
||||
// called only once per update usage information period
|
||||
CdmResponseType status = iter->second->UpdateUsageInformation();
|
||||
if (NO_ERROR != status) {
|
||||
LOGW("Update usage information failed: %u", status);
|
||||
} else {
|
||||
update_usage_information = false;
|
||||
iter->second->OnTimerEvent(usage_update_period_expired);
|
||||
}
|
||||
|
||||
if (is_usage_update_needed &&
|
||||
(usage_update_period_expired || is_initial_usage_update)) {
|
||||
bool has_usage_been_updated = false;
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->reset_usage_flags();
|
||||
if (!has_usage_been_updated) {
|
||||
// usage is updated for all sessions so this needs to be
|
||||
// called only once per update usage information period
|
||||
CdmResponseType status = iter->second->UpdateUsageInformation();
|
||||
if (NO_ERROR != status) {
|
||||
LOGW("Update usage information failed: %d", status);
|
||||
} else {
|
||||
has_usage_been_updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
iter->second->reset_is_usage_update_needed();
|
||||
}
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
AutoLock lock(session_list_lock_);
|
||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||
iter != sessions_.end(); ++iter) {
|
||||
iter->second->OnKeyReleaseEvent(key_set_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CdmEngine::MapHdcpVersion(
|
||||
CryptoSession::HdcpCapability version) {
|
||||
switch (version) {
|
||||
case HDCP_NONE:
|
||||
return QUERY_VALUE_UNPROTECTED;
|
||||
case HDCP_V1:
|
||||
return QUERY_VALUE_HDCP_V1;
|
||||
case HDCP_V2:
|
||||
return QUERY_VALUE_HDCP_V2_0;
|
||||
case HDCP_V2_1:
|
||||
return QUERY_VALUE_HDCP_V2_1;
|
||||
case HDCP_V2_2:
|
||||
return QUERY_VALUE_HDCP_V2_2;
|
||||
case HDCP_NO_DIGITAL_OUTPUT:
|
||||
return QUERY_VALUE_DISCONNECTED;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#include "cdm_session.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "cdm_engine.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
@@ -22,58 +25,41 @@ const size_t kKeySetIdLength = 14;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
|
||||
|
||||
CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) {
|
||||
Create(new CdmLicense(), new CryptoSession(), new PolicyEngine(),
|
||||
new DeviceFiles(), cdm_client_property_set);
|
||||
}
|
||||
|
||||
CdmSession::CdmSession(CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
Create(license_parser, crypto_session, policy_engine, file_handle,
|
||||
cdm_client_property_set);
|
||||
}
|
||||
|
||||
void CdmSession::Create(CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine, DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
// Just return on failures. Failures will be signaled in Init.
|
||||
if (NULL == license_parser) {
|
||||
LOGE("CdmSession::Create: License parser not provided");
|
||||
return;
|
||||
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),
|
||||
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_);
|
||||
assert(ok);
|
||||
}
|
||||
session_id_ = key_set_id_;
|
||||
}
|
||||
if (NULL == crypto_session) {
|
||||
LOGE("CdmSession::Create: Crypto session not provided");
|
||||
return;
|
||||
}
|
||||
if (NULL == policy_engine) {
|
||||
LOGE("CdmSession::Create: Policy engine not provided");
|
||||
return;
|
||||
}
|
||||
if (NULL == file_handle) {
|
||||
LOGE("CdmSession::Create: Device files not provided");
|
||||
return;
|
||||
}
|
||||
initialized_ = false;
|
||||
session_id_ = GenerateSessionId();
|
||||
license_parser_.reset(license_parser);
|
||||
crypto_session_.reset(crypto_session);
|
||||
file_handle_.reset(file_handle);
|
||||
policy_engine_.reset(policy_engine);
|
||||
license_received_ = false;
|
||||
is_offline_ = false;
|
||||
is_release_ = false;
|
||||
is_usage_update_needed_ = false;
|
||||
is_initial_decryption_ = true;
|
||||
requested_security_level_ = kLevelDefault;
|
||||
if (NULL != cdm_client_property_set) {
|
||||
if (QUERY_VALUE_SECURITY_LEVEL_L3.compare(
|
||||
cdm_client_property_set->security_level()) == 0) {
|
||||
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);
|
||||
}
|
||||
@@ -84,11 +70,11 @@ CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); }
|
||||
CdmResponseType CdmSession::Init() {
|
||||
if (session_id_.empty()) {
|
||||
LOGE("CdmSession::Init: Failed, session not properly constructed");
|
||||
return UNKNOWN_ERROR;
|
||||
return SESSION_INIT_ERROR_1;
|
||||
}
|
||||
if (initialized_) {
|
||||
LOGE("CdmSession::Init: Failed due to previous initialization");
|
||||
return UNKNOWN_ERROR;
|
||||
return SESSION_INIT_ERROR_2;
|
||||
}
|
||||
CdmResponseType sts = crypto_session_->Open(requested_security_level_);
|
||||
if (NO_ERROR != sts) return sts;
|
||||
@@ -98,17 +84,18 @@ CdmResponseType CdmSession::Init() {
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
std::string wrapped_key;
|
||||
if (!file_handle_->Init(security_level_) ||
|
||||
!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
|
||||
!file_handle_->RetrieveCertificate(origin_, &token, &wrapped_key) ||
|
||||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
} else {
|
||||
if (!crypto_session_->GetToken(&token)) return UNKNOWN_ERROR;
|
||||
if (!crypto_session_->GetToken(&token))
|
||||
return SESSION_INIT_GET_KEYBOX_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_->Init(token, crypto_session_.get(),
|
||||
policy_engine_.get()))
|
||||
return UNKNOWN_ERROR;
|
||||
return LICENSE_PARSER_INIT_ERROR;
|
||||
|
||||
license_received_ = false;
|
||||
is_initial_decryption_ = true;
|
||||
@@ -121,27 +108,42 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
if (!file_handle_->Reset(security_level_)) return UNKNOWN_ERROR;
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return RESTORE_OFFLINE_LICENSE_ERROR_1;
|
||||
|
||||
DeviceFiles::LicenseState license_state;
|
||||
int64_t playback_start_time;
|
||||
int64_t last_playback_time;
|
||||
|
||||
if (!file_handle_->RetrieveLicense(
|
||||
key_set_id, &license_state, &offline_init_data_, &key_request_,
|
||||
&key_response_, &offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_, &offline_release_server_url_)) {
|
||||
&offline_key_renewal_response_, &offline_release_server_url_,
|
||||
&playback_start_time, &last_playback_time, &app_parameters_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
return GET_LICENSE_ERROR;
|
||||
}
|
||||
|
||||
if (license_state != DeviceFiles::kLicenseStateActive) {
|
||||
LOGE("CdmSession::Init invalid offline license state = %s", license_state);
|
||||
return UNKNOWN_ERROR;
|
||||
// Do not restore a released offline license, unless a release retry
|
||||
if (!(license_type == kLicenseTypeRelease ||
|
||||
license_state == DeviceFiles::kLicenseStateActive)) {
|
||||
LOGE("CdmSession::Init invalid offline license state = %d, type = %d",
|
||||
license_state, license_type);
|
||||
return GET_RELEASED_LICENSE_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_->RestoreOfflineLicense(key_request_, key_response_,
|
||||
offline_key_renewal_response_)) {
|
||||
return UNKNOWN_ERROR;
|
||||
if (license_type == kLicenseTypeRelease) {
|
||||
if (!license_parser_->RestoreLicenseForRelease(key_request_,
|
||||
key_response_)) {
|
||||
return RELEASE_LICENSE_ERROR_1;
|
||||
}
|
||||
} else {
|
||||
if (!license_parser_->RestoreOfflineLicense(
|
||||
key_request_, key_response_, offline_key_renewal_response_,
|
||||
playback_start_time, last_playback_time)) {
|
||||
return RESTORE_OFFLINE_LICENSE_ERROR_2;
|
||||
}
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
@@ -155,8 +157,8 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
key_request_ = key_request;
|
||||
key_response_ = key_response;
|
||||
|
||||
if (!license_parser_->RestoreUsageLicense(key_request_, key_response_)) {
|
||||
return UNKNOWN_ERROR;
|
||||
if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) {
|
||||
return RELEASE_LICENSE_ERROR_2;
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
@@ -166,17 +168,18 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url, CdmKeySetId* key_set_id) {
|
||||
CdmKeyRequestType* key_request_type, std::string* server_url,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_1;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_1;
|
||||
}
|
||||
|
||||
switch (license_type) {
|
||||
@@ -189,36 +192,61 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
case kLicenseTypeRelease:
|
||||
is_release_ = true;
|
||||
break;
|
||||
case kLicenseTypeDeferred:
|
||||
// If you're going to pass Deferred, you must have empty init data in
|
||||
// this call and stored init data from the previous call.
|
||||
if (!init_data.IsEmpty() || !license_parser_->HasInitData()) {
|
||||
return INVALID_LICENSE_TYPE;
|
||||
}
|
||||
// The arguments check out.
|
||||
// The is_release_ and is_offline_ flags were already set last time based
|
||||
// on the original license type. Do not change them, and use them to
|
||||
// re-derive the original license type.
|
||||
if (is_release_) {
|
||||
license_type = kLicenseTypeRelease;
|
||||
} else if (is_offline_) {
|
||||
license_type = kLicenseTypeOffline;
|
||||
} else {
|
||||
license_type = kLicenseTypeStreaming;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
|
||||
license_type);
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_LICENSE_TYPE;
|
||||
}
|
||||
|
||||
if (is_release_) {
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeRelease;
|
||||
return GenerateReleaseRequest(key_request, server_url);
|
||||
} else if (license_received_) { // renewal
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeRenewal;
|
||||
return GenerateRenewalRequest(key_request, server_url);
|
||||
} else {
|
||||
if (!init_data.is_supported()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
return KEY_ERROR;
|
||||
if (key_request_type) *key_request_type = kKeyRequestTypeInitial;
|
||||
if (!license_parser_->HasInitData()) {
|
||||
if (!init_data.is_supported()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
return UNSUPPORTED_INIT_DATA;
|
||||
}
|
||||
if (init_data.IsEmpty()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||
return INIT_DATA_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if (init_data.IsEmpty() && !license_parser_->HasInitData()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
if (is_offline_ && !GenerateKeySetId(&key_set_id_)) {
|
||||
if (is_offline_ && key_set_id_.empty() &&
|
||||
!GenerateKeySetId(&key_set_id_)) {
|
||||
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
return KEY_REQUEST_ERROR_1;
|
||||
}
|
||||
|
||||
if (!license_parser_->PrepareKeyRequest(init_data, license_type,
|
||||
app_parameters, session_id_,
|
||||
key_request, server_url)) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
app_parameters_ = app_parameters;
|
||||
CdmResponseType status = license_parser_->PrepareKeyRequest(
|
||||
init_data, license_type,
|
||||
app_parameters, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
key_request_ = *key_request;
|
||||
if (is_offline_) {
|
||||
@@ -236,12 +264,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::AddKey: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_2;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::AddKey: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_2;
|
||||
}
|
||||
|
||||
if (is_release_) {
|
||||
@@ -252,7 +280,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
} else {
|
||||
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
|
||||
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? ADD_KEY_ERROR : sts;
|
||||
|
||||
license_received_ = true;
|
||||
key_response_ = key_response;
|
||||
@@ -270,12 +298,12 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGE("CdmSession::QueryStatus: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_3;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGE("CdmSession::QueryStatus: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_3;
|
||||
}
|
||||
|
||||
switch (security_level_) {
|
||||
@@ -294,7 +322,7 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
|
||||
break;
|
||||
default:
|
||||
return KEY_ERROR;
|
||||
return INVALID_QUERY_KEY;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -306,12 +334,12 @@ CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
|
||||
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::QueryKeyControlInfo: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
return INVALID_CRYPTO_SESSION_4;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::QueryKeyControlInfo: Crypto session not open");
|
||||
return UNKNOWN_ERROR;
|
||||
return CRYPTO_SESSION_OPEN_ERROR_4;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
@@ -322,20 +350,40 @@ CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
|
||||
// Decrypt() - Accept encrypted buffer and return decrypted data.
|
||||
CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||
return UNKNOWN_ERROR;
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::Decrypt: Invalid crypto session");
|
||||
return INVALID_CRYPTO_SESSION_5;
|
||||
}
|
||||
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGW("CdmSession::Decrypt: Crypto session not open");
|
||||
return CRYPTO_SESSION_OPEN_ERROR_5;
|
||||
}
|
||||
|
||||
// Playback may not begin until either the start time passes or the license
|
||||
// is updated, so we treat this Decrypt call as invalid.
|
||||
if (params.is_encrypted && !policy_engine_->CanDecrypt(*params.key_id)) {
|
||||
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
|
||||
}
|
||||
|
||||
CdmResponseType status = crypto_session_->Decrypt(params);
|
||||
|
||||
if (NO_ERROR == status) {
|
||||
if (status == NO_ERROR) {
|
||||
if (is_initial_decryption_) {
|
||||
policy_engine_->BeginDecryption();
|
||||
is_initial_decryption_ = false;
|
||||
}
|
||||
has_decrypted_since_last_report_ = true;
|
||||
if (!is_usage_update_needed_) {
|
||||
is_usage_update_needed_ =
|
||||
!license_parser_->provider_session_token().empty();
|
||||
}
|
||||
} else {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
if (policy_engine_->IsLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -346,11 +394,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (!license_parser_->PrepareKeyUpdateRequest(true, key_request,
|
||||
server_url)) {
|
||||
LOGE("CdmSession::GenerateRenewalRequest: ERROR on prepare");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
true, app_parameters_, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
if (is_offline_) {
|
||||
offline_key_renewal_request_ = *key_request;
|
||||
@@ -362,11 +409,12 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(true, key_response);
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? RENEW_KEY_ERROR_1 : sts;
|
||||
|
||||
if (is_offline_) {
|
||||
offline_key_renewal_response_ = key_response;
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) return UNKNOWN_ERROR;
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive))
|
||||
return RENEW_KEY_ERROR_2;
|
||||
}
|
||||
return KEY_ADDED;
|
||||
}
|
||||
@@ -374,12 +422,14 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
is_release_ = true;
|
||||
if (!license_parser_->PrepareKeyUpdateRequest(false, key_request, server_url))
|
||||
return UNKNOWN_ERROR;
|
||||
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
|
||||
false, app_parameters_, key_request, server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
if (is_offline_) { // Mark license as being released
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))
|
||||
return UNKNOWN_ERROR;
|
||||
return RELEASE_KEY_REQUEST_ERROR;
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
@@ -388,7 +438,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(false, key_response);
|
||||
if (KEY_ADDED != sts) return sts;
|
||||
if (KEY_ADDED != sts) return (KEY_ERROR == sts) ? RELEASE_KEY_ERROR : sts;
|
||||
|
||||
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
|
||||
DeleteLicense();
|
||||
@@ -427,6 +477,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
||||
key_set_id->clear();
|
||||
}
|
||||
}
|
||||
// Reserve the license ID to avoid collisions.
|
||||
file_handle_->ReserveLicenseId(*key_set_id);
|
||||
return true;
|
||||
}
|
||||
@@ -435,7 +486,7 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
if (is_offline_) {
|
||||
if (key_set_id_.empty()) {
|
||||
LOGE("CdmSession::StoreLicense: No key set ID");
|
||||
return UNKNOWN_ERROR;
|
||||
return EMPTY_KEYSET_ID;
|
||||
}
|
||||
|
||||
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
|
||||
@@ -447,7 +498,7 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
}
|
||||
|
||||
key_set_id_.clear();
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_LICENSE_ERROR_1;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -456,18 +507,20 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
license_parser_->provider_session_token();
|
||||
if (provider_session_token.empty()) {
|
||||
LOGE("CdmSession::StoreLicense: No provider session token and not offline");
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_LICENSE_ERROR_2;
|
||||
}
|
||||
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_LICENSE_ERROR_3;
|
||||
}
|
||||
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
|
||||
key_response_)) {
|
||||
key_response_, app_id)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to store usage info");
|
||||
return UNKNOWN_ERROR;
|
||||
return STORE_USAGE_INFO_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -478,57 +531,67 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
return file_handle_->StoreLicense(
|
||||
key_set_id_, state, offline_init_data_, key_request_, key_response_,
|
||||
offline_key_renewal_request_, offline_key_renewal_response_,
|
||||
offline_release_server_url_);
|
||||
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
|
||||
policy_engine_->GetLastPlaybackTime(), app_parameters_);
|
||||
}
|
||||
|
||||
bool CdmSession::DeleteLicense() {
|
||||
if (!is_offline_ && license_parser_->provider_session_token().empty())
|
||||
return false;
|
||||
|
||||
if (!license_parser_->provider_session_token().empty()) {
|
||||
if (crypto_session_->DeleteUsageInformation(
|
||||
license_parser_->provider_session_token()) != NO_ERROR) {
|
||||
LOGE("CdmSession::DeleteLicense: error deleting usage info");
|
||||
}
|
||||
}
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_offline_)
|
||||
if (is_offline_) {
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
else
|
||||
} else {
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
return file_handle_->DeleteUsageInfo(
|
||||
license_parser_->provider_session_token());
|
||||
app_id, license_parser_->provider_session_token());
|
||||
}
|
||||
}
|
||||
|
||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||
std::pair<CdmEventListenerIter, bool> result = listeners_.insert(listener);
|
||||
return result.second;
|
||||
void CdmSession::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
policy_engine_->NotifyResolution(width, height);
|
||||
}
|
||||
|
||||
bool CdmSession::DetachEventListener(WvCdmEventListener* listener) {
|
||||
return (listeners_.erase(listener) == 1);
|
||||
}
|
||||
|
||||
void CdmSession::OnTimerEvent() {
|
||||
bool event_occurred = false;
|
||||
CdmEventType event;
|
||||
|
||||
policy_engine_->OnTimerEvent(&event_occurred, &event);
|
||||
|
||||
if (event_occurred) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
(*iter)->OnEvent(session_id_, event);
|
||||
void CdmSession::OnTimerEvent(bool update_usage) {
|
||||
if (update_usage && has_decrypted_since_last_report_) {
|
||||
policy_engine_->DecryptionEvent();
|
||||
has_decrypted_since_last_report_ = false;
|
||||
if (is_offline_ && !is_release_) {
|
||||
StoreLicense(DeviceFiles::kLicenseStateActive);
|
||||
}
|
||||
}
|
||||
policy_engine_->OnTimerEvent();
|
||||
}
|
||||
|
||||
void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
if (key_set_id_ == key_set_id) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
iter != listeners_.end(); ++iter) {
|
||||
(*iter)->OnEvent(session_id_, LICENSE_EXPIRED_EVENT);
|
||||
}
|
||||
policy_engine_->NotifySessionExpiration();
|
||||
}
|
||||
}
|
||||
|
||||
void CdmSession::GetApplicationId(std::string* app_id) {
|
||||
if (app_id && !Properties::GetApplicationId(session_id_, app_id)) {
|
||||
*app_id = "";
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
return crypto_session_->DeleteMultipleUsageInformation(
|
||||
provider_session_tokens);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageInformation() {
|
||||
return crypto_session_->UpdateUsageInformation();
|
||||
}
|
||||
@@ -538,4 +601,20 @@ CdmResponseType CdmSession::ReleaseCrypto() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void CdmSession::set_license_parser(CdmLicense* license_parser) {
|
||||
license_parser_.reset(license_parser);
|
||||
}
|
||||
|
||||
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
|
||||
crypto_session_.reset(crypto_session);
|
||||
}
|
||||
|
||||
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {
|
||||
policy_engine_.reset(policy_engine);
|
||||
}
|
||||
|
||||
void CdmSession::set_file_handle(DeviceFiles* file_handle) {
|
||||
file_handle_.reset(file_handle);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -53,15 +53,15 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
* in *default_url.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
||||
const std::string& cert_authority, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
const std::string& cert_authority, const std::string& origin,
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
if (!default_url) {
|
||||
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_1;
|
||||
}
|
||||
|
||||
default_url->assign(kProvisioningServerUrl);
|
||||
@@ -79,14 +79,14 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
std::string token;
|
||||
if (!crypto_session_.GetToken(&token)) {
|
||||
LOGE("GetProvisioningRequest: fails to get token");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_GET_KEYBOX_ERROR_1;
|
||||
}
|
||||
client_id->set_token(token);
|
||||
|
||||
uint32_t nonce;
|
||||
if (!crypto_session_.GenerateNonce(&nonce)) {
|
||||
LOGE("GetProvisioningRequest: fails to generate a nonce");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_2;
|
||||
}
|
||||
|
||||
// The provisioning server does not convert the nonce to uint32_t, it just
|
||||
@@ -107,12 +107,21 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
break;
|
||||
default:
|
||||
LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type);
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_INVALID_CERT_TYPE;
|
||||
}
|
||||
|
||||
cert_type_ = cert_type;
|
||||
options->set_certificate_authority(cert_authority);
|
||||
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
std::string device_unique_id;
|
||||
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
|
||||
LOGE("GetProvisioningRequest: fails to get device unique ID");
|
||||
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
|
||||
}
|
||||
provisioning_request.set_stable_id(device_unique_id + origin);
|
||||
}
|
||||
|
||||
std::string serialized_message;
|
||||
provisioning_request.SerializeToString(&serialized_message);
|
||||
|
||||
@@ -121,11 +130,11 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
if (!crypto_session_.PrepareRequest(serialized_message, true,
|
||||
&request_signature)) {
|
||||
LOGE("GetProvisioningRequest: fails to prepare request");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_3;
|
||||
}
|
||||
if (request_signature.empty()) {
|
||||
LOGE("GetProvisioningRequest: request signature is empty");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_4;
|
||||
}
|
||||
|
||||
SignedProvisioningMessage signed_provisioning_msg;
|
||||
@@ -177,11 +186,11 @@ bool CertificateProvisioning::ParseJsonResponse(
|
||||
* The device RSA key is stored in the T.E.E. The device certificate is stored
|
||||
* in the device.
|
||||
*
|
||||
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
|
||||
* Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails.
|
||||
*/
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
const std::string& origin, 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\": \"";
|
||||
const std::string kMessageEnd = "\"";
|
||||
@@ -189,7 +198,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
|
||||
&serialized_signed_response)) {
|
||||
LOGE("Fails to extract signed serialized response from JSON response");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_1;
|
||||
}
|
||||
|
||||
// Authenticates provisioning response using D1s (server key derived from
|
||||
@@ -198,7 +207,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
SignedProvisioningMessage signed_response;
|
||||
if (!signed_response.ParseFromString(serialized_signed_response)) {
|
||||
LOGE("HandleProvisioningResponse: fails to parse signed response");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_2;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
@@ -212,19 +221,19 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error) return UNKNOWN_ERROR;
|
||||
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
|
||||
|
||||
const std::string& signed_message = signed_response.message();
|
||||
ProvisioningResponse provisioning_response;
|
||||
|
||||
if (!provisioning_response.ParseFromString(signed_message)) {
|
||||
LOGE("HandleProvisioningResponse: Fails to parse signed message");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_4;
|
||||
}
|
||||
|
||||
if (!provisioning_response.has_device_rsa_key()) {
|
||||
LOGE("HandleProvisioningResponse: key not found");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_5;
|
||||
}
|
||||
|
||||
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
|
||||
@@ -236,7 +245,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
enc_rsa_key, rsa_key_iv,
|
||||
&wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_6;
|
||||
}
|
||||
|
||||
crypto_session_.Close();
|
||||
@@ -253,11 +262,11 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(crypto_session_.GetSecurityLevel())) {
|
||||
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_7;
|
||||
}
|
||||
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
|
||||
if (!handle.StoreCertificate(origin, device_certificate, wrapped_rsa_key)) {
|
||||
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
|
||||
return UNKNOWN_ERROR;
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_8;
|
||||
}
|
||||
handle.DeleteAllLicenses();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "crypto_session.h"
|
||||
|
||||
#include <arpa/inet.h> // needed for ntoh()
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "crypto_key.h"
|
||||
@@ -24,6 +25,7 @@ std::string EncodeUint32(unsigned int u) {
|
||||
s.append(1, (u >> 0) & 0xFF);
|
||||
return s;
|
||||
}
|
||||
const uint32_t kRsaSignatureLength = 256;
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -35,6 +37,7 @@ uint64_t CryptoSession::request_id_index_ = 0;
|
||||
|
||||
CryptoSession::CryptoSession()
|
||||
: open_(false),
|
||||
update_usage_table_after_close_session_(false),
|
||||
is_destination_buffer_type_valid_(false),
|
||||
requested_security_level_(kLevelDefault),
|
||||
request_id_base_(0) {
|
||||
@@ -64,7 +67,11 @@ void CryptoSession::Init() {
|
||||
void CryptoSession::Terminate() {
|
||||
LOGV("CryptoSession::Terminate");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
session_count_ -= 1;
|
||||
if (session_count_ > 0) {
|
||||
session_count_ -= 1;
|
||||
} else {
|
||||
LOGE("CryptoSession::Terminate error, session count: %d", session_count_);
|
||||
}
|
||||
if (session_count_ > 0 || !initialized_) return;
|
||||
OEMCryptoResult sts = OEMCrypto_Terminate();
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
@@ -168,8 +175,6 @@ bool CryptoSession::GetApiVersion(uint32_t* version) {
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGV("CryptoSession::GetApiVersion: Lock");
|
||||
*version = OEMCrypto_APIVersion(requested_security_level_);
|
||||
return true;
|
||||
}
|
||||
@@ -242,9 +247,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_);
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
}
|
||||
if (!open_) return UNKNOWN_ERROR;
|
||||
if (!open_) {
|
||||
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_));
|
||||
@@ -259,6 +270,11 @@ void CryptoSession::Close() {
|
||||
if (!open_) return;
|
||||
if (OEMCrypto_SUCCESS == OEMCrypto_CloseSession(oec_session_id_)) {
|
||||
open_ = false;
|
||||
if (update_usage_table_after_close_session_) {
|
||||
OEMCryptoResult sts = OEMCrypto_UpdateUsageTable();
|
||||
if (sts != OEMCrypto_SUCCESS)
|
||||
LOGW("CryptoSession::Close: OEMCrypto_UpdateUsageTable error=%ld", sts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,13 +426,21 @@ CdmResponseType CryptoSession::LoadKeys(
|
||||
provider_session_token.length());
|
||||
|
||||
if (OEMCrypto_SUCCESS == sts) {
|
||||
if (!provider_session_token.empty()) {
|
||||
update_usage_table_after_close_session_ = true;
|
||||
sts = OEMCrypto_UpdateUsageTable();
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
LOGW("CryptoSession::LoadKeys: OEMCrypto_UpdateUsageTable error=%ld",
|
||||
sts);
|
||||
}
|
||||
}
|
||||
return KEY_ADDED;
|
||||
} else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) {
|
||||
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
} else {
|
||||
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return KEY_ERROR;
|
||||
LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
|
||||
return LOAD_KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +454,7 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
|
||||
wrapped_key.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts);
|
||||
LOGE("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -500,7 +524,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message) {
|
||||
enc_deriv_message.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
|
||||
LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -524,7 +548,7 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
|
||||
enc_deriv_message.size());
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
|
||||
LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -545,7 +569,7 @@ bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -558,7 +582,7 @@ bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
&length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -574,6 +598,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) return false;
|
||||
|
||||
signature->resize(kRsaSignatureLength);
|
||||
size_t length = signature->size();
|
||||
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
@@ -583,7 +608,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -596,7 +621,7 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
&length, kSign_RSASSA_PSS);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -612,18 +637,6 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
if (!SetDestinationBufferType()) return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor;
|
||||
buffer_descriptor.type =
|
||||
params.is_secure ? destination_buffer_type_ : OEMCrypto_BufferType_Clear;
|
||||
@@ -647,10 +660,29 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
break;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCTR(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
if (!params.is_encrypted) {
|
||||
sts = OEMCrypto_CopyBuffer(requested_security_level_,
|
||||
params.encrypt_buffer, params.encrypt_length,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
}
|
||||
if (params.is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
sts = OEMCrypto_DecryptCTR(
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
}
|
||||
|
||||
switch (sts) {
|
||||
case OEMCrypto_SUCCESS:
|
||||
@@ -668,8 +700,7 @@ bool CryptoSession::UsageInformationSupport(bool* has_support) {
|
||||
LOGV("UsageInformationSupport: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!initialized_) return false;
|
||||
|
||||
*has_support = OEMCrypto_SupportsUsageTable(
|
||||
kSecurityLevelL3 == GetSecurityLevel() ? kLevel3 : kLevelDefault);
|
||||
*has_support = OEMCrypto_SupportsUsageTable(requested_security_level_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -686,14 +717,9 @@ CdmResponseType CryptoSession::UpdateUsageInformation() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report) {
|
||||
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
if (NULL == usage_report) {
|
||||
LOGE("usage_report parameter is null");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
CdmResponseType CryptoSession::DeactivateUsageInformation(
|
||||
const std::string& provider_session_token) {
|
||||
LOGV("DeactivateUsageInformation: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
uint8_t* pst = reinterpret_cast<uint8_t*>(
|
||||
@@ -702,20 +728,44 @@ CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
OEMCryptoResult status =
|
||||
OEMCrypto_DeactivateUsageEntry(pst, provider_session_token.length());
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: Deactivate Usage Entry error=%ld",
|
||||
status);
|
||||
switch (status) {
|
||||
case OEMCrypto_SUCCESS:
|
||||
return NO_ERROR;
|
||||
case OEMCrypto_ERROR_INVALID_CONTEXT:
|
||||
LOGE("CryptoSession::DeactivateUsageInformation: invalid context error");
|
||||
return KEY_CANCELED;
|
||||
default:
|
||||
LOGE("CryptoSession::DeactivateUsageInformation: error=%ld", status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report,
|
||||
UsageDurationStatus* usage_duration_status, int64_t* seconds_since_started,
|
||||
int64_t* seconds_since_last_played) {
|
||||
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
|
||||
|
||||
if (NULL == usage_report) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: usage_report parameter is null");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
size_t usage_length = 0;
|
||||
status = OEMCrypto_ReportUsage(oec_session_id_, pst,
|
||||
provider_session_token.length(), NULL,
|
||||
&usage_length);
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
uint8_t* pst = reinterpret_cast<uint8_t*>(
|
||||
const_cast<char*>(provider_session_token.data()));
|
||||
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld", status);
|
||||
return UNKNOWN_ERROR;
|
||||
size_t usage_length = 0;
|
||||
OEMCryptoResult status = OEMCrypto_ReportUsage(
|
||||
oec_session_id_, pst, provider_session_token.length(), NULL,
|
||||
&usage_length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld",
|
||||
status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
usage_report->resize(usage_length);
|
||||
@@ -730,10 +780,49 @@ CdmResponseType CryptoSession::GenerateUsageReport(
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (usage_length < usage_report->length()) {
|
||||
if (usage_length != usage_report->length()) {
|
||||
usage_report->resize(usage_length);
|
||||
}
|
||||
|
||||
OEMCrypto_PST_Report pst_report;
|
||||
*usage_duration_status = kUsageDurationsInvalid;
|
||||
if (usage_length < sizeof(pst_report)) {
|
||||
LOGE("CryptoSession::GenerateUsageReport: usage report too small=%ld",
|
||||
usage_length);
|
||||
return NO_ERROR; // usage report available but no duration information
|
||||
}
|
||||
|
||||
memcpy(&pst_report, usage_report->data(), sizeof(pst_report));
|
||||
if (kUnused == pst_report.status) {
|
||||
*usage_duration_status = kUsageDurationPlaybackNotBegun;
|
||||
return NO_ERROR;
|
||||
}
|
||||
LOGV("OEMCrypto_PST_Report.status: %d\n", pst_report.status);
|
||||
LOGV("OEMCrypto_PST_Report.clock_security_level: %d\n",
|
||||
pst_report.clock_security_level);
|
||||
LOGV("OEMCrypto_PST_Report.pst_length: %d\n", pst_report.pst_length);
|
||||
LOGV("OEMCrypto_PST_Report.padding: %d\n", pst_report.padding);
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_license_received: %lld\n",
|
||||
ntohll64(pst_report.seconds_since_license_received));
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_first_decrypt: %lld\n",
|
||||
ntohll64(pst_report.seconds_since_first_decrypt));
|
||||
LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %lld\n",
|
||||
ntohll64(pst_report.seconds_since_last_decrypt));
|
||||
LOGV("OEMCrypto_PST_Report: %s\n", b2a_hex(*usage_report).c_str());
|
||||
|
||||
// When usage report state is inactive, we have to deduce whether the
|
||||
// license was ever used.
|
||||
if (kInactive == pst_report.status &&
|
||||
(0 > ntohll64(pst_report.seconds_since_first_decrypt) ||
|
||||
ntohll64(pst_report.seconds_since_license_received) <
|
||||
ntohll64(pst_report.seconds_since_first_decrypt))) {
|
||||
*usage_duration_status = kUsageDurationPlaybackNotBegun;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
*usage_duration_status = kUsageDurationsValid;
|
||||
*seconds_since_started = ntohll64(pst_report.seconds_since_first_decrypt);
|
||||
*seconds_since_last_played = ntohll64(pst_report.seconds_since_last_decrypt);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -755,10 +844,81 @@ CdmResponseType CryptoSession::ReleaseUsageInformation(
|
||||
status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGW("CryptoSession::ReleaseUsageInformation: update table error=%ld",
|
||||
status);
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteUsageInformation(
|
||||
const std::string& provider_session_token) {
|
||||
CdmResponseType response = NO_ERROR;
|
||||
LOGV("CryptoSession::DeleteUsageInformation");
|
||||
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
|
||||
reinterpret_cast<const uint8_t*>(provider_session_token.c_str()),
|
||||
provider_session_token.length());
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("CryptoSession::DeleteUsageInformation: Delete Usage Table error =%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeleteUsageInformation: update table error=%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteMultipleUsageInformation(
|
||||
const std::vector<std::string>& provider_session_tokens) {
|
||||
LOGV("CryptoSession::DeleteMultipleUsageInformation");
|
||||
CdmResponseType response = NO_ERROR;
|
||||
for (size_t i=0; i < provider_session_tokens.size(); ++i) {
|
||||
OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry(
|
||||
reinterpret_cast<const uint8_t*>(provider_session_tokens[i].c_str()),
|
||||
provider_session_tokens[i].length());
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("CryptoSession::DeleteMultipleUsageInformation: "
|
||||
"Delete Usage Table error =%ld", status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
OEMCryptoResult status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeleteMultipleUsageInformation: update table error=%ld",
|
||||
status);
|
||||
response = UNKNOWN_ERROR;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::DeleteAllUsageReports() {
|
||||
LOGV("DeleteAllUsageReports");
|
||||
OEMCryptoResult status = OEMCrypto_DeleteUsageTable();
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGE("CryptoSession::DeleteAllUsageReports: Delete Usage Table error =%ld",
|
||||
status);
|
||||
}
|
||||
|
||||
status = OEMCrypto_UpdateUsageTable();
|
||||
if (status != OEMCrypto_SUCCESS) {
|
||||
LOGE("CryptoSession::DeletaAllUsageReports: update table error=%ld",
|
||||
status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool CryptoSession::IsAntiRollbackHwPresent() {
|
||||
return OEMCrypto_IsAntiRollbackHwPresent(requested_security_level_);
|
||||
}
|
||||
|
||||
bool CryptoSession::GenerateNonce(uint32_t* nonce) {
|
||||
if (!nonce) {
|
||||
LOGE("input parameter is null");
|
||||
@@ -796,7 +956,7 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
const std::string& enc_rsa_key,
|
||||
const std::string& rsa_key_iv,
|
||||
std::string* wrapped_rsa_key) {
|
||||
LOGD("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
|
||||
LOGV("CryptoSession::RewrapDeviceRSAKey, session id=%ld",
|
||||
static_cast<uint32_t>(oec_session_id_));
|
||||
|
||||
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
|
||||
@@ -841,20 +1001,20 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetHdcpCapabilities(OemCryptoHdcpVersion* current_version,
|
||||
OemCryptoHdcpVersion* max_version) {
|
||||
bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
|
||||
HdcpCapability* max) {
|
||||
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!initialized_) return UNKNOWN_ERROR;
|
||||
OEMCrypto_HDCP_Capability current, max;
|
||||
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(¤t, &max);
|
||||
|
||||
if (!initialized_) return false;
|
||||
if (current == NULL || max == NULL) {
|
||||
LOGE("CryptoSession::GetHdcpCapabilities: |current|, |max| cannot be NULL");
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(
|
||||
requested_security_level_, current, max);
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("OEMCrypto_GetHDCPCapability fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
*current_version = static_cast<OemCryptoHdcpVersion>(current);
|
||||
*max_version = static_cast<OemCryptoHdcpVersion>(max);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -873,4 +1033,42 @@ bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
bool CryptoSession::GetNumberOfOpenSessions(size_t* count) {
|
||||
LOGV("GetNumberOfOpenSessions");
|
||||
if (!initialized_) return false;
|
||||
if (count == NULL) {
|
||||
LOGE("CryptoSession::GetNumberOfOpenSessions: |count| cannot be NULL");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sessions_count;
|
||||
OEMCryptoResult status = OEMCrypto_GetNumberOfOpenSessions(
|
||||
requested_security_level_, &sessions_count);
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("OEMCrypto_GetNumberOfOpenSessions fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
*count = sessions_count;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetMaxNumberOfSessions(size_t* max) {
|
||||
LOGV("GetMaxNumberOfSessions");
|
||||
if (!initialized_) return false;
|
||||
if (max == NULL) {
|
||||
LOGE("CryptoSession::GetMaxNumberOfSessions: |max| cannot be NULL");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t max_sessions;
|
||||
OEMCryptoResult status = OEMCrypto_GetMaxNumberOfSessions(
|
||||
requested_security_level_, &max_sessions);
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
LOGW("OEMCrypto_GetMaxNumberOfSessions fails with %d", status);
|
||||
return false;
|
||||
}
|
||||
*max = max_sessions;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,15 +2,7 @@
|
||||
|
||||
#include "device_files.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#define SHA256 CC_SHA256
|
||||
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
||||
#else
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "device_files.pb.h"
|
||||
@@ -18,6 +10,16 @@
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#define SHA256 CC_SHA256
|
||||
#define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
||||
#else
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/md5.h>
|
||||
#endif
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_client::sdk::DeviceCertificate;
|
||||
@@ -25,22 +27,25 @@ using video_widevine_client::sdk::HashedFile;
|
||||
using video_widevine_client::sdk::License;
|
||||
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
|
||||
using video_widevine_client::sdk::License_LicenseState_RELEASING;
|
||||
using video_widevine_client::sdk::NameValue;
|
||||
using video_widevine_client::sdk::UsageInfo;
|
||||
using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kUsageInfoFileName[] = "usage.bin";
|
||||
const char kCertificateFileNamePrefix[] = "cert";
|
||||
const char kCertificateFileNameExt[] = ".bin";
|
||||
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"};
|
||||
const char* kSecurityLevelPathCompatibilityExclusionList[] = {
|
||||
"ay64.dat", "ay64.dat2", "ay64.dat3"};
|
||||
size_t kSecurityLevelPathCompatibilityExclusionListSize =
|
||||
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
|
||||
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
|
||||
// Some platforms cannot store a truly blank file, so we use a W for Widevine.
|
||||
const char kBlankFileData[] = "W";
|
||||
|
||||
bool Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash) return false;
|
||||
@@ -53,10 +58,13 @@ bool Hash(const std::string& data, std::string* hash) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// static
|
||||
std::set<std::string> DeviceFiles::reserved_license_ids_;
|
||||
|
||||
DeviceFiles::DeviceFiles()
|
||||
: file_(NULL),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
@@ -68,14 +76,10 @@ DeviceFiles::~DeviceFiles() {
|
||||
}
|
||||
|
||||
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
||||
switch (security_level) {
|
||||
case kSecurityLevelL1:
|
||||
case kSecurityLevelL2:
|
||||
case kSecurityLevelL3:
|
||||
break;
|
||||
default:
|
||||
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
|
||||
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;
|
||||
@@ -83,7 +87,8 @@ bool DeviceFiles::Init(CdmSecurityLevel security_level) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
bool DeviceFiles::StoreCertificate(const std::string& origin,
|
||||
const std::string& certificate,
|
||||
const std::string& wrapped_private_key) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreCertificate: not initialized");
|
||||
@@ -103,10 +108,11 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
return StoreFileWithHash(kCertificateFileName, serialized_file);
|
||||
return StoreFileWithHash(GetCertificateFileName(origin), serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
bool DeviceFiles::RetrieveCertificate(const std::string& origin,
|
||||
std::string* certificate,
|
||||
std::string* wrapped_private_key) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
|
||||
@@ -118,7 +124,9 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveHashedFile(kCertificateFileName, &serialized_file)) return false;
|
||||
if (!RetrieveHashedFile(GetCertificateFileName(origin), &serialized_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -148,14 +156,32 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
const LicenseState state,
|
||||
const CdmInitData& pssh_data,
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url) {
|
||||
bool DeviceFiles::HasCertificate(const std::string& origin) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::HasCertificate: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return FileExists(GetCertificateFileName(origin));
|
||||
}
|
||||
|
||||
bool DeviceFiles::RemoveCertificate(const std::string& origin) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RemoveCertificate: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return RemoveFile(GetCertificateFileName(origin));
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreLicense(
|
||||
const std::string& key_set_id, const LicenseState state,
|
||||
const CdmInitData& pssh_data, const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_message,
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url, int64_t playback_start_time,
|
||||
int64_t last_playback_time, const CdmAppParameterMap& app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreLicense: not initialized");
|
||||
return false;
|
||||
@@ -186,29 +212,38 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
|
||||
license->set_renewal_request(license_renewal_request);
|
||||
license->set_renewal(license_renewal);
|
||||
license->set_release_server_url(release_server_url);
|
||||
license->set_playback_start_time(playback_start_time);
|
||||
license->set_last_playback_time(last_playback_time);
|
||||
NameValue* app_params;
|
||||
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
|
||||
iter != app_parameters.end(); ++iter) {
|
||||
app_params = license->add_app_parameters();
|
||||
app_params->set_name(iter->first);
|
||||
app_params->set_value(iter->second);
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
file.SerializeToString(&serialized_file);
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFileWithHash(file_name.c_str(), serialized_file);
|
||||
reserved_license_ids_.erase(key_set_id);
|
||||
return StoreFileWithHash(key_set_id + kLicenseFileNameExt, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
LicenseState* state, CdmInitData* pssh_data,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request,
|
||||
CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url) {
|
||||
bool DeviceFiles::RetrieveLicense(
|
||||
const std::string& key_set_id, LicenseState* state, CdmInitData* pssh_data,
|
||||
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url, int64_t* playback_start_time,
|
||||
int64_t* last_playback_time, CdmAppParameterMap* app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
if (!RetrieveHashedFile(file_name.c_str(), &serialized_file)) return false;
|
||||
if (!RetrieveHashedFile(key_set_id + kLicenseFileNameExt, &serialized_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -252,6 +287,12 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
||||
*license_renewal_request = license.renewal_request();
|
||||
*license_renewal = license.renewal();
|
||||
*release_server_url = license.release_server_url();
|
||||
*playback_start_time = license.playback_start_time();
|
||||
*last_playback_time = license.last_playback_time();
|
||||
for (int i = 0; i < license.app_parameters_size(); ++i) {
|
||||
(*app_parameters)[license.app_parameters(i).name()] =
|
||||
license.app_parameters(i).value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -260,16 +301,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||
LOGW("DeviceFiles::DeleteLicense: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteLicense: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(key_set_id);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Remove(path);
|
||||
return RemoveFile(key_set_id + kLicenseFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteAllLicenses() {
|
||||
@@ -277,16 +309,7 @@ bool DeviceFiles::DeleteAllLicenses() {
|
||||
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteAllLicenses: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(kWildcard);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Remove(path);
|
||||
return RemoveFile(std::string(kWildcard) + kLicenseFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteAllFiles() {
|
||||
@@ -295,13 +318,9 @@ bool DeviceFiles::DeleteAllFiles() {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteAllFiles: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_->Remove(path);
|
||||
// We pass an empty string to RemoveFile to delete the device files base
|
||||
// directory itself.
|
||||
return RemoveFile(kEmptyFileName);
|
||||
}
|
||||
|
||||
bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
@@ -309,16 +328,8 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
|
||||
LOGW("DeviceFiles::LicenseExists: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::LicenseExists: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
path.append(key_set_id);
|
||||
path.append(kLicenseFileNameExt);
|
||||
|
||||
return file_->Exists(path);
|
||||
return reserved_license_ids_.count(key_set_id) ||
|
||||
FileExists(key_set_id + kLicenseFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
|
||||
@@ -326,14 +337,14 @@ bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
|
||||
LOGW("DeviceFiles::ReserveLicenseId: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string file_name = key_set_id + kLicenseFileNameExt;
|
||||
return StoreFileRaw(file_name.c_str(), kBlankFileData);
|
||||
reserved_license_ids_.insert(key_set_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response) {
|
||||
const CdmKeyResponse& key_response,
|
||||
const std::string& app_id) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
|
||||
return false;
|
||||
@@ -341,7 +352,8 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
|
||||
std::string serialized_file;
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) {
|
||||
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
|
||||
file.set_version(video_widevine_client::sdk::File::VERSION_1);
|
||||
} else {
|
||||
@@ -360,17 +372,18 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
|
||||
provider_session->set_license(key_response.data(), key_response.size());
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
|
||||
return StoreFileWithHash(file_name, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) return false;
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
@@ -382,8 +395,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
int index = 0;
|
||||
bool found = false;
|
||||
for (; index < usage_info->sessions_size(); ++index) {
|
||||
if (usage_info->sessions(index).token().compare(provider_session_token) ==
|
||||
0) {
|
||||
if (usage_info->sessions(index).token() == provider_session_token) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -405,26 +417,43 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
sessions->RemoveLast();
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFileWithHash(kUsageInfoFileName, serialized_file);
|
||||
return StoreFileWithHash(file_name, serialized_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteUsageInfo() {
|
||||
bool DeviceFiles::DeleteAllUsageInfoForApp(
|
||||
const std::string& app_id,
|
||||
std::vector<std::string>* provider_session_tokens) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::DeleteUsageInfo: Unable to get base path");
|
||||
if (NULL == provider_session_tokens) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: pst destination not provided");
|
||||
return false;
|
||||
}
|
||||
path.append(kUsageInfoFileName);
|
||||
provider_session_tokens->clear();
|
||||
|
||||
return file_->Remove(path);
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!FileExists(file_name)) return true;
|
||||
std::string serialized_file;
|
||||
if (RetrieveHashedFile(file_name, &serialized_file)) {
|
||||
video_widevine_client::sdk::File file_proto;
|
||||
if (!file_proto.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to parse file");
|
||||
} else {
|
||||
for (int i = 0; i < file_proto.usage_info().sessions_size(); ++i) {
|
||||
provider_session_tokens->push_back(
|
||||
file_proto.usage_info().sessions(i).token());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file");
|
||||
}
|
||||
return RemoveFile(file_name);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveUsageInfo(
|
||||
const std::string& app_id,
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
||||
@@ -439,15 +468,9 @@ bool DeviceFiles::RetrieveUsageInfo(
|
||||
}
|
||||
|
||||
std::string serialized_file;
|
||||
if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path += kUsageInfoFileName;
|
||||
|
||||
if (!file_->Exists(path) || 0 == file_->FileSize(path)) {
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) {
|
||||
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
|
||||
usage_info->resize(0);
|
||||
return true;
|
||||
}
|
||||
@@ -471,15 +494,45 @@ bool DeviceFiles::RetrieveUsageInfo(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileWithHash(const char* name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
|
||||
bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
|
||||
const std::string& provider_session_token,
|
||||
CdmKeyMessage* license_request,
|
||||
CdmKeyResponse* license_response) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
|
||||
return false;
|
||||
}
|
||||
std::string serialized_file;
|
||||
std::string file_name = GetUsageInfoFileName(app_id);
|
||||
if (!RetrieveHashedFile(file_name, &serialized_file)) return false;
|
||||
|
||||
video_widevine_client::sdk::File file;
|
||||
if (!file.ParseFromString(serialized_file)) {
|
||||
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
|
||||
return false;
|
||||
}
|
||||
int index = 0;
|
||||
bool found = false;
|
||||
for (; index < file.usage_info().sessions_size(); ++index) {
|
||||
if (file.usage_info().sessions(index).token() == provider_session_token) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Unspecified file name parameter");
|
||||
*license_request = file.usage_info().sessions(index).license_request();
|
||||
*license_response = file.usage_info().sessions(index).license();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileWithHash(const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -501,18 +554,13 @@ bool DeviceFiles::StoreFileWithHash(const char* name,
|
||||
return StoreFileRaw(name, serialized_hash_file);
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFileRaw(const char* name,
|
||||
bool DeviceFiles::StoreFileRaw(const std::string& name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
|
||||
@@ -546,18 +594,13 @@ bool DeviceFiles::StoreFileRaw(const char* name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
bool DeviceFiles::RetrieveHashedFile(const std::string& name,
|
||||
std::string* serialized_file) {
|
||||
if (!file_.get()) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file name parameter");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!serialized_file) {
|
||||
LOGW(
|
||||
"DeviceFiles::RetrieveHashedFile: Unspecified serialized_file "
|
||||
@@ -582,6 +625,9 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -614,8 +660,11 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hash.compare(hash_file.hash())) {
|
||||
if (hash != hash_file.hash()) {
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -623,6 +672,54 @@ bool DeviceFiles::RetrieveHashedFile(const char* name,
|
||||
return true;
|
||||
}
|
||||
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->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");
|
||||
return false;
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->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");
|
||||
return -1;
|
||||
}
|
||||
path += name;
|
||||
|
||||
return file_->FileSize(path);
|
||||
}
|
||||
|
||||
void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
@@ -687,17 +784,33 @@ void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
|
||||
}
|
||||
}
|
||||
|
||||
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::GetLicenseFileNameExtension() {
|
||||
return kLicenseFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetUsageInfoFileName() { return kUsageInfoFileName; }
|
||||
std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) {
|
||||
std::string hash;
|
||||
if (app_id != "") {
|
||||
hash = GetFileNameSafeHash(app_id);
|
||||
}
|
||||
return kUsageInfoFileNamePrefix + hash + kUsageInfoFileNameExt;
|
||||
}
|
||||
|
||||
std::string DeviceFiles::GetBlankFileData() { return kBlankFileData; }
|
||||
std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
|
||||
std::vector<uint8_t> hash(MD5_DIGEST_LENGTH);
|
||||
const unsigned char* input_ptr =
|
||||
reinterpret_cast<const unsigned char*>(input.data());
|
||||
MD5(input_ptr, input.size(), &hash[0]);
|
||||
return wvcdm::Base64SafeEncode(hash);
|
||||
}
|
||||
|
||||
void DeviceFiles::SetTestFile(File* file) {
|
||||
file_.reset(file);
|
||||
|
||||
@@ -13,6 +13,11 @@ package video_widevine_client.sdk;
|
||||
// need this if we are using libprotobuf-cpp-2.3.0-lite
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message NameValue {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
message DeviceCertificate {
|
||||
optional bytes certificate = 1;
|
||||
optional bytes wrapped_private_key = 2;
|
||||
@@ -31,6 +36,9 @@ message License {
|
||||
optional bytes renewal_request = 5;
|
||||
optional bytes renewal = 6;
|
||||
optional bytes release_server_url = 7;
|
||||
optional int64 playback_start_time = 8 [default = 0];
|
||||
optional int64 last_playback_time = 9 [default = 0];
|
||||
repeated NameValue app_parameters = 10;
|
||||
}
|
||||
|
||||
message UsageInfo {
|
||||
|
||||
@@ -38,76 +38,122 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
|
||||
BufferReader reader(reinterpret_cast<const uint8_t*>(init_data.data()),
|
||||
init_data.length());
|
||||
|
||||
// TODO(kqyang): Extracted from an actual init_data;
|
||||
// Need to find out where it comes from.
|
||||
// Widevine's registered system ID.
|
||||
static const uint8_t kWidevineSystemId[] = {
|
||||
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
|
||||
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
|
||||
};
|
||||
|
||||
// one PSSH blob consists of:
|
||||
// 4 byte size of the PSSH atom, inclusive
|
||||
// "pssh"
|
||||
// 4 byte flags, value 0
|
||||
// 16 byte system id
|
||||
// 4 byte size of PSSH data, exclusive
|
||||
// one PSSH box consists of:
|
||||
// 4 byte size of the atom, inclusive. (0 means the rest of the buffer.)
|
||||
// 4 byte atom type, "pssh".
|
||||
// (optional, if size == 1) 8 byte size of the atom, inclusive.
|
||||
// 1 byte version, value 0 or 1. (skip if larger.)
|
||||
// 3 byte flags, value 0. (ignored.)
|
||||
// 16 byte system id.
|
||||
// (optional, if version == 1) 4 byte key ID count. (K)
|
||||
// (optional, if version == 1) K * 16 byte key ID.
|
||||
// 4 byte size of PSSH data, exclusive. (N)
|
||||
// N byte PSSH data.
|
||||
while (1) {
|
||||
// size of PSSH atom, used for skipping
|
||||
uint32_t size;
|
||||
if (!reader.Read4(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH atom size");
|
||||
size_t start_pos = reader.pos();
|
||||
|
||||
// atom size, used for skipping.
|
||||
uint64_t size;
|
||||
if (!reader.Read4Into8(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> atom_type;
|
||||
if (!reader.ReadVec(&atom_type, 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
if (!reader.Read8(&size)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
|
||||
"size.");
|
||||
return false;
|
||||
}
|
||||
} else if (size == 0) {
|
||||
size = reader.size() - start_pos;
|
||||
}
|
||||
|
||||
// "pssh"
|
||||
std::vector<uint8_t> pssh;
|
||||
if (!reader.ReadVec(&pssh, 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH literal");
|
||||
return false;
|
||||
if (memcmp(&atom_type[0], "pssh", 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present.");
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
|
||||
"atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (memcmp(&pssh[0], "pssh", 4)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH literal not present");
|
||||
|
||||
// version
|
||||
uint8_t version;
|
||||
if (!reader.Read1(&version)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// flags
|
||||
uint32_t flags;
|
||||
if (!reader.Read4(&flags)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH flags");
|
||||
return false;
|
||||
if (version > 1) {
|
||||
// unrecognized version - skip.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
|
||||
"atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (flags != 0) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: PSSH flags not zero");
|
||||
|
||||
// flags
|
||||
if (!reader.SkipBytes(3)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// system id
|
||||
std::vector<uint8_t> system_id;
|
||||
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID");
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read system ID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
|
||||
// skip the remaining contents of the atom,
|
||||
// after size field, atom name, flags and system id
|
||||
if (!reader.SkipBytes(size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
|
||||
// skip non-Widevine PSSH boxes.
|
||||
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of the "
|
||||
"atom.");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// size of PSSH box
|
||||
uint32_t pssh_length;
|
||||
if (!reader.Read4(&pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH box size");
|
||||
if (version == 1) {
|
||||
// v1 has additional fields for key IDs. We can skip them.
|
||||
uint32_t num_key_ids;
|
||||
if (!reader.Read4(&num_key_ids)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read num key IDs.");
|
||||
return false;
|
||||
}
|
||||
if (!reader.SkipBytes(num_key_ids * 16)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to skip key IDs.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// size of PSSH data
|
||||
uint32_t data_length;
|
||||
if (!reader.Read4(&data_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->clear();
|
||||
if (!reader.ReadString(output, pssh_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH");
|
||||
if (!reader.ReadString(output, data_length)) {
|
||||
LOGW("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
1405
core/src/license.cpp
1405
core/src/license.cpp
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,10 @@ message License {
|
||||
// Indicates that the license shall be sent for renewal when usage is
|
||||
// started.
|
||||
optional bool renew_with_usage = 11 [default = false];
|
||||
|
||||
// Indicates to client that license renewal and release requests ought to
|
||||
// include ClientIdentification (client_id).
|
||||
optional bool renew_with_client_id = 12 [default = false];
|
||||
}
|
||||
|
||||
message KeyContainer {
|
||||
@@ -167,6 +171,8 @@ message License {
|
||||
optional KeyType type = 4;
|
||||
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
|
||||
optional OutputProtection required_protection = 6;
|
||||
// NOTE: Use of requested_protection is not recommended as it is only
|
||||
// supported on a small number of platforms.
|
||||
optional OutputProtection requested_protection = 7;
|
||||
optional KeyControl key_control = 8;
|
||||
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
|
||||
@@ -174,7 +180,13 @@ message License {
|
||||
// content being decrypted/decoded falls within one of the specified ranges,
|
||||
// the optional required_protections may be applied. Otherwise an error will
|
||||
// be reported.
|
||||
// NOTE: Use of this feature is not recommended, as it is only supported on
|
||||
// a small number of platforms.
|
||||
repeated VideoResolutionConstraint video_resolution_constraints = 10;
|
||||
// Optional flag to indicate the key must only be used if the client
|
||||
// supports anti rollback of the user table. Content provider can query the
|
||||
// client capabilities to determine if the client support this feature.
|
||||
optional bool anti_rollback_usage_table = 11 [default = false];
|
||||
}
|
||||
|
||||
optional LicenseIdentification id = 1;
|
||||
@@ -286,36 +298,6 @@ message SignedMessage {
|
||||
optional RemoteAttestation remote_attestation = 5;
|
||||
}
|
||||
|
||||
// This message is used to pass optional data on initial license issuance.
|
||||
message SessionInit {
|
||||
optional bytes session_id = 1;
|
||||
optional bytes purchase_id = 2;
|
||||
// master_signing_key should be 128 bits in length.
|
||||
optional bytes master_signing_key = 3;
|
||||
// signing_key should be 512 bits in length to be split into two
|
||||
// (server || client) HMAC-SHA256 keys.
|
||||
optional bytes signing_key = 4;
|
||||
optional int64 license_start_time = 5;
|
||||
// Client token for the session. This session is for use by the license
|
||||
// provider, and is akin to a client cookie. It will be copied to
|
||||
// License::provider_client_token, and sent back by the client in
|
||||
// ClientIdentification::provider_client_token in all license requests
|
||||
// thereafter.
|
||||
optional bytes provider_client_token = 6;
|
||||
// Session token for the session. This token is for use by the license
|
||||
// provider, and is akin to a session cookie. It will be copied to
|
||||
// LicenseIdentfication::provider_session_token, and sent back in all
|
||||
// license renewal and release requests for the session thereafter.
|
||||
optional bytes provider_session_token = 7;
|
||||
}
|
||||
|
||||
// This message is used by the server to preserve and restore session state.
|
||||
message SessionState {
|
||||
optional LicenseIdentification license_id = 1;
|
||||
optional bytes signing_key = 2;
|
||||
optional uint32 keybox_system_id = 3;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// certificate_provisioning.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -348,6 +330,9 @@ message ProvisioningRequest {
|
||||
optional bytes nonce = 2;
|
||||
// Options for type of certificate to generate. Optional.
|
||||
optional ProvisioningOptions options = 3;
|
||||
// Stable identifier, unique for each device + application (or origin).
|
||||
// Required if doing per-origin provisioning.
|
||||
optional bytes stable_id = 4;
|
||||
}
|
||||
|
||||
// Provisioning response sent by the provisioning server to client devices.
|
||||
@@ -410,6 +395,7 @@ message ClientIdentification {
|
||||
optional bool video_resolution_constraints = 3 [default = false];
|
||||
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
|
||||
optional uint32 oem_crypto_api_version = 5;
|
||||
optional bool anti_rollback_usage_table = 6 [default = false];
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
|
||||
173
core/src/max_res_engine.cpp
Normal file
173
core/src/max_res_engine.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
// 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
|
||||
@@ -5,7 +5,6 @@
|
||||
// compile time.
|
||||
//
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -15,6 +14,12 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
|
||||
return ::OEMCrypto_OpenSession(session);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
|
||||
return ::OEMCrypto_IsKeyboxValid();
|
||||
}
|
||||
@@ -29,12 +34,6 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
|
||||
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
|
||||
}
|
||||
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
|
||||
return ::OEMCrypto_APIVersion();
|
||||
}
|
||||
@@ -43,8 +42,34 @@ const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
SecurityLevel level, OEMCrypto_HDCP_Capability* current,
|
||||
OEMCrypto_HDCP_Capability* maximum) {
|
||||
return ::OEMCrypto_GetHDCPCapability(current, maximum);
|
||||
}
|
||||
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
|
||||
return ::OEMCrypto_SupportsUsageTable();
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level) {
|
||||
return ::OEMCrypto_IsAntiRollbackHwPresent();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
|
||||
size_t* count) {
|
||||
return ::OEMCrypto_GetNumberOfOpenSessions(count);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
|
||||
size_t* maximum) {
|
||||
return ::OEMCrypto_GetMaxNumberOfSessions(maximum);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
|
||||
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
|
||||
return ::OEMCrypto_CopyBuffer(data_addr, data_length, out_buffer,
|
||||
subsample_flags);
|
||||
}
|
||||
} // namespace wvcdm
|
||||
|
||||
62
core/src/oemcrypto_adapter_static_v10.cpp
Normal file
62
core/src/oemcrypto_adapter_static_v10.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 (v10) which is not available in v9
|
||||
// to allow an older oemcrypto implementation to be linked with CDM.
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability_V9(uint8_t* current,
|
||||
uint8_t* maximum);
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadTestKeybox() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadTestRSAKey() {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_QueryKeyControl(
|
||||
OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length,
|
||||
uint8_t* key_control_block, size_t* key_control_block_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
|
||||
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) {
|
||||
if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
if (maximum == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
uint8_t current_byte, maximum_byte;
|
||||
OEMCryptoResult sts = OEMCrypto_GetHDCPCapability_V9(¤t_byte,
|
||||
&maximum_byte);
|
||||
*current = static_cast<OEMCrypto_HDCP_Capability>(current_byte);
|
||||
*maximum = static_cast<OEMCrypto_HDCP_Capability>(maximum_byte);
|
||||
return sts;
|
||||
}
|
||||
|
||||
extern "C" bool OEMCrypto_IsAntiRollbackHwPresent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(
|
||||
const uint8_t* pst, size_t pst_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* 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.
|
||||
* An implementation should compile either oemcrypto_adapter_dynamic.cpp or
|
||||
* oemcrypto_adapter_static.cpp, but not both.
|
||||
* This version contains shim code to allow an older, version 8 API, oemcrypto,
|
||||
* to be linked with CDM.
|
||||
*
|
||||
******************************************************************************/
|
||||
// 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.
|
||||
//
|
||||
// An implementation should compile either oemcrypto_adapter_dynamic.cpp or
|
||||
// oemcrypto_adapter_static.cpp, but not both.
|
||||
//
|
||||
// Defines APIs introduced in newer version (v9) which is not available in v8
|
||||
// to allow an older oemcrypto implementation to be linked with CDM.
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V8(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
@@ -25,45 +22,6 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_OpenSession(session);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
|
||||
return ::OEMCrypto_IsKeyboxValid();
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_GetDeviceID(deviceID, idLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength,
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
|
||||
}
|
||||
|
||||
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
|
||||
return ::OEMCrypto_APIVersion();
|
||||
}
|
||||
|
||||
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
|
||||
return ::OEMCrypto_SecurityLevel();
|
||||
}
|
||||
|
||||
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
|
||||
return ::OEMCrypto_SupportsUsageTable();
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
@@ -113,4 +71,3 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
|
||||
size_t signature_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}; // namespace wvcdm
|
||||
@@ -13,48 +13,60 @@
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
using video_widevine_server::sdk::License;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
PolicyEngine::PolicyEngine() { Init(new Clock()); }
|
||||
PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
WvCdmEventListener* event_listener,
|
||||
CryptoSession* crypto_session)
|
||||
: license_state_(kLicenseStateInitial),
|
||||
license_start_time_(0),
|
||||
playback_start_time_(0),
|
||||
last_playback_time_(0),
|
||||
last_expiry_time_(0),
|
||||
last_expiry_time_set_(false),
|
||||
next_renewal_time_(0),
|
||||
policy_max_duration_seconds_(0),
|
||||
session_id_(session_id),
|
||||
event_listener_(event_listener),
|
||||
max_res_engine_(new MaxResEngine(crypto_session)),
|
||||
clock_(new Clock) {}
|
||||
|
||||
PolicyEngine::PolicyEngine(Clock* clock) { Init(clock); }
|
||||
PolicyEngine::~PolicyEngine() {}
|
||||
|
||||
PolicyEngine::~PolicyEngine() {
|
||||
if (clock_) delete clock_;
|
||||
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
|
||||
if (keys_status_.find(key_id) == keys_status_.end()) {
|
||||
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
|
||||
b2a_hex(key_id).c_str());
|
||||
return false;
|
||||
}
|
||||
return keys_status_[key_id] == kKeyStatusUsable;
|
||||
}
|
||||
|
||||
void PolicyEngine::Init(Clock* clock) {
|
||||
license_state_ = kLicenseStateInitial;
|
||||
can_decrypt_ = false;
|
||||
license_start_time_ = 0;
|
||||
playback_start_time_ = 0;
|
||||
next_renewal_time_ = 0;
|
||||
policy_max_duration_seconds_ = 0;
|
||||
clock_ = clock;
|
||||
}
|
||||
|
||||
void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
*event_occurred = false;
|
||||
void PolicyEngine::OnTimerEvent() {
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
// License expiration trumps all.
|
||||
if ((IsLicenseDurationExpired(current_time) ||
|
||||
IsPlaybackDurationExpired(current_time)) &&
|
||||
if (IsLicenseOrPlaybackDurationExpired(current_time) &&
|
||||
license_state_ != kLicenseStateExpired) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
can_decrypt_ = false;
|
||||
*event = LICENSE_EXPIRED_EVENT;
|
||||
*event_occurred = true;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
return;
|
||||
}
|
||||
|
||||
max_res_engine_->OnTimerEvent();
|
||||
|
||||
bool renewal_needed = false;
|
||||
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateCanPlay: {
|
||||
if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
|
||||
// HDCP may change, so force a check.
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -71,7 +83,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
case kLicenseStatePending: {
|
||||
if (current_time >= license_start_time_) {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -83,28 +95,46 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
|
||||
default: {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
can_decrypt_ = false;
|
||||
NotifyKeysChange(kKeyStatusInternalError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (renewal_needed) {
|
||||
UpdateRenewalRequest(current_time);
|
||||
*event = LICENSE_RENEWAL_NEEDED_EVENT;
|
||||
*event_occurred = true;
|
||||
if (event_listener_) event_listener_->OnSessionRenewalNeeded(session_id_);
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::SetLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
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;
|
||||
}
|
||||
|
||||
UpdateLicense(license);
|
||||
max_res_engine_->SetLicense(license);
|
||||
}
|
||||
|
||||
void PolicyEngine::SetLicenseForRelease(const License& license) {
|
||||
license_id_.Clear();
|
||||
license_id_.CopyFrom(license.id());
|
||||
policy_.Clear();
|
||||
|
||||
// Expire any old keys.
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
|
||||
UpdateLicense(license);
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
void PolicyEngine::UpdateLicense(const License& license) {
|
||||
if (!license.has_policy()) return;
|
||||
|
||||
if (kLicenseStateExpired == license_state_) {
|
||||
@@ -144,23 +174,22 @@ void PolicyEngine::UpdateLicense(
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
if (!policy_.can_play()) {
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
if (!policy_.can_play() || IsLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
if (IsLicenseDurationExpired(current_time)) return;
|
||||
if (IsPlaybackDurationExpired(current_time)) return;
|
||||
|
||||
// Update state
|
||||
if (current_time >= license_start_time_) {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
} else {
|
||||
license_state_ = kLicenseStatePending;
|
||||
can_decrypt_ = false;
|
||||
NotifyKeysChange(kKeyStatusPending);
|
||||
}
|
||||
NotifyExpirationUpdate();
|
||||
}
|
||||
|
||||
void PolicyEngine::BeginDecryption() {
|
||||
@@ -170,10 +199,12 @@ void PolicyEngine::BeginDecryption() {
|
||||
case kLicenseStateNeedRenewal:
|
||||
case kLicenseStateWaitingLicenseUpdate:
|
||||
playback_start_time_ = clock_->GetCurrentTime();
|
||||
last_playback_time_ = playback_start_time_;
|
||||
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
NotifyExpirationUpdate();
|
||||
break;
|
||||
case kLicenseStateInitial:
|
||||
case kLicenseStatePending:
|
||||
@@ -184,11 +215,27 @@ void PolicyEngine::BeginDecryption() {
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::DecryptionEvent() {
|
||||
last_playback_time_ = clock_->GetCurrentTime();
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
max_res_engine_->SetResolution(width, height);
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifySessionExpiration() {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
}
|
||||
|
||||
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
std::stringstream ss;
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
if (license_state_ == kLicenseStateInitial) return UNKNOWN_ERROR;
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
key_info->clear();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
|
||||
license_id_.type() == video_widevine_server::sdk::STREAMING
|
||||
@@ -210,47 +257,73 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
|
||||
if (playback_start_time_ == 0) return false;
|
||||
|
||||
*seconds_since_started = clock_->GetCurrentTime() - playback_start_time_;
|
||||
return (*seconds_since_started >= 0) ? true : false;
|
||||
}
|
||||
|
||||
bool PolicyEngine::GetSecondsSinceLastPlayed(
|
||||
int64_t* seconds_since_last_played) {
|
||||
if (last_playback_time_ == 0) return false;
|
||||
|
||||
*seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_;
|
||||
return (*seconds_since_last_played >= 0) ? true : false;
|
||||
}
|
||||
|
||||
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time) {
|
||||
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
|
||||
last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0;
|
||||
NotifyExpirationUpdate();
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
license_state_ = kLicenseStateWaitingLicenseUpdate;
|
||||
next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds();
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsLicenseOrPlaybackDurationExpired(int64_t current_time) {
|
||||
int64_t expiry_time =
|
||||
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
|
||||
return (expiry_time == NEVER_EXPIRES) ? false : (expiry_time <= current_time);
|
||||
}
|
||||
|
||||
// For the policy time fields checked in the following methods, a value of 0
|
||||
// indicates that there is no limit to the duration. These methods
|
||||
// will always return false if the value is 0.
|
||||
bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
|
||||
return policy_max_duration_seconds_ &&
|
||||
license_start_time_ + policy_max_duration_seconds_ <= current_time;
|
||||
int64_t PolicyEngine::GetLicenseExpiryTime() {
|
||||
return policy_max_duration_seconds_ > 0
|
||||
? license_start_time_ + policy_max_duration_seconds_
|
||||
: NEVER_EXPIRES;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackExpiryTime() {
|
||||
return (playback_start_time_ > 0 && policy_.playback_duration_seconds() > 0)
|
||||
? (playback_start_time_ + policy_.playback_duration_seconds())
|
||||
: NEVER_EXPIRES;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_max_duration_seconds_) return LLONG_MAX;
|
||||
|
||||
int64_t remaining_time =
|
||||
policy_max_duration_seconds_ + license_start_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0)
|
||||
remaining_time = 0;
|
||||
else if (remaining_time > policy_max_duration_seconds_)
|
||||
remaining_time = policy_max_duration_seconds_;
|
||||
return remaining_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
|
||||
return (policy_.playback_duration_seconds() > 0) && playback_start_time_ &&
|
||||
playback_start_time_ + policy_.playback_duration_seconds() <=
|
||||
current_time;
|
||||
int64_t license_expiry_time = GetLicenseExpiryTime();
|
||||
if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX;
|
||||
if (license_expiry_time < current_time) return 0;
|
||||
return std::min(license_expiry_time - current_time,
|
||||
policy_max_duration_seconds_);
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_.playback_duration_seconds()) return LLONG_MAX;
|
||||
if (0 == playback_start_time_) return policy_.playback_duration_seconds();
|
||||
int64_t playback_expiry_time = GetPlaybackExpiryTime();
|
||||
if (playback_expiry_time == NEVER_EXPIRES) {
|
||||
return (policy_.playback_duration_seconds() != 0)
|
||||
? policy_.playback_duration_seconds()
|
||||
: LLONG_MAX;
|
||||
}
|
||||
|
||||
int64_t remaining_time =
|
||||
policy_.playback_duration_seconds() + playback_start_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0) remaining_time = 0;
|
||||
return remaining_time;
|
||||
if (playback_expiry_time < current_time) return 0;
|
||||
return std::min(playback_expiry_time - current_time,
|
||||
policy_.playback_duration_seconds());
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
|
||||
@@ -271,4 +344,45 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
next_renewal_time_ <= current_time;
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
|
||||
bool keys_changed = false;
|
||||
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_,
|
||||
has_new_usable_key);
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyExpirationUpdate() {
|
||||
int64_t expiry_time =
|
||||
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
|
||||
if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) {
|
||||
last_expiry_time_ = expiry_time;
|
||||
if (event_listener_)
|
||||
event_listener_->OnExpirationUpdate(session_id_, expiry_time);
|
||||
}
|
||||
last_expiry_time_set_ = true;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -108,8 +108,8 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
}
|
||||
|
||||
int padding = 0;
|
||||
if (EVP_EncryptFinal(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
|
||||
&padding) == 0) {
|
||||
if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
|
||||
&padding) == 0) {
|
||||
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
@@ -180,6 +180,64 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
return true;
|
||||
}
|
||||
|
||||
// LogOpenSSLError is a callback from OpenSSL which is called with each error
|
||||
// in the thread's error queue.
|
||||
static int LogOpenSSLError(const char *msg, size_t /* len */, void */* ctx */) {
|
||||
LOGE(" %s", msg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
|
||||
const std::string &signature) {
|
||||
EVP_MD_CTX ctx;
|
||||
EVP_MD_CTX_init(&ctx);
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
|
||||
if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */,
|
||||
pkey) != 1) {
|
||||
LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, kPssSaltLength) != 1) {
|
||||
LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) {
|
||||
LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (EVP_DigestVerifyFinal(
|
||||
&ctx, const_cast<uint8_t *>(
|
||||
reinterpret_cast<const uint8_t *>(signature.data())),
|
||||
signature.size()) != 1) {
|
||||
LOGE(
|
||||
"EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad "
|
||||
"signature.)");
|
||||
goto err;
|
||||
}
|
||||
|
||||
EVP_MD_CTX_cleanup(&ctx);
|
||||
return true;
|
||||
|
||||
err:
|
||||
ERR_print_errors_cb(LogOpenSSLError, NULL);
|
||||
EVP_MD_CTX_cleanup(&ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
const std::string& signature) {
|
||||
if (serialized_key_.empty()) {
|
||||
@@ -190,50 +248,25 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
LOGE("RsaPublicKey::VerifySignature: signed message is empty");
|
||||
return false;
|
||||
}
|
||||
RSA* key = GetKey(serialized_key_);
|
||||
if (key == NULL) {
|
||||
RSA* rsa_key = GetKey(serialized_key_);
|
||||
if (rsa_key == NULL) {
|
||||
// Error already logged by GetKey.
|
||||
return false;
|
||||
}
|
||||
|
||||
int rsa_size = RSA_size(key);
|
||||
if (static_cast<int>(signature.size()) != rsa_size) {
|
||||
LOGE(
|
||||
"RsaPublicKey::VerifySignature: message signature is of the wrong "
|
||||
"size (expected %d, actual %d)",
|
||||
rsa_size, signature.size());
|
||||
FreeKey(key);
|
||||
EVP_PKEY *pkey = EVP_PKEY_new();
|
||||
if (pkey == NULL ||
|
||||
EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) {
|
||||
FreeKey(rsa_key);
|
||||
LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY");
|
||||
return false;
|
||||
}
|
||||
FreeKey(rsa_key);
|
||||
|
||||
// Decrypt the signature.
|
||||
std::string padded_digest(signature.size(), 0);
|
||||
if (RSA_public_decrypt(
|
||||
signature.size(),
|
||||
const_cast<unsigned char*>(
|
||||
reinterpret_cast<const unsigned char*>(signature.data())),
|
||||
reinterpret_cast<unsigned char*>(&padded_digest[0]), key,
|
||||
RSA_NO_PADDING) != rsa_size) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
FreeKey(key);
|
||||
return false;
|
||||
}
|
||||
const bool ok = VerifyPSSSignature(pkey, message, signature);
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
// Hash the message using SHA1.
|
||||
std::string message_digest(SHA_DIGEST_LENGTH, 0);
|
||||
SHA1(reinterpret_cast<const unsigned char*>(message.data()), message.size(),
|
||||
reinterpret_cast<unsigned char*>(&message_digest[0]));
|
||||
|
||||
// Verify PSS padding.
|
||||
if (RSA_verify_PKCS1_PSS(
|
||||
key, reinterpret_cast<const unsigned char*>(message_digest.data()),
|
||||
EVP_sha1(),
|
||||
reinterpret_cast<const unsigned char*>(padded_digest.data()),
|
||||
kPssSaltLength) == 0) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
FreeKey(key);
|
||||
if (!ok) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA verify failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "log.h"
|
||||
#include "properties_configuration.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace {
|
||||
@@ -12,31 +12,18 @@ namespace wvcdm {
|
||||
bool Properties::oem_crypto_use_secure_buffers_;
|
||||
bool Properties::oem_crypto_use_fifo_;
|
||||
bool Properties::oem_crypto_use_userspace_buffers_;
|
||||
bool Properties::oem_crypto_require_usage_tables_;
|
||||
bool Properties::use_certificates_as_identification_;
|
||||
bool Properties::security_level_path_backward_compatibility_support_;
|
||||
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
||||
|
||||
void Properties::Init() {
|
||||
oem_crypto_use_secure_buffers_ = kPropertyOemCryptoUseSecureBuffers;
|
||||
oem_crypto_use_fifo_ = kPropertyOemCryptoUseFifo;
|
||||
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
|
||||
oem_crypto_require_usage_tables_ = kPropertyOemCryptoRequireUsageTable;
|
||||
use_certificates_as_identification_ =
|
||||
kPropertyUseCertificatesAsIdentification;
|
||||
security_level_path_backward_compatibility_support_ =
|
||||
kSecurityLevelPathBackwardCompatibilitySupport;
|
||||
session_property_set_.reset(new CdmClientPropertySetMap());
|
||||
}
|
||||
|
||||
bool Properties::AddSessionPropertySet(
|
||||
const CdmSessionId& session_id, const CdmClientPropertySet* property_set) {
|
||||
const CdmSessionId& session_id, CdmClientPropertySet* property_set) {
|
||||
if (NULL == session_property_set_.get()) {
|
||||
return false;
|
||||
}
|
||||
std::pair<CdmClientPropertySetMap::iterator, bool> result =
|
||||
session_property_set_->insert(
|
||||
std::pair<const CdmSessionId, const CdmClientPropertySet*>(
|
||||
std::pair<const CdmSessionId, CdmClientPropertySet*>(
|
||||
session_id, property_set));
|
||||
return result.second;
|
||||
}
|
||||
@@ -48,10 +35,10 @@ bool Properties::RemoveSessionPropertySet(const CdmSessionId& session_id) {
|
||||
return (1 == session_property_set_->erase(session_id));
|
||||
}
|
||||
|
||||
const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
|
||||
CdmClientPropertySet* Properties::GetCdmClientPropertySet(
|
||||
const CdmSessionId& session_id) {
|
||||
if (NULL != session_property_set_.get()) {
|
||||
CdmClientPropertySetMap::const_iterator it =
|
||||
CdmClientPropertySetMap::iterator it =
|
||||
session_property_set_->find(session_id);
|
||||
if (it != session_property_set_->end()) {
|
||||
return it->second;
|
||||
@@ -60,14 +47,14 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Properties::GetSecurityLevel(const CdmSessionId& session_id,
|
||||
std::string* security_level) {
|
||||
bool Properties::GetApplicationId(const CdmSessionId& session_id,
|
||||
std::string* app_id) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
return false;
|
||||
}
|
||||
*security_level = property_set->security_level();
|
||||
*app_id = property_set->app_id();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -82,6 +69,17 @@ bool Properties::GetServiceCertificate(const CdmSessionId& session_id,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::SetServiceCertificate(const CdmSessionId& session_id,
|
||||
const std::string& service_certificate) {
|
||||
CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
return false;
|
||||
}
|
||||
property_set->set_service_certificate(service_certificate);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <modp_b64w.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "modp_b64w.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
@@ -43,8 +44,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
unsigned char lsb = 0; // least significant 4 bits
|
||||
if (!CharToDigit(byte[i * 2], &msb) ||
|
||||
!CharToDigit(byte[i * 2 + 1], &lsb)) {
|
||||
LOGE("Invalid hex value %c%c at index %d",
|
||||
byte[i * 2], byte[i * 2 + 1], i);
|
||||
LOGE("Invalid hex value %c%c at index %d", byte[i * 2], byte[i * 2 + 1],
|
||||
i);
|
||||
return array;
|
||||
}
|
||||
array.push_back((msb << 4) | lsb);
|
||||
@@ -56,8 +57,9 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
|
||||
// dump the string with the label.
|
||||
std::vector<uint8_t> a2b_hex(const std::string& label,
|
||||
const std::string& byte) {
|
||||
std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]"
|
||||
<< std::endl << std::endl;
|
||||
std::cout << std::endl
|
||||
<< "[[DUMP: " << label << " ]= \"" << byte << "\"]" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
return a2b_hex(byte);
|
||||
}
|
||||
@@ -173,15 +175,15 @@ int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order)
|
||||
int64_t number;
|
||||
} mixed;
|
||||
mixed.number = 1;
|
||||
if (mixed.array[0] == 1) {
|
||||
mixed.number = x; // Little Endian.
|
||||
if (mixed.array[0] == 1) { // Little Endian.
|
||||
mixed.number = x;
|
||||
uint32_t temp = mixed.array[0];
|
||||
mixed.array[0] = htonl(mixed.array[1]);
|
||||
mixed.array[1] = htonl(temp);
|
||||
return mixed.number;
|
||||
} else {
|
||||
return x; // Big Endian.
|
||||
} else { // Big Endian.
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
// Test vectors as suggested by http://tools.ietf.org/html/rfc4648#section-10
|
||||
@@ -50,9 +53,7 @@ const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
|
||||
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
|
||||
make_pair(&kTestData, &kB64TestData)};
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
namespace wvcdm {
|
||||
} // namespace
|
||||
|
||||
class Base64EncodeDecodeTest
|
||||
: public ::testing::TestWithParam<
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
// This is because we need a valid RSA certificate, and will attempt to connect
|
||||
// to the provisioning server to request one if we don't.
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "cdm_engine.h"
|
||||
#include "config_test_env.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "initialization_data.h"
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
@@ -23,6 +21,8 @@
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
// Http OK response code.
|
||||
const int kHttpOk = 200;
|
||||
@@ -30,29 +30,45 @@ const int kHttpOk = 200;
|
||||
// Default license server, can be configured using --server command line option
|
||||
// Default key id (pssh), can be configured using --keyid command line option
|
||||
std::string g_client_auth;
|
||||
wvcdm::KeyId g_key_id_pssh;
|
||||
wvcdm::KeyId g_key_id_unwrapped;
|
||||
wvcdm::CdmKeySystem g_key_system;
|
||||
KeyId g_key_id_pssh;
|
||||
KeyId g_key_id_unwrapped;
|
||||
CdmKeySystem g_key_system;
|
||||
std::string g_license_server;
|
||||
wvcdm::KeyId g_wrong_key_id;
|
||||
KeyId g_wrong_key_id;
|
||||
|
||||
const std::string kCencMimeType = "video/mp4";
|
||||
const std::string kWebmMimeType = "video/webm";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class WvCdmEngineTest : public testing::Test {
|
||||
public:
|
||||
static void SetUpTestCase() {
|
||||
ConfigTestEnv config(kContentProtectionServer);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
g_key_system.assign(config.key_system());
|
||||
g_wrong_key_id.assign(config.wrong_key_id());
|
||||
g_license_server.assign(config.license_server());
|
||||
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);
|
||||
g_key_id_unwrapped = extractor.data();
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
CdmResponseType status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
CdmResponseType status =
|
||||
cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
|
||||
NULL /* forced_session_id */, &session_id_);
|
||||
if (status == NEED_PROVISIONING) {
|
||||
Provision();
|
||||
status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
status = cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
|
||||
NULL /* forced_session_id */,
|
||||
&session_id_);
|
||||
}
|
||||
ASSERT_EQ(NO_ERROR, status);
|
||||
ASSERT_NE("", session_id_) << "Could not open CDM session.";
|
||||
ASSERT_TRUE(cdm_engine_.IsOpenSession(session_id_));
|
||||
}
|
||||
|
||||
virtual void TearDown() { cdm_engine_.CloseSession(session_id_); }
|
||||
@@ -64,19 +80,17 @@ class WvCdmEngineTest : public testing::Test {
|
||||
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));
|
||||
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
|
||||
cert_type, cert_authority, EMPTY_ORIGIN,
|
||||
&prov_request, &provisioning_server_url));
|
||||
UrlRequest url_request(provisioning_server_url);
|
||||
url_request.PostCertRequestInQueryString(prov_request);
|
||||
std::string message;
|
||||
bool ok = url_request.GetResponse(&message);
|
||||
EXPECT_TRUE(ok);
|
||||
ASSERT_EQ(NO_ERROR,
|
||||
cdm_engine_.HandleProvisioningResponse(message,
|
||||
&cert, &wrapped_key));
|
||||
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(EMPTY_ORIGIN,
|
||||
message, &cert,
|
||||
&wrapped_key));
|
||||
}
|
||||
|
||||
void GenerateKeyRequest(const std::string& key_id,
|
||||
@@ -87,10 +101,12 @@ class WvCdmEngineTest : public testing::Test {
|
||||
|
||||
InitializationData init_data(init_data_type_string, key_id);
|
||||
|
||||
EXPECT_EQ(KEY_MESSAGE,
|
||||
cdm_engine_.GenerateKeyRequest(
|
||||
session_id_, key_set_id, init_data, kLicenseTypeStreaming,
|
||||
app_parameters, &key_msg_, &server_url, NULL));
|
||||
CdmKeyRequestType key_request_type;
|
||||
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);
|
||||
}
|
||||
|
||||
void GenerateRenewalRequest() {
|
||||
@@ -144,14 +160,14 @@ class WvCdmEngineTest : public testing::Test {
|
||||
const std::string& client_auth) {
|
||||
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
||||
CdmKeySetId key_set_id;
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED,
|
||||
EXPECT_EQ(KEY_ADDED,
|
||||
cdm_engine_.AddKey(session_id_, resp, &key_set_id));
|
||||
}
|
||||
|
||||
void VerifyRenewalKeyResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
std::string resp = GetKeyRequestResponse(server_url, client_auth);
|
||||
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
|
||||
EXPECT_EQ(KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
|
||||
}
|
||||
|
||||
CdmEngine cdm_engine_;
|
||||
@@ -206,94 +222,3 @@ TEST_F(WvCdmEngineTest, LicenseRenewal) {
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
wvcdm::InitLogging(argc, argv);
|
||||
|
||||
wvcdm::ConfigTestEnv config(wvcdm::kContentProtectionServer);
|
||||
g_client_auth.assign(config.client_auth());
|
||||
g_key_system.assign(config.key_system());
|
||||
g_wrong_key_id.assign(config.wrong_key_id());
|
||||
|
||||
// The following variables are configurable through command line options.
|
||||
g_license_server.assign(config.license_server());
|
||||
g_key_id_pssh.assign(config.key_id());
|
||||
std::string license_server(g_license_server);
|
||||
|
||||
int show_usage = 0;
|
||||
static const struct option long_options[] = {
|
||||
{"keyid", required_argument, NULL, 'k'},
|
||||
{"server", required_argument, NULL, 's'},
|
||||
{NULL, 0, NULL, '\0'}};
|
||||
|
||||
int option_index = 0;
|
||||
int opt = 0;
|
||||
while ((opt = getopt_long(argc, argv, "k:s:v", long_options,
|
||||
&option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'k': {
|
||||
g_key_id_pssh.clear();
|
||||
g_key_id_pssh.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
g_license_server.clear();
|
||||
g_license_server.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
// This option _may_ have already been consumed by wvcdm::InitLogging()
|
||||
// above, depending on the platform-specific logging implementation.
|
||||
// We only tell getopt about it so that it is not an error. We ignore
|
||||
// the option here when seen.
|
||||
// TODO: Stop passing argv to InitLogging, and instead set the log
|
||||
// level here through the logging API. We should keep all command-line
|
||||
// parsing at the application level, rather than split between various
|
||||
// apps and various platform-specific logging implementations.
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
show_usage = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (show_usage) {
|
||||
std::cout << std::endl;
|
||||
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
|
||||
std::cout << " enclose multiple arguments in '' when using adb shell"
|
||||
<< std::endl;
|
||||
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'"
|
||||
<< std::endl << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --server=<server_url>";
|
||||
std::cout
|
||||
<< "configure the license server url, please include http[s] in the url"
|
||||
<< std::endl;
|
||||
std::cout << std::setw(30) << std::left << " ";
|
||||
std::cout << "default: " << license_server << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
|
||||
std::cout << "configure the key id or pssh, in hex format" << std::endl;
|
||||
std::cout << std::setw(30) << std::left << " default keyid:";
|
||||
std::cout << g_key_id_pssh << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "Server: " << g_license_server << std::endl;
|
||||
std::cout << "KeyID: " << g_key_id_pssh << std::endl << std::endl;
|
||||
|
||||
g_key_id_pssh = wvcdm::a2bs_hex(g_key_id_pssh);
|
||||
config.set_license_server(g_license_server);
|
||||
config.set_key_id(g_key_id_pssh);
|
||||
|
||||
// Extract the key ID from the PSSH box.
|
||||
wvcdm::InitializationData extractor(wvcdm::CENC_INIT_DATA_FORMAT,
|
||||
g_key_id_pssh);
|
||||
g_key_id_unwrapped = extractor.data();
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "cdm_session.h"
|
||||
#include "crypto_key.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "test_printers.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
const std::string kToken = wvcdm::a2bs_hex(
|
||||
|
||||
const std::string kToken = a2bs_hex(
|
||||
"0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02"
|
||||
"82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77"
|
||||
"93BDA057E6BC3E0CA2348BC6831E03609445CA4D418CB98EAC98FFC87AB2364CE76BA26BEE"
|
||||
@@ -45,7 +50,7 @@ const std::string kToken = wvcdm::a2bs_hex(
|
||||
"8CD5A9DF6E3D3A99B806F6D60991358C5BE77117D4F3168F3348E9A048539F892F4D783152"
|
||||
"C7A8095224AA56B78C5CF7BD1AB1B179C0C0D11E3C3BAC84C141A00191321E3ACC17242E68"
|
||||
"3C");
|
||||
const std::string kWrappedKey = wvcdm::a2bs_hex(
|
||||
const std::string kWrappedKey = a2bs_hex(
|
||||
"3B84252DD84F1A710365014A114507FFFA3DD404625D61D1EEC7C3A39D72CB8D9318ADE9DA"
|
||||
"05D69F9776DAFDA49A97BC30E84CA275925DFD98CA04F7DB23465103A224852192DE232902"
|
||||
"99FF82024F5CCA7716ACA9BE0B56348BA16B9E3136D73789C842CB2ECA4820DDAAF59CCB9B"
|
||||
@@ -82,9 +87,42 @@ const std::string kWrappedKey = wvcdm::a2bs_hex(
|
||||
"33EF70621A98184DDAB5E14BC971CF98CF6C91A37FFA83B00AD3BCABBAAB2DEF1C52F43003"
|
||||
"E74C92B44F9205D22262FB47948654229DE1920F8EDF96A19A88A1CA1552F8856FB4CBF83B"
|
||||
"AA3348419159D207F65FCE9C1A500C6818");
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
const std::string kTestOrigin = "com.google";
|
||||
|
||||
class MockDeviceFiles : public DeviceFiles {
|
||||
public:
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD3(RetrieveCertificate, bool(const std::string&, std::string*,
|
||||
std::string*));
|
||||
};
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD1(GetToken, bool(std::string*));
|
||||
MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel());
|
||||
MOCK_METHOD0(Open, CdmResponseType());
|
||||
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
|
||||
MOCK_METHOD1(LoadCertificatePrivateKey, bool(std::string&));
|
||||
MOCK_METHOD0(DeleteAllUsageReports, CdmResponseType());
|
||||
};
|
||||
|
||||
class MockPolicyEngine : public PolicyEngine {
|
||||
public:
|
||||
MockPolicyEngine() : PolicyEngine("mock_session_id", NULL, NULL) {}
|
||||
|
||||
// Leaving a place-holder for when PolicyEngine methods need to be mocked
|
||||
};
|
||||
|
||||
class MockCdmLicense : public CdmLicense {
|
||||
public:
|
||||
MockCdmLicense(const CdmSessionId& session_id)
|
||||
: CdmLicense(session_id) {}
|
||||
|
||||
MOCK_METHOD3(Init, bool(const std::string&, CryptoSession*, PolicyEngine*));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// gmock methods
|
||||
using ::testing::_;
|
||||
@@ -95,53 +133,22 @@ using ::testing::SetArgPointee;
|
||||
using ::testing::Sequence;
|
||||
using ::testing::StrEq;
|
||||
|
||||
class MockDeviceFiles : public DeviceFiles {
|
||||
public:
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*));
|
||||
};
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD1(GetToken, bool(std::string*));
|
||||
MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel());
|
||||
MOCK_METHOD0(Open, CdmResponseType());
|
||||
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
|
||||
MOCK_METHOD1(LoadCertificatePrivateKey, bool(std::string&));
|
||||
};
|
||||
|
||||
class MockPolicyEngine : public PolicyEngine {
|
||||
public:
|
||||
// Leaving a place holder for when PolicyEngine methods need to be mocked
|
||||
};
|
||||
|
||||
class MockCdmLicense : public CdmLicense {
|
||||
public:
|
||||
MOCK_METHOD3(Init, bool(const std::string&, CryptoSession*, PolicyEngine*));
|
||||
};
|
||||
|
||||
class CdmSessionTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
license_parser_ = new MockCdmLicense();
|
||||
cdm_session_.reset(new CdmSession(NULL, kTestOrigin, NULL, NULL));
|
||||
// Inject testing mocks.
|
||||
license_parser_ = new MockCdmLicense(cdm_session_->session_id());
|
||||
cdm_session_->set_license_parser(license_parser_);
|
||||
crypto_session_ = new MockCryptoSession();
|
||||
cdm_session_->set_crypto_session(crypto_session_);
|
||||
policy_engine_ = new MockPolicyEngine();
|
||||
cdm_session_->set_policy_engine(policy_engine_);
|
||||
file_handle_ = new MockDeviceFiles();
|
||||
cdm_session_->set_file_handle(file_handle_);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
if (cdm_session_) delete cdm_session_;
|
||||
}
|
||||
|
||||
void CreateSession() { CreateSession(NULL); }
|
||||
|
||||
void CreateSession(const CdmClientPropertySet* cdm_client_property_set) {
|
||||
cdm_session_ =
|
||||
new CdmSession(license_parser_, crypto_session_, policy_engine_,
|
||||
file_handle_, cdm_client_property_set);
|
||||
}
|
||||
|
||||
CdmSession* cdm_session_;
|
||||
scoped_ptr<CdmSession> cdm_session_;
|
||||
MockCdmLicense* license_parser_;
|
||||
MockCryptoSession* crypto_session_;
|
||||
MockPolicyEngine* policy_engine_;
|
||||
@@ -158,8 +165,9 @@ 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(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
@@ -170,7 +178,6 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
@@ -192,7 +199,6 @@ TEST_F(CdmSessionTest, InitWithKeybox) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(false);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
@@ -206,8 +212,9 @@ 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(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
@@ -218,9 +225,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
|
||||
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init());
|
||||
ASSERT_NE(NO_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
TEST_F(CdmSessionTest, InitFailCryptoError) {
|
||||
@@ -230,7 +236,6 @@ TEST_F(CdmSessionTest, InitFailCryptoError) {
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init());
|
||||
}
|
||||
|
||||
@@ -244,13 +249,13 @@ 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(NotNull(), NotNull()))
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
|
||||
NotNull()))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
CreateSession();
|
||||
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init());
|
||||
}
|
||||
|
||||
} // wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
#include "config_test_env.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kWidevineKeySystem = "com.widevine.alpha";
|
||||
|
||||
// Content Protection license server data
|
||||
// Content Protection license server (UAT) data
|
||||
// For staging server replace url with http://wv-staging-proxy.appspot.com/proxy
|
||||
const std::string kCpLicenseServer = "http://widevine-proxy.appspot.com/proxy";
|
||||
const std::string kCpClientAuth = "";
|
||||
const std::string kCpKeyId =
|
||||
@@ -26,7 +30,7 @@ const std::string kCpOfflineKeyId =
|
||||
"00000020" // pssh data size
|
||||
// pssh data:
|
||||
"08011a0d7769646576696e655f746573"
|
||||
"74220d6f66666c696e655f636c697031";
|
||||
"74220d6f66666c696e655f636c697032";
|
||||
|
||||
// Google Play license server data
|
||||
const std::string kGpLicenseServer =
|
||||
@@ -72,17 +76,15 @@ const std::string kProductionProvisioningServerUrl =
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
|
||||
{wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
|
||||
const ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
|
||||
{kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
|
||||
kGpOfflineKeyId},
|
||||
{wvcdm::kContentProtectionServer, kCpLicenseServer, kCpClientAuth, kCpKeyId,
|
||||
{kContentProtectionServer, kCpLicenseServer, kCpClientAuth, kCpKeyId,
|
||||
kCpOfflineKeyId},
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) { Init(server_id); }
|
||||
|
||||
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) {
|
||||
|
||||
@@ -59,6 +59,6 @@ class ConfigTestEnv {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_TEST_CONFIG_TEST_ENV_H_
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,13 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "device_files.h"
|
||||
#include "file_store.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "properties.h"
|
||||
#include "test_vectors.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
const std::string kTestDirName = "test_dir";
|
||||
const std::string kTestFileName = "test.txt";
|
||||
@@ -14,8 +16,6 @@ const std::string kTestFileNameExt = ".txt";
|
||||
const std::string kWildcard = "*";
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class FileTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() { CreateTestDir(); }
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function to tokenize a string. This makes it easier to avoid silly
|
||||
@@ -99,8 +101,6 @@ bool SocketWait(int fd, bool for_read, int timeout_in_ms) {
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Parses the URL and extracts all relevant information.
|
||||
// static
|
||||
bool HttpSocket::ParseUrl(const std::string& url, std::string* scheme,
|
||||
|
||||
@@ -51,6 +51,6 @@ class HttpSocket {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(HttpSocket);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_TEST_HTTP_SOCKET_H_
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <errno.h>
|
||||
#include "gtest/gtest.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include "http_socket.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "log.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "string_conversions.h"
|
||||
#include "url_request.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
// Arbitrary URL for tests.
|
||||
const std::string kHttpsTestServer("https://www.google.com");
|
||||
@@ -21,8 +23,6 @@ const int kHttpBufferSize = 4096;
|
||||
const int kTimeout = 3000;
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class HttpSocketTest : public testing::Test {
|
||||
public:
|
||||
HttpSocketTest() {}
|
||||
@@ -159,7 +159,7 @@ ParseUrlTests parse_url_tests[] = {
|
||||
8888, // port
|
||||
"/", // path
|
||||
},
|
||||
{NULL} // list terminator
|
||||
{NULL, NULL, false, NULL, 0, NULL} // list terminator
|
||||
};
|
||||
|
||||
TEST_F(HttpSocketTest, ParseUrlTest) {
|
||||
@@ -204,6 +204,8 @@ TEST_F(HttpSocketTest, RoundTripTest) {
|
||||
} // namespace wvcdm
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
using namespace wvcdm;
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
std::string temp;
|
||||
|
||||
164
core/test/initialization_data_unittest.cpp
Normal file
164
core/test/initialization_data_unittest.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "initialization_data.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
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kWidevinePssh = a2bs_hex(
|
||||
// Widevine PSSH box
|
||||
"00000042" // atom size
|
||||
"70737368" // atom type="pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
const std::string kWidevinePsshFirst = a2bs_hex(
|
||||
// first PSSH box, Widevine
|
||||
"00000042" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031"
|
||||
|
||||
// second PSSH box, Playready [1]
|
||||
"00000028" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"9a04f07998404286ab92e65be0885f95" // system id (PlayReady)
|
||||
"00000008" // data size
|
||||
// arbitrary data:
|
||||
"0102030405060708");
|
||||
|
||||
const std::string kWidevinePsshAfterV0Pssh = a2bs_hex(
|
||||
// first PSSH box, Playready [1]
|
||||
"00000028" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"9a04f07998404286ab92e65be0885f95" // system id (PlayReady)
|
||||
"00000008" // data size
|
||||
// arbitrary data:
|
||||
"0102030405060708"
|
||||
|
||||
// second PSSH box, Widevine
|
||||
"00000042" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
const std::string kWidevinePsshAfterNonZeroFlags = a2bs_hex(
|
||||
// first PSSH box, Playready [1]
|
||||
"00000028" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00abcdef" // v0, flags=abcdef
|
||||
"9a04f07998404286ab92e65be0885f95" // system id (PlayReady)
|
||||
"00000008" // data size
|
||||
// arbitrary data:
|
||||
"0102030405060708"
|
||||
|
||||
// second PSSH box, Widevine
|
||||
"00000042" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
const std::string kWidevinePsshAfterV1Pssh = a2bs_hex(
|
||||
// first PSSH box, generic CENC [2]
|
||||
"00000044" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"01000000" // v1, flags=0
|
||||
"1077efecc0b24d02ace33c1e52e2fb4b" // system id (generic CENC)
|
||||
"00000002" // key ID count
|
||||
"30313233343536373839303132333435" // key ID="0123456789012345"
|
||||
"38393031323334354142434445464748" // key ID="ABCDEFGHIJKLMNOP"
|
||||
"00000000" // data size=0
|
||||
|
||||
// second PSSH box, Widevine
|
||||
"00000042" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
const std::string kWidevineV1Pssh = a2bs_hex(
|
||||
// Widevine PSSH box, v1 format
|
||||
"00000044" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"01000000" // v1, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000002" // key ID count
|
||||
"30313233343536373839303132333435" // key ID="0123456789012345"
|
||||
"38393031323334354142434445464748" // key ID="ABCDEFGHIJKLMNOP"
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
const std::string kOtherBoxFirst = a2bs_hex(
|
||||
// first box, not a PSSH box
|
||||
"00000018" // atom size
|
||||
"77686174" // atom type "what"
|
||||
"deadbeefdeadbeefdeadbeefdeadbeef" // garbage box data
|
||||
|
||||
// second box, a Widevine PSSH box
|
||||
"00000042" // atom size
|
||||
"70737368" // atom type "pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
const std::string kZeroSizedPsshBox = a2bs_hex(
|
||||
// Widevine PSSH box
|
||||
"00000000" // atom size (whole buffer)
|
||||
"70737368" // atom type="pssh"
|
||||
"00000000" // v0, flags=0
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // system id (Widevine)
|
||||
"00000022" // data size
|
||||
// data:
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
|
||||
class InitializationDataTest : public ::testing::TestWithParam<std::string> {};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_P(InitializationDataTest, Parse) {
|
||||
InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, GetParam());
|
||||
EXPECT_FALSE(init_data.IsEmpty());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ParsePssh, InitializationDataTest,
|
||||
::testing::Values(
|
||||
kWidevinePssh,
|
||||
kWidevinePsshFirst,
|
||||
kWidevinePsshAfterV0Pssh,
|
||||
kWidevinePsshAfterNonZeroFlags,
|
||||
kWidevinePsshAfterV1Pssh,
|
||||
kWidevineV1Pssh,
|
||||
kOtherBoxFirst,
|
||||
kZeroSizedPsshBox
|
||||
));
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
static const std::string kTwoBlankLines("\r\n\r\n");
|
||||
namespace {
|
||||
const std::string kTwoBlankLines("\r\n\r\n");
|
||||
} // namespace
|
||||
|
||||
size_t LicenseRequest::FindHeaderEndPosition(
|
||||
const std::string& response) const {
|
||||
|
||||
@@ -25,6 +25,6 @@ class LicenseRequest {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(LicenseRequest);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_TEST_LICENSE_REQUEST_H_
|
||||
|
||||
@@ -1,36 +1,39 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "license.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "initialization_data.h"
|
||||
#include "license.h"
|
||||
#include "policy_engine.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kAesBlockSize = 16;
|
||||
const std::string kAesKey = wvcdm::a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kAesIv = wvcdm::a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kCencInitDataHdr = wvcdm::a2bs_hex(
|
||||
const std::string kAesKey = a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kAesIv = a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kCencInitDataHdr = a2bs_hex(
|
||||
"00000042" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000022"); // pssh data size
|
||||
const std::string kCencPssh = wvcdm::a2bs_hex(
|
||||
const std::string kCencPssh = a2bs_hex(
|
||||
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
|
||||
const std::string kCdmSessionId = "sid2";
|
||||
const std::string kCryptoSessionId = "id2";
|
||||
const std::string kCryptoRequestId = wvcdm::a2bs_hex(
|
||||
const std::string kCryptoRequestId = a2bs_hex(
|
||||
"4341444542353737444337393044394330313030303030303030303030303030");
|
||||
const uint32_t kNonce = 0x49e81305;
|
||||
const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013
|
||||
const std::string kToken = wvcdm::a2bs_hex(
|
||||
const std::string kToken = a2bs_hex(
|
||||
"0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02"
|
||||
"82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77"
|
||||
"93BDA057E6BC3E0CA2348BC6831E03609445CA4D418CB98EAC98FFC87AB2364CE76BA26BEE"
|
||||
@@ -66,7 +69,7 @@ const std::string kToken = wvcdm::a2bs_hex(
|
||||
"8CD5A9DF6E3D3A99B806F6D60991358C5BE77117D4F3168F3348E9A048539F892F4D783152"
|
||||
"C7A8095224AA56B78C5CF7BD1AB1B179C0C0D11E3C3BAC84C141A00191321E3ACC17242E68"
|
||||
"3C");
|
||||
const std::string kLicenseRequestSignature = wvcdm::a2bs_hex(
|
||||
const std::string kLicenseRequestSignature = a2bs_hex(
|
||||
"4A560ACFED04787BE0D29D7396234FA2E11D6DD0B22F87FD77AEAEDAA6C8FE54AD9859AE4E"
|
||||
"C9F12BCB947892D906DAEC1AD78CABD6F9D479CCF91AF5587DB6FC29CBEBF9C338BAF17790"
|
||||
"90980B1F3333BC901CDBF877490C7B85DB2BF9BC559C98450C6F1E8B2E192959F59CC53BD4"
|
||||
@@ -74,30 +77,13 @@ const std::string kLicenseRequestSignature = wvcdm::a2bs_hex(
|
||||
"8D24103EB15C63C227A0D57A9D90F5A409D2D55147EE10A35AE291D2D725C7F161FF827221"
|
||||
"9AE18B91516E0CDD0B581590DDDEA2A2527E2C9ABA273629B586A9D22D451A827E332CFC3E"
|
||||
"9BEDB6CF3D8713F9E11675DF1F5DB9038DBBECAB9D1683F8722CAF6E18EC8C04AEE5");
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::LicenseRequest;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
|
||||
// gmock methods
|
||||
using ::testing::_;
|
||||
using ::testing::Eq;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD0(IsOpen, bool());
|
||||
MOCK_METHOD1(GenerateRequestId, bool(std::string*));
|
||||
MOCK_METHOD1(UsageInformationSupport, bool(bool*));
|
||||
MOCK_METHOD2(GetHdcpCapabilities,
|
||||
bool(OemCryptoHdcpVersion*, OemCryptoHdcpVersion*));
|
||||
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
|
||||
MOCK_METHOD1(GetApiVersion, bool(uint32_t*));
|
||||
MOCK_METHOD1(GenerateNonce, bool(uint32_t*));
|
||||
MOCK_METHOD3(PrepareRequest, bool(const std::string&, bool, std::string*));
|
||||
@@ -105,6 +91,8 @@ class MockCryptoSession : public CryptoSession {
|
||||
|
||||
class MockPolicyEngine : public PolicyEngine {
|
||||
public:
|
||||
MockPolicyEngine(CryptoSession* crypto)
|
||||
: PolicyEngine("mock_session_id", NULL, crypto) {}
|
||||
};
|
||||
|
||||
class MockClock : public Clock {
|
||||
@@ -120,6 +108,21 @@ class MockInitializationData : public InitializationData {
|
||||
MOCK_METHOD0(is_cenc, bool());
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Protobuf generated classes
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::LicenseRequest;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
|
||||
// gmock methods
|
||||
using ::testing::_;
|
||||
using ::testing::Eq;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
|
||||
class CdmLicenseTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
@@ -127,7 +130,7 @@ class CdmLicenseTest : public ::testing::Test {
|
||||
crypto_session_ = new MockCryptoSession();
|
||||
init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT,
|
||||
kCencInitDataHdr + kCencPssh);
|
||||
policy_engine_ = new MockPolicyEngine();
|
||||
policy_engine_ = new MockPolicyEngine(crypto_session_);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
@@ -139,7 +142,7 @@ class CdmLicenseTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
void CreateCdmLicense() {
|
||||
cdm_license_ = new CdmLicense(clock_);
|
||||
cdm_license_ = new CdmLicense(kCdmSessionId, clock_);
|
||||
clock_ = NULL;
|
||||
}
|
||||
|
||||
@@ -154,7 +157,7 @@ TEST_F(CdmLicenseTest, InitSuccess) {
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
|
||||
CreateCdmLicense();
|
||||
ASSERT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
|
||||
EXPECT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, InitFail_EmptyToken) {
|
||||
@@ -176,22 +179,22 @@ TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) {
|
||||
|
||||
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
bool usage_information_support = true;
|
||||
CryptoSession::OemCryptoHdcpVersion current_hdcp_version =
|
||||
CryptoSession::kOemCryptoNoHdcpDeviceAttached;
|
||||
CryptoSession::OemCryptoHdcpVersion max_hdcp_version =
|
||||
CryptoSession::kOemCryptoHdcpVersion2_1;
|
||||
CryptoSession::HdcpCapability current_hdcp_version = HDCP_NO_DIGITAL_OUTPUT;
|
||||
CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1;
|
||||
uint32_t crypto_session_api_version = 9;
|
||||
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
EXPECT_CALL(*crypto_session_, GenerateRequestId(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull())).WillOnce(
|
||||
DoAll(SetArgPointee<0>(usage_information_support), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(usage_information_support), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version),
|
||||
SetArgPointee<1>(max_hdcp_version), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())).WillOnce(
|
||||
DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true)));
|
||||
EXPECT_CALL(*clock_, GetCurrentTime()).WillOnce(Return(kLicenseStartTime));
|
||||
EXPECT_CALL(*crypto_session_, GenerateNonce(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kNonce), Return(true)));
|
||||
@@ -207,7 +210,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
std::string server_url;
|
||||
EXPECT_TRUE(cdm_license_->PrepareKeyRequest(
|
||||
*init_data_, kLicenseTypeStreaming, app_parameters, kCdmSessionId,
|
||||
*init_data_, kLicenseTypeStreaming, app_parameters,
|
||||
&signed_request, &server_url));
|
||||
|
||||
EXPECT_TRUE(!signed_request.empty());
|
||||
@@ -279,4 +282,5 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
license_request.protocol_version());
|
||||
EXPECT_EQ(kNonce, license_request.key_control_nonce());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
323
core/test/max_res_engine_unittest.cpp
Normal file
323
core/test/max_res_engine_unittest.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
// 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
|
||||
18
core/test/mock_clock.h
Normal file
18
core/test/mock_clock.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef CDM_TEST_MOCK_CLOCK_H_
|
||||
#define CDM_TEST_MOCK_CLOCK_H_
|
||||
|
||||
#include "clock.h"
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class MockClock : public Clock {
|
||||
public:
|
||||
MOCK_METHOD0(GetCurrentTime, int64_t());
|
||||
};
|
||||
|
||||
} // wvcdm
|
||||
|
||||
#endif // CDM_TEST_MOCK_CLOCK_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,87 +7,386 @@
|
||||
namespace wvcdm {
|
||||
|
||||
void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case NO_ERROR: *os << "NO_ERROR";
|
||||
switch (value) {
|
||||
case NO_ERROR: *os << "NO_ERROR";
|
||||
break;
|
||||
case UNKNOWN_ERROR: *os << "UNKNOWN_ERROR";
|
||||
case UNKNOWN_ERROR: *os << "UNKNOWN_ERROR";
|
||||
break;
|
||||
case KEY_ADDED: *os << "KEY_ADDED";
|
||||
case KEY_ADDED: *os << "KEY_ADDED";
|
||||
break;
|
||||
case KEY_ERROR: *os << "KEY_ERROR";
|
||||
case KEY_ERROR: *os << "KEY_ERROR";
|
||||
break;
|
||||
case KEY_MESSAGE: *os << "KEY_MESSAGE";
|
||||
case KEY_MESSAGE: *os << "KEY_MESSAGE";
|
||||
break;
|
||||
case NEED_KEY: *os << "NEED_KEY";
|
||||
case NEED_KEY: *os << "NEED_KEY";
|
||||
break;
|
||||
case KEY_CANCELED: *os << "KEY_CANCELED";
|
||||
case KEY_CANCELED: *os << "KEY_CANCELED";
|
||||
break;
|
||||
case NEED_PROVISIONING: *os << "NEED_PROVISIONING";
|
||||
case NEED_PROVISIONING: *os << "NEED_PROVISIONING";
|
||||
break;
|
||||
case DEVICE_REVOKED: *os << "DEVICE_REVOKED";
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_2: *os << "DEVICE_CERTIFICATE_ERROR_2";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_3: *os << "DEVICE_CERTIFICATE_ERROR_3";
|
||||
break;
|
||||
case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4";
|
||||
break;
|
||||
case EMPTY_KEY_DATA_1: *os << "EMPTY_KEY_DATA_1";
|
||||
break;
|
||||
case EMPTY_KEY_DATA_2: *os << "EMPTY_KEY_DATA_2";
|
||||
break;
|
||||
case EMPTY_KEYSET_ID: *os << "EMPTY_KEYSET_ID";
|
||||
break;
|
||||
case EMPTY_KEYSET_ID_ENG_1: *os << "EMPTY_KEYSET_ID_ENG_1";
|
||||
break;
|
||||
case EMPTY_KEYSET_ID_ENG_2: *os << "EMPTY_KEYSET_ID_ENG_2";
|
||||
break;
|
||||
case EMPTY_KEYSET_ID_ENG_3: *os << "EMPTY_KEYSET_ID_ENG_3";
|
||||
break;
|
||||
case EMPTY_KEYSET_ID_ENG_4: *os << "EMPTY_KEYSET_ID_ENG_4";
|
||||
break;
|
||||
case EMPTY_LICENSE_RENEWAL: *os << "EMPTY_LICENSE_RENEWAL";
|
||||
break;
|
||||
case EMPTY_LICENSE_RESPONSE_1: *os << "EMPTY_LICENSE_RESPONSE_1";
|
||||
break;
|
||||
case EMPTY_LICENSE_RESPONSE_2: *os << "EMPTY_LICENSE_RESPONSE_2";
|
||||
break;
|
||||
case EMPTY_PROVISIONING_CERTIFICATE: *os << "EMPTY_PROVISIONING_CERTIFICATE";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case LICENSE_RENEWAL_NONCE_GENERATION_ERROR: *os << "LICENSE_RENEWAL_NONCE_GENERATION_ERROR";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case GET_USAGE_INFO_ERROR_1: *os << "GET_USAGE_INFO_ERROR_1";
|
||||
break;
|
||||
case GET_USAGE_INFO_ERROR_2: *os << "GET_USAGE_INFO_ERROR_2";
|
||||
break;
|
||||
case GET_USAGE_INFO_ERROR_3: *os << "GET_USAGE_INFO_ERROR_3";
|
||||
break;
|
||||
case GET_USAGE_INFO_ERROR_4: *os << "GET_USAGE_INFO_ERROR_4";
|
||||
break;
|
||||
case INIT_DATA_NOT_FOUND: *os << "INIT_DATA_NOT_FOUND";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_1: *os << "INVALID_CRYPTO_SESSION_1";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_2: *os << "INVALID_CRYPTO_SESSION_2";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_3: *os << "INVALID_CRYPTO_SESSION_3";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_4: *os << "INVALID_CRYPTO_SESSION_4";
|
||||
break;
|
||||
case INVALID_CRYPTO_SESSION_5: *os << "INVALID_CRYPTO_SESSION_5";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case INVALID_LICENSE_TYPE: *os << "INVALID_LICENSE_TYPE";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_1: *os << "INVALID_PARAMETERS_ENG_1";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_2: *os << "INVALID_PARAMETERS_ENG_2";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_3: *os << "INVALID_PARAMETERS_ENG_3";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_4: *os << "INVALID_PARAMETERS_ENG_4";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_1: *os << "INVALID_PARAMETERS_LIC_1";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_2: *os << "INVALID_PARAMETERS_LIC_2";
|
||||
break;
|
||||
case INVALID_PROVISIONING_PARAMETERS_1: *os << "INVALID_PROVISIONING_PARAMETERS_1";
|
||||
break;
|
||||
case INVALID_PROVISIONING_PARAMETERS_2: *os << "INVALID_PROVISIONING_PARAMETERS_2";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case INVALID_QUERY_KEY: *os << "INVALID_QUERY_KEY";
|
||||
break;
|
||||
case INVALID_SESSION_ID: *os << "INVALID_SESSION_ID";
|
||||
break;
|
||||
case KEY_REQUEST_ERROR_1: *os << "KEY_REQUEST_ERROR_1";
|
||||
break;
|
||||
case KEY_SIZE_ERROR: *os << "KEY_SIZE_ERROR";
|
||||
break;
|
||||
case KEYSET_ID_NOT_FOUND_1: *os << "KEYSET_ID_NOT_FOUND_1";
|
||||
break;
|
||||
case KEYSET_ID_NOT_FOUND_2: *os << "KEYSET_ID_NOT_FOUND_2";
|
||||
break;
|
||||
case KEYSET_ID_NOT_FOUND_3: *os << "KEYSET_ID_NOT_FOUND_3";
|
||||
break;
|
||||
case LICENSE_ID_NOT_FOUND: *os << "LICENSE_ID_NOT_FOUND";
|
||||
break;
|
||||
case LICENSE_PARSER_INIT_ERROR: *os << "LICENSE_PARSER_INIT_ERROR";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case LICENSE_PARSER_NOT_INITIALIZED_3: *os << "LICENSE_PARSER_NOT_INITIALIZED_3";
|
||||
break;
|
||||
case LICENSE_RESPONSE_NOT_SIGNED: *os << "LICENSE_RESPONSE_NOT_SIGNED";
|
||||
break;
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case LICENSE_RESPONSE_PARSE_ERROR_3: *os << "LICENSE_RESPONSE_PARSE_ERROR_3";
|
||||
break;
|
||||
case LOAD_KEY_ERROR: *os << "LOAD_KEY_ERROR";
|
||||
break;
|
||||
case NO_CONTENT_KEY: *os << "NO_CONTENT_KEY";
|
||||
break;
|
||||
case REFRESH_KEYS_ERROR: *os << "REFRESH_KEYS_ERROR";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case RELEASE_KEY_ERROR: *os << "RELEASE_KEY_ERROR";
|
||||
break;
|
||||
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";
|
||||
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";
|
||||
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";
|
||||
break;
|
||||
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
|
||||
break;
|
||||
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_2: *os << "SESSION_NOT_FOUND_2";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_3: *os << "SESSION_NOT_FOUND_3";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_4: *os << "SESSION_NOT_FOUND_4";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_5: *os << "SESSION_NOT_FOUND_5";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_6: *os << "SESSION_NOT_FOUND_6";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_7: *os << "SESSION_NOT_FOUND_7";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_8: *os << "SESSION_NOT_FOUND_8";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_9: *os << "SESSION_NOT_FOUND_9";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_10: *os << "SESSION_NOT_FOUND_10";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_FOR_DECRYPT: *os << "SESSION_NOT_FOUND_FOR_DECRYPT";
|
||||
break;
|
||||
case SESSION_KEYS_NOT_FOUND: *os << "SESSION_KEYS_NOT_FOUND";
|
||||
break;
|
||||
case SIGNATURE_NOT_FOUND: *os << "SIGNATURE_NOT_FOUND";
|
||||
break;
|
||||
case STORE_LICENSE_ERROR_1: *os << "STORE_LICENSE_ERROR_1";
|
||||
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";
|
||||
break;
|
||||
case UNPROVISION_ERROR_2: *os << "UNPROVISION_ERROR_2";
|
||||
break;
|
||||
case UNPROVISION_ERROR_3: *os << "UNPROVISION_ERROR_3";
|
||||
break;
|
||||
case UNPROVISION_ERROR_4: *os << "UNPROVISION_ERROR_4";
|
||||
break;
|
||||
case UNSUPPORTED_INIT_DATA: *os << "UNSUPPORTED_INIT_DATA";
|
||||
break;
|
||||
case USAGE_INFO_NOT_FOUND: *os << "USAGE_INFO_NOT_FOUND";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case PARSE_SERVICE_CERTIFICATE_ERROR: *os << "PARSE_SERVICE_CERTIFICATE_ERROR";
|
||||
break;
|
||||
case SERVICE_CERTIFICATE_TYPE_ERROR: *os << "SERVICE_CERTIFICATE_TYPE_ERROR";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_3: *os << "INVALID_PARAMETERS_LIC_3";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_4: *os << "INVALID_PARAMETERS_LIC_4";
|
||||
break;
|
||||
case INVALID_PARAMETERS_LIC_6: *os << "INVALID_PARAMETERS_LIC_6";
|
||||
break;
|
||||
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";
|
||||
break;
|
||||
case PREPARE_CENC_CONTENT_ID_FAILED: *os << "PREPARE_CENC_CONTENT_ID_FAILED";
|
||||
break;
|
||||
case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE";
|
||||
break;
|
||||
case PREPARE_WEBM_CONTENT_ID_FAILED: *os << "PREPARE_WEBM_CONTENT_ID_FAILED";
|
||||
break;
|
||||
case UNSUPPORTED_INIT_DATA_FORMAT: *os << "UNSUPPORTED_INIT_DATA_FORMAT";
|
||||
break;
|
||||
case LICENSE_REQUEST_NONCE_GENERATION_ERROR: *os << "LICENSE_REQUEST_NONCE_GENERATION_ERROR";
|
||||
break;
|
||||
case LICENSE_REQUEST_SIGNING_ERROR: *os << "LICENSE_REQUEST_SIGNING_ERROR";
|
||||
break;
|
||||
case EMPTY_LICENSE_REQUEST: *os << "EMPTY_LICENSE_REQUEST";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmResponseType";
|
||||
*os << "Unknown CdmResponseType";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintTo(const enum CdmEventType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case LICENSE_EXPIRED_EVENT: *os << "LICENSE_EXPIRED_EVENT";
|
||||
break;
|
||||
case LICENSE_RENEWAL_NEEDED_EVENT: *os << "LICENSE_RENEWAL_NEEDED_EVENT";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmEventType";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case kLicenseTypeOffline: *os << "kLicenseTypeOffline";
|
||||
switch (value) {
|
||||
case kLicenseTypeOffline: *os << "kLicenseTypeOffline";
|
||||
break;
|
||||
case kLicenseTypeStreaming: *os << "kLicenseTypeStreaming";
|
||||
case kLicenseTypeStreaming: *os << "kLicenseTypeStreaming";
|
||||
break;
|
||||
case kLicenseTypeRelease: *os << "kLicenseTypeRelease";
|
||||
case kLicenseTypeRelease: *os << "kLicenseTypeRelease";
|
||||
break;
|
||||
case kLicenseTypeDeferred: *os << "kLicenseTypeDeferred";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmLicenseType";
|
||||
*os << "Unknown CdmLicenseType";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
|
||||
switch (value) {
|
||||
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
|
||||
break;
|
||||
case kSecurityLevelL1: *os << "kSecurityLevelL1";
|
||||
case kSecurityLevelL1: *os << "kSecurityLevelL1";
|
||||
break;
|
||||
case kSecurityLevelL2: *os << "kSecurityLevelL2";
|
||||
case kSecurityLevelL2: *os << "kSecurityLevelL2";
|
||||
break;
|
||||
case kSecurityLevelL3: *os << "kSecurityLevelL3";
|
||||
case kSecurityLevelL3: *os << "kSecurityLevelL3";
|
||||
break;
|
||||
case kSecurityLevelUnknown: *os << "kSecurityLevelUnknown";
|
||||
case kSecurityLevelUnknown: *os << "kSecurityLevelUnknown";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmSecurityLevel";
|
||||
*os << "Unknown CdmSecurityLevel";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) {
|
||||
switch(value) {
|
||||
case kCertificateWidevine: *os << "kCertificateWidevine";
|
||||
switch (value) {
|
||||
case kCertificateWidevine: *os << "kCertificateWidevine";
|
||||
break;
|
||||
case kCertificateX509: *os << "kCertificateX509";
|
||||
case kCertificateX509: *os << "kCertificateX509";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmCertificateType";
|
||||
*os << "Unknown CdmCertificateType";
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
|
||||
namespace wvcdm {
|
||||
void PrintTo(const enum CdmResponseType& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmEventType& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os);
|
||||
void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os);
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_TEST_PRINTERS_H_
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class TestTimerHandler : public TimerHandler {
|
||||
public:
|
||||
TestTimerHandler() : timer_events_(0) {};
|
||||
virtual ~TestTimerHandler() {};
|
||||
|
||||
virtual void OnTimerEvent() { timer_events_++; }
|
||||
|
||||
uint32_t timer_events() { return timer_events_; }
|
||||
void ResetTimerEvents() { timer_events_ = 0; }
|
||||
|
||||
private:
|
||||
uint32_t timer_events_;
|
||||
};
|
||||
|
||||
TEST(TimerTest, ParametersCheck) {
|
||||
Timer timer;
|
||||
EXPECT_FALSE(timer.Start(NULL, 10));
|
||||
|
||||
TestTimerHandler handler;
|
||||
EXPECT_FALSE(timer.Start(&handler, 0));
|
||||
}
|
||||
|
||||
TEST(TimerTest, TimerCheck) {
|
||||
TestTimerHandler handler;
|
||||
Timer timer;
|
||||
uint32_t duration = 10;
|
||||
|
||||
EXPECT_EQ(0u, handler.timer_events());
|
||||
EXPECT_FALSE(timer.IsRunning());
|
||||
|
||||
EXPECT_TRUE(timer.Start(&handler, 1));
|
||||
EXPECT_TRUE(timer.IsRunning());
|
||||
sleep(duration);
|
||||
|
||||
EXPECT_LE(duration - 1, handler.timer_events());
|
||||
EXPECT_LE(handler.timer_events(), duration + 1);
|
||||
timer.Stop();
|
||||
EXPECT_FALSE(timer.IsRunning());
|
||||
sleep(duration);
|
||||
|
||||
EXPECT_LE(duration - 1, handler.timer_events());
|
||||
EXPECT_LE(handler.timer_events(), duration + 1);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const int kReadBufferSize = 1024;
|
||||
@@ -69,8 +71,6 @@ void ConcatenateChunkedResponse(const std::string http_response,
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
UrlRequest::UrlRequest(const std::string& url)
|
||||
: is_connected_(false), socket_(url) {
|
||||
Reconnect();
|
||||
@@ -78,7 +78,7 @@ UrlRequest::UrlRequest(const std::string& url)
|
||||
|
||||
UrlRequest::~UrlRequest() {}
|
||||
|
||||
bool UrlRequest::Reconnect() {
|
||||
void UrlRequest::Reconnect() {
|
||||
socket_.CloseSocket();
|
||||
if (socket_.Connect(kConnectTimeoutMs)) {
|
||||
is_connected_ = true;
|
||||
@@ -109,7 +109,7 @@ bool UrlRequest::GetResponse(std::string* message) {
|
||||
}
|
||||
|
||||
ConcatenateChunkedResponse(response, message);
|
||||
LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());
|
||||
LOGV("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ bool UrlRequest::PostRequestWithPath(const std::string& path,
|
||||
request.append(data);
|
||||
|
||||
int ret = socket_.Write(request.c_str(), request.size(), kWriteTimeoutMs);
|
||||
LOGD("HTTP request: (%d): %s", request.size(), b2a_hex(request).c_str());
|
||||
LOGV("HTTP request: (%d): %s", request.size(), b2a_hex(request).c_str());
|
||||
return ret != -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class UrlRequest {
|
||||
~UrlRequest();
|
||||
|
||||
bool is_connected() const { return is_connected_; }
|
||||
bool Reconnect();
|
||||
void Reconnect();
|
||||
|
||||
bool PostRequest(const std::string& data);
|
||||
bool PostCertRequestInQueryString(const std::string& data);
|
||||
@@ -34,6 +34,6 @@ class UrlRequest {
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(UrlRequest);
|
||||
};
|
||||
|
||||
}; // namespace wvcdm
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // CDM_TEST_URL_REQUEST_H_
|
||||
|
||||
Reference in New Issue
Block a user