Source release v3.1.0

This commit is contained in:
Gene Morgan
2016-07-19 18:43:15 -07:00
parent 7a7f78d654
commit 643b91b616
108 changed files with 16537 additions and 7174 deletions

View File

@@ -7,6 +7,7 @@
#include "certificate_provisioning.h"
#include "crypto_session.h"
#include "file_store.h"
#include "initialization_data.h"
#include "lock.h"
#include "oemcrypto_adapter.h"
@@ -26,22 +27,24 @@ typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap;
class CdmEngine {
public:
CdmEngine();
CdmEngine(FileSystem* file_system);
virtual ~CdmEngine();
// Session related methods
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
const std::string& origin,
const CdmSessionId& forced_session_id,
WvCdmEventListener* event_listener);
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual bool IsOpenSession(const CdmSessionId& session_id);
virtual CdmResponseType OpenKeySetSession(
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
const std::string& origin, WvCdmEventListener* event_listener);
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id,
CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener);
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
// License related methods
@@ -58,27 +61,17 @@ class CdmEngine {
// app_parameters: Additional, application-specific parameters that factor
// into the request generation. This is ignored for release
// and renewal requests.
// key_request: This must be non-null and point to a CdmKeyMessage. The buffer
// will have its contents replaced with the key request.
// key_request_type: May be null. If it is non-null, it will be filled with
// key request type, whether it is an initial request,
// renewal request or release request etc.
// server_url: This must be non-null and point to a string. The string will
// have its contents replaced with the default URL (if one is
// known) to send this key request to.
// key_set_id_out: May be null. If it is non-null, the CdmKeySetId pointed to
// will have its contents replaced with the key set ID of the
// session. Note that for non-offline license requests, the
// key set ID is empty, so the CdmKeySetId will be cleared.
// TODO(kqyang): Consider refactor GenerateKeyRequest to reduce the number of
// parameters.
// key_request: This must be non-null and point to a CdmKeyRequest. The
// message field will be filled with the key request, the
// type field will be filled with the key request type,
// whether it is an initial request, renewal request,
// release request, etc. The url field will be filled with
// the default URL (if one is known) to send this key
// request to.
virtual CdmResponseType GenerateKeyRequest(
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const InitializationData& init_data, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
CdmKeyRequestType* key_request_type, std::string* server_url,
CdmKeySetId* key_set_id_out);
CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
// Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
@@ -90,9 +83,8 @@ class CdmEngine {
virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id);
// Construct valid renewal request for the current session keys.
virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
CdmKeyMessage* key_request,
std::string* server_url);
virtual CdmResponseType GenerateRenewalRequest(
const CdmSessionId& session_id, CdmKeyRequest* key_request);
// Accept renewal response and update key info.
virtual CdmResponseType RenewKey(const CdmSessionId& session_id,
@@ -100,38 +92,48 @@ class CdmEngine {
// Query system information
virtual CdmResponseType QueryStatus(SecurityLevel security_level,
const std::string& key,
std::string* value);
const std::string& query_token,
std::string* query_response);
// Query session information
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info);
CdmQueryMap* query_response);
virtual bool IsReleaseSession(const CdmSessionId& session_id);
virtual bool IsOfflineSession(const CdmSessionId& session_id);
// Query license information
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info);
CdmQueryMap* query_response);
// Query session control information
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
CdmQueryMap* key_info);
// Query the types of usage permitted for the specified key.
virtual CdmResponseType QueryKeyAllowedUsage(const CdmSessionId& session_id,
const std::string& key_id,
CdmKeyAllowedUsage* key_usage);
// Query the types of usage permitted for the specified key.
// Apply the query across all sessions. If the key is found in more than
// one session, return the allowed usage settings only if the usage settings
// are identical for each instance of the key. Otherwise, clear the settings
// and return KEY_CONFLICT_1.
virtual CdmResponseType QueryKeyAllowedUsage(const std::string& key_id,
CdmKeyAllowedUsage* key_usage);
// Query OEMCrypto session ID
virtual CdmResponseType QueryOemCryptoSessionId(
const CdmSessionId& session_id, CdmQueryMap* query_response);
// Provisioning related methods
virtual CdmResponseType GetProvisioningRequest(
CdmCertificateType cert_type, const std::string& cert_authority,
const std::string& origin, CdmProvisioningRequest* request,
std::string* default_url);
CdmProvisioningRequest* request, std::string* default_url);
virtual CdmResponseType HandleProvisioningResponse(
const std::string& origin, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key);
const CdmProvisioningResponse& response, std::string* cert,
std::string* wrapped_key);
virtual bool IsProvisioned(CdmSecurityLevel security_level,
const std::string& origin);
virtual bool IsProvisioned(CdmSecurityLevel security_level);
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level,
const std::string& origin);
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
// Usage related methods for streaming licenses
// Retrieve a random usage info from the list of all usage infos for this app
@@ -154,6 +156,35 @@ class CdmEngine {
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters);
// Generic crypto operations - provides basic crypto operations that an
// application can use outside of content stream processing
// Encrypts a buffer of app-level data.
virtual CdmResponseType GenericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm, std::string* out_buffer);
// Decrypts a buffer of app-level data.
virtual CdmResponseType GenericDecrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm, std::string* out_buffer);
// Computes the signature for a message.
virtual CdmResponseType GenericSign(const std::string& session_id,
const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
std::string* signature);
// Verifies the signature on a buffer of app-level data.
virtual CdmResponseType GenericVerify(const std::string& session_id,
const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
const std::string& signature);
virtual size_t SessionSize() const { return sessions_.size(); }
// Is the key known to any session?
@@ -175,6 +206,12 @@ class CdmEngine {
private:
// private methods
CdmResponseType OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id,
CdmSessionId* session_id);
void DeleteAllUsageReportsUponFactoryReset();
bool ValidateKeySystem(const CdmKeySystem& key_system);
CdmResponseType GetUsageInfo(const std::string& app_id,
@@ -190,6 +227,7 @@ class CdmEngine {
CdmReleaseKeySetMap release_key_sets_;
scoped_ptr<CertificateProvisioning> cert_provisioning_;
SecurityLevel cert_provisioning_requested_security_level_;
FileSystem* file_system_;
static bool seeded_;

View File

@@ -8,6 +8,7 @@
#include "crypto_session.h"
#include "device_files.h"
#include "file_store.h"
#include "initialization_data.h"
#include "license.h"
#include "oemcrypto_adapter.h"
@@ -22,12 +23,13 @@ class WvCdmEventListener;
class CdmSession {
public:
CdmSession(CdmClientPropertySet* cdm_client_property_set,
const std::string& origin, WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id);
CdmSession(FileSystem* file_system);
virtual ~CdmSession();
virtual CdmResponseType Init();
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set);
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id,
WvCdmEventListener* event_listener);
virtual CdmResponseType RestoreOfflineSession(
const CdmKeySetId& key_set_id, const CdmLicenseType license_type);
@@ -35,25 +37,27 @@ class CdmSession {
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response);
virtual const CdmSessionId& session_id() { return session_id_; }
virtual const CdmKeySetId& key_set_id() { return key_set_id_; }
virtual CdmResponseType GenerateKeyRequest(
const InitializationData& init_data, CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
CdmKeyRequestType* key_request_type, std::string* server_url,
CdmKeySetId* key_set_id);
const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
// AddKey() - Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response,
CdmKeySetId* key_set_id);
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response);
// Query session status
virtual CdmResponseType QueryStatus(CdmQueryMap* key_info);
virtual CdmResponseType QueryStatus(CdmQueryMap* query_response);
// Query license information
virtual CdmResponseType QueryKeyStatus(CdmQueryMap* key_info);
virtual CdmResponseType QueryKeyStatus(CdmQueryMap* query_response);
// Query session control info
virtual CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info);
// Query allowed usages for key
virtual CdmResponseType QueryKeyAllowedUsage(const std::string& key_id,
CdmKeyAllowedUsage* key_usage);
// Query OEMCrypto session ID
virtual CdmResponseType QueryOemCryptoSessionId(CdmQueryMap* query_response);
// Decrypt() - Accept encrypted buffer and return decrypted data.
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
@@ -61,8 +65,7 @@ class CdmSession {
// License renewal
// GenerateRenewalRequest() - Construct valid renewal request for the current
// session keys.
virtual CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request,
std::string* server_url);
virtual CdmResponseType GenerateRenewalRequest(CdmKeyRequest* key_request);
// RenewKey() - Accept renewal response and update key info.
virtual CdmResponseType RenewKey(const CdmKeyResponse& key_response);
@@ -70,13 +73,13 @@ class CdmSession {
// License release
// GenerateReleaseRequest() - Construct valid release request for the current
// session keys.
virtual CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request,
std::string* server_url);
virtual CdmResponseType GenerateReleaseRequest(CdmKeyRequest* key_request);
// ReleaseKey() - Accept response and release key.
virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
virtual bool IsKeyLoaded(const KeyId& key_id);
virtual int64_t GetDurationRemaining();
// Used for notifying the Policy Engine of resolution changes
virtual void NotifyResolution(uint32_t width, uint32_t height);
@@ -116,11 +119,41 @@ class CdmSession {
bool DeleteLicense();
// Generate unique ID for each new session.
CdmSessionId GenerateSessionId();
// Generic crypto operations - provides basic crypto operations that an
// application can use outside of content stream processing
// Encrypts a buffer of app-level data.
virtual CdmResponseType GenericEncrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer);
// Decrypts a buffer of app-level data.
virtual CdmResponseType GenericDecrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer);
// Computes the signature for a message.
virtual CdmResponseType GenericSign(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
std::string* signature);
// Verifies the signature on a buffer of app-level data.
virtual CdmResponseType GenericVerify(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
const std::string& signature);
private:
friend class CdmSessionTest;
// Generate unique ID for each new session.
CdmSessionId GenerateSessionId();
bool GenerateKeySetId(CdmKeySetId* key_set_id);
CdmResponseType StoreLicense();
@@ -135,7 +168,6 @@ class CdmSession {
// instance variables
bool initialized_;
CdmSessionId session_id_;
const std::string origin_;
scoped_ptr<CdmLicense> license_parser_;
scoped_ptr<CryptoSession> crypto_session_;
scoped_ptr<PolicyEngine> policy_engine_;
@@ -167,6 +199,9 @@ class CdmSession {
// license type release and offline related information
CdmKeySetId key_set_id_;
bool mock_license_parser_in_use_;
bool mock_policy_engine_in_use_;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession);
};

View File

@@ -12,6 +12,7 @@
namespace wvcdm {
class CdmSession;
class FileSystem;
class CertificateProvisioning {
public:
@@ -26,7 +27,7 @@ class CertificateProvisioning {
CdmProvisioningRequest* request,
std::string* default_url);
CdmResponseType HandleProvisioningResponse(
const std::string& origin,
FileSystem* file_system,
const CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key);

View File

@@ -17,6 +17,7 @@ class CryptoKey {
const std::string& key_data_iv() const { return key_data_iv_; }
const std::string& key_control() const { return key_control_; }
const std::string& key_control_iv() const { return key_control_iv_; }
CdmCipherMode cipher_mode() const { return cipher_mode_; }
void set_key_id(const std::string& key_id) { key_id_ = key_id; }
void set_key_data(const std::string& key_data) { key_data_ = key_data; }
void set_key_data_iv(const std::string& iv) { key_data_iv_ = iv; }
@@ -24,6 +25,9 @@ class CryptoKey {
void set_key_control_iv(const std::string& ctl_iv) {
key_control_iv_ = ctl_iv;
}
void set_cipher_mode(CdmCipherMode cipher_mode) {
cipher_mode_ = cipher_mode;
}
bool HasKeyControl() const { return key_control_.size() >= 16; }
@@ -33,6 +37,7 @@ class CryptoKey {
std::string key_data_;
std::string key_control_;
std::string key_control_iv_;
CdmCipherMode cipher_mode_;
};
} // namespace wvcdm

View File

@@ -35,6 +35,7 @@ class CryptoSession {
virtual bool GetApiVersion(uint32_t* version);
virtual bool GetSystemId(uint32_t* system_id);
virtual bool GetProvisioningId(std::string* provisioning_id);
virtual uint8_t GetSecurityPatchLevel();
virtual CdmResponseType Open() { return Open(kLevelDefault); }
virtual CdmResponseType Open(SecurityLevel requested_security_level);
@@ -102,6 +103,25 @@ class CryptoSession {
virtual bool GetNumberOfOpenSessions(size_t* count);
virtual bool GetMaxNumberOfSessions(size_t* max);
virtual CdmResponseType GenericEncrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer);
virtual CdmResponseType GenericDecrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer);
virtual CdmResponseType GenericSign(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
std::string* signature);
virtual CdmResponseType GenericVerify(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
const std::string& signature);
private:
void Init();
void Terminate();
@@ -113,9 +133,17 @@ class CryptoSession {
bool GenerateRsaSignature(const std::string& message, std::string* signature);
size_t GetOffset(std::string message, std::string field);
bool SetDestinationBufferType();
bool SelectKey(const std::string& key_id);
static const OEMCrypto_Algorithm kInvalidAlgorithm =
static_cast<OEMCrypto_Algorithm>(-1);
OEMCrypto_Algorithm GenericSigningAlgorithm(CdmSigningAlgorithm algorithm);
OEMCrypto_Algorithm GenericEncryptionAlgorithm(
CdmEncryptionAlgorithm algorithm);
size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm);
static const size_t kAes128BlockSize = 16; // Block size for AES_CBC_128
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
static Lock crypto_lock_;
static bool initialized_;
@@ -129,11 +157,13 @@ class CryptoSession {
bool is_destination_buffer_type_valid_;
SecurityLevel requested_security_level_;
KeyId key_id_;
KeyId cached_key_id_;
uint64_t request_id_base_;
static uint64_t request_id_index_;
CdmCipherMode cipher_mode_;
CORE_DISALLOW_COPY_AND_ASSIGN(CryptoSession);
};

View File

@@ -18,7 +18,7 @@
namespace wvcdm {
class File;
class FileSystem;
class DeviceFiles {
public:
@@ -28,7 +28,7 @@ class DeviceFiles {
kLicenseStateUnknown,
} LicenseState;
DeviceFiles();
DeviceFiles(FileSystem*);
virtual ~DeviceFiles();
virtual bool Init(CdmSecurityLevel security_level);
@@ -36,14 +36,12 @@ class DeviceFiles {
return Init(security_level);
}
virtual bool StoreCertificate(const std::string& origin,
const std::string& certificate,
virtual bool StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key);
virtual bool RetrieveCertificate(const std::string& origin,
std::string* certificate,
virtual bool RetrieveCertificate(std::string* certificate,
std::string* wrapped_private_key);
virtual bool HasCertificate(const std::string& origin);
virtual bool RemoveCertificate(const std::string& origin);
virtual bool HasCertificate();
virtual bool RemoveCertificate();
virtual bool StoreLicense(const std::string& key_set_id,
const LicenseState state,
@@ -100,6 +98,13 @@ class DeviceFiles {
CdmKeyMessage* license_request,
CdmKeyResponse* license_response);
virtual bool StoreHlsAttributes(const std::string& key_set_id,
const CdmHlsMethod method,
const std::vector<uint8_t>& media_segment_iv);
virtual bool RetrieveHlsAttributes(const std::string& key_set_id,
CdmHlsMethod* method,
std::vector<uint8_t>* media_segment_iv);
virtual bool DeleteHlsAttributes(const std::string& key_set_id);
private:
// Helpers that wrap the File interface and automatically handle hashing, as
// well as adding the device files base path to to the file name.
@@ -113,29 +118,25 @@ class DeviceFiles {
bool RemoveFile(const std::string& name);
ssize_t GetFileSize(const std::string& name);
// Certificate and offline licenses are now stored in security
// level specific directories. In an earlier version they were
// stored in a common directory and need to be copied over.
virtual void SecurityLevelPathBackwardCompatibility();
static std::string GetCertificateFileName(const std::string& origin);
static std::string GetCertificateFileName();
static std::string GetHlsAttributesFileNameExtension();
static std::string GetLicenseFileNameExtension();
static std::string GetUsageInfoFileName(const std::string& app_id);
static std::string GetFileNameSafeHash(const std::string& input);
// For testing only:
void SetTestFile(File* file);
#if defined(UNIT_TEST)
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
FRIEND_TEST(DeviceCertificateStoreTest, StoreCertificate);
FRIEND_TEST(DeviceCertificateTest, ReadCertificate);
FRIEND_TEST(DeviceCertificateTest, HasCertificate);
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
FRIEND_TEST(DeviceFilesHlsAttributesTest, Delete);
FRIEND_TEST(DeviceFilesHlsAttributesTest, Read);
FRIEND_TEST(DeviceFilesHlsAttributesTest, Store);
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem);
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
FRIEND_TEST(DeviceFilesTest, AppParametersBackwardCompatibility);
FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility);
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
FRIEND_TEST(DeviceFilesTest, UpdateLicenseState);
FRIEND_TEST(DeviceFilesUsageInfoTest, Delete);
@@ -153,12 +154,10 @@ class DeviceFiles {
static std::set<std::string> reserved_license_ids_;
scoped_ptr<File> file_;
FileSystem* file_system_;
CdmSecurityLevel security_level_;
bool initialized_;
bool test_file_;
CORE_DISALLOW_COPY_AND_ASSIGN(DeviceFiles);
};

View File

@@ -15,39 +15,54 @@ namespace wvcdm {
// File class. The implementation is platform dependent.
class File {
public:
virtual ssize_t Read(char* buffer, size_t bytes);
virtual ssize_t Write(const char* buffer, size_t bytes);
virtual void Close();
protected:
class Impl;
File(Impl*);
virtual ~File();
private:
Impl* impl_;
friend class FileSystem;
CORE_DISALLOW_COPY_AND_ASSIGN(File);
};
class FileSystem {
public:
class Impl;
// defines as bit flag
enum OpenFlags {
kNoFlags = 0,
kBinary = 1,
kCreate = 2,
kReadOnly = 4, // defaults to read and write access
kTruncate = 8
kCreate = 1,
kReadOnly = 2, // defaults to read and write access
kTruncate = 4
};
File();
virtual ~File();
FileSystem();
FileSystem(const std::string& origin, void* extra_data);
virtual ~FileSystem();
virtual bool Open(const std::string& file_path, int flags);
virtual ssize_t Read(char* buffer, size_t bytes);
virtual ssize_t Write(const char* buffer, size_t bytes);
virtual void Close();
virtual File* Open(const std::string& file_path, int flags);
virtual bool Exists(const std::string& file_path);
virtual bool Remove(const std::string& file_path);
virtual bool Copy(const std::string& old_path, const std::string& new_path);
virtual bool List(const std::string& path, std::vector<std::string>* files);
virtual bool CreateDirectory(const std::string dir_path);
virtual bool IsDirectory(const std::string& dir_path);
virtual bool IsRegularFile(const std::string& file_path);
virtual ssize_t FileSize(const std::string& file_path);
const std::string& origin() const { return origin_; }
void SetOrigin(const std::string& origin);
private:
Impl* impl_;
std::string origin_;
CORE_DISALLOW_COPY_AND_ASSIGN(File);
CORE_DISALLOW_COPY_AND_ASSIGN(FileSystem);
};
} // namespace wvcdm

View File

@@ -16,24 +16,62 @@ class InitializationData {
InitializationData(const std::string& type = std::string(),
const CdmInitData& data = CdmInitData());
bool is_supported() const { return is_cenc_ || is_webm_; }
bool is_supported() const { return is_cenc_ || is_webm_ || is_hls_; }
bool is_cenc() const { return is_cenc_; }
bool is_hls() const { return is_hls_; }
bool is_webm() const { return is_webm_; }
bool IsEmpty() const { return data_.empty(); }
const std::string& type() const { return type_; }
const CdmInitData& data() const { return data_; }
std::vector<uint8_t> hls_iv() const { return hls_iv_; }
CdmHlsMethod hls_method() const { return hls_method_; }
private:
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH.
bool ExtractWidevinePssh(const CdmInitData& init_data, CdmInitData* output);
bool ExtractHlsAttributes(const std::string& attribute_list,
CdmHlsMethod* method, std::vector<uint8_t>* iv,
std::string* uri);
static bool ConstructWidevineInitData(CdmHlsMethod method,
const std::string& uri,
CdmInitData* output);
static bool ExtractQuotedAttribute(const std::string& attribute_list,
const std::string& key,
std::string* value);
static bool ExtractHexAttribute(const std::string& attribute_list,
const std::string& key,
std::vector<uint8_t>* value);
static bool ExtractAttribute(const std::string& attribute_list,
const std::string& key, std::string* value);
static std::vector<std::string> ExtractKeyFormatVersions(
const std::string& key_format_versions);
// For testing only:
#if defined(UNIT_TEST)
FRIEND_TEST(HlsAttributeExtractionTest, ExtractAttribute);
FRIEND_TEST(HlsConstructionTest, InitData);
FRIEND_TEST(HlsInitDataConstructionTest, InvalidUriDataFormat);
FRIEND_TEST(HlsInitDataConstructionTest, InvalidUriBase64Encode);
FRIEND_TEST(HlsHexAttributeExtractionTest, ExtractHexAttribute);
FRIEND_TEST(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions);
FRIEND_TEST(HlsParseTest, Parse);
FRIEND_TEST(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute);
FRIEND_TEST(HlsTest, ExtractHlsAttributes);
#endif
std::string type_;
CdmInitData data_;
bool is_cenc_;
bool is_hls_;
bool is_webm_;
std::vector<uint8_t> hls_iv_;
CdmHlsMethod hls_method_;
};
} // namespace wvcdm

View File

@@ -0,0 +1,139 @@
// Copyright 2016 Google Inc. All Rights Reserved.
#ifndef WVCDM_CORE_LICENSE_KEY_STATUS_H_
#define WVCDM_CORE_LICENSE_KEY_STATUS_H_
#include <map>
#include "crypto_session.h"
#include "license_protocol.pb.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class LicenseKeyStatus;
// Holds all content and operator session keys for a session.
class LicenseKeys {
public:
LicenseKeys() {}
virtual ~LicenseKeys() { Clear(); }
virtual bool Empty() { return keys_.empty(); }
// Returns true if the key is a content key (not an operator session key)
virtual bool IsContentKey(const KeyId& key_id);
// Returns true if the key is currently usable for content decryption.
virtual bool CanDecryptContent(const KeyId& key_id);
// Returns the allowed usages for a key.
virtual bool GetAllowedUsage(const KeyId& key_id,
CdmKeyAllowedUsage* allowed_usage);
// Applies a new status to each content key.
// Returns true if any statuses changed, and sets new_usable_keys to
// true if the status changes resulted in keys becoming usable.
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Populates the CdmKeyStatusMap with the current content keys.
virtual void ExtractKeyStatuses(CdmKeyStatusMap* content_keys);
// Determines whether the specified key can be used under the current
// resolution and/or hdcp constraints. If no constraints have been applied
// to the key, returns true.
virtual bool MeetsConstraints(const KeyId& key_id);
// Applies a resolution and/or hdcp change to each key, updating their
// useability under their constraints.
virtual void ApplyConstraints(uint32_t new_resolution,
CryptoSession::HdcpCapability new_hdcp_level);
// Extracts the keys from a license and makes them available for
// querying usage and constraint settings.
virtual void SetFromLicense(
const video_widevine_server::sdk::License& license);
private:
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef std::map<wvcdm::KeyId, LicenseKeyStatus*>::const_iterator
LicenseKeyStatusIterator;
void Clear();
bool is_initialized_;
std::map<KeyId, LicenseKeyStatus*> keys_;
CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeys);
};
// Holds the current license status of a key.
class LicenseKeyStatus {
friend class LicenseKeys;
public:
// Returns true if the key is a content key (not an operator session key)
virtual bool IsContentKey() { return is_content_key_; }
// Returns true if the key is currently usable for content decryption
virtual bool CanDecryptContent();
// Returns the usages allowed for this key.
virtual bool GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage);
// Returns the current status of the key.
virtual CdmKeyStatus GetKeyStatus() const { return key_status_; }
// Applies a new status to this key.
// Returns true if the status changed, and sets new_usable_keys to
// true if the status changes resulted in the key becoming usable.
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Returns the current constraint status of this key. The result
// may change due to calls to ApplyConstraints().
// Note: this will return true until the first call to ApplyConstraints().
virtual bool MeetsConstraints() const { return meets_constraints_; }
// Applies the given changes in resolution or HDCP settings.
virtual void ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level);
protected:
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef KeyContainer::OperatorSessionKeyPermissions
OperatorSessionKeyPermissions;
typedef KeyContainer::OutputProtection OutputProtection;
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
ConstraintList;
LicenseKeyStatus(const KeyContainer& key);
virtual ~LicenseKeyStatus() {}
private:
void ParseContentKey(const KeyContainer& key);
void ParseOperatorSessionKey(const KeyContainer& key);
bool HasConstraints() {
return is_content_key_ && constraints_.size() != 0;
}
void SetConstraints(const ConstraintList& constraints);
bool is_content_key_;
CdmKeyStatus key_status_;
bool meets_constraints_;
CdmKeyAllowedUsage allowed_usage_;
CryptoSession::HdcpCapability default_hdcp_level_;
ConstraintList constraints_;
CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeyStatus);
};
} // namespace wvcdm
#endif // WVCDM_CORE_LICENSE_KEY_STATUS_H_

View File

@@ -1,105 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#ifndef WVCDM_CORE_MAX_RES_ENGINE_H_
#define WVCDM_CORE_MAX_RES_ENGINE_H_
#include <map>
#include "crypto_session.h"
#include "license_protocol.pb.h"
#include "lock.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class Clock;
class MaxResEngineTest;
// Similar to the Policy Engine, this acts as an oracle that basically says
// "Yes(true) you may still decrypt or no(false) you may not decrypt this data
// anymore."
class MaxResEngine {
public:
explicit MaxResEngine(CryptoSession* crypto_session);
virtual ~MaxResEngine();
// The value returned is computed during the last call to SetLicense/
// SetResolution/OnTimerEvent and may be out of sync depending on the amount
// of time elapsed. The current decryption status is not calculated when this
// function is called to avoid overhead in the decryption path.
virtual bool CanDecrypt(const KeyId& key_id);
// SetLicense is used in handling the initial license response. It stores
// an exact copy of the key constraints from the license.
virtual void SetLicense(const video_widevine_server::sdk::License& license);
// SetResolution is called when the current output resolution is updated by
// the decoder. The max-res engine will recalculate the current resolution
// constraints, (if any) which may affect the results for CanDecrypt().
virtual void SetResolution(uint32_t width, uint32_t height);
// OnTimerEvent is called when a timer fires. The max-res engine may check the
// current HDCP level using the crypto session, which may affect the results
// for CanDecrypt().
virtual void OnTimerEvent();
private:
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
OutputProtection;
typedef ::video_widevine_server::sdk::License::KeyContainer::
VideoResolutionConstraint VideoResolutionConstraint;
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
ConstraintList;
class KeyStatus {
public:
explicit KeyStatus(const ConstraintList& constraints);
KeyStatus(const ConstraintList& constraints,
const OutputProtection::HDCP& default_hdcp_level);
bool can_decrypt() const { return can_decrypt_; }
void Update(uint32_t res,
CryptoSession::HdcpCapability current_hdcp_level);
private:
void Init(const ConstraintList& constraints);
VideoResolutionConstraint* GetConstraintForRes(uint32_t res);
static CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp(
const OutputProtection::HDCP& input);
bool can_decrypt_;
CryptoSession::HdcpCapability default_hdcp_level_;
ConstraintList constraints_;
};
typedef std::map<wvcdm::KeyId,
wvcdm::MaxResEngine::KeyStatus*>::const_iterator KeyIterator;
void Init(CryptoSession* crypto_session, Clock* clock);
void DeleteAllKeys();
Lock status_lock_;
std::map<KeyId, KeyStatus*> keys_;
uint32_t current_resolution_;
int64_t next_check_time_;
scoped_ptr<Clock> clock_;
CryptoSession* crypto_session_;
// For testing
friend class MaxResEngineTest;
MaxResEngine(CryptoSession* crypto_session, Clock* clock);
CORE_DISALLOW_COPY_AND_ASSIGN(MaxResEngine);
};
} // wvcdm
#endif // WVCDM_CORE_MAX_RES_ENGINE_H_

View File

@@ -34,6 +34,7 @@ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
size_t* count);
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
size_t* maximum);
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level);
} // namespace wvcdm
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_

View File

@@ -6,8 +6,8 @@
#include <map>
#include <string>
#include "license_key_status.h"
#include "license_protocol.pb.h"
#include "max_res_engine.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
@@ -67,7 +67,10 @@ class PolicyEngine {
virtual void NotifySessionExpiration();
virtual CdmResponseType Query(CdmQueryMap* key_info);
virtual CdmResponseType Query(CdmQueryMap* query_response);
virtual CdmResponseType QueryKeyAllowedUsage(const KeyId& key_id,
CdmKeyAllowedUsage* key_usage);
virtual const LicenseIdentification& license_id() { return license_id_; }
@@ -84,11 +87,20 @@ class PolicyEngine {
bool IsPlaybackStarted() { return playback_start_time_ > 0; }
bool IsLicenseOrPlaybackDurationExpired(int64_t current_time);
int64_t GetLicenseOrPlaybackDurationRemaining();
bool CanRenew() { return policy_.can_renew(); }
private:
friend class PolicyEngineTest;
friend class PolicyEngineConstraintsTest;
void InitDevice(CryptoSession* crypto_session);
void CheckDevice(int64_t current_time);
void SetDeviceResolution(uint32_t width, uint32_t height) {
current_resolution_ = width * height;
}
typedef enum {
kLicenseStateInitial,
@@ -119,9 +131,9 @@ class PolicyEngine {
// expiry time changes.
void NotifyExpirationUpdate();
// These setters are for testing only. Takes ownership of the pointers.
// set_clock() is for testing only. It alters ownership of the
// passed-in pointer.
void set_clock(Clock* clock);
void set_max_res_engine(MaxResEngine* max_res_engine);
LicenseState license_state_;
@@ -153,9 +165,14 @@ class PolicyEngine {
CdmSessionId session_id_;
WvCdmEventListener* event_listener_;
scoped_ptr<MaxResEngine> max_res_engine_;
// Keys associated with license - holds allowed usage, usage constraints,
// and current status (CdmKeyStatus)
scoped_ptr<LicenseKeys> license_keys_;
std::map<KeyId, CdmKeyStatus> keys_status_;
// Device checks
int64_t next_device_check_;
uint32_t current_resolution_;
CryptoSession* crypto_session_;
scoped_ptr<Clock> clock_;

View File

@@ -15,12 +15,13 @@ std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& b);
std::string a2bs_hex(const std::string& b);
std::string b2a_hex(const std::vector<uint8_t>& b);
std::string b2a_hex(const std::string& b);
std::string Base64Encode(const std::vector<uint8_t>& bin_input);
std::vector<uint8_t> Base64Decode(const std::string& bin_input);
std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input);
std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input);
std::vector<uint8_t> Base64SafeDecode(const std::string& bin_input);
std::string HexEncode(const uint8_t* bytes, unsigned size);
std::string IntToString(int value);
std::string UintToString(unsigned int value);
int64_t htonll64(int64_t x);
inline int64_t ntohll64(int64_t x) { return htonll64(x); }

View File

@@ -80,8 +80,19 @@ static const std::string ISO_BMFF_AUDIO_MIME_TYPE = "audio/mp4";
static const std::string WEBM_VIDEO_MIME_TYPE = "video/webm";
static const std::string WEBM_AUDIO_MIME_TYPE = "audio/webm";
static const std::string CENC_INIT_DATA_FORMAT = "cenc";
static const std::string HLS_INIT_DATA_FORMAT = "hls";
static const std::string WEBM_INIT_DATA_FORMAT = "webm";
static const std::string HLS_KEYFORMAT_ATTRIBUTE = "KEYFORMAT";
static const std::string HLS_KEYFORMAT_VERSIONS_ATTRIBUTE = "KEYFORMATVERSIONS";
static const std::string HLS_KEYFORMAT_VERSION_VALUE_1 = "1";
static const std::string HLS_METHOD_ATTRIBUTE = "METHOD";
static const std::string HLS_METHOD_AES_128 = "AES-128";
static const std::string HLS_METHOD_NONE = "NONE";
static const std::string HLS_METHOD_SAMPLE_AES = "SAMPLE-AES";
static const std::string HLS_IV_ATTRIBUTE = "IV";
static const std::string HLS_URI_ATTRIBUTE = "URI";
static const char EMPTY_ORIGIN[] = "";
} // namespace wvcdm

View File

@@ -149,9 +149,9 @@ enum CdmResponseType {
RENEW_KEY_ERROR_1,
RENEW_KEY_ERROR_2,
LICENSE_RENEWAL_SIGNING_ERROR,
RESTORE_OFFLINE_LICENSE_ERROR_1,
UNUSED_4, /* previously RESTORE_OFFLINE_LICENSE_ERROR_1 */
RESTORE_OFFLINE_LICENSE_ERROR_2,
SESSION_INIT_ERROR_1,
UNUSED_5, /* SESSION_INIT_ERROR_1 */
SESSION_INIT_ERROR_2,
SESSION_INIT_GET_KEYBOX_ERROR,
SESSION_NOT_FOUND_1,
@@ -169,7 +169,7 @@ enum CdmResponseType {
SIGNATURE_NOT_FOUND,
STORE_LICENSE_ERROR_1,
STORE_LICENSE_ERROR_2,
STORE_LICENSE_ERROR_3,
UNUSED_6, /* previously STORE_LICENSE_ERROR_3 */
STORE_USAGE_INFO_ERROR,
UNPROVISION_ERROR_1,
UNPROVISION_ERROR_2,
@@ -213,6 +213,38 @@ enum CdmResponseType {
SESSION_NOT_FOUND_11,
LOAD_USAGE_INFO_FILE_ERROR,
LOAD_USAGE_INFO_MISSING,
SESSION_FILE_HANDLE_INIT_ERROR,
INCORRECT_CRYPTO_MODE,
INVALID_PARAMETERS_ENG_5,
DECRYPT_ERROR,
INSUFFICIENT_OUTPUT_PROTECTION,
SESSION_NOT_FOUND_12,
KEY_NOT_FOUND_1,
KEY_NOT_FOUND_2,
KEY_CONFLICT_1,
INVALID_PARAMETERS_ENG_6,
INVALID_PARAMETERS_ENG_7,
INVALID_PARAMETERS_ENG_8,
INVALID_PARAMETERS_ENG_9,
INVALID_PARAMETERS_ENG_10,
INVALID_PARAMETERS_ENG_11,
INVALID_PARAMETERS_ENG_12,
SESSION_NOT_FOUND_13,
SESSION_NOT_FOUND_14,
SESSION_NOT_FOUND_15,
SESSION_NOT_FOUND_16,
KEY_NOT_FOUND_3,
KEY_NOT_FOUND_4,
KEY_NOT_FOUND_5,
KEY_NOT_FOUND_6,
KEY_ERROR_1,
KEY_ERROR_2,
KEY_ERROR_3,
KEY_ERROR_4,
INVALID_PARAMETERS_ENG_13,
INVALID_PARAMETERS_ENG_14,
INVALID_PARAMETERS_ENG_15,
INVALID_PARAMETERS_ENG_16,
};
enum CdmKeyStatus {
@@ -258,9 +290,89 @@ enum CdmCertificateType {
kCertificateX509,
};
enum CdmHlsMethod {
kHlsMethodNone,
kHlsMethodAes128,
kHlsMethodSampleAes,
};
enum CdmCipherMode {
kCipherModeCtr,
kCipherModeCbc,
};
enum CdmEncryptionAlgorithm {
kEncryptionAlgorithmUnknown,
kEncryptionAlgorithmAesCbc128
};
enum CdmSigningAlgorithm {
kSigningAlgorithmUnknown,
kSigningAlgorithmHmacSha256
};
class CdmKeyAllowedUsage {
public:
CdmKeyAllowedUsage() {
Clear();
}
bool Valid() const { return valid_; }
void SetValid() { valid_ = true; }
void Clear() {
decrypt_to_clear_buffer = false;
decrypt_to_secure_buffer = false;
generic_encrypt = false;
generic_decrypt = false;
generic_sign = false;
generic_verify = false;
valid_ = false;
}
bool Equals(const CdmKeyAllowedUsage& other) {
if (!valid_ || !other.Valid() ||
decrypt_to_clear_buffer != other.decrypt_to_clear_buffer ||
decrypt_to_secure_buffer != other.decrypt_to_secure_buffer ||
generic_encrypt != other.generic_encrypt ||
generic_decrypt != other.generic_decrypt ||
generic_sign != other.generic_sign ||
generic_verify != other.generic_verify) {
return false;
}
return true;
}
bool decrypt_to_clear_buffer;
bool decrypt_to_secure_buffer;
bool generic_encrypt;
bool generic_decrypt;
bool generic_sign;
bool generic_verify;
private:
bool valid_;
};
// For schemes that do not use pattern encryption (cenc and cbc1), encrypt
// and skip should be set to 0. For those that do (cens and cbcs), it is
// recommended that encrypt+skip bytes sum to 10 and for cbcs that a 1:9
// encrypt:skip ratio be used. See ISO/IEC DIS 23001-7, section 10.4.2 for
// more information.
struct CdmCencPatternEncryptionDescriptor {
size_t encrypt_blocks; // number of 16 byte blocks to decrypt
size_t skip_blocks; // number of 16 byte blocks to leave in clear
size_t offset_blocks; // offset into the pattern for this call, in blocks
CdmCencPatternEncryptionDescriptor()
: encrypt_blocks(0),
skip_blocks(0),
offset_blocks(0) {}
};
struct CdmDecryptionParameters {
bool is_encrypted;
bool is_secure;
CdmCipherMode cipher_mode;
const KeyId* key_id;
const uint8_t* encrypt_buffer;
size_t encrypt_length;
@@ -271,9 +383,11 @@ struct CdmDecryptionParameters {
size_t decrypt_buffer_offset;
uint8_t subsample_flags;
bool is_video;
CdmCencPatternEncryptionDescriptor pattern_descriptor;
CdmDecryptionParameters()
: is_encrypted(true),
is_secure(true),
cipher_mode(kCipherModeCtr),
key_id(NULL),
encrypt_buffer(NULL),
encrypt_length(0),
@@ -290,6 +404,7 @@ struct CdmDecryptionParameters {
size_t offset, void* decrypted_buffer)
: is_encrypted(true),
is_secure(true),
cipher_mode(kCipherModeCtr),
key_id(key),
encrypt_buffer(encrypted_buffer),
encrypt_length(encrypted_length),
@@ -302,6 +417,12 @@ struct CdmDecryptionParameters {
is_video(true) {}
};
struct CdmKeyRequest {
CdmKeyMessage message;
CdmKeyRequestType type;
std::string url;
};
// forward class references
class KeyMessage;
class Request;

View File

@@ -15,7 +15,6 @@
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
@@ -55,11 +54,13 @@ class UsagePropertySet : public CdmClientPropertySet {
bool CdmEngine::seeded_ = false;
CdmEngine::CdmEngine()
CdmEngine::CdmEngine(FileSystem* file_system)
: cert_provisioning_(NULL),
cert_provisioning_requested_security_level_(kLevelDefault),
file_system_(file_system),
usage_session_(NULL),
last_usage_information_update_time_(0) {
assert(file_system);
Properties::Init();
if (!seeded_) {
Clock clock;
@@ -79,7 +80,22 @@ CdmEngine::~CdmEngine() {
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
const std::string& origin,
const CdmSessionId& forced_session_id,
WvCdmEventListener* event_listener) {
return OpenSession(key_system, property_set, event_listener,
&forced_session_id, NULL);
}
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener,
CdmSessionId* session_id) {
return OpenSession(key_system, property_set, event_listener, NULL,
session_id);
}
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id,
CdmSessionId* session_id) {
@@ -90,8 +106,8 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
return INVALID_KEY_SYSTEM;
}
if (!session_id) {
LOGE("CdmEngine::OpenSession: no session ID destination provided");
if (!session_id && !forced_session_id) {
LOGE("CdmEngine::OpenSession: no (forced/)session ID destination provided");
return INVALID_PARAMETERS_ENG_1;
}
@@ -101,32 +117,33 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
}
}
scoped_ptr<CdmSession> new_session(
new CdmSession(property_set, origin, event_listener, forced_session_id));
if (new_session->session_id().empty()) {
LOGE("CdmEngine::OpenSession: failure to generate session ID");
return EMPTY_SESSION_ID;
}
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
CdmResponseType sts = new_session->Init();
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
event_listener);
if (sts != NO_ERROR) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
new_session->GetRequestedSecurityLevel();
// Reserve a session ID so the CDM can return success.
if (session_id)
*session_id = new_session->GenerateSessionId();
} else {
LOGE("CdmEngine::OpenSession: bad session init: %d", sts);
}
return sts;
}
*session_id = new_session->session_id();
CdmSessionId id = new_session->session_id();
AutoLock lock(session_list_lock_);
sessions_[*session_id] = new_session.release();
sessions_[id] = new_session.release();
if (session_id) *session_id = id;
return NO_ERROR;
}
CdmResponseType CdmEngine::OpenKeySetSession(
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
const std::string& origin, WvCdmEventListener* event_listener) {
WvCdmEventListener* event_listener) {
LOGI("CdmEngine::OpenKeySetSession");
if (key_set_id.empty()) {
@@ -135,9 +152,8 @@ CdmResponseType CdmEngine::OpenKeySetSession(
}
CdmSessionId session_id;
CdmResponseType sts =
OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
NULL /* forced_session_id */, &session_id);
CdmResponseType sts = OpenSession(KEY_SYSTEM, property_set, event_listener,
NULL /* forced_session_id */, &session_id);
if (sts != NO_ERROR) return sts;
@@ -182,9 +198,7 @@ bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const InitializationData& init_data, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
CdmKeyRequestType* key_request_type, std::string* server_url,
CdmKeySetId* key_set_id_out) {
CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request) {
LOGI("CdmEngine::GenerateKeyRequest");
CdmSessionId id = session_id;
@@ -223,11 +237,11 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
}
if (!key_request) {
LOGE("CdmEngine::GenerateKeyRequest: no key request destination provided");
LOGE("CdmEngine::GenerateKeyRequest: output destination provided");
return INVALID_PARAMETERS_ENG_2;
}
key_request->clear();
key_request->message.clear();
if (license_type == kLicenseTypeRelease &&
!iter->second->license_received()) {
@@ -240,8 +254,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
}
sts = iter->second->GenerateKeyRequest(
init_data, license_type, app_parameters, key_request, key_request_type,
server_url, key_set_id_out);
init_data, license_type, app_parameters, key_request);
if (KEY_MESSAGE != sts) {
if (sts == NEED_PROVISIONING) {
@@ -300,7 +313,10 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
return EMPTY_KEY_DATA_1;
}
CdmResponseType sts = iter->second->AddKey(key_data, key_set_id);
CdmResponseType sts = iter->second->AddKey(key_data);
if (key_set_id) {
*key_set_id = iter->second->key_set_id();
}
switch (sts) {
case KEY_ADDED:
@@ -359,8 +375,7 @@ CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
}
CdmResponseType CdmEngine::GenerateRenewalRequest(
const CdmSessionId& session_id, CdmKeyMessage* key_request,
std::string* server_url) {
const CdmSessionId& session_id, CdmKeyRequest* key_request) {
LOGI("CdmEngine::GenerateRenewalRequest");
CdmSessionMap::iterator iter = sessions_.find(session_id);
@@ -371,14 +386,13 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
}
if (!key_request) {
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination");
LOGE("CdmEngine::GenerateRenewalRequest: no request destination");
return INVALID_PARAMETERS_ENG_4;
}
key_request->clear();
key_request->message.clear();
CdmResponseType sts =
iter->second->GenerateRenewalRequest(key_request, server_url);
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
if (KEY_MESSAGE != sts) {
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
@@ -414,8 +428,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
}
CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
const std::string& key,
std::string* value) {
const std::string& query_token,
std::string* query_response) {
LOGI("CdmEngine::QueryStatus");
CryptoSession crypto_session;
if (security_level == kLevel3) {
@@ -423,36 +437,41 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
if (NO_ERROR != status) return INVALID_QUERY_STATUS;
}
if (key == QUERY_KEY_SECURITY_LEVEL) {
if (!query_response) {
LOGE("CdmEngine::QueryStatus: no query response destination");
return INVALID_PARAMETERS_ENG_6;
}
if (query_token == QUERY_KEY_SECURITY_LEVEL) {
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
switch (security_level) {
case kSecurityLevelL1:
*value = QUERY_VALUE_SECURITY_LEVEL_L1;
*query_response = QUERY_VALUE_SECURITY_LEVEL_L1;
break;
case kSecurityLevelL2:
*value = QUERY_VALUE_SECURITY_LEVEL_L2;
*query_response = QUERY_VALUE_SECURITY_LEVEL_L2;
break;
case kSecurityLevelL3:
*value = QUERY_VALUE_SECURITY_LEVEL_L3;
*query_response = QUERY_VALUE_SECURITY_LEVEL_L3;
break;
case kSecurityLevelUninitialized:
case kSecurityLevelUnknown:
*value = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
*query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
LOGW("CdmEngine::QueryStatus: Unknown security level: %d",
security_level);
return UNKNOWN_ERROR;
}
} else if (key == QUERY_KEY_DEVICE_ID) {
} else if (query_token == QUERY_KEY_DEVICE_ID) {
std::string deviceId;
if (!crypto_session.GetDeviceUniqueId(&deviceId)) {
LOGW("CdmEngine::QueryStatus: GetDeviceUniqueId failed");
return UNKNOWN_ERROR;
}
*value = deviceId;
} else if (key == QUERY_KEY_SYSTEM_ID) {
*query_response = deviceId;
} else if (query_token == QUERY_KEY_SYSTEM_ID) {
uint32_t system_id;
if (!crypto_session.GetSystemId(&system_id)) {
LOGW("CdmEngine::QueryStatus: GetSystemId failed");
@@ -461,35 +480,36 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
std::ostringstream system_id_stream;
system_id_stream << system_id;
*value = system_id_stream.str();
} else if (key == QUERY_KEY_PROVISIONING_ID) {
*query_response = system_id_stream.str();
} else if (query_token == QUERY_KEY_PROVISIONING_ID) {
std::string provisioning_id;
if (!crypto_session.GetProvisioningId(&provisioning_id)) {
LOGW("CdmEngine::QueryStatus: GetProvisioningId failed");
return UNKNOWN_ERROR;
}
*value = provisioning_id;
} else if (key == QUERY_KEY_CURRENT_HDCP_LEVEL ||
key == QUERY_KEY_MAX_HDCP_LEVEL) {
*query_response = provisioning_id;
} else if (query_token == QUERY_KEY_CURRENT_HDCP_LEVEL ||
query_token == QUERY_KEY_MAX_HDCP_LEVEL) {
CryptoSession::HdcpCapability current_hdcp;
CryptoSession::HdcpCapability max_hdcp;
if (!crypto_session.GetHdcpCapabilities(&current_hdcp, &max_hdcp)) {
LOGW("CdmEngine::QueryStatus: GetHdcpCapabilities failed");
return UNKNOWN_ERROR;
}
*value = MapHdcpVersion(key == QUERY_KEY_CURRENT_HDCP_LEVEL ? current_hdcp
: max_hdcp);
} else if (key == QUERY_KEY_USAGE_SUPPORT) {
*query_response =
MapHdcpVersion(query_token == QUERY_KEY_CURRENT_HDCP_LEVEL ?
current_hdcp : max_hdcp);
} else if (query_token == QUERY_KEY_USAGE_SUPPORT) {
bool supports_usage_reporting;
if (!crypto_session.UsageInformationSupport(&supports_usage_reporting)) {
LOGW("CdmEngine::QueryStatus: UsageInformationSupport failed");
return UNKNOWN_ERROR;
}
*value = supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
} else if (key == QUERY_KEY_NUMBER_OF_OPEN_SESSIONS) {
*query_response =
supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
} else if (query_token == QUERY_KEY_NUMBER_OF_OPEN_SESSIONS) {
size_t number_of_open_sessions;
if (!crypto_session.GetNumberOfOpenSessions(&number_of_open_sessions)) {
LOGW("CdmEngine::QueryStatus: GetNumberOfOpenSessions failed");
@@ -498,8 +518,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
std::ostringstream open_sessions_stream;
open_sessions_stream << number_of_open_sessions;
*value = open_sessions_stream.str();
} else if (key == QUERY_KEY_MAX_NUMBER_OF_SESSIONS) {
*query_response = open_sessions_stream.str();
} else if (query_token == QUERY_KEY_MAX_NUMBER_OF_SESSIONS) {
size_t maximum_number_of_sessions;
if (!crypto_session.GetMaxNumberOfSessions(&maximum_number_of_sessions)) {
LOGW("CdmEngine::QueryStatus: GetMaxNumberOfOpenSessions failed");
@@ -508,8 +528,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
std::ostringstream max_sessions_stream;
max_sessions_stream << maximum_number_of_sessions;
*value = max_sessions_stream.str();
} else if (key == QUERY_KEY_OEMCRYPTO_API_VERSION) {
*query_response = max_sessions_stream.str();
} else if (query_token == QUERY_KEY_OEMCRYPTO_API_VERSION) {
uint32_t api_version;
if (!crypto_session.GetApiVersion(&api_version)) {
LOGW("CdmEngine::QueryStatus: GetApiVersion failed");
@@ -518,10 +538,10 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
std::ostringstream api_version_stream;
api_version_stream << api_version;
*value = api_version_stream.str();
*query_response = api_version_stream.str();
} else {
LOGW("CdmEngine::QueryStatus: Unknown status requested, key = %s",
key.c_str());
LOGW("CdmEngine::QueryStatus: Unknown status requested, token = %s",
query_token.c_str());
return INVALID_QUERY_KEY;
}
@@ -529,7 +549,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
}
CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info) {
CdmQueryMap* query_response) {
LOGI("CdmEngine::QuerySessionStatus");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
@@ -537,7 +557,7 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
session_id.c_str());
return SESSION_NOT_FOUND_8;
}
return iter->second->QueryStatus(key_info);
return iter->second->QueryStatus(query_response);
}
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
@@ -563,7 +583,7 @@ bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
}
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info) {
CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryKeyStatus");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
@@ -571,19 +591,72 @@ CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
session_id.c_str());
return SESSION_NOT_FOUND_9;
}
return iter->second->QueryKeyStatus(key_info);
return iter->second->QueryKeyStatus(query_response);
}
CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyControlInfo");
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
const std::string& key_id,
CdmKeyAllowedUsage* key_usage) {
LOGI("CdmEngine::QueryKeyAllowedUsage");
if (!key_usage) {
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
return INVALID_PARAMETERS_ENG_12;
}
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s",
LOGE("CdmEngine::QueryKeyAllowedUsage: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_12;
}
return iter->second->QueryKeyAllowedUsage(key_id, key_usage);
}
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
CdmKeyAllowedUsage* key_usage) {
LOGI("CdmEngine::QueryKeyAllowedUsage (all sessions)");
CdmResponseType session_sts;
CdmKeyAllowedUsage found_in_this_session;
bool found = false;
if (!key_usage) {
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
return INVALID_PARAMETERS_ENG_7;
}
key_usage->Clear();
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
session_sts = iter->second->QueryKeyAllowedUsage(key_id,
&found_in_this_session);
if (session_sts == NO_ERROR) {
if (found) {
// Found another key. If usage settings do not match, fail.
if (!key_usage->Equals(found_in_this_session)) {
key_usage->Clear();
return KEY_CONFLICT_1;
}
} else {
*key_usage = found_in_this_session;
found = true;
}
} else if (session_sts != KEY_NOT_FOUND_1) {
LOGE("CdmEngine::QueryKeyAllowedUsage (all sessions) FAILED = %d",
session_sts);
key_usage->Clear();
return session_sts;
}
}
return (found) ? NO_ERROR : KEY_NOT_FOUND_2;
}
CdmResponseType CdmEngine::QueryOemCryptoSessionId(
const CdmSessionId& session_id, CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryOemCryptoSessionId");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryOemCryptoSessionId: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_10;
}
return iter->second->QueryKeyControlInfo(key_info);
return iter->second->QueryOemCryptoSessionId(query_response);
}
/*
@@ -595,8 +668,7 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
*/
CdmResponseType CdmEngine::GetProvisioningRequest(
CdmCertificateType cert_type, const std::string& cert_authority,
const std::string& origin, CdmProvisioningRequest* request,
std::string* default_url) {
CdmProvisioningRequest* request, std::string* default_url) {
if (!request) {
LOGE("CdmEngine::GetProvisioningRequest: invalid output parameters");
return INVALID_PROVISIONING_REQUEST_PARAM_1;
@@ -613,7 +685,7 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
}
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_type, cert_authority,
origin, request, default_url);
file_system_->origin(), request, default_url);
if (ret != NO_ERROR) {
cert_provisioning_.reset(NULL); // Release resources.
}
@@ -628,8 +700,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
* Returns NO_ERROR for success and CdmResponseType error code if fails.
*/
CdmResponseType CdmEngine::HandleProvisioningResponse(
const std::string& origin, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
const CdmProvisioningResponse& response, std::string* cert,
std::string* wrapped_key) {
if (response.empty()) {
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
cert_provisioning_.reset(NULL);
@@ -661,7 +733,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
return EMPTY_PROVISIONING_CERTIFICATE_2;
}
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
if (!IsProvisioned(security_level, origin)) {
if (!IsProvisioned(security_level)) {
LOGE(
"CdmEngine::HandleProvisioningResponse: provisioning object "
"missing.");
@@ -671,7 +743,7 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
}
CdmResponseType ret = cert_provisioning_->HandleProvisioningResponse(
origin, response, cert, wrapped_key);
file_system_, response, cert, wrapped_key);
// Release resources only on success. It is possible that a provisioning
// attempt was made after this one was requested but before the response was
// received, which will cause this attempt to fail. Not releasing will
@@ -680,28 +752,25 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
return ret;
}
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level,
const std::string& origin) {
DeviceFiles handle;
bool CdmEngine::IsProvisioned(CdmSecurityLevel security_level) {
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::IsProvisioned: unable to initialize device files");
return false;
}
return handle.HasCertificate(origin);
return handle.HasCertificate();
}
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level,
const std::string& origin) {
DeviceFiles handle;
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::Unprovision: unable to initialize device files");
return UNPROVISION_ERROR_1;
}
if (origin != EMPTY_ORIGIN) {
if (!handle.RemoveCertificate(origin)) {
LOGE("CdmEngine::Unprovision: unable to delete certificate for origin %s",
origin.c_str());
if (!file_system_->origin().empty()) {
if (!handle.RemoveCertificate()) {
LOGE("CdmEngine::Unprovision: unable to delete certificate");
return UNPROVISION_ERROR_2;
}
return NO_ERROR;
@@ -732,16 +801,19 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
}
if (!usage_info) {
LOGE("CdmEngine::GetUsageInfo: no usage info destination");
return INVALID_PARAMETERS_ENG_8;
}
usage_property_set_->set_security_level(kLevelDefault);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
CdmResponseType status = usage_session_->Init();
usage_session_.reset(new CdmSession(file_system_));
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
return status;
}
DeviceFiles handle;
DeviceFiles handle(file_system_);
if (!handle.Init(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: device file init error");
return GET_USAGE_INFO_ERROR_1;
@@ -753,9 +825,8 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
&license_response)) {
usage_property_set_->set_security_level(kLevel3);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
status = usage_session_->Init();
usage_session_.reset(new CdmSession(file_system_));
status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
return status;
@@ -771,18 +842,20 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
}
}
std::string server_url;
usage_info->resize(1);
status =
usage_session_->RestoreUsageSession(license_request, license_response);
usage_session_->RestoreUsageSession(license_request,license_response);
if (KEY_ADDED != status) {
LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status);
usage_info->clear();
return status;
}
status =
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
CdmKeyRequest request;
status = usage_session_->GenerateReleaseRequest(&request);
usage_info->clear();
usage_info->push_back(request.message);
if (KEY_MESSAGE != status) {
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d", status);
@@ -797,6 +870,10 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
// Return a random usage report from a random security level
SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3;
CdmResponseType status = UNKNOWN_ERROR;
if (!usage_info) {
LOGE("CdmEngine::GetUsageInfo: no usage info destination");
return INVALID_PARAMETERS_ENG_9;
}
do {
status = GetUsageInfo(app_id, security_level, usage_info);
@@ -822,16 +899,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
usage_property_set_->set_security_level(requested_security_level);
usage_property_set_->set_app_id(app_id);
usage_session_.reset(
new CdmSession(usage_property_set_.get(), EMPTY_ORIGIN, NULL, NULL));
usage_session_.reset(new CdmSession(file_system_));
CdmResponseType status = usage_session_->Init();
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
if (NO_ERROR != status) {
LOGE("CdmEngine::GetUsageInfo: session init error");
return status;
}
DeviceFiles handle;
DeviceFiles handle(file_system_);
if (!handle.Init(usage_session_->GetSecurityLevel())) {
LOGE("CdmEngine::GetUsageInfo: unable to initialize device files");
return GET_USAGE_INFO_ERROR_3;
@@ -843,12 +919,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
return GET_USAGE_INFO_ERROR_4;
}
if (!usage_info) {
LOGE("CdmEngine::GetUsageInfo: no usage info destination");
return INVALID_PARAMETERS_ENG_10;
}
if (0 == license_info.size()) {
usage_info->resize(0);
return NO_ERROR;
}
std::string server_url;
usage_info->resize(kUsageReportsPerRequest);
uint32_t index = rand() % license_info.size();
@@ -861,8 +940,11 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
return status;
}
status =
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
CdmKeyRequest request;
status = usage_session_->GenerateReleaseRequest(&request);
usage_info->clear();
usage_info->push_back(request.message);
switch (status) {
case KEY_MESSAGE:
@@ -888,7 +970,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
CdmResponseType status = NO_ERROR;
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
DeviceFiles handle;
DeviceFiles handle(file_system_);
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
@@ -901,9 +983,8 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
? kLevel3
: kLevelDefault;
usage_property_set_->set_security_level(security_level);
usage_session_.reset(
new CdmSession(usage_property_set_.get(),
EMPTY_ORIGIN, NULL, NULL));
usage_session_.reset(new CdmSession(file_system_));
usage_session_->Init(usage_property_set_.get());
CdmResponseType status2 = usage_session_->
DeleteMultipleUsageInformation(provider_session_tokens);
if (status2 != NO_ERROR) {
@@ -954,7 +1035,12 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
return SESSION_NOT_FOUND_11;
}
DeviceFiles handle;
if (!release_message) {
LOGE("CdmEngine::LoadUsageSession: no release message destination");
return INVALID_PARAMETERS_ENG_11;
}
DeviceFiles handle(file_system_);
if (!handle.Init(iter->second->GetSecurityLevel())) {
LOGE("CdmEngine::LoadUsageSession: unable to initialize device files");
return LOAD_USAGE_INFO_FILE_ERROR;
@@ -978,8 +1064,9 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
return status;
}
std::string server_url;
status = iter->second->GenerateReleaseRequest(release_message, &server_url);
CdmKeyRequest request;
status = iter->second->GenerateReleaseRequest(&request);
*release_message = request.message;
switch (status) {
case KEY_MESSAGE:
@@ -1021,24 +1108,86 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
// else we must be level 1 direct and we don't need to return a buffer.
}
CdmSessionMap::iterator iter;
CdmSessionMap::iterator session_iter = sessions_.end();
if (session_id.empty()) {
// Loop through the sessions to find the session containing the key_id.
for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
// Loop through the sessions to find the session containing the key_id
// with the longest remaining license validity.
int64_t seconds_remaining = 0;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyLoaded(*parameters.key_id)) {
break;
int64_t duration = iter->second->GetDurationRemaining();
if (duration > seconds_remaining) {
session_iter = iter;
seconds_remaining = duration;
}
}
}
} else {
iter = sessions_.find(session_id);
session_iter = sessions_.find(session_id);
}
if (iter == sessions_.end()) {
if (session_iter == sessions_.end()) {
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.c_str(), session_id.size());
return SESSION_NOT_FOUND_FOR_DECRYPT;
}
return iter->second->Decrypt(parameters);
return session_iter->second->Decrypt(parameters);
}
CdmResponseType CdmEngine::GenericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm, std::string* out_buffer) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenericEncrypt: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_13;
}
return iter->second->GenericEncrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
}
CdmResponseType CdmEngine::GenericDecrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenericDecrypt: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_14;
}
return iter->second->GenericDecrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
}
CdmResponseType CdmEngine::GenericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
std::string* signature) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenericSign: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_15;
}
return iter->second->GenericSign(message, key_id, algorithm, signature);
}
CdmResponseType CdmEngine::GenericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
const std::string& signature) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenericVerify: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_16;
}
return iter->second->GenericVerify(message, key_id, algorithm, signature);
}
bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
@@ -1058,25 +1207,30 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
return false;
}
CdmSessionMap::iterator iter = sessions_.find(*session_id);
if (iter != sessions_.end()) {
if (iter->second->IsKeyLoaded(key_id)) {
return true;
}
}
uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id);
for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
CdmSessionMap::iterator session_iter = sessions_.end();
int64_t seconds_remaining = 0;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
CdmSessionId local_session_id = iter->second->session_id();
if (Properties::GetSessionSharingId(local_session_id) ==
session_sharing_id) {
if (iter->second->IsKeyLoaded(key_id)) {
*session_id = local_session_id;
return true;
int64_t duration = iter->second->GetDurationRemaining();
if (duration > seconds_remaining) {
session_iter = iter;
seconds_remaining = duration;
}
}
}
}
if (session_iter != sessions_.end()) {
*session_id = session_iter->second->session_id();
return true;
}
return false;
}
@@ -1172,9 +1326,8 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
Properties::GetDeviceFilesBasePath(kSecurityLevelL3,
&device_base_path_level3);
File file;
if (!file.Exists(device_base_path_level1) &&
!file.Exists(device_base_path_level3)) {
if (!file_system_->Exists(device_base_path_level1) &&
!file_system_->Exists(device_base_path_level3)) {
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
cert_provisioning_requested_security_level_);

View File

@@ -10,8 +10,6 @@
#include "cdm_engine.h"
#include "clock.h"
#include "crypto_session.h"
#include "device_files.h"
#include "file_store.h"
#include "log.h"
#include "properties.h"
@@ -25,47 +23,22 @@ const size_t kKeySetIdLength = 14;
namespace wvcdm {
CdmSession::CdmSession(CdmClientPropertySet* cdm_client_property_set,
const std::string& origin,
WvCdmEventListener* event_listener,
const CdmSessionId* forced_session_id)
: initialized_(false),
session_id_(GenerateSessionId()),
origin_(origin),
crypto_session_(new CryptoSession),
file_handle_(new DeviceFiles),
license_received_(false),
is_offline_(false),
is_release_(false),
is_temporary_(false),
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_decryption_(true),
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false) {
if (Properties::AlwaysUseKeySetIds()) {
if (forced_session_id) {
key_set_id_ = *forced_session_id;
} else {
bool ok = GenerateKeySetId(&key_set_id_);
(void)ok; // ok is now used when assertions are turned off.
assert(ok);
}
session_id_ = key_set_id_;
}
license_parser_.reset(new CdmLicense(session_id_));
policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get()));
if (cdm_client_property_set) {
if (cdm_client_property_set->security_level() ==
QUERY_VALUE_SECURITY_LEVEL_L3) {
requested_security_level_ = kLevel3;
security_level_ = kSecurityLevelL3;
}
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
}
}
CdmSession::CdmSession(FileSystem* file_system) :
initialized_(false),
crypto_session_(new CryptoSession),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
is_offline_(false),
is_release_(false),
is_temporary_(false),
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_decryption_(true),
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {}
CdmSession::~CdmSession() {
if (!key_set_id_.empty()) {
@@ -75,24 +48,38 @@ CdmSession::~CdmSession() {
Properties::RemoveSessionPropertySet(session_id_);
}
CdmResponseType CdmSession::Init() {
if (session_id_.empty()) {
LOGE("CdmSession::Init: Failed, session not properly constructed");
return SESSION_INIT_ERROR_1;
}
CdmResponseType CdmSession::Init(
CdmClientPropertySet* cdm_client_property_set) {
return Init(cdm_client_property_set, NULL, NULL);
}
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id,
WvCdmEventListener* event_listener) {
if (initialized_) {
LOGE("CdmSession::Init: Failed due to previous initialization");
return SESSION_INIT_ERROR_2;
}
if (cdm_client_property_set &&
cdm_client_property_set->security_level() ==
QUERY_VALUE_SECURITY_LEVEL_L3) {
requested_security_level_ = kLevel3;
security_level_ = kSecurityLevelL3;
}
CdmResponseType sts = crypto_session_->Open(requested_security_level_);
if (NO_ERROR != sts) return sts;
security_level_ = crypto_session_->GetSecurityLevel();
if (!file_handle_->Init(security_level_)) {
LOGE("CdmSession::Init: Unable to initialize file handle");
return SESSION_FILE_HANDLE_INIT_ERROR;
}
std::string token;
if (Properties::use_certificates_as_identification()) {
std::string wrapped_key;
if (!file_handle_->Init(security_level_) ||
!file_handle_->RetrieveCertificate(origin_, &token, &wrapped_key) ||
if (!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING;
}
@@ -101,6 +88,30 @@ CdmResponseType CdmSession::Init() {
return SESSION_INIT_GET_KEYBOX_ERROR;
}
if (forced_session_id) {
key_set_id_ = *forced_session_id;
} else {
bool ok = GenerateKeySetId(&key_set_id_);
(void)ok; // ok is now used when assertions are turned off.
assert(ok);
}
session_id_ =
Properties::AlwaysUseKeySetIds() ? key_set_id_ : GenerateSessionId();
if (session_id_.empty()) {
LOGE("CdmSession::Init: empty session ID");
return EMPTY_SESSION_ID;
}
if (cdm_client_property_set)
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
if (!mock_license_parser_in_use_)
license_parser_.reset(new CdmLicense(session_id_));
if (!mock_policy_engine_in_use_)
policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get()));
if (!license_parser_->Init(token, crypto_session_.get(),
policy_engine_.get()))
return LICENSE_PARSER_INIT_ERROR;
@@ -115,10 +126,6 @@ CdmResponseType CdmSession::RestoreOfflineSession(
const CdmKeySetId& key_set_id, const CdmLicenseType license_type) {
key_set_id_ = key_set_id;
// Retrieve license information from persistent store
if (!file_handle_->Reset(security_level_))
return RESTORE_OFFLINE_LICENSE_ERROR_1;
DeviceFiles::LicenseState license_state;
int64_t playback_start_time;
int64_t last_playback_time;
@@ -177,9 +184,9 @@ CdmResponseType CdmSession::RestoreUsageSession(
CdmResponseType CdmSession::GenerateKeyRequest(
const InitializationData& init_data, CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
CdmKeyRequestType* key_request_type, std::string* server_url,
CdmKeySetId* key_set_id) {
const CdmAppParameterMap& app_parameters,
CdmKeyRequest* key_request) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
return INVALID_CRYPTO_SESSION_1;
@@ -190,6 +197,11 @@ CdmResponseType CdmSession::GenerateKeyRequest(
return CRYPTO_SESSION_OPEN_ERROR_1;
}
if (!key_request) {
LOGE("CdmSession::GenerateKeyRequest: No output destination provided");
return INVALID_PARAMETERS_ENG_5;
}
switch (license_type) {
case kLicenseTypeTemporary:
is_temporary_ = true;
@@ -230,13 +242,12 @@ CdmResponseType CdmSession::GenerateKeyRequest(
}
if (is_release_) {
if (key_request_type) *key_request_type = kKeyRequestTypeRelease;
return GenerateReleaseRequest(key_request, server_url);
return GenerateReleaseRequest(key_request);
} else if (license_received_) { // renewal
if (key_request_type) *key_request_type = kKeyRequestTypeRenewal;
return GenerateRenewalRequest(key_request, server_url);
return GenerateRenewalRequest(key_request);
} else {
if (key_request_type) *key_request_type = kKeyRequestTypeInitial;
key_request->type = kKeyRequestTypeInitial;
if (!license_parser_->HasInitData()) {
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
@@ -248,8 +259,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
return INIT_DATA_NOT_FOUND;
}
}
if (is_offline_ && key_set_id_.empty() &&
!GenerateKeySetId(&key_set_id_)) {
if (is_offline_ && key_set_id_.empty()) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
return KEY_REQUEST_ERROR_1;
}
@@ -257,24 +267,24 @@ CdmResponseType CdmSession::GenerateKeyRequest(
app_parameters_ = app_parameters;
CdmResponseType status = license_parser_->PrepareKeyRequest(
init_data, license_type,
app_parameters, key_request, server_url);
app_parameters, &key_request->message,
&key_request->url);
if (KEY_MESSAGE != status) return status;
key_request_ = *key_request;
key_request_ = key_request->message;
if (is_offline_) {
offline_init_data_ = init_data.data();
offline_release_server_url_ = *server_url;
offline_release_server_url_ = key_request->url;
}
if (key_set_id) *key_set_id = key_set_id_;
return KEY_MESSAGE;
}
}
// AddKey() - Accept license response and extract key info.
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
CdmKeySetId* key_set_id) {
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::AddKey: Invalid crypto session");
return INVALID_CRYPTO_SESSION_2;
@@ -303,12 +313,11 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
if (sts != NO_ERROR) return sts;
}
if (key_set_id) *key_set_id = key_set_id_;
return KEY_ADDED;
}
}
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* query_response) {
if (crypto_session_.get() == NULL) {
LOGE("CdmSession::QueryStatus: Invalid crypto session");
return INVALID_CRYPTO_SESSION_3;
@@ -321,17 +330,20 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
switch (security_level_) {
case kSecurityLevelL1:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_L1;
break;
case kSecurityLevelL2:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2;
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_L2;
break;
case kSecurityLevelL3:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_L3;
break;
case kSecurityLevelUninitialized:
case kSecurityLevelUnknown:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] =
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
@@ -340,24 +352,30 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
return NO_ERROR;
}
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
return policy_engine_->Query(key_info);
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* query_response) {
return policy_engine_->Query(query_response);
}
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
CdmResponseType CdmSession::QueryKeyAllowedUsage(
const std::string& key_id, CdmKeyAllowedUsage* key_usage) {
return policy_engine_->QueryKeyAllowedUsage(key_id, key_usage);
}
CdmResponseType CdmSession::QueryOemCryptoSessionId(
CdmQueryMap* query_response) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::QueryKeyControlInfo: Invalid crypto session");
LOGW("CdmSession::QueryOemCryptoSessionId: Invalid crypto session");
return INVALID_CRYPTO_SESSION_4;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::QueryKeyControlInfo: Crypto session not open");
LOGW("CdmSession::QueryOemCryptoSessionId: Crypto session not open");
return CRYPTO_SESSION_OPEN_ERROR_4;
}
std::stringstream ss;
ss << crypto_session_->oec_session_id();
(*key_info)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str();
(*query_response)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str();
return NO_ERROR;
}
@@ -405,15 +423,17 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// License renewal
// GenerateRenewalRequest() - Construct valid renewal request for the current
// session keys.
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
std::string* server_url) {
CdmResponseType CdmSession::GenerateRenewalRequest(
CdmKeyRequest* key_request) {
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
true, app_parameters_, key_request, server_url);
true, app_parameters_, &key_request->message, &key_request->url);
key_request->type = kKeyRequestTypeRenewal;
if (KEY_MESSAGE != status) return status;
if (is_offline_) {
offline_key_renewal_request_ = *key_request;
offline_key_renewal_request_ = key_request->message;
}
return KEY_MESSAGE;
}
@@ -432,11 +452,14 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
return KEY_ADDED;
}
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
std::string* server_url) {
CdmResponseType CdmSession::GenerateReleaseRequest(
CdmKeyRequest* key_request) {
is_release_ = true;
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
false, app_parameters_, key_request, server_url);
false, app_parameters_, &key_request->message,
&key_request->url);
key_request->type = kKeyRequestTypeRelease;
if (KEY_MESSAGE != status) return status;
@@ -463,6 +486,11 @@ bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
return license_parser_->IsKeyLoaded(key_id);
}
int64_t CdmSession::GetDurationRemaining() {
if (policy_engine_->IsLicenseForFuture()) return 0;
return policy_engine_->GetLicenseOrPlaybackDurationRemaining();
}
CdmSessionId CdmSession::GenerateSessionId() {
static int session_num = 1;
return SESSION_ID_PREFIX + IntToString(++session_num);
@@ -477,8 +505,6 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
std::vector<uint8_t> random_data(
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
if (!file_handle_->Reset(security_level_)) return false;
while (key_set_id->empty()) {
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0]))
return false;
@@ -514,13 +540,6 @@ CdmResponseType CdmSession::StoreLicense() {
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::StoreLicense: Unable to store license");
CdmResponseType sts = Init();
if (sts != NO_ERROR) {
LOGW("CdmSession::StoreLicense: Reinitialization failed");
return sts;
}
key_set_id_.clear();
return STORE_LICENSE_ERROR_1;
}
return NO_ERROR;
@@ -533,11 +552,6 @@ CdmResponseType CdmSession::StoreLicense() {
return STORE_LICENSE_ERROR_2;
}
if (!file_handle_->Reset(security_level_)) {
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
return STORE_LICENSE_ERROR_3;
}
std::string app_id;
GetApplicationId(&app_id);
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
@@ -549,8 +563,6 @@ CdmResponseType CdmSession::StoreLicense() {
}
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
if (!file_handle_->Reset(security_level_)) return false;
return file_handle_->StoreLicense(
key_set_id_, state, offline_init_data_, key_request_, key_response_,
offline_key_renewal_request_, offline_key_renewal_response_,
@@ -558,14 +570,15 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
policy_engine_->GetLastPlaybackTime(), app_parameters_);
}
CdmResponseType CdmSession::ReleaseCrypto() {
crypto_session_->Close();
return NO_ERROR;
}
bool CdmSession::DeleteLicense() {
if (!is_offline_ && license_parser_->provider_session_token().empty())
return false;
if (!file_handle_->Reset(security_level_)) {
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
return false;
}
if (is_offline_) {
return file_handle_->DeleteLicense(key_set_id_);
} else {
@@ -613,13 +626,55 @@ CdmResponseType CdmSession::UpdateUsageInformation() {
return crypto_session_->UpdateUsageInformation();
}
CdmResponseType CdmSession::ReleaseCrypto() {
crypto_session_->Close();
return NO_ERROR;
CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
if (!out_buffer) {
LOGE("CdmSession::GenericEncrypt: No output destination provided");
return INVALID_PARAMETERS_ENG_6;
}
return crypto_session_->GenericEncrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
}
CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
if (!out_buffer) {
LOGE("CdmSession::GenericDecrypt: No output destination provided");
return INVALID_PARAMETERS_ENG_7;
}
return crypto_session_->GenericDecrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
}
CdmResponseType CdmSession::GenericSign(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
std::string* signature) {
if (!signature) {
LOGE("CdmSession::GenericSign: No output destination provided");
return INVALID_PARAMETERS_ENG_8;
}
return crypto_session_->GenericSign(message, key_id, algorithm, signature);
}
CdmResponseType CdmSession::GenericVerify(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
const std::string& signature) {
return crypto_session_->GenericVerify(message, key_id, algorithm, signature);
}
// For testing only - takes ownership of pointers
void CdmSession::set_license_parser(CdmLicense* license_parser) {
license_parser_.reset(license_parser);
mock_license_parser_in_use_ = true;
}
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
@@ -628,6 +683,7 @@ void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {
policy_engine_.reset(policy_engine);
mock_policy_engine_in_use_ = true;
}
void CdmSession::set_file_handle(DeviceFiles* file_handle) {

View File

@@ -189,7 +189,7 @@ bool CertificateProvisioning::ParseJsonResponse(
* Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails.
*/
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
const std::string& origin, const CdmProvisioningResponse& response,
FileSystem* file_system, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
@@ -259,12 +259,12 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
const std::string& device_certificate =
provisioning_response.device_certificate();
DeviceFiles handle;
DeviceFiles handle(file_system);
if (!handle.Init(crypto_session_.GetSecurityLevel())) {
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
return CERT_PROVISIONING_RESPONSE_ERROR_7;
}
if (!handle.StoreCertificate(origin, device_certificate, wrapped_rsa_key)) {
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
return CERT_PROVISIONING_RESPONSE_ERROR_8;
}

View File

@@ -40,7 +40,8 @@ CryptoSession::CryptoSession()
update_usage_table_after_close_session_(false),
is_destination_buffer_type_valid_(false),
requested_security_level_(kLevelDefault),
request_id_base_(0) {
request_id_base_(0),
cipher_mode_(kCipherModeCtr) {
Init();
}
@@ -55,17 +56,19 @@ void CryptoSession::Init() {
LOGV("CryptoSession::Init");
AutoLock auto_lock(crypto_lock_);
session_count_ += 1;
if (initialized_) return;
OEMCryptoResult sts = OEMCrypto_Initialize();
if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_Initialize failed: %d", sts);
return;
if (!initialized_) {
OEMCryptoResult sts = OEMCrypto_Initialize();
if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_Initialize failed: %d", sts);
return;
}
initialized_ = true;
}
initialized_ = true;
}
void CryptoSession::Terminate() {
LOGV("CryptoSession::Terminate");
LOGE("CryptoSession::Terminate: initialized_=%d, session_count_=%d",
initialized_, session_count_);
AutoLock auto_lock(crypto_lock_);
if (session_count_ > 0) {
session_count_ -= 1;
@@ -233,6 +236,10 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
return true;
}
uint8_t CryptoSession::GetSecurityPatchLevel() {
return OEMCrypto_Security_Patch_Level(requested_security_level_);
}
CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
LOGV("CryptoSession::Open: Lock");
AutoLock auto_lock(crypto_lock_);
@@ -247,16 +254,15 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
LOGV("OpenSession: id= %ld", (uint32_t)oec_session_id_);
open_ = true;
} else if (OEMCrypto_ERROR_TOO_MANY_SESSIONS == sts) {
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
session_count_, (int)initialized_);
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d",
sts, session_count_, (int)initialized_);
return INSUFFICIENT_CRYPTO_RESOURCES;
}
if (!open_) {
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d", sts,
session_count_, (int)initialized_);
LOGE("OEMCrypto_Open failed: %d, open sessions: %ld, initialized: %d",
sts, session_count_, (int)initialized_);
return UNKNOWN_ERROR;
}
OEMCrypto_GetRandom(reinterpret_cast<uint8_t*>(&request_id_base_),
sizeof(request_id_base_));
++request_id_index_;
@@ -412,6 +418,10 @@ CdmResponseType CryptoSession::LoadKeys(
ko->key_control_iv = NULL;
ko->key_control = NULL;
}
ko->cipher_mode = ki->cipher_mode() == kCipherModeCbc
? OEMCrypto_CipherMode_CBC
: OEMCrypto_CipherMode_CTR;
cipher_mode_ = ki->cipher_mode();
}
uint8_t* pst = NULL;
if (!provider_session_token.empty()) {
@@ -498,12 +508,22 @@ bool CryptoSession::RefreshKeys(const std::string& message,
}
bool CryptoSession::SelectKey(const std::string& key_id) {
// Crypto session lock already locked.
if (!cached_key_id_.empty() && cached_key_id_ == key_id) {
// Already using the desired key.
return true;
}
cached_key_id_ = key_id;
const uint8_t* key_id_string =
reinterpret_cast<const uint8_t*>(key_id.data());
reinterpret_cast<const uint8_t*>(cached_key_id_.data());
OEMCryptoResult sts =
OEMCrypto_SelectKey(oec_session_id_, key_id_string, key_id.size());
OEMCrypto_SelectKey(oec_session_id_, key_id_string,
cached_key_id_.size());
if (OEMCrypto_SUCCESS != sts) {
cached_key_id_.clear();
return false;
}
return true;
@@ -558,78 +578,76 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
bool CryptoSession::GenerateSignature(const std::string& message,
std::string* signature) {
LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_);
if (!signature) return false;
if (!signature) {
LOGE("GenerateSignature: null signature string");
return false;
}
OEMCryptoResult sts;
size_t length = signature->size();
OEMCryptoResult sts = OEMCrypto_GenerateSignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
if (OEMCrypto_SUCCESS != sts) {
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
}
// Retry with proper-sized signature buffer
signature->resize(length);
// At most two attempts.
// The first attempt may fail due to buffer too short
for (int i = 0; i < 2; ++i) {
sts = OEMCrypto_GenerateSignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
if (OEMCrypto_SUCCESS == sts) {
// Trim signature buffer and done
signature->resize(length);
return true;
}
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
break;
}
// Retry with proper-sized signature buffer
signature->resize(length);
}
// Trim signature buffer
signature->resize(length);
return true;
LOGE("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
return false;
}
bool CryptoSession::GenerateRsaSignature(const std::string& message,
std::string* signature) {
LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_);
if (!signature) return false;
if (!signature) {
LOGE("GenerateRsaSignature: null signature string");
return false;
}
OEMCryptoResult sts;
signature->resize(kRsaSignatureLength);
size_t length = signature->size();
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())), &length,
kSign_RSASSA_PSS);
if (OEMCrypto_SUCCESS != sts) {
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
// Retry with proper-sized signature buffer
signature->resize(length);
// At most two attempts.
// The first attempt may fail due to buffer too short
for (int i = 0; i < 2; ++i) {
sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length, kSign_RSASSA_PSS);
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
if (OEMCrypto_SUCCESS == sts) {
// Trim signature buffer and done
signature->resize(length);
return true;
}
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
break;
}
// Retry with proper-sized signature buffer
signature->resize(length);
}
// Trim signature buffer
signature->resize(length);
return true;
LOGE("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
return false;
}
CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
@@ -666,27 +684,32 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
}
OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED;
if (!params.is_encrypted) {
if (!params.is_encrypted &&
params.subsample_flags ==
(OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)) {
sts = OEMCrypto_CopyBuffer(requested_security_level_,
params.encrypt_buffer, params.encrypt_length,
&buffer_descriptor, params.subsample_flags);
}
if (params.is_encrypted && params.cipher_mode != cipher_mode_) {
return INCORRECT_CRYPTO_MODE;
}
if (params.is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
OEMCrypto_CENCEncryptPatternDesc pattern_descriptor;
pattern_descriptor.encrypt = params.pattern_descriptor.encrypt_blocks;
pattern_descriptor.skip = params.pattern_descriptor.skip_blocks;
pattern_descriptor.offset = params.pattern_descriptor.offset_blocks;
AutoLock auto_lock(crypto_lock_);
// Check if key needs to be selected
if (params.is_encrypted) {
if (key_id_ != *params.key_id) {
if (SelectKey(*params.key_id)) {
key_id_ = *params.key_id;
} else {
return NEED_KEY;
}
if (!SelectKey(*params.key_id)) {
return NEED_KEY;
}
}
sts = OEMCrypto_DecryptCTR(
sts = OEMCrypto_DecryptCENC(
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
params.is_encrypted, &(*params.iv).front(), params.block_offset,
&buffer_descriptor, params.subsample_flags);
&buffer_descriptor, &pattern_descriptor, params.subsample_flags);
}
switch (sts) {
@@ -696,6 +719,13 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
return INSUFFICIENT_CRYPTO_RESOURCES;
case OEMCrypto_ERROR_KEY_EXPIRED:
return NEED_KEY;
case OEMCrypto_ERROR_INVALID_SESSION:
return SESSION_NOT_FOUND_FOR_DECRYPT;
case OEMCrypto_ERROR_DECRYPT_FAILED:
case OEMCrypto_ERROR_UNKNOWN_FAILURE:
return DECRYPT_ERROR;
case OEMCrypto_ERROR_INSUFFICIENT_HDCP:
return INSUFFICIENT_OUTPUT_PROTECTION;
default:
return UNKNOWN_ERROR;
}
@@ -1076,4 +1106,200 @@ bool CryptoSession::GetMaxNumberOfSessions(size_t* max) {
return true;
}
CdmResponseType CryptoSession::GenericEncrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
LOGV("GenericEncrypt: id=%ld", (uint32_t)oec_session_id_);
if (!out_buffer) return INVALID_PARAMETERS_ENG_9;
OEMCrypto_Algorithm oec_algorithm = GenericEncryptionAlgorithm(algorithm);
if (iv.size() != GenericEncryptionBlockSize(algorithm) ||
oec_algorithm == kInvalidAlgorithm) {
return INVALID_PARAMETERS_ENG_13;
}
if (out_buffer->size() < in_buffer.size()) {
out_buffer->resize(in_buffer.size());
}
AutoLock auto_lock(crypto_lock_);
if (!SelectKey(key_id)) {
return KEY_ERROR_1;
}
OEMCryptoResult sts = OEMCrypto_Generic_Encrypt(
oec_session_id_, reinterpret_cast<const uint8_t*>(in_buffer.data()),
in_buffer.size(), reinterpret_cast<const uint8_t*>(iv.data()),
oec_algorithm,
reinterpret_cast<uint8_t*>(const_cast<char*>(out_buffer->data())));
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenericEncrypt: OEMCrypto_Generic_Encrypt err=%d", sts);
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
return KEY_NOT_FOUND_3;
} else {
return UNKNOWN_ERROR;
}
}
return NO_ERROR;
}
CdmResponseType CryptoSession::GenericDecrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
LOGV("GenericDecrypt: id=%ld", (uint32_t)oec_session_id_);
if (!out_buffer) return INVALID_PARAMETERS_ENG_10;
OEMCrypto_Algorithm oec_algorithm = GenericEncryptionAlgorithm(algorithm);
if (iv.size() != GenericEncryptionBlockSize(algorithm) ||
oec_algorithm == kInvalidAlgorithm) {
return INVALID_PARAMETERS_ENG_14;
}
if (out_buffer->size() < in_buffer.size()) {
out_buffer->resize(in_buffer.size());
}
AutoLock auto_lock(crypto_lock_);
if (!SelectKey(key_id)) {
return KEY_ERROR_2;
}
OEMCryptoResult sts = OEMCrypto_Generic_Decrypt(
oec_session_id_, reinterpret_cast<const uint8_t*>(in_buffer.data()),
in_buffer.size(), reinterpret_cast<const uint8_t*>(iv.data()),
oec_algorithm,
reinterpret_cast<uint8_t*>(const_cast<char*>(out_buffer->data())));
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenericDecrypt: OEMCrypto_Generic_Decrypt err=%d", sts);
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
return KEY_NOT_FOUND_4;
} else {
return UNKNOWN_ERROR;
}
}
return NO_ERROR;
}
CdmResponseType CryptoSession::GenericSign(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
std::string* signature) {
LOGV("GenericSign: id=%ld", (uint32_t)oec_session_id_);
if (!signature) {
LOGE("GenerateSign: null signature string");
return INVALID_PARAMETERS_ENG_11;
}
OEMCrypto_Algorithm oec_algorithm = GenericSigningAlgorithm(algorithm);
if (oec_algorithm == kInvalidAlgorithm) {
return INVALID_PARAMETERS_ENG_15;
}
OEMCryptoResult sts;
size_t length = signature->size();
AutoLock auto_lock(crypto_lock_);
if (!SelectKey(key_id)) {
return KEY_ERROR_3;
}
// At most two attempts.
// The first attempt may fail due to buffer too short
for (int i = 0; i < 2; ++i) {
sts = OEMCrypto_Generic_Sign(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), oec_algorithm,
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())),
&length);
if (OEMCrypto_SUCCESS == sts) {
// Trim signature buffer and done
signature->resize(length);
return NO_ERROR;
}
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
break;
}
// Retry with proper-sized return buffer
signature->resize(length);
}
LOGE("GenericSign: OEMCrypto_Generic_Sign err=%d", sts);
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
return KEY_NOT_FOUND_5;
} else {
return UNKNOWN_ERROR;
}
}
CdmResponseType CryptoSession::GenericVerify(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
const std::string& signature) {
LOGV("GenericVerify: id=%ld", (uint32_t)oec_session_id_);
OEMCrypto_Algorithm oec_algorithm = GenericSigningAlgorithm(algorithm);
if (oec_algorithm == kInvalidAlgorithm) {
return INVALID_PARAMETERS_ENG_16;
}
AutoLock auto_lock(crypto_lock_);
if (!SelectKey(key_id)) {
return KEY_ERROR_4;
}
OEMCryptoResult sts = OEMCrypto_Generic_Verify(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), oec_algorithm,
reinterpret_cast<const uint8_t*>(signature.data()), signature.size());
if (OEMCrypto_SUCCESS != sts) {
LOGE("GenericVerify: OEMCrypto_Generic_Verify err=%d", sts);
if (OEMCrypto_ERROR_KEY_EXPIRED == sts ||
OEMCrypto_ERROR_NO_CONTENT_KEY == sts) {
return KEY_NOT_FOUND_6;
} else {
return UNKNOWN_ERROR;
}
}
return NO_ERROR;
}
OEMCrypto_Algorithm CryptoSession::GenericSigningAlgorithm(
CdmSigningAlgorithm algorithm) {
if (kSigningAlgorithmHmacSha256 == algorithm) {
return OEMCrypto_HMAC_SHA256;
} else {
return kInvalidAlgorithm;
}
}
OEMCrypto_Algorithm CryptoSession::GenericEncryptionAlgorithm(
CdmEncryptionAlgorithm algorithm) {
if (kEncryptionAlgorithmAesCbc128 == algorithm) {
return OEMCrypto_AES_CBC_128_NO_PADDING;
} else {
return kInvalidAlgorithm;
}
}
size_t CryptoSession::GenericEncryptionBlockSize(
CdmEncryptionAlgorithm algorithm) {
if (kEncryptionAlgorithmAesCbc128 == algorithm) {
return kAes128BlockSize;
} else {
return 0;
}
}
} // namespace wvcdm

View File

@@ -18,13 +18,16 @@
#define MD5 CC_MD5
#define MD5_DIGEST_LENGTH CC_MD5_DIGEST_LENGTH
#else
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#endif
// Protobuf generated classes.
using video_widevine_client::sdk::DeviceCertificate;
using video_widevine_client::sdk::HashedFile;
using video_widevine_client::sdk::HlsAttributes;
using video_widevine_client::sdk::HlsAttributes_Method_AES_128;
using video_widevine_client::sdk::HlsAttributes_Method_SAMPLE_AES;
using video_widevine_client::sdk::License;
using video_widevine_client::sdk::License_LicenseState_ACTIVE;
using video_widevine_client::sdk::License_LicenseState_RELEASING;
@@ -34,19 +37,13 @@ using video_widevine_client::sdk::UsageInfo_ProviderSession;
namespace {
const char kCertificateFileNamePrefix[] = "cert";
const char kCertificateFileNameExt[] = ".bin";
const char kCertificateFileName[] = "cert.bin";
const char kHlsAttributesFileNameExt[] = ".hal";
const char kUsageInfoFileNamePrefix[] = "usage";
const char kUsageInfoFileNameExt[] = ".bin";
const char kLicenseFileNameExt[] = ".lic";
const char kEmptyFileName[] = "";
const char kWildcard[] = "*";
const char kDirectoryDelimiter = '/';
const char* kSecurityLevelPathCompatibilityExclusionList[] = {
"ay64.dat", "ay64.dat2", "ay64.dat3"};
size_t kSecurityLevelPathCompatibilityExclusionListSize =
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
bool Hash(const std::string& data, std::string* hash) {
if (!hash) return false;
@@ -66,30 +63,30 @@ namespace wvcdm {
// static
std::set<std::string> DeviceFiles::reserved_license_ids_;
DeviceFiles::DeviceFiles()
: file_(NULL),
DeviceFiles::DeviceFiles(FileSystem* file_system)
: file_system_(file_system),
security_level_(kSecurityLevelUninitialized),
initialized_(false),
test_file_(false) {}
initialized_(false) {}
DeviceFiles::~DeviceFiles() {
if (test_file_) file_.release();
}
DeviceFiles::~DeviceFiles() {}
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
if (!file_system_) {
LOGD("DeviceFiles::Init: Invalid FileSystem given.");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level, &path)) {
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
return false;
}
if (!test_file_) file_.reset(new File());
security_level_ = security_level;
initialized_ = true;
return true;
}
bool DeviceFiles::StoreCertificate(const std::string& origin,
const std::string& certificate,
bool DeviceFiles::StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key) {
if (!initialized_) {
LOGW("DeviceFiles::StoreCertificate: not initialized");
@@ -109,23 +106,18 @@ bool DeviceFiles::StoreCertificate(const std::string& origin,
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(GetCertificateFileName(origin), serialized_file);
return StoreFileWithHash(GetCertificateFileName(), serialized_file);
}
bool DeviceFiles::RetrieveCertificate(const std::string& origin,
std::string* certificate,
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
std::string* wrapped_private_key) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
return false;
}
if (Properties::security_level_path_backward_compatibility_support()) {
SecurityLevelPathBackwardCompatibility();
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(GetCertificateFileName(origin), &file)) {
if (!RetrieveHashedFile(GetCertificateFileName(), &file)) {
return false;
}
@@ -151,22 +143,22 @@ bool DeviceFiles::RetrieveCertificate(const std::string& origin,
return true;
}
bool DeviceFiles::HasCertificate(const std::string& origin) {
bool DeviceFiles::HasCertificate() {
if (!initialized_) {
LOGW("DeviceFiles::HasCertificate: not initialized");
return false;
}
return FileExists(GetCertificateFileName(origin));
return FileExists(GetCertificateFileName());
}
bool DeviceFiles::RemoveCertificate(const std::string& origin) {
bool DeviceFiles::RemoveCertificate() {
if (!initialized_) {
LOGW("DeviceFiles::RemoveCertificate: not initialized");
return false;
}
return RemoveFile(GetCertificateFileName(origin));
return RemoveFile(GetCertificateFileName());
}
bool DeviceFiles::StoreLicense(
@@ -534,13 +526,105 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId(
return false;
}
bool DeviceFiles::StoreFileWithHash(const std::string& name,
const std::string& serialized_file) {
if (!file_.get()) {
LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
bool DeviceFiles::StoreHlsAttributes(
const std::string& key_set_id, const CdmHlsMethod method,
const std::vector<uint8_t>& media_segment_iv) {
if (!initialized_) {
LOGW("DeviceFiles::StoreHlsAttributes: not initialized");
return false;
}
// Fill in file information
video_widevine_client::sdk::File file;
file.set_type(video_widevine_client::sdk::File::HLS_ATTRIBUTES);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
HlsAttributes* hls_attributes = file.mutable_hls_attributes();
switch (method) {
case kHlsMethodAes128:
hls_attributes->set_method(HlsAttributes_Method_AES_128);
break;
case kHlsMethodSampleAes:
hls_attributes->set_method(HlsAttributes_Method_SAMPLE_AES);
break;
case kHlsMethodNone:
default:
LOGW("DeviceFiles::StoreHlsAttributeInfo: Unknown HLS method: %u",
method);
return false;
break;
}
hls_attributes->set_media_segment_iv(&media_segment_iv[0],
media_segment_iv.size());
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(key_set_id + kHlsAttributesFileNameExt,
serialized_file);
}
bool DeviceFiles::RetrieveHlsAttributes(
const std::string& key_set_id, CdmHlsMethod* method,
std::vector<uint8_t>* media_segment_iv) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveHlsAttributes: not initialized");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(key_set_id + kHlsAttributesFileNameExt, &file)) {
return false;
}
if (file.type() != video_widevine_client::sdk::File::HLS_ATTRIBUTES) {
LOGW("DeviceFiles::RetrieveHlsAttributes: Incorrect file type: %u",
file.type());
return false;
}
if (file.version() != video_widevine_client::sdk::File::VERSION_1) {
LOGW("DeviceFiles::RetrieveHlsAttributes: Incorrect file version: %u",
file.version());
return false;
}
if (!file.has_hls_attributes()) {
LOGW("DeviceFiles::RetrieveHlsAttributes: HLS attributes not present");
return false;
}
HlsAttributes attributes = file.hls_attributes();
switch (attributes.method()) {
case HlsAttributes_Method_AES_128:
*method = kHlsMethodAes128;
break;
case HlsAttributes_Method_SAMPLE_AES:
*method = kHlsMethodSampleAes;
break;
default:
LOGW("DeviceFiles::RetrieveHlsAttributes: Unrecognized HLS method: %u",
attributes.method());
*method = kHlsMethodNone;
break;
}
media_segment_iv->assign(attributes.media_segment_iv().begin(),
attributes.media_segment_iv().end());
return true;
}
bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteHlsAttributes: not initialized");
return false;
}
return RemoveFile(key_set_id + kHlsAttributesFileNameExt);
}
bool DeviceFiles::StoreFileWithHash(const std::string& name,
const std::string& serialized_file) {
// calculate SHA hash
std::string hash;
if (!Hash(serialized_file, &hash)) {
@@ -561,30 +645,23 @@ bool DeviceFiles::StoreFileWithHash(const std::string& name,
bool DeviceFiles::StoreFileRaw(const std::string& name,
const std::string& serialized_file) {
if (!file_.get()) {
LOGW("DeviceFiles::StoreFileRaw: Invalid file handle");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
return false;
}
if (!file_->IsDirectory(path)) {
if (!file_->CreateDirectory(path)) return false;
}
path += name;
if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) {
File* file =
file_system_->Open(path, FileSystem::kCreate | FileSystem::kTruncate);
if (!file) {
LOGW("DeviceFiles::StoreFileRaw: File open failed: %s", path.c_str());
return false;
}
ssize_t bytes = file_->Write(serialized_file.data(), serialized_file.size());
file_->Close();
ssize_t bytes = file->Write(serialized_file.data(), serialized_file.size());
file->Close();
if (bytes != static_cast<ssize_t>(serialized_file.size())) {
LOGW(
@@ -599,16 +676,12 @@ bool DeviceFiles::StoreFileRaw(const std::string& name,
return true;
}
bool DeviceFiles::RetrieveHashedFile(const std::string& name,
video_widevine_client::sdk::File* file) {
bool DeviceFiles::RetrieveHashedFile(
const std::string& name,
video_widevine_client::sdk::File* deserialized_file) {
std::string serialized_file;
if (!file_.get()) {
LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle");
return false;
}
if (!file) {
if (!deserialized_file) {
LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file parameter");
return false;
}
@@ -621,29 +694,30 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name,
path += name;
if (!file_->Exists(path)) {
if (!file_system_->Exists(path)) {
LOGW("DeviceFiles::RetrieveHashedFile: %s does not exist", path.c_str());
return false;
}
ssize_t bytes = file_->FileSize(path);
ssize_t bytes = file_system_->FileSize(path);
if (bytes <= 0) {
LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s",
path.c_str());
// Remove the corrupted file so the caller will not get the same error
// when trying to access the file repeatedly, causing the system to stall.
file_->Remove(path);
file_system_->Remove(path);
return false;
}
if (!file_->Open(path, File::kReadOnly | File::kBinary)) {
File* file = file_system_->Open(path, FileSystem::kReadOnly);
if (!file) {
return false;
}
std::string serialized_hash_file;
serialized_hash_file.resize(bytes);
bytes = file_->Read(&serialized_hash_file[0], serialized_hash_file.size());
file_->Close();
bytes = file->Read(&serialized_hash_file[0], serialized_hash_file.size());
file->Close();
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
LOGW("DeviceFiles::RetrieveHashedFile: read failed");
@@ -669,11 +743,11 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name,
LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
// Remove the corrupted file so the caller will not get the same error
// when trying to access the file repeatedly, causing the system to stall.
file_->Remove(path);
file_system_->Remove(path);
return false;
}
if (!file->ParseFromString(hash_file.file())) {
if (!deserialized_file->ParseFromString(hash_file.file())) {
LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse file");
return false;
}
@@ -681,11 +755,6 @@ bool DeviceFiles::RetrieveHashedFile(const std::string& name,
}
bool DeviceFiles::FileExists(const std::string& name) {
if (!file_.get()) {
LOGW("DeviceFiles::FileExists: Invalid file handle");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::FileExists: Unable to get base path");
@@ -693,15 +762,10 @@ bool DeviceFiles::FileExists(const std::string& name) {
}
path += name;
return file_->Exists(path);
return file_system_->Exists(path);
}
bool DeviceFiles::RemoveFile(const std::string& name) {
if (!file_.get()) {
LOGW("DeviceFiles::RemoveFile: Invalid file handle");
return false;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
@@ -709,15 +773,10 @@ bool DeviceFiles::RemoveFile(const std::string& name) {
}
path += name;
return file_->Remove(path);
return file_system_->Remove(path);
}
ssize_t DeviceFiles::GetFileSize(const std::string& name) {
if (!file_.get()) {
LOGW("DeviceFiles::GetFileSize: Invalid file handle");
return -1;
}
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::GetFileSize: Unable to get base path");
@@ -725,79 +784,15 @@ ssize_t DeviceFiles::GetFileSize(const std::string& name) {
}
path += name;
return file_->FileSize(path);
return file_system_->FileSize(path);
}
void DeviceFiles::SecurityLevelPathBackwardCompatibility() {
std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get base path");
return;
}
std::vector<std::string> security_dirs;
if (!Properties::GetSecurityLevelDirectories(&security_dirs)) {
LOGW(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Unable to "
"get security directories");
return;
}
size_t pos = std::string::npos;
for (size_t i = 0; i < security_dirs.size(); ++i) {
pos = path.find(security_dirs[i]);
if (pos != std::string::npos && pos > 0 &&
pos == path.size() - security_dirs[i].size() &&
path[pos - 1] == kDirectoryDelimiter) {
break;
}
}
if (pos == std::string::npos) {
LOGV(
"DeviceFiles::SecurityLevelPathBackwardCompatibility: Security level "
"specific path not found. Check properties?");
return;
}
std::string from_dir(path, 0, pos);
std::vector<std::string> files;
if (!file_->List(from_dir, &files)) {
return;
}
for (size_t i = 0; i < files.size(); ++i) {
std::string from = from_dir + files[i];
bool exclude = false;
for (size_t j = 0; j < kSecurityLevelPathCompatibilityExclusionListSize;
++j) {
if (files[i] == kSecurityLevelPathCompatibilityExclusionList[j]) {
exclude = true;
break;
}
}
if (exclude) continue;
if (!file_->IsRegularFile(from)) continue;
for (size_t j = 0; j < security_dirs.size(); ++j) {
std::string to_dir = from_dir + security_dirs[j];
if (!file_->Exists(to_dir)) file_->CreateDirectory(to_dir);
std::string to = to_dir + files[i];
file_->Copy(from, to);
}
file_->Remove(from);
}
std::string DeviceFiles::GetCertificateFileName() {
return kCertificateFileName;
}
std::string DeviceFiles::GetCertificateFileName(const std::string& origin) {
std::string hash;
if (origin != EMPTY_ORIGIN) {
hash = GetFileNameSafeHash(origin);
}
return kCertificateFileNamePrefix + hash + kCertificateFileNameExt;
std::string DeviceFiles::GetHlsAttributesFileNameExtension() {
return kHlsAttributesFileNameExt;
}
std::string DeviceFiles::GetLicenseFileNameExtension() {
@@ -820,9 +815,4 @@ std::string DeviceFiles::GetFileNameSafeHash(const std::string& input) {
return wvcdm::Base64SafeEncode(hash);
}
void DeviceFiles::SetTestFile(File* file) {
file_.reset(file);
test_file_ = true;
}
} // namespace wvcdm

View File

@@ -52,11 +52,21 @@ message UsageInfo {
repeated ProviderSession sessions = 1;
}
message HlsAttributes {
enum Method {
AES_128 = 1;
SAMPLE_AES = 2;
}
optional Method method = 1;
optional bytes media_segment_iv = 2;
}
message File {
enum FileType {
DEVICE_CERTIFICATE = 1;
LICENSE = 2;
USAGE_INFO = 3;
HLS_ATTRIBUTES = 4;
}
enum FileVersion {
@@ -68,6 +78,7 @@ message File {
optional DeviceCertificate device_certificate = 3;
optional License license = 4;
optional UsageInfo usage_info = 5;
optional HlsAttributes hls_attributes = 6;
}
message HashedFile {

View File

@@ -2,31 +2,70 @@
#include "initialization_data.h"
#include <arpa/inet.h>
#include <string.h>
#include "buffer_reader.h"
#include "jsmn.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace {
const char kKeyFormatVersionsSeparator = '/';
const char kColon = ':';
const char kDoubleQuote = '\"';
const char kLeftBracket = '[';
const char kRightBracket = ']';
const std::string kBase64String = "base64,";
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;
// json init data key values
const std::string kProvider = "provider";
const std::string kContentId = "content_id";
const std::string kKeyIds = "key_ids";
// Being conservative, usually we expect 6 + number of Key Ids
const int kDefaultNumJsonTokens = 128;
} // namespace
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::WidevineCencHeader;
using video_widevine_server::sdk::WidevineCencHeader_Algorithm;
using video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR;
InitializationData::InitializationData(const std::string& type,
const CdmInitData& data)
: type_(type), is_cenc_(false), is_webm_(false) {
: type_(type),
is_cenc_(false),
is_hls_(false),
is_webm_(false),
hls_method_(kHlsMethodNone) {
if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE ||
type == CENC_INIT_DATA_FORMAT) {
is_cenc_ = true;
} else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE ||
type == WEBM_INIT_DATA_FORMAT) {
is_webm_ = true;
} else if (type == HLS_INIT_DATA_FORMAT) {
is_hls_ = true;
}
if (is_supported()) {
if (is_cenc()) {
ExtractWidevinePssh(data, &data_);
} else {
} else if (is_webm()) {
data_ = data;
} else if (is_hls()) {
std::string uri;
if (ExtractHlsAttributes(data, &hls_method_, &hls_iv_, &uri)) {
ConstructWidevineInitData(hls_method_, uri, &data_);
}
}
}
}
@@ -61,19 +100,22 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
// atom size, used for skipping.
uint64_t size;
if (!reader.Read4Into8(&size)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom size.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read atom size.");
return false;
}
std::vector<uint8_t> atom_type;
if (!reader.ReadVec(&atom_type, 4)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read atom type.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read atom type.");
return false;
}
if (size == 1) {
if (!reader.Read8(&size)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read 64-bit atom "
"size.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read 64-bit "
"atom size.");
return false;
}
} else if (size == 0) {
@@ -82,10 +124,12 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
// "pssh"
if (memcmp(&atom_type[0], "pssh", 4)) {
LOGV("CdmEngine::ExtractWidevinePssh: PSSH literal not present.");
LOGV(
"InitializationData::ExtractWidevinePssh: PSSH literal not present.");
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
"the atom.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
"of the atom.");
return false;
}
continue;
@@ -94,15 +138,18 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
// version
uint8_t version;
if (!reader.Read1(&version)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH version.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read PSSH "
"version.");
return false;
}
if (version > 1) {
// unrecognized version - skip.
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
"the atom.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
"of the atom.");
return false;
}
continue;
@@ -110,25 +157,31 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
// flags
if (!reader.SkipBytes(3)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the PSSH flags.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to skip the PSSH "
"flags.");
return false;
}
// system id
std::vector<uint8_t> system_id;
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read system ID.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read system ID.");
return false;
}
if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
// skip non-Widevine PSSH boxes.
if (!reader.SkipBytes(size - (reader.pos() - start_pos))) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip the rest of "
"the atom.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to skip the rest "
"of the atom.");
return false;
}
LOGV("CdmEngine::ExtractWidevinePssh: Skipping non-Widevine PSSH.");
LOGV(
"InitializationData::ExtractWidevinePssh: Skipping non-Widevine "
"PSSH.");
continue;
}
@@ -136,11 +189,14 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
// v1 has additional fields for key IDs. We can skip them.
uint32_t num_key_ids;
if (!reader.Read4(&num_key_ids)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read num key IDs.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read num key "
"IDs.");
return false;
}
if (!reader.SkipBytes(num_key_ids * 16)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to skip key IDs.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to skip key IDs.");
return false;
}
}
@@ -148,13 +204,16 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
// size of PSSH data
uint32_t data_length;
if (!reader.Read4(&data_length)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data size.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data "
"size.");
return false;
}
output->clear();
if (!reader.ReadString(output, data_length)) {
LOGV("CdmEngine::ExtractWidevinePssh: Unable to read PSSH data.");
LOGV(
"InitializationData::ExtractWidevinePssh: Unable to read PSSH data.");
return false;
}
@@ -165,4 +224,357 @@ bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
return false;
}
// Parse an EXT-X-KEY tag attribute list. Verify that Widevine supports it
// by validating KEYFORMAT and KEYFORMATVERSION attributes. Extract out
// method, IV, URI and WV init data.
//
// An example of a widevine supported attribute list from an HLS media playlist
// is,
// "EXT-X-KEY: METHOD=SAMPLE-AES, \"
// "URI=”data:text/plain;base64,eyANCiAgICJwcm92aWRlciI6Im1sYmFtaGJvIiwNCiAg"
// "ICJjb250ZW50X2lkIjoiMjAxNV9UZWFycyIsDQogICAia2V5X2lkcyI6DQogICBbDQo"
// "gICAgICAiMzcxZTEzNWUxYTk4NWQ3NWQxOThhN2Y0MTAyMGRjMjMiDQogICBdDQp9DQ"
// "o=, \"
// "IV=0x6df49213a781e338628d0e9c812d328e, \"
// "KEYFORMAT=”com.widevine”, \"
// "KEYFORMATVERSIONS=”1”"
bool InitializationData::ExtractHlsAttributes(const std::string& attribute_list,
CdmHlsMethod* method,
std::vector<uint8_t>* iv,
std::string* uri) {
std::string value;
if (!ExtractQuotedAttribute(attribute_list, HLS_KEYFORMAT_ATTRIBUTE,
&value)) {
LOGV(
"InitializationData::ExtractHlsInitDataAtttribute: Unable to read HLS "
"keyformat value");
return false;
}
if (value.compare(0, sizeof(KEY_SYSTEM) - 1, KEY_SYSTEM) != 0) {
LOGV(
"InitializationData::ExtractHlsInitDataAtttribute: unrecognized HLS "
"keyformat value: %s",
value.c_str());
return false;
}
// KEYFORMATVERSIONS is an optional parameter. If absent its
// value defaults to "1"
if (ExtractQuotedAttribute(attribute_list, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
&value)) {
std::vector<std::string> versions = ExtractKeyFormatVersions(value);
bool supported = false;
for (size_t i = 0; i < versions.size(); ++i) {
if (versions[i].compare(HLS_KEYFORMAT_VERSION_VALUE_1) == 0) {
supported = true;
break;
}
}
if (!supported) {
LOGV(
"InitializationData::ExtractHlsInitDataAtttribute: HLS keyformat "
"version is not supported: %s",
value.c_str());
return false;
}
}
if (!ExtractAttribute(attribute_list, HLS_METHOD_ATTRIBUTE, &value)) {
LOGV(
"InitializationData::ExtractHlsInitDataAtttribute: Unable to read HLS "
"method");
return false;
}
if (value.compare(HLS_METHOD_AES_128) == 0) {
*method = kHlsMethodAes128;
} else if (value.compare(HLS_METHOD_SAMPLE_AES) == 0) {
*method = kHlsMethodSampleAes;
} else if (value.compare(HLS_METHOD_NONE) == 0) {
*method = kHlsMethodNone;
} else {
LOGV(
"InitializationData::ExtractHlsInitDataAtttribute: HLS method "
"unrecognized: %s",
value.c_str());
return false;
}
if (!ExtractHexAttribute(attribute_list, HLS_IV_ATTRIBUTE, iv)) {
LOGV(
"InitializationData::ExtractHlsInitDataAtttribute: HLS IV attribute "
"not present");
return false;
}
if (!ExtractQuotedAttribute(attribute_list, HLS_URI_ATTRIBUTE, uri)) {
LOGV(
"InitializationData::ExtractHlsInitDataAtttribute: HLS URI attribute "
"not present");
return false;
}
return true;
}
// Extracts a base64 encoded string from URI data. This is then base64 decoded
// and the Json formatted init data is then parsed. The information is used
// to generate a Widevine init data protobuf (WidevineCencHeader).
//
// An example of a widevine supported json formatted init data string is,
//
// {
// "provider":"mlbamhbo",
// "content_id":"MjAxNV9UZWFycw==",
// "key_ids":
// [
// "371e135e1a985d75d198a7f41020dc23"
// ]
// }
bool InitializationData::ConstructWidevineInitData(
CdmHlsMethod method, const std::string& uri, CdmInitData* init_data_proto) {
if (!init_data_proto) {
LOGV("InitializationData::ConstructWidevineInitData: Invalid parameter");
return false;
}
if (method != kHlsMethodAes128 && method != kHlsMethodSampleAes) {
LOGV("InitializationData::ConstructWidevineInitData: Invalid method"
" parameter");
return false;
}
size_t pos = uri.find(kBase64String);
if (pos == std::string::npos) {
LOGV(
"InitializationData::ConstructWidevineInitData: URI attribute "
"unexpected format: %s",
uri.c_str());
return false;
}
std::vector<uint8_t> json_init_data =
Base64Decode(uri.substr(pos + kBase64String.size()));
if (json_init_data.size() == 0) {
LOGV(
"InitializationData::ConstructWidevineInitData: Base64 decode of json "
"data failed");
return false;
}
std::string json_string((const char*)(&json_init_data[0]),
json_init_data.size());
// Parse the Json string using jsmn
jsmn_parser parser;
jsmntok_t tokens[kDefaultNumJsonTokens];
jsmn_init(&parser);
int num_of_tokens =
jsmn_parse(&parser, json_string.c_str(), json_string.size(), tokens,
kDefaultNumJsonTokens);
if (num_of_tokens <= 0) {
LOGV(
"InitializationData::ConstructWidevineInitData: Json parsing failed: "
"%d",
num_of_tokens);
return false;
}
std::string provider;
std::string content_id;
std::vector<std::string> key_ids;
enum JsmnParserState {
kParseState,
kProviderState,
kContentIdState,
kKeyIdsState,
} state = kParseState;
int number_of_key_ids = 0;
// Extract the provider, content_id and key_ids
for (int i = 0; i < num_of_tokens; ++i) {
if (tokens[i].start < 0 || tokens[i].end < 0) {
LOGV(
"InitializationData::ConstructWidevineInitData: Invalid start or end "
"of token");
return false;
}
switch (state) {
case kParseState:
if (tokens[i].type == JSMN_STRING) {
std::string token(json_string, tokens[i].start,
tokens[i].end - tokens[i].start);
if (token == kProvider) {
state = kProviderState;
} else if (token == kContentId) {
state = kContentIdState;
} else if (token == kKeyIds) {
state = kKeyIdsState;
}
}
break;
case kProviderState:
if (tokens[i].type == JSMN_STRING) {
provider.assign(json_string, tokens[i].start,
tokens[i].end - tokens[i].start);
}
state = kParseState;
break;
case kContentIdState:
if (tokens[i].type == JSMN_STRING) {
std::string base64_content_id(json_string, tokens[i].start,
tokens[i].end - tokens[i].start);
std::vector<uint8_t> content_id_data =
Base64Decode(base64_content_id);
content_id.assign(reinterpret_cast<const char*>(&content_id_data[0]),
content_id_data.size());
}
state = kParseState;
break;
case kKeyIdsState:
if (tokens[i].type == JSMN_ARRAY) {
number_of_key_ids = tokens[i].size;
} else if (tokens[i].type == JSMN_STRING) {
std::string key_id(a2bs_hex(json_string.substr(
tokens[i].start, tokens[i].end - tokens[i].start)));
if (key_id.size() == 16) key_ids.push_back(key_id);
--number_of_key_ids;
} else {
state = kParseState;
}
if (number_of_key_ids <= 0) state = kParseState;
break;
}
}
if (provider.size() == 0) {
LOGV("InitializationData::ConstructWidevineInitData: Invalid provider");
return false;
}
if (content_id.size() == 0) {
LOGV("InitializationData::ConstructWidevineInitData: Invalid content_id");
return false;
}
if (key_ids.size() == 0) {
LOGV("InitializationData::ConstructWidevineInitData: No key_ids present");
return false;
}
// Now format as Widevine init data protobuf
WidevineCencHeader cenc_header;
// TODO(rfrias): The algorithm is a deprecated field, but proto changes
// have not yet been pushed to production. Set until then.
cenc_header.set_algorithm(WidevineCencHeader_Algorithm_AESCTR);
for (size_t i = 0; i < key_ids.size(); ++i) {
cenc_header.add_key_id(key_ids[i]);
}
cenc_header.set_provider(provider);
cenc_header.set_content_id(content_id);
if (method == kHlsMethodAes128)
cenc_header.set_protection_scheme(htonl(kFourCcCbc1));
else
cenc_header.set_protection_scheme(htonl(kFourCcCbcs));
cenc_header.SerializeToString(init_data_proto);
return true;
}
bool InitializationData::ExtractQuotedAttribute(
const std::string& attribute_list, const std::string& key,
std::string* value) {
bool result = ExtractAttribute(attribute_list, key, value);
if (value->size() < 2 || value->at(0) != '\"' ||
value->at(value->size() - 1) != '\"')
return false;
*value = value->substr(1, value->size() - 2);
if (value->find('\"') != std::string::npos) return false;
return result;
}
bool InitializationData::ExtractHexAttribute(const std::string& attribute_list,
const std::string& key,
std::vector<uint8_t>* value) {
std::string val;
bool result = ExtractAttribute(attribute_list, key, &val);
if (!result || val.size() <= 2 || val.size() % 2 != 0 || val[0] != '0' ||
((val[1] != 'x') && (val[1] != 'X')))
return false;
for (size_t i = 2; i < val.size(); ++i) {
if (!isxdigit(val[i])) return false;
}
*value = a2b_hex(val.substr(2, val.size() - 2));
return result;
}
bool InitializationData::ExtractAttribute(const std::string& attribute_list,
const std::string& key,
std::string* value) {
// validate the key
for (size_t i = 0; i < key.size(); ++i)
if (!isupper(key[i]) && !isdigit(key[i]) && key[i] != '-') return false;
bool found = false;
size_t pos = 0;
// Find the key followed by '='
while (!found) {
pos = attribute_list.find(key, pos);
if (pos == std::string::npos) return false;
pos += key.size();
if (attribute_list[pos] != '=') continue;
found = true;
}
if (attribute_list.size() <= ++pos) return false;
size_t end_pos = pos;
found = false;
// Find the next comma outside the quote or end of string
while (!found) {
end_pos = attribute_list.find(',', end_pos);
if (end_pos != std::string::npos && attribute_list[pos] == '\"' &&
attribute_list[end_pos - 1] != '\"') {
++end_pos;
continue;
}
if (end_pos == std::string::npos)
end_pos = attribute_list.size() - 1;
else
--end_pos;
found = true;
}
*value = attribute_list.substr(pos, end_pos - pos + 1);
// validate the value
for (size_t i = 0; i < value->size(); ++i)
if (!isgraph(value->at(i))) return false;
return true;
}
// Key format versions are individual values or multiple versions
// separated by '/'. "1" or "1/2/5"
std::vector<std::string> InitializationData::ExtractKeyFormatVersions(
const std::string& key_format_versions) {
std::vector<std::string> versions;
size_t pos = 0;
while (pos < key_format_versions.size()) {
size_t next_pos =
key_format_versions.find(kKeyFormatVersionsSeparator, pos);
if (next_pos == std::string::npos) {
versions.push_back(key_format_versions.substr(pos));
break;
} else {
versions.push_back(key_format_versions.substr(pos, next_pos - pos));
pos = next_pos + 1;
}
}
return versions;
}
} // namespace wvcdm

View File

@@ -2,6 +2,8 @@
#include "license.h"
#include <arpa/inet.h>
#include <sstream>
#include <vector>
#include "clock.h"
@@ -24,6 +26,7 @@ std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
std::string kWVCdmVersionKey = "widevine_cdm_version";
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
const unsigned char kServiceCertificateCAPublicKey[] = {
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39,
0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd,
@@ -60,6 +63,10 @@ const unsigned char kServiceCertificateCAPublicKey[] = {
0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01,
0x00, 0x01};
}
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;
const uint32_t kFourCcCenc = 0x63656e63;
const uint32_t kFourCcCens = 0x63656e73;
namespace wvcdm {
@@ -91,7 +98,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
size_t length;
switch (license.key(i).type()) {
case License_KeyContainer::CONTENT:
case License_KeyContainer::OPERATOR_SESSION:
case License_KeyContainer::OPERATOR_SESSION: {
key.set_key_id(license.key(i).id());
// Strip off PKCS#5 padding - since we know the key is 16 or 32 bytes,
// the padding will always be 16 bytes.
@@ -106,8 +113,17 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
key.set_key_control(license.key(i).key_control().key_control_block());
key.set_key_control_iv(license.key(i).key_control().iv());
}
uint32_t four_cc = kFourCcCenc;
if (license.has_protection_scheme()) {
four_cc = ntohl(license.protection_scheme());
}
if (four_cc == kFourCcCbc1 || four_cc == kFourCcCbcs)
key.set_cipher_mode(kCipherModeCbc);
else
key.set_cipher_mode(kCipherModeCtr);
key_array.push_back(key);
break;
}
case License_KeyContainer::KEY_CONTROL:
if (license.key(i).has_key_control()) {
key.set_key_control(license.key(i).key_control().key_control_block());
@@ -232,7 +248,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
LicenseRequest_ContentIdentification* content_id =
license_request.mutable_content_id();
if (init_data.is_cenc()) {
if (init_data.is_cenc() || init_data.is_hls()) {
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
content_id->mutable_cenc_id();
@@ -535,8 +551,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
server_url_ = license.policy().renewal_server_url();
}
policy_engine_->SetLicense(license);
if (license.policy().has_renew_with_client_id()) {
renew_with_client_id_ = license.policy().renew_with_client_id();
}
@@ -551,6 +565,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
it != key_array.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
policy_engine_->SetLicense(license);
}
return resp;
}
@@ -612,8 +627,6 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
return LICENSE_ID_NOT_FOUND;
}
policy_engine_->UpdateLicense(license);
if (license.policy().has_renew_with_client_id()) {
renew_with_client_id_ = license.policy().renew_with_client_id();
}
@@ -637,6 +650,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(),
key_array.size(), &key_array[0])) {
policy_engine_->UpdateLicense(license);
return KEY_ADDED;
} else {
return REFRESH_KEYS_ERROR;
@@ -981,6 +996,11 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info->set_name(kWVCdmVersionKey);
client_info->set_value(value);
}
client_info = client_id->add_client_info();
client_info->set_name(kOemCryptoSecurityPatchLevelKey);
std::stringstream ss;
ss << (uint32_t)session_->GetSecurityPatchLevel();
client_info->set_value(ss.str());
ClientIdentification_ClientCapabilities* client_capabilities =
client_id->mutable_client_capabilities();

View File

@@ -0,0 +1,285 @@
// Copyright 2016 Google Inc. All Rights Reserved.
#include "license_key_status.h"
#include <string>
#include "log.h"
namespace {
// License protocol aliases
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef KeyContainer::OutputProtection OutputProtection;
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
ConstraintList;
// Map the HDCP protection associated with a key in the license to
// an equivalent OEMCrypto HDCP protection level
wvcdm::CryptoSession::HdcpCapability ProtobufHdcpToOemCryptoHdcp(
const OutputProtection::HDCP& input) {
switch (input) {
case OutputProtection::HDCP_NONE:
return HDCP_NONE;
case OutputProtection::HDCP_V1:
return HDCP_V1;
case OutputProtection::HDCP_V2:
return HDCP_V2;
case OutputProtection::HDCP_V2_1:
return HDCP_V2_1;
case OutputProtection::HDCP_V2_2:
return HDCP_V2_2;
case OutputProtection::HDCP_NO_DIGITAL_OUTPUT:
return HDCP_NO_DIGITAL_OUTPUT;
default:
LOGE("ContentKeyStatus::ProtobufHdcpToOemCryptoHdcp: "
"Unknown HDCP Level: input=%d, returning HDCP_NO_DIGITAL_OUTPUT",
input);
return HDCP_NO_DIGITAL_OUTPUT;
}
}
// Returns the constraint from a set of constraints that matches the
// specified resolution, or null if none match
VideoResolutionConstraint* GetConstraintForRes(
uint32_t res, ConstraintList& constraints_from_key) {
typedef ConstraintList::pointer_iterator Iterator;
for (Iterator i = constraints_from_key.pointer_begin();
i != constraints_from_key.pointer_end(); ++i) {
VideoResolutionConstraint* constraint = *i;
if (constraint->has_min_resolution_pixels() &&
constraint->has_max_resolution_pixels() &&
res >= constraint->min_resolution_pixels() &&
res <= constraint->max_resolution_pixels()) {
return constraint;
}
}
return NULL;
}
} // namespace
namespace wvcdm {
bool LicenseKeys::IsContentKey(const std::string& key_id) {
if (keys_.count(key_id) > 0) {
return keys_[key_id]->IsContentKey();
} else {
return false;
}
}
bool LicenseKeys::CanDecryptContent(const std::string& key_id) {
if (keys_.count(key_id) > 0) {
return keys_[key_id]->CanDecryptContent();
} else {
return false;
}
}
bool LicenseKeys::GetAllowedUsage(const KeyId& key_id,
CdmKeyAllowedUsage* allowed_usage) {
if (keys_.count(key_id) > 0) {
return keys_[key_id]->GetAllowedUsage(allowed_usage);
} else {
return false;
}
}
bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys) {
bool keys_changed = false;
bool newly_usable = false;
*new_usable_keys = false;
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
bool usable;
if (it->second->ApplyStatusChange(new_status, &usable)) {
newly_usable |= usable;
keys_changed = true;
}
}
*new_usable_keys = newly_usable;
return keys_changed;
}
void LicenseKeys::ExtractKeyStatuses(CdmKeyStatusMap* content_keys) {
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
if (it->second->IsContentKey()) {
const KeyId key_id = it->first;
CdmKeyStatus key_status = it->second->GetKeyStatus();
(*content_keys)[key_id] = key_status;
}
}
}
bool LicenseKeys::MeetsConstraints(const KeyId& key_id) {
if (keys_.count(key_id) > 0) {
return keys_[key_id]->MeetsConstraints();
} else {
// If a Key ID is unknown to us, we don't know of any constraints for it,
// so never block decryption.
return true;
}
}
void LicenseKeys::ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) {
i->second->ApplyConstraints(new_resolution, new_hdcp_level);
}
}
void LicenseKeys::SetFromLicense(
const video_widevine_server::sdk::License& license) {
this->Clear();
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
const KeyContainer& key = license.key(key_index);
if (key.has_id() && (key.type() == KeyContainer::CONTENT ||
key.type() == KeyContainer::OPERATOR_SESSION)) {
const KeyId& key_id = key.id();
keys_[key_id] = new LicenseKeyStatus(key);
}
}
}
LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) :
is_content_key_(false),
key_status_(kKeyStatusInternalError),
meets_constraints_(true),
default_hdcp_level_(HDCP_NONE) {
allowed_usage_.Clear();
constraints_.Clear();
if (key.type() == KeyContainer::CONTENT) {
ParseContentKey(key);
} else if (key.type() == KeyContainer::OPERATOR_SESSION) {
ParseOperatorSessionKey(key);
}
}
void LicenseKeyStatus::ParseContentKey(const KeyContainer& key) {
is_content_key_ = true;
if (key.has_level() &&
((key.level() == KeyContainer::HW_SECURE_DECODE) ||
(key.level() == KeyContainer::HW_SECURE_ALL))) {
allowed_usage_.decrypt_to_clear_buffer = false;
allowed_usage_.decrypt_to_secure_buffer = true;
} else {
allowed_usage_.decrypt_to_clear_buffer = true;
allowed_usage_.decrypt_to_secure_buffer = true;
}
allowed_usage_.SetValid();
if (key.video_resolution_constraints_size() > 0) {
SetConstraints(key.video_resolution_constraints());
}
if (key.has_required_protection()) {
default_hdcp_level_ =
ProtobufHdcpToOemCryptoHdcp(key.required_protection().hdcp());
}
}
void LicenseKeyStatus::ParseOperatorSessionKey(const KeyContainer& key) {
is_content_key_ = false;
if (key.has_operator_session_key_permissions()) {
OperatorSessionKeyPermissions permissions =
key.operator_session_key_permissions();
if (permissions.has_allow_encrypt())
allowed_usage_.generic_encrypt = permissions.allow_encrypt();
if (permissions.has_allow_decrypt())
allowed_usage_.generic_decrypt = permissions.allow_decrypt();
if (permissions.has_allow_sign())
allowed_usage_.generic_sign = permissions.allow_sign();
if (permissions.has_allow_signature_verify())
allowed_usage_.generic_verify = permissions.allow_signature_verify();
} else {
allowed_usage_.generic_encrypt = false;
allowed_usage_.generic_decrypt = false;
allowed_usage_.generic_sign = false;
allowed_usage_.generic_verify = false;
}
allowed_usage_.SetValid();
}
void LicenseKeys::Clear() {
for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) {
delete i->second;
}
keys_.clear();
}
bool LicenseKeyStatus::CanDecryptContent() {
return is_content_key_ && key_status_ == kKeyStatusUsable;
}
bool LicenseKeyStatus::GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage) {
if (NULL == allowed_usage)
return false;
*allowed_usage = allowed_usage_;
return true;
}
bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_key) {
*new_usable_key = false;
if (!is_content_key_) {
return false;
}
CdmKeyStatus updated_status = new_status;
if (updated_status == kKeyStatusUsable) {
if (!MeetsConstraints()) {
updated_status = kKeyStatusOutputNotAllowed;
}
}
if (key_status_ != updated_status) {
key_status_ = updated_status;
if (updated_status == kKeyStatusUsable) {
*new_usable_key = true;
}
return true;
}
return false;
}
// If the key has constraints, find the constraint that applies.
// If none found, then the constraint test fails.
// If a constraint is found, verify that the device's current HDCP
// level is sufficient. If the constraint has an HDCP setting, use it,
// If the key has no constraints, or if the constraint has no HDCP
// requirement, use the key's default HDCP setting to check against the
// device's current HDCP level.
void LicenseKeyStatus::ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
VideoResolutionConstraint* current_constraint = NULL;
if (HasConstraints()) {
current_constraint = GetConstraintForRes(new_resolution, constraints_);
if (NULL == current_constraint) {
meets_constraints_ = false;
return;
}
}
CryptoSession::HdcpCapability desired_hdcp_level;
if (current_constraint && current_constraint->has_required_protection()) {
desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp(
current_constraint->required_protection().hdcp());
} else {
desired_hdcp_level = default_hdcp_level_;
}
meets_constraints_ = (new_hdcp_level >= desired_hdcp_level);
}
void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) {
if (!is_content_key_) {
return;
}
constraints_.Clear();
constraints_.MergeFrom(constraints);
meets_constraints_ = true;
}
} // namespace wvcdm

View File

@@ -196,6 +196,10 @@ message License {
optional bool remote_attestation_verified = 5 [default = false];
// Client token generated by the content provider. Optional.
optional bytes provider_client_token = 6;
// Protection scheme identifying the encryption algorithm. Represented as one
// of the following 4CC values: 'cenc' (AES-CTR), 'cbc1' (AES-CBC),
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
optional uint32 protection_scheme = 7;
}
enum ProtocolVersion {
@@ -268,6 +272,28 @@ message LicenseError {
optional Error error_code = 1;
}
message MetricData {
enum MetricType {
// The time spent in the 'stage', specified in microseconds.
LATENCY = 1;
// The UNIX epoch timestamp at which the 'stage' was first accessed in
// microseconds.
TIMESTAMP = 2;
}
message TypeValue {
optional MetricType type = 1;
// The value associated with 'type'. For example if type == LATENCY, the
// value would be the time in microseconds spent in this 'stage'.
optional int64 value = 2 [default = 0];
}
// 'stage' that is currently processing the SignedMessage. Required.
optional string stage_name = 1;
// metric and associated value.
repeated TypeValue metric_data = 2;
}
message RemoteAttestation {
// Encrypted ClientIdentification message containing the device remote
// attestation certificate. Required.
@@ -296,6 +322,14 @@ message SignedMessage {
// request for ChromeOS client devices operating in verified mode. Remote
// attestation challenge data is |msg| field above. Optional.
optional RemoteAttestation remote_attestation = 5;
repeated MetricData metric_data = 6;
}
message GroupKeys {
repeated License.KeyContainer key = 1;
// Byte string that identifies the group to which this license material
// belongs.
optional bytes group_id = 2;
}
// ----------------------------------------------------------------------------
@@ -427,7 +461,7 @@ message EncryptedClientIdentification {
optional bytes encrypted_client_id = 3;
// Initialization vector needed to decrypt encrypted_client_id.
optional bytes encrypted_client_id_iv = 4;
// AES-128 privacy key, encrytped with the service public public key using
// AES-128 privacy key, encrypted with the service public public key using
// RSA-OAEP.
optional bytes encrypted_privacy_key = 5;
}
@@ -547,3 +581,48 @@ message SignedCertificateStatusList {
// key using RSASSA-PSS. Required.
optional bytes signature = 2;
}
// ----------------------------------------------------------------------------
// widevine_header.proto
// ----------------------------------------------------------------------------
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Description:
// Public protocol buffer definitions for Widevine Cenc Header
// protocol.
message WidevineCencHeader {
enum Algorithm {
UNENCRYPTED = 0;
AESCTR = 1;
};
// Replaced with protection_scheme.
optional Algorithm algorithm = 1 [deprecated=true];
repeated bytes key_id = 2;
// Content provider name.
optional string provider = 3;
// A content identifier, specified by content provider.
optional bytes content_id = 4;
// Track type. Acceptable values are SD, HD and AUDIO. Used to differentiate
// content keys used by an asset.
// No longer adding track_type to the PSSH since the Widevine license server
// will return keys for all allowed track types in a single license.
optional string track_type_deprecated = 5;
// The name of a registered policy to be used for this asset.
optional string policy = 6 [deprecated=true];
// Crypto period index, for media using key rotation.
optional uint32 crypto_period_index = 7;
// Optional protected context for group content. The grouped_license is a
// serialized SignedMessage.
optional bytes grouped_license = 8;
// Protection scheme identifying the encryption algorithm. Represented as one
// of the following 4CC values: 'cenc' (AES-CTR), 'cbc1' (AES-CBC),
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
optional uint32 protection_scheme = 9;
}

View File

@@ -1,173 +0,0 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "max_res_engine.h"
#include "clock.h"
#include "log.h"
namespace {
const int64_t kHdcpCheckInterval = 10;
const uint32_t kNoResolution = 0;
} // namespace
namespace wvcdm {
MaxResEngine::MaxResEngine(CryptoSession* crypto_session) {
Init(crypto_session, new Clock());
}
MaxResEngine::MaxResEngine(CryptoSession* crypto_session, Clock* clock) {
Init(crypto_session, clock);
}
MaxResEngine::~MaxResEngine() {
AutoLock lock(status_lock_);
DeleteAllKeys();
}
bool MaxResEngine::CanDecrypt(const KeyId& key_id) {
AutoLock lock(status_lock_);
if (keys_.count(key_id) > 0) {
return keys_[key_id]->can_decrypt();
} else {
// If a Key ID is unknown to us, we don't know of any constraints for it,
// so never block decryption.
return true;
}
}
void MaxResEngine::Init(CryptoSession* crypto_session, Clock* clock) {
AutoLock lock(status_lock_);
current_resolution_ = kNoResolution;
clock_.reset(clock);
next_check_time_ = clock_->GetCurrentTime();
crypto_session_ = crypto_session;
}
void MaxResEngine::SetLicense(
const video_widevine_server::sdk::License& license) {
AutoLock lock(status_lock_);
DeleteAllKeys();
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
const KeyContainer& key = license.key(key_index);
if (key.type() == KeyContainer::CONTENT && key.has_id() &&
key.video_resolution_constraints_size() > 0) {
const ConstraintList& constraints = key.video_resolution_constraints();
const KeyId& key_id = key.id();
if (key.has_required_protection()) {
keys_[key_id] =
new KeyStatus(constraints, key.required_protection().hdcp());
} else {
keys_[key_id] = new KeyStatus(constraints);
}
}
}
}
void MaxResEngine::SetResolution(uint32_t width, uint32_t height) {
AutoLock lock(status_lock_);
current_resolution_ = width * height;
}
void MaxResEngine::OnTimerEvent() {
AutoLock lock(status_lock_);
int64_t current_time = clock_->GetCurrentTime();
if (!keys_.empty() && current_resolution_ != kNoResolution &&
current_time >= next_check_time_) {
CryptoSession::HdcpCapability current_hdcp_level;
CryptoSession::HdcpCapability ignored;
if (!crypto_session_->GetHdcpCapabilities(&current_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

View File

@@ -38,6 +38,10 @@ uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
return ::OEMCrypto_APIVersion();
}
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level) {
return ::OEMCrypto_Security_Patch_Level();
}
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
return ::OEMCrypto_SecurityLevel();
}

View File

@@ -0,0 +1,67 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
// This should be used when liboemcrypto.so is linked with the CDM code at
// compile time.
//
// Defines APIs introduced in newer version (v11) which is not available in v10
// to allow an older oemcrypto implementation to be linked with CDM.
#include "OEMCryptoCENC.h"
extern "C" OEMCryptoResult OEMCrypto_DecryptCTR_V10(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv, size_t block_offset,
const OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags);
typedef struct {
const uint8_t* key_id;
size_t key_id_length;
const uint8_t* key_data_iv;
const uint8_t* key_data;
size_t key_data_length;
const uint8_t* key_control_iv;
const uint8_t* key_control;
} OEMCrypto_KeyObject_V9;
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V9_or_V10(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject_V9* key_array,
const uint8_t* pst, size_t pst_length);
extern "C" uint8_t OEMCrypto_Security_Patch_Level() { return 0; }
extern "C" OEMCryptoResult OEMCrypto_DecryptCENC(
OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length,
bool is_encrypted, const uint8_t* iv, size_t block_offset,
OEMCrypto_DestBufferDesc* out_buffer,
const OEMCrypto_CENCEncryptPatternDesc* pattern, uint8_t subsample_flags) {
return OEMCrypto_DecryptCTR_V10(session, data_addr, data_length, is_encrypted,
iv, block_offset, out_buffer,
subsample_flags);
}
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_length,
const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
size_t pst_length) {
OEMCrypto_KeyObject_V9 key_array_v9[num_keys];
for (size_t key_index = 0; key_index < num_keys; key_index++) {
key_array_v9[key_index].key_id = key_array[key_index].key_id;
key_array_v9[key_index].key_id_length = key_array[key_index].key_id_length;
key_array_v9[key_index].key_data_iv = key_array[key_index].key_data_iv;
key_array_v9[key_index].key_data = key_array[key_index].key_data;
key_array_v9[key_index].key_data_length =
key_array[key_index].key_data_length;
key_array_v9[key_index].key_control_iv =
key_array[key_index].key_control_iv;
key_array_v9[key_index].key_control = key_array[key_index].key_control;
}
return OEMCrypto_LoadKeys_V9_or_V10(
session, message, message_length, signature, signature_length,
enc_mac_keys_iv, enc_mac_keys, num_keys, key_array_v9, pst, pst_length);
}

View File

@@ -3,10 +3,7 @@
#include "policy_engine.h"
#include <limits.h>
#include <sstream>
#include <string>
#include <vector>
#include "clock.h"
#include "log.h"
@@ -17,6 +14,13 @@
using video_widevine_server::sdk::License;
namespace {
const int64_t kHdcpCheckInterval = 10;
const uint32_t kNoResolution = 0;
} // namespace
namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
@@ -32,18 +36,43 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
policy_max_duration_seconds_(0),
session_id_(session_id),
event_listener_(event_listener),
max_res_engine_(new MaxResEngine(crypto_session)),
clock_(new Clock) {}
license_keys_(new LicenseKeys),
clock_(new Clock) {
InitDevice(crypto_session);
}
PolicyEngine::~PolicyEngine() {}
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
if (keys_status_.find(key_id) == keys_status_.end()) {
if (license_keys_->IsContentKey(key_id)) {
return license_keys_->CanDecryptContent(key_id);
} else {
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
b2a_hex(key_id).c_str());
return false;
}
return keys_status_[key_id] == kKeyStatusUsable;
}
void PolicyEngine::InitDevice(CryptoSession* crypto_session) {
current_resolution_ = kNoResolution;
next_device_check_ = 0;
crypto_session_ = crypto_session;
}
void PolicyEngine::CheckDevice(int64_t current_time) {
if (current_time < next_device_check_) {
return;
}
if (!license_keys_->Empty() && current_resolution_ != kNoResolution) {
CryptoSession::HdcpCapability current_hdcp_level;
CryptoSession::HdcpCapability ignored;
if (!crypto_session_->GetHdcpCapabilities(&current_hdcp_level, &ignored)) {
current_hdcp_level = HDCP_NONE;
}
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
next_device_check_ = current_time + kHdcpCheckInterval;
}
}
void PolicyEngine::OnTimerEvent() {
@@ -57,7 +86,8 @@ void PolicyEngine::OnTimerEvent() {
return;
}
max_res_engine_->OnTimerEvent();
// Check device conditions that affect playability (HDCP, resolution)
CheckDevice(current_time);
bool renewal_needed = false;
@@ -110,17 +140,8 @@ void PolicyEngine::SetLicense(const License& license) {
license_id_.Clear();
license_id_.CopyFrom(license.id());
policy_.Clear();
// Extract content key ids.
keys_status_.clear();
for (int key_index = 0; key_index < license.key_size(); ++key_index) {
const License::KeyContainer& key = license.key(key_index);
if (key.type() == License::KeyContainer::CONTENT && key.has_id())
keys_status_[key.id()] = kKeyStatusInternalError;
}
license_keys_->SetFromLicense(license);
UpdateLicense(license);
max_res_engine_->SetLicense(license);
}
void PolicyEngine::SetLicenseForRelease(const License& license) {
@@ -130,7 +151,6 @@ void PolicyEngine::SetLicenseForRelease(const License& license) {
// Expire any old keys.
NotifyKeysChange(kKeyStatusExpired);
UpdateLicense(license);
}
@@ -220,7 +240,7 @@ void PolicyEngine::DecryptionEvent() {
}
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
max_res_engine_->SetResolution(width, height);
SetDeviceResolution(width, height);
}
void PolicyEngine::NotifySessionExpiration() {
@@ -228,35 +248,46 @@ void PolicyEngine::NotifySessionExpiration() {
NotifyKeysChange(kKeyStatusExpired);
}
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
std::stringstream ss;
int64_t current_time = clock_->GetCurrentTime();
if (license_state_ == kLicenseStateInitial) {
key_info->clear();
query_response->clear();
return NO_ERROR;
}
(*key_info)[QUERY_KEY_LICENSE_TYPE] =
(*query_response)[QUERY_KEY_LICENSE_TYPE] =
license_id_.type() == video_widevine_server::sdk::STREAMING
? QUERY_VALUE_STREAMING
: QUERY_VALUE_OFFLINE;
(*key_info)[QUERY_KEY_PLAY_ALLOWED] =
(*query_response)[QUERY_KEY_PLAY_ALLOWED] =
policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
(*key_info)[QUERY_KEY_PERSIST_ALLOWED] =
(*query_response)[QUERY_KEY_PERSIST_ALLOWED] =
policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
(*key_info)[QUERY_KEY_RENEW_ALLOWED] =
(*query_response)[QUERY_KEY_RENEW_ALLOWED] =
policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
ss << GetLicenseDurationRemaining(current_time);
(*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
(*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
ss.str("");
ss << GetPlaybackDurationRemaining(current_time);
(*key_info)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
(*key_info)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
(*query_response)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = ss.str();
(*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] = policy_.renewal_server_url();
return NO_ERROR;
}
CdmResponseType PolicyEngine::QueryKeyAllowedUsage(
const KeyId& key_id, CdmKeyAllowedUsage* key_usage) {
if (NULL == key_usage) {
return INVALID_PARAMETERS_ENG_12;
}
if (license_keys_->GetAllowedUsage(key_id, key_usage)) {
return NO_ERROR;
}
return KEY_NOT_FOUND_1;
}
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
if (playback_start_time_ == 0) return false;
@@ -272,6 +303,12 @@ bool PolicyEngine::GetSecondsSinceLastPlayed(
return (*seconds_since_last_played >= 0) ? true : false;
}
int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() {
int64_t current_time = clock_->GetCurrentTime();
return std::min(GetLicenseDurationRemaining(current_time),
GetPlaybackDurationRemaining(current_time));
}
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time) {
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
@@ -345,25 +382,14 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
}
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
bool keys_changed = false;
bool keys_changed;
bool has_new_usable_key = false;
for (std::map<KeyId, CdmKeyStatus>::iterator it = keys_status_.begin();
it != keys_status_.end(); ++it) {
const KeyId key_id = it->first;
CdmKeyStatus& key_status = it->second;
CdmKeyStatus updated_status = new_status;
if (updated_status == kKeyStatusUsable) {
if (!max_res_engine_->CanDecrypt(key_id))
updated_status = kKeyStatusOutputNotAllowed;
}
if (key_status != updated_status) {
key_status = updated_status;
if (updated_status == kKeyStatusUsable) has_new_usable_key = true;
keys_changed = true;
}
}
if (keys_changed && event_listener_) {
event_listener_->OnSessionKeysChange(session_id_, keys_status_,
keys_changed = license_keys_->ApplyStatusChange(new_status,
&has_new_usable_key);
if (event_listener_ && keys_changed) {
CdmKeyStatusMap content_keys;
license_keys_->ExtractKeyStatuses(&content_keys);
event_listener_->OnSessionKeysChange(session_id_, content_keys,
has_new_usable_key);
}
}
@@ -381,8 +407,4 @@ void PolicyEngine::NotifyExpirationUpdate() {
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
void PolicyEngine::set_max_res_engine(MaxResEngine* max_res_engine) {
max_res_engine_.reset(max_res_engine);
}
} // wvcdm
} // namespace wvcdm

View File

@@ -10,6 +10,7 @@
#include <iostream>
#include <vector>
#include <modp_b64.h>
#include <modp_b64w.h>
#include "log.h"
@@ -78,6 +79,26 @@ std::string b2a_hex(const std::string& byte) {
byte.length());
}
// Encode for standard base64 encoding (RFC4648).
std::string Base64Encode(const std::vector<uint8_t>& bin_input) {
if (bin_input.empty()) {
return std::string();
}
int in_size = bin_input.size();
std::string b64_output(modp_b64_encode_len(in_size), 0);
int out_size = modp_b64_encode(
&b64_output[0], reinterpret_cast<const char*>(&bin_input[0]), in_size);
if (out_size == -1) {
LOGE("Base64Encode failed");
return std::string();
}
b64_output.resize(out_size);
return b64_output;
}
// Filename-friendly base64 encoding (RFC4648), commonly referred to
// as Base64WebSafeEncode.
//
@@ -111,6 +132,25 @@ std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input) {
return b64_output;
}
// Decode for standard base64 encoding (RFC4648).
std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
if (b64_input.empty()) {
return std::vector<uint8_t>();
}
int in_size = b64_input.size();
std::vector<uint8_t> bin_output(modp_b64_decode_len(in_size), 0);
int out_size = modp_b64_decode(reinterpret_cast<char*>(&bin_output[0]),
b64_input.data(), in_size);
if (out_size == -1) {
LOGE("Base64Decode failed");
return std::vector<uint8_t>(0);
}
bin_output.resize(out_size);
return bin_output;
}
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
// as Base64WebSafeDecode.
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
@@ -157,18 +197,6 @@ std::string IntToString(int value) {
return out_string;
}
std::string UintToString(unsigned int value) {
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
// So round up to allocate 3 output characters per byte.
const int kOutputBufSize = 3 * sizeof(unsigned int);
char buffer[kOutputBufSize];
memset(buffer, 0, kOutputBufSize);
snprintf(buffer, kOutputBufSize, "%u", value);
std::string out_string(buffer);
return out_string;
}
int64_t htonll64(int64_t x) { // Convert to big endian (network-byte-order)
union {
uint32_t array[2];

View File

@@ -38,34 +38,55 @@ const std::string kTestData =
const std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh");
const std::string kOneByteOverB64Data("SGVsbG8gRnJpZW5kIQ==");
const std::string kTwoBytesOverB64Data("SGVsbG8gRnJpZW5kISE=");
const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
const std::string kB64TestData = "GPFc9rc+INmI8FwtyTrUrv6xnKHWZNZ/5uaT21nFjNg=";
const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
make_pair(&kNullString, &kNullString),
make_pair(&kf, &kfB64),
make_pair(&kfo, &kfoB64),
make_pair(&kfoo, &kfooB64),
make_pair(&kfoob, &kfoobB64),
make_pair(&kfooba, &kfoobaB64),
make_pair(&kfoobar, &kfoobarB64),
make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data),
make_pair(&kOneByteOverData, &kOneByteOverB64Data),
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
make_pair(&kTestData, &kB64TestData)};
const std::pair<const std::string *, const std::string *> kBase64TestVectors[] =
{make_pair(&kNullString, &kNullString),
make_pair(&kf, &kfB64),
make_pair(&kfo, &kfoB64),
make_pair(&kfoo, &kfooB64),
make_pair(&kfoob, &kfoobB64),
make_pair(&kfooba, &kfoobaB64),
make_pair(&kfoobar, &kfoobarB64),
make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data),
make_pair(&kOneByteOverData, &kOneByteOverB64Data),
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
make_pair(&kTestData, &kB64TestData)};
std::string ConvertToBase64WebSafe(const std::string &std_base64_string) {
std::string str(std_base64_string);
for (size_t i = 0; i < str.size(); ++i) {
if (str[i] == '+')
str[i] = '-';
else if (str[i] == '/')
str[i] = '_';
}
return str;
}
} // namespace
class Base64EncodeDecodeTest
: public ::testing::TestWithParam<
std::pair<const std::string*, const std::string*> > {};
std::pair<const std::string *, const std::string *> > {};
TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
std::pair<const std::string *, const std::string *> values = GetParam();
std::vector<uint8_t> decoded_vector = Base64SafeDecode(values.second->data());
std::vector<uint8_t> decoded_vector = Base64Decode(values.second->data());
std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(values.first->data(), decoded_string.data());
std::string b64_string = Base64Encode(decoded_vector);
EXPECT_STREQ(values.second->data(), b64_string.data());
}
TEST_P(Base64EncodeDecodeTest, WebSafeEncodeDecodeTest) {
std::pair<const std::string *, const std::string *> values = GetParam();
std::string encoded_string = ConvertToBase64WebSafe(*(values.second));
std::vector<uint8_t> decoded_vector = Base64SafeDecode(encoded_string);
std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(values.first->data(), decoded_string.data());
std::string b64_string = Base64SafeEncode(decoded_vector);
EXPECT_STREQ(values.second->data(), b64_string.data());
EXPECT_STREQ(encoded_string.data(), b64_string.data());
}
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest,

View File

@@ -4,6 +4,7 @@
// This is because we need a valid RSA certificate, and will attempt to connect
// to the provisioning server to request one if we don't.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string>
@@ -43,6 +44,8 @@ const std::string kWebmMimeType = "video/webm";
class WvCdmEngineTest : public testing::Test {
public:
WvCdmEngineTest() : cdm_engine_(&file_system_) {}
static void SetUpTestCase() {
ConfigTestEnv config(kContentProtectionUatServer);
g_client_auth.assign(config.client_auth());
@@ -52,20 +55,16 @@ class WvCdmEngineTest : public testing::Test {
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
// Extract the key ID from the PSSH box.
InitializationData extractor(CENC_INIT_DATA_FORMAT,
g_key_id_pssh);
InitializationData extractor(CENC_INIT_DATA_FORMAT, g_key_id_pssh);
g_key_id_unwrapped = extractor.data();
}
virtual void SetUp() {
CdmResponseType status =
cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
NULL /* forced_session_id */, &session_id_);
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
Provision();
status = cdm_engine_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
NULL /* forced_session_id */,
&session_id_);
status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
}
ASSERT_EQ(NO_ERROR, status);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
@@ -82,37 +81,43 @@ class WvCdmEngineTest : public testing::Test {
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, EMPTY_ORIGIN,
&prov_request, &provisioning_server_url));
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
EXPECT_TRUE(ok);
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(EMPTY_ORIGIN,
message, &cert,
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(message, &cert,
&wrapped_key));
}
void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string) {
CdmAppParameterMap app_parameters;
std::string server_url;
CdmKeySetId key_set_id;
InitializationData init_data(init_data_type_string, key_id);
CdmKeyRequestType key_request_type;
CdmKeyRequest key_request;
EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateKeyRequest(
session_id_, key_set_id, init_data,
kLicenseTypeStreaming, app_parameters, &key_msg_,
&key_request_type, &server_url, NULL));
EXPECT_EQ(kKeyRequestTypeInitial, key_request_type);
kLicenseTypeStreaming, app_parameters,
&key_request));
key_msg_ = key_request.message;
EXPECT_EQ(kKeyRequestTypeInitial, key_request.type);
}
void GenerateRenewalRequest() {
EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateRenewalRequest(
session_id_, &key_msg_, &server_url_));
CdmKeyRequest request;
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_.GenerateRenewalRequest(session_id_, &request));
key_msg_ = request.message;
server_url_ = request.url;
}
std::string GetKeyRequestResponse(const std::string& server_url,
@@ -171,6 +176,7 @@ class WvCdmEngineTest : public testing::Test {
EXPECT_EQ(KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
}
FileSystem file_system_;
CdmEngine cdm_engine_;
std::string key_msg_;
std::string session_id_;

View File

@@ -88,13 +88,12 @@ const std::string kWrappedKey = a2bs_hex(
"E74C92B44F9205D22262FB47948654229DE1920F8EDF96A19A88A1CA1552F8856FB4CBF83B"
"AA3348419159D207F65FCE9C1A500C6818");
const std::string kTestOrigin = "com.google";
class MockDeviceFiles : public DeviceFiles {
public:
MockDeviceFiles() : DeviceFiles(NULL) {}
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
MOCK_METHOD3(RetrieveCertificate, bool(const std::string&, std::string*,
std::string*));
MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*));
};
class MockCryptoSession : public CryptoSession {
@@ -136,7 +135,7 @@ using ::testing::StrEq;
class CdmSessionTest : public ::testing::Test {
protected:
virtual void SetUp() {
cdm_session_.reset(new CdmSession(NULL, kTestOrigin, NULL, NULL));
cdm_session_.reset(new CdmSession(NULL));
// Inject testing mocks.
license_parser_ = new MockCdmLicense(cdm_session_->session_id());
cdm_session_->set_license_parser(license_parser_);
@@ -165,9 +164,8 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
.InSequence(crypto_session_seq)
@@ -178,7 +176,7 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
Properties::set_use_certificates_as_identification(true);
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
}
TEST_F(CdmSessionTest, InitWithKeybox) {
@@ -193,13 +191,14 @@ TEST_F(CdmSessionTest, InitWithKeybox) {
EXPECT_CALL(*crypto_session_, GetToken(NotNull()))
.InSequence(crypto_session_seq)
.WillOnce(DoAll(SetArgPointee<0>(kToken), Return(true)));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*license_parser_,
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_)))
.WillOnce(Return(true));
Properties::set_use_certificates_as_identification(false);
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
}
TEST_F(CdmSessionTest, ReInitFail) {
@@ -212,9 +211,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(kToken), SetArgPointee<2>(kWrappedKey),
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
.InSequence(crypto_session_seq)
@@ -225,8 +223,8 @@ TEST_F(CdmSessionTest, ReInitFail) {
Properties::set_use_certificates_as_identification(true);
ASSERT_EQ(NO_ERROR, cdm_session_->Init());
ASSERT_NE(NO_ERROR, cdm_session_->Init());
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
ASSERT_NE(NO_ERROR, cdm_session_->Init(NULL));
}
TEST_F(CdmSessionTest, InitFailCryptoError) {
@@ -235,7 +233,7 @@ TEST_F(CdmSessionTest, InitFailCryptoError) {
Properties::set_use_certificates_as_identification(true);
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init());
ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init(NULL));
}
TEST_F(CdmSessionTest, InitNeedsProvisioning) {
@@ -248,13 +246,12 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
.InSequence(crypto_session_seq)
.WillOnce(Return(level));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(StrEq(kTestOrigin), NotNull(),
NotNull()))
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
.WillOnce(Return(false));
Properties::set_use_certificates_as_identification(true);
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init());
ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init(NULL));
}
} // namespace wvcdm

View File

@@ -1408,23 +1408,49 @@ UsageInfo kUsageInfoTestData[] = {
"0E12202FF1FBA9926A24A1F79970EC427DDF87B4421488F7952499BC33CEB282D9E48"
"A")}};
const std::string kTestOrigin = "com.google";
struct HlsAttributesInfo {
std::string key_set_id;
CdmHlsMethod method;
std::string media_segment_iv;
std::string file_data;
};
HlsAttributesInfo kHlsAttributesTestData[] = {
{
"ksidC8EAA2579A282EB0", kHlsMethodAes128, // hls attributes 0
a2bs_hex("F7C4D15BD466BF285E241A4E58638543"),
a2bs_hex("0A1A08041001321408011210F7C4D15BD466BF285E241A4E5863854312201"
"39114B0372FF80FADF92614106E27BE8BD1588B4CAE6E1AEFB7F9C34EA52E"
"CC"),
},
{
"ksidE8C37662C88DC673", kHlsMethodSampleAes, // hls attributes 1
a2bs_hex("16413F038088438B5D4CD99F03EBB3D8"),
a2bs_hex("0A1A0804100132140802121016413F038088438B5D4CD99F03EBB3D812205"
"9EA13188B75C55D1EB78B3A65DB3EA3F43BD1B16642266D988E3543943C5F"
"41"),
}};
class MockFile : public File {
public:
MOCK_METHOD2(Open, bool(const std::string&, int flags));
MOCK_METHOD0(Close, void());
MockFile() : File(NULL) {}
~MockFile() {}
MOCK_METHOD2(Read, ssize_t(char*, size_t));
MOCK_METHOD2(Write, ssize_t(const char*, size_t));
MOCK_METHOD0(Close, void());
};
class MockFileSystem : public FileSystem {
public:
MockFileSystem() {}
~MockFileSystem() {}
MOCK_METHOD2(Open, File*(const std::string&, int flags));
MOCK_METHOD0(IsFactoryReset, bool());
MOCK_METHOD1(Exists, bool(const std::string&));
MOCK_METHOD1(Remove, bool(const std::string&));
MOCK_METHOD2(Copy, bool(const std::string&, const std::string&));
MOCK_METHOD2(List, bool(const std::string&, std::vector<std::string>*));
MOCK_METHOD1(CreateDirectory, bool(const std::string));
MOCK_METHOD1(IsDirectory, bool(const std::string&));
MOCK_METHOD1(IsRegularFile, bool(const std::string&));
MOCK_METHOD1(FileSize, ssize_t(const std::string&));
};
@@ -1490,21 +1516,9 @@ class DeviceFilesTest : public ::testing::Test {
class DeviceFilesStoreTest : public DeviceFilesTest,
public ::testing::WithParamInterface<bool> {};
struct CertStorageVariant {
CertStorageVariant(bool dir_exists_value, const std::string& origin_value)
: dir_exists(dir_exists_value), origin(origin_value) {}
class DeviceCertificateStoreTest : public DeviceFilesTest {};
const bool dir_exists;
const std::string origin;
};
class DeviceCertificateStoreTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<CertStorageVariant> {};
class DeviceCertificateTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<std::string> {};
class DeviceCertificateTest : public DeviceFilesTest {};
class DeviceFilesSecurityLevelTest
: public DeviceFilesTest,
@@ -1513,13 +1527,22 @@ class DeviceFilesSecurityLevelTest
class DeviceFilesUsageInfoTest : public DeviceFilesTest,
public ::testing::WithParamInterface<int> {};
MATCHER(IsCreateFileFlagSet, "") { return File::kCreate & arg; }
MATCHER(IsBinaryFileFlagSet, "") { return File::kBinary & arg; }
class DeviceFilesHlsAttributesTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<HlsAttributesInfo*> {};
MATCHER(IsCreateFileFlagSet, "") { return FileSystem::kCreate & arg; }
MATCHER_P(IsStrEq, str, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
return memcmp(arg, str.c_str(), str.size()) == 0;
}
MATCHER_P2(Contains, str1, size, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, size + str1.size() + kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos);
}
MATCHER_P3(Contains, str1, str2, size, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
@@ -1586,105 +1609,78 @@ MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") {
data.find(str6) != std::string::npos && map7_entries_present);
}
TEST_P(DeviceCertificateStoreTest, StoreCertificate) {
MockFile file;
CertStorageVariant params = GetParam();
TEST_F(DeviceCertificateStoreTest, StoreCertificate) {
MockFileSystem file_system;
std::string certificate(GenerateRandomData(kCertificateLen));
std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen));
std::string device_certificate_path =
device_base_path_ + DeviceFiles::GetCertificateFileName(params.origin);
device_base_path_ + DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillOnce(Return(params.dir_exists));
if (params.dir_exists) {
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
} else {
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_)))
.WillOnce(Return(true));
}
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
MockFile file;
EXPECT_CALL(file_system,
Open(StrEq(device_certificate_path), IsCreateFileFlagSet()))
.WillOnce(Return(&file));
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
Gt(certificate.size() + wrapped_private_key.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(params.origin, certificate,
wrapped_private_key));
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
INSTANTIATE_TEST_CASE_P(
StoreCertificate, DeviceCertificateStoreTest,
::testing::Values(CertStorageVariant(true, EMPTY_ORIGIN),
CertStorageVariant(true, kTestOrigin),
CertStorageVariant(false, EMPTY_ORIGIN),
CertStorageVariant(false, kTestOrigin)));
TEST_P(DeviceCertificateTest, ReadCertificate) {
MockFile file;
std::string origin = GetParam();
TEST_F(DeviceCertificateTest, ReadCertificate) {
MockFileSystem file_system;
std::string device_certificate_path =
device_base_path_ + DeviceFiles::GetCertificateFileName(origin);
device_base_path_ + DeviceFiles::GetCertificateFileName();
std::string data = a2bs_hex(kTestCertificateFileData);
EXPECT_CALL(file, Exists(StrEq(device_certificate_path)))
MockFile file;
EXPECT_CALL(file_system, Exists(StrEq(device_certificate_path)))
.WillOnce(Return(true));
EXPECT_CALL(file, FileSize(StrEq(device_certificate_path)))
EXPECT_CALL(file_system, FileSize(StrEq(device_certificate_path)))
.WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(device_certificate_path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file_system, Open(StrEq(device_certificate_path), _))
.WillOnce(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
Return(data.size())));
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Write(_, _)).Times(0);
if (Properties::security_level_path_backward_compatibility_support()) {
EXPECT_CALL(file, List(_, NotNull())).WillOnce(Return(false));
}
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
std::string certificate, wrapped_private_key;
ASSERT_TRUE(device_files.RetrieveCertificate(origin, &certificate,
&wrapped_private_key));
ASSERT_TRUE(
device_files.RetrieveCertificate(&certificate, &wrapped_private_key));
EXPECT_EQ(kTestCertificate, b2a_hex(certificate));
EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key));
}
TEST_P(DeviceCertificateTest, HasCertificate) {
MockFile file;
std::string origin = GetParam();
TEST_F(DeviceCertificateTest, HasCertificate) {
MockFileSystem file_system;
std::string device_certificate_path =
device_base_path_ + DeviceFiles::GetCertificateFileName(origin);
device_base_path_ + DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, Exists(StrEq(device_certificate_path)))
EXPECT_CALL(file_system, Exists(StrEq(device_certificate_path)))
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_CALL(file, Open(_, _)).Times(0);
EXPECT_CALL(file_system, Open(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
ASSERT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
// MockFile returns false.
EXPECT_FALSE(device_files.HasCertificate(origin));
EXPECT_FALSE(device_files.HasCertificate());
// MockFile returns true.
EXPECT_TRUE(device_files.HasCertificate(origin));
EXPECT_TRUE(device_files.HasCertificate());
}
INSTANTIATE_TEST_CASE_P(CertificateUseTests, DeviceCertificateTest,
::testing::Values(EMPTY_ORIGIN, kTestOrigin));
TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
MockFile file;
MockFileSystem file_system;
std::string certificate(GenerateRandomData(kCertificateLen));
std::string wrapped_private_key(GenerateRandomData(kWrappedKeyLen));
@@ -1693,55 +1689,39 @@ TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
ASSERT_TRUE(
Properties::GetDeviceFilesBasePath(security_level, &device_base_path));
std::string device_certificate_path =
device_base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
device_base_path + DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path)))
.WillOnce(Return(false));
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path)))
.WillOnce(Return(true));
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
MockFile file;
EXPECT_CALL(file_system,
Open(StrEq(device_certificate_path), IsCreateFileFlagSet()))
.WillOnce(Return(&file));
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
Gt(certificate.size() + wrapped_private_key.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(security_level));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(EMPTY_ORIGIN, certificate,
wrapped_private_key));
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
INSTANTIATE_TEST_CASE_P(SecurityLevel, DeviceFilesSecurityLevelTest,
::testing::Values(kSecurityLevelL1, kSecurityLevelL3));
TEST_P(DeviceFilesStoreTest, StoreLicense) {
MockFile file;
MockFileSystem file_system;
size_t license_num = 0;
std::string license_path = device_base_path_ +
license_test_data[license_num].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
bool dir_exists = GetParam();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillOnce(Return(dir_exists));
if (dir_exists) {
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
} else {
EXPECT_CALL(file, CreateDirectory(StrEq(device_base_path_)))
.WillOnce(Return(true));
}
CdmAppParameterMap app_parameters =
GetAppParameters(license_test_data[license_num].app_parameters);
EXPECT_CALL(file, Open(StrEq(license_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
MockFile file;
EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet()))
.WillOnce(Return(&file));
EXPECT_CALL(
file, Write(Contains(license_test_data[license_num].pssh_data,
license_test_data[license_num].key_request,
@@ -1755,9 +1735,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreLicense(
license_test_data[license_num].key_set_id,
license_test_data[license_num].license_state,
@@ -1774,11 +1753,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, ::testing::Bool());
TEST_F(DeviceFilesTest, StoreLicenses) {
MockFileSystem file_system;
MockFile file;
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.Times(kNumberOfLicenses)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
for (size_t i = 0; i < kNumberOfLicenses; ++i) {
std::string license_path = device_base_path_ +
@@ -1788,9 +1764,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
CdmAppParameterMap app_parameters =
GetAppParameters(license_test_data[i].app_parameters);
EXPECT_CALL(file, Open(StrEq(license_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet()))
.WillOnce(Return(&file));
EXPECT_CALL(file, Write(Contains(license_test_data[i].pssh_data,
license_test_data[i].key_request,
@@ -1805,9 +1780,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
EXPECT_CALL(file, Close()).Times(kNumberOfLicenses);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
for (size_t i = 0; i < kNumberOfLicenses; i++) {
CdmAppParameterMap app_parameters =
GetAppParameters(license_test_data[i].app_parameters);
@@ -1825,6 +1799,7 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
}
TEST_F(DeviceFilesTest, RetrieveLicenses) {
MockFileSystem file_system;
MockFile file;
for (size_t i = 0; i < kNumberOfLicenses; ++i) {
@@ -1834,10 +1809,12 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
size_t size = license_test_data[i].file_data.size();
EXPECT_CALL(file, Exists(StrEq(license_path))).WillOnce(Return(true));
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
EXPECT_CALL(file_system, Exists(StrEq(license_path)))
.WillOnce(Return(true));
EXPECT_CALL(file_system, FileSize(StrEq(license_path)))
.WillOnce(Return(size));
EXPECT_CALL(file_system, Open(StrEq(license_path), _))
.WillOnce(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(size)))
.WillOnce(
DoAll(SetArrayArgument<0>(license_test_data[i].file_data.begin(),
@@ -1847,9 +1824,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
EXPECT_CALL(file, Close()).Times(kNumberOfLicenses);
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
CdmInitData pssh_data;
CdmKeyMessage key_request;
CdmKeyResponse key_response;
@@ -1886,7 +1862,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
}
TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
MockFile file;
MockFileSystem file_system;
LicenseInfo* test_data =
&license_app_parameters_backwards_compatibility_test_data;
@@ -1895,10 +1871,12 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
size_t size = test_data->file_data.size();
EXPECT_CALL(file, Exists(StrEq(license_path))).WillOnce(Return(true));
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
MockFile file;
EXPECT_CALL(file_system, Exists(StrEq(license_path))).WillOnce(Return(true));
EXPECT_CALL(file_system, FileSize(StrEq(license_path)))
.WillOnce(Return(size));
EXPECT_CALL(file_system, Open(StrEq(license_path), _))
.WillOnce(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(size)))
.WillOnce(DoAll(SetArrayArgument<0>(test_data->file_data.begin(),
test_data->file_data.end()),
@@ -1907,9 +1885,8 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
DeviceFiles::LicenseState license_state;
CdmInitData pssh_data;
CdmKeyMessage key_request;
@@ -1936,102 +1913,16 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
EXPECT_EQ(0u, app_parameters.size());
}
TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
if (!Properties::security_level_path_backward_compatibility_support()) {
return;
}
MockFile file;
std::vector<std::string> security_dirs;
EXPECT_TRUE(Properties::GetSecurityLevelDirectories(&security_dirs));
size_t pos = std::string::npos;
for (size_t i = 0; i < security_dirs.size(); ++i) {
pos = device_base_path_.rfind(security_dirs[i]);
if (std::string::npos != pos) break;
}
EXPECT_NE(std::string::npos, pos);
std::string base_path(device_base_path_, 0, pos);
std::vector<std::string> old_files;
std::string new_path;
for (size_t i = 0; i < security_dirs.size(); ++i) {
old_files.push_back(security_dirs[i]);
new_path = base_path + security_dirs[i];
EXPECT_CALL(file, IsRegularFile(StrEq(new_path))).WillOnce(Return(false));
EXPECT_CALL(file, Exists(StrEq(new_path)))
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(StrEq(new_path))).WillOnce(Return(true));
}
std::string old_path =
base_path + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
old_files.push_back(DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN));
EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true));
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
for (size_t i = 0; i < security_dirs.size(); ++i) {
new_path = base_path + security_dirs[i] +
DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
.WillOnce(Return(true));
}
for (size_t j = 0; j < kNumberOfLicenses; ++j) {
std::string file_name = license_test_data[j].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
old_path = base_path + file_name;
old_files.push_back(file_name);
EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true));
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
for (size_t i = 0; i < security_dirs.size(); ++i) {
new_path = base_path + security_dirs[i] + file_name;
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
.WillOnce(Return(true));
}
}
EXPECT_CALL(file, List(StrEq(base_path), NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(old_files), Return(true)));
std::string data = a2bs_hex(kTestCertificateFileData);
new_path =
device_base_path_ + DeviceFiles::GetCertificateFileName(EMPTY_ORIGIN);
EXPECT_CALL(file, Exists(StrEq(new_path))).WillOnce(Return(true));
EXPECT_CALL(file, FileSize(_)).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(_, _)).WillOnce(Return(true));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
Return(data.size())));
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
Properties::Init();
std::string certificate, wrapped_private_key;
ASSERT_TRUE(device_files.RetrieveCertificate(EMPTY_ORIGIN, &certificate,
&wrapped_private_key));
}
TEST_F(DeviceFilesTest, UpdateLicenseState) {
MockFile file;
MockFileSystem file_system;
std::string license_path = device_base_path_ +
license_update_test_data[0].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
MockFile file;
EXPECT_CALL(file_system, Open(StrEq(license_path), IsCreateFileFlagSet()))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Open(StrEq(license_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.Times(2)
.WillRepeatedly(Return(true));
.WillRepeatedly(Return(&file));
EXPECT_CALL(file, Write(IsStrEq(license_update_test_data[0].file_data),
Eq(license_update_test_data[0].file_data.size())))
.WillOnce(ReturnArg<1>());
@@ -2041,9 +1932,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
EXPECT_CALL(file, Close()).Times(2);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreLicense(
license_update_test_data[0].key_set_id,
license_update_test_data[0].license_state,
@@ -2072,32 +1962,33 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
}
TEST_F(DeviceFilesTest, DeleteLicense) {
MockFile file;
MockFileSystem file_system;
std::string license_path = device_base_path_ +
license_test_data[0].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
size_t size = license_test_data[0].file_data.size();
EXPECT_CALL(file, Exists(StrEq(license_path)))
MockFile file;
EXPECT_CALL(file_system, Exists(StrEq(license_path)))
.Times(2)
.WillOnce(Return(true))
.WillOnce(Return(false));
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file_system, FileSize(StrEq(license_path)))
.WillOnce(Return(size));
EXPECT_CALL(file_system, Open(StrEq(license_path), _))
.WillOnce(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(size)))
.WillOnce(
DoAll(SetArrayArgument<0>(license_test_data[0].file_data.begin(),
license_test_data[0].file_data.end()),
Return(size)));
EXPECT_CALL(file, Remove(StrEq(license_path))).WillOnce(Return(true));
EXPECT_CALL(file_system, Remove(StrEq(license_path))).WillOnce(Return(true));
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
DeviceFiles::LicenseState license_state;
CdmInitData pssh_data;
CdmKeyMessage key_request;
@@ -2135,17 +2026,11 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) {
// Validate that ReserveLicenseIds does not touch the file system.
MockFile file;
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))).Times(0);
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Open(_, _)).Times(0);
EXPECT_CALL(file, Write(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(0);
EXPECT_CALL(file, Read(_, _)).Times(0);
MockFileSystem file_system;
EXPECT_CALL(file_system, Open(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
for (size_t i = 0; i < kNumberOfLicenses; i++) {
EXPECT_TRUE(device_files.ReserveLicenseId(license_test_data[i].key_set_id));
// Validate that the license IDs are actually reserved.
@@ -2156,6 +2041,7 @@ TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) {
}
TEST_P(DeviceFilesUsageInfoTest, Read) {
MockFileSystem file_system;
MockFile file;
std::string app_id; // TODO(fredgc): add tests with multiple app_ids.
std::string path =
@@ -2167,28 +2053,24 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
data = kUsageInfoTestData[index].file_data;
}
if (index >= 0) {
EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(true));
EXPECT_CALL(file, FileSize(StrEq(path)))
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
EXPECT_CALL(file_system, FileSize(StrEq(path)))
.WillRepeatedly(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
Return(data.size())));
EXPECT_CALL(file, Close()).Times(1);
} else {
EXPECT_CALL(file, Exists(StrEq(path)))
.WillRepeatedly(Return(false));
EXPECT_CALL(file, FileSize(_)).Times(0);
EXPECT_CALL(file, Open(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(0);
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(false));
EXPECT_CALL(file_system, FileSize(_)).Times(0);
EXPECT_CALL(file_system, Open(_, _)).Times(0);
}
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
ASSERT_TRUE(device_files.RetrieveUsageInfo(app_id, &license_info));
@@ -2212,7 +2094,7 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
}
TEST_P(DeviceFilesUsageInfoTest, Store) {
MockFile file;
MockFileSystem file_system;
std::string app_id; // TODO(fredgc): multiple app ids.
std::string pst(GenerateRandomData(kProviderSessionTokenLen));
std::string license_request(GenerateRandomData(kLicenseRequestLen));
@@ -2226,24 +2108,24 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
if (index >= 0) {
data = kUsageInfoTestData[index].file_data;
}
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(index >= 0));
MockFile file;
EXPECT_CALL(file_system, Exists(StrEq(path)))
.WillRepeatedly(Return(index >= 0));
if (index >= 0) {
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
EXPECT_CALL(file_system, FileSize(StrEq(path)))
.WillOnce(Return(data.size()));
EXPECT_CALL(file_system, Open(StrEq(path), _))
.Times(2)
.WillRepeatedly(Return(true));
.WillRepeatedly(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
Return(data.size())));
EXPECT_CALL(file, Close()).Times(2);
} else {
EXPECT_CALL(file, FileSize(_)).Times(0);
EXPECT_CALL(file, Open(_, _)).Times(1).WillOnce(Return(true));
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file_system, FileSize(_)).Times(0);
EXPECT_CALL(file_system, Open(_, _)).Times(1).WillOnce(Return(&file));
EXPECT_CALL(file, Close());
}
EXPECT_CALL(file,
@@ -2253,9 +2135,8 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
key_set_id.size())))
.WillOnce(ReturnArg<1>());
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
ASSERT_TRUE(
device_files.StoreUsageInfo(pst, license_request, license, app_id,
@@ -2263,6 +2144,7 @@ TEST_P(DeviceFilesUsageInfoTest, Store) {
}
TEST_P(DeviceFilesUsageInfoTest, Delete) {
MockFileSystem file_system;
MockFile file;
std::string app_id; // TODO(fredgc): expand tests.
std::string path =
@@ -2282,24 +2164,19 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
}
}
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(index >= 0));
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(index >= 0));
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
EXPECT_CALL(file_system, FileSize(StrEq(path))).WillOnce(Return(data.size()));
if (index >= 1) {
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
EXPECT_CALL(file_system, Open(StrEq(path), _))
.Times(2)
.WillRepeatedly(Return(true));
.WillRepeatedly(Return(&file));
EXPECT_CALL(file, Write(Contains(prev_pst, prev_license, prev_data.size()),
Gt(prev_pst.size() + prev_license.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(2);
} else {
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
EXPECT_CALL(file, Write(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(1);
}
@@ -2307,9 +2184,8 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
Return(data.size())));
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
if (index >= 1) {
ASSERT_TRUE(device_files.DeleteUsageInfo(app_id, pst));
@@ -2319,36 +2195,33 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) {
}
TEST_P(DeviceFilesUsageInfoTest, DeleteAll) {
MockFileSystem file_system;
MockFile file;
std::string app_id; // TODO(fredgc): expand tests.
std::string path =
device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id);
int index = GetParam();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Write(_, _)).Times(0);
std::string data;
if (index < 0) {
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(false));
EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(false));
} else {
data = kUsageInfoTestData[index].file_data;
EXPECT_CALL(file, Exists(StrEq(path))).WillRepeatedly(Return(true));
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
EXPECT_CALL(file_system, FileSize(StrEq(path)))
.WillOnce(Return(data.size()));
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size())))
.WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()),
Return(data.size())));
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Remove(StrEq(path))).WillOnce(Return(true));
EXPECT_CALL(file_system, Remove(StrEq(path))).WillOnce(Return(true));
}
DeviceFiles device_files;
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
std::vector<std::string> psts;
ASSERT_TRUE(device_files.DeleteAllUsageInfoForApp(app_id, &psts));
@@ -2367,4 +2240,75 @@ TEST_P(DeviceFilesUsageInfoTest, DeleteAll) {
INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageInfoTest,
::testing::Range(-1, 4));
TEST_P(DeviceFilesHlsAttributesTest, Read) {
MockFileSystem file_system;
MockFile file;
HlsAttributesInfo* param = GetParam();
std::string path = device_base_path_ + param->key_set_id +
DeviceFiles::GetHlsAttributesFileNameExtension();
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
EXPECT_CALL(file_system, FileSize(StrEq(path)))
.WillRepeatedly(Return(param->file_data.size()));
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
EXPECT_CALL(file, Read(NotNull(), Eq(param->file_data.size())))
.WillOnce(DoAll(
SetArrayArgument<0>(param->file_data.begin(), param->file_data.end()),
Return(param->file_data.size())));
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
CdmHlsMethod method;
std::vector<uint8_t> media_segment_iv;
ASSERT_TRUE(device_files.RetrieveHlsAttributes(param->key_set_id, &method,
&media_segment_iv));
EXPECT_EQ(param->method, method);
EXPECT_EQ(b2a_hex(param->media_segment_iv), b2a_hex(media_segment_iv));
}
TEST_P(DeviceFilesHlsAttributesTest, Store) {
MockFileSystem file_system;
MockFile file;
HlsAttributesInfo* param = GetParam();
std::string path = device_base_path_ + param->key_set_id +
DeviceFiles::GetHlsAttributesFileNameExtension();
EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true));
EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file));
EXPECT_CALL(file, Write(Contains(param->media_segment_iv, 0),
Gt(param->media_segment_iv.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Read(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(1);
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
std::vector<uint8_t> iv(param->media_segment_iv.begin(),
param->media_segment_iv.end());
ASSERT_TRUE(
device_files.StoreHlsAttributes(param->key_set_id, param->method, iv));
}
TEST_P(DeviceFilesHlsAttributesTest, Delete) {
MockFileSystem file_system;
MockFile file;
HlsAttributesInfo* param = GetParam();
std::string path = device_base_path_ + param->key_set_id +
DeviceFiles::GetHlsAttributesFileNameExtension();
EXPECT_CALL(file_system, Remove(StrEq(path))).WillOnce(Return(true));
DeviceFiles device_files(&file_system);
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
ASSERT_TRUE(device_files.DeleteHlsAttributes(param->key_set_id));
}
INSTANTIATE_TEST_CASE_P(HlsAttributes, DeviceFilesHlsAttributesTest,
::testing::Range(&kHlsAttributesTestData[0],
&kHlsAttributesTestData[2]));
} // namespace wvcdm

View File

@@ -1,9 +1,8 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include <gtest/gtest.h>
#include "device_files.h"
#include "file_store.h"
#include "properties.h"
#include "test_vectors.h"
namespace wvcdm {
@@ -18,20 +17,12 @@ const std::string kWildcard = "*";
class FileTest : public testing::Test {
protected:
virtual void SetUp() { CreateTestDir(); }
FileTest() {}
virtual void TearDown() { RemoveTestDir(); }
void CreateTestDir() {
File file;
if (!file.Exists(test_vectors::kTestDir)) {
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
}
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
}
void RemoveTestDir() {
File file;
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir));
}
std::string GenerateRandomData(uint32_t len) {
@@ -41,64 +32,52 @@ class FileTest : public testing::Test {
}
return data;
}
FileSystem file_system;
};
TEST_F(FileTest, FileExists) {
File file;
EXPECT_TRUE(file.Exists(test_vectors::kExistentFile));
EXPECT_TRUE(file.Exists(test_vectors::kExistentDir));
EXPECT_FALSE(file.Exists(test_vectors::kNonExistentFile));
EXPECT_FALSE(file.Exists(test_vectors::kNonExistentDir));
}
TEST_F(FileTest, CreateDirectory) {
File file;
std::string dir_wo_delimiter =
test_vectors::kTestDir.substr(0, test_vectors::kTestDir.size() - 1);
if (file.Exists(dir_wo_delimiter)) EXPECT_TRUE(file.Remove(dir_wo_delimiter));
EXPECT_FALSE(file.Exists(dir_wo_delimiter));
EXPECT_TRUE(file.CreateDirectory(dir_wo_delimiter));
EXPECT_TRUE(file.Exists(dir_wo_delimiter));
EXPECT_TRUE(file.Remove(dir_wo_delimiter));
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
EXPECT_TRUE(file_system.Exists(test_vectors::kExistentFile));
EXPECT_TRUE(file_system.Exists(test_vectors::kExistentDir));
EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentFile));
EXPECT_FALSE(file_system.Exists(test_vectors::kNonExistentDir));
}
TEST_F(FileTest, RemoveDir) {
File file;
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
EXPECT_TRUE(file_system.Remove(test_vectors::kTestDir));
EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir));
}
TEST_F(FileTest, OpenFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File handle;
EXPECT_TRUE(handle.Remove(path));
EXPECT_TRUE(file_system.Remove(path));
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
File* file = file_system.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
file->Close();
EXPECT_TRUE(handle.Exists(path));
EXPECT_TRUE(file_system.Exists(path));
}
TEST_F(FileTest, RemoveDirAndFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Remove(path));
EXPECT_FALSE(file.Exists(path));
File* file = file_system.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
file->Close();
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file_system.Exists(path));
EXPECT_TRUE(file_system.Remove(path));
EXPECT_FALSE(file_system.Exists(path));
file = file_system.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
file->Close();
EXPECT_TRUE(file_system.Exists(path));
RemoveTestDir();
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file.Exists(path));
EXPECT_FALSE(file_system.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file_system.Exists(path));
}
TEST_F(FileTest, RemoveWildcardFiles) {
@@ -107,159 +86,53 @@ TEST_F(FileTest, RemoveWildcardFiles) {
std::string wildcard_path =
test_vectors::kTestDir + kWildcard + kTestFileNameExt;
File file;
EXPECT_TRUE(file.Open(path1, File::kCreate));
file.Close();
EXPECT_TRUE(file.Open(path2, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path1));
EXPECT_TRUE(file.Exists(path2));
EXPECT_TRUE(file.Remove(wildcard_path));
EXPECT_FALSE(file.Exists(path1));
EXPECT_FALSE(file.Exists(path2));
}
File* file = file_system.Open(path1, FileSystem::kCreate);
ASSERT_TRUE(file);
file->Close();
file = file_system.Open(path2, FileSystem::kCreate);
ASSERT_TRUE(file);
file->Close();
TEST_F(FileTest, IsDir) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file.IsDirectory(path));
EXPECT_TRUE(file.IsDirectory(test_vectors::kTestDir));
}
TEST_F(FileTest, IsRegularFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_TRUE(file.IsRegularFile(path));
EXPECT_FALSE(file.IsRegularFile(test_vectors::kTestDir));
EXPECT_TRUE(file_system.Exists(path1));
EXPECT_TRUE(file_system.Exists(path2));
EXPECT_TRUE(file_system.Remove(wildcard_path));
EXPECT_FALSE(file_system.Exists(path1));
EXPECT_FALSE(file_system.Exists(path2));
}
TEST_F(FileTest, FileSize) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
file_system.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
File* file = file_system.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
EXPECT_TRUE(file->Write(write_data.data(), write_data.size()));
file->Close();
EXPECT_TRUE(file_system.Exists(path));
EXPECT_EQ(static_cast<ssize_t>(write_data.size()), file.FileSize(path));
}
TEST_F(FileTest, WriteReadTextFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = "This is a test";
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
std::string read_data;
read_data.resize(file.FileSize(path));
File rd_file;
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
EXPECT_EQ(static_cast<ssize_t>(write_data.size()),
file_system.FileSize(path));
}
TEST_F(FileTest, WriteReadBinaryFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
file_system.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
File* file = file_system.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file);
EXPECT_TRUE(file->Write(write_data.data(), write_data.size()));
file->Close();
EXPECT_TRUE(file_system.Exists(path));
std::string read_data;
read_data.resize(file.FileSize(path));
File rd_file;
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
read_data.resize(file_system.FileSize(path));
file = file_system.Open(path, FileSystem::kReadOnly);
ASSERT_TRUE(file);
EXPECT_TRUE(file->Read(&read_data[0], read_data.size()));
file->Close();
EXPECT_EQ(write_data, read_data);
}
TEST_F(FileTest, CopyFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
ASSERT_TRUE(file.Exists(path));
std::string path_copy = test_vectors::kTestDir + kTestFileName2;
EXPECT_FALSE(file.Exists(path_copy));
EXPECT_TRUE(file.Copy(path, path_copy));
std::string read_data;
read_data.resize(file.FileSize(path_copy));
File rd_file;
EXPECT_TRUE(rd_file.Open(path_copy, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
EXPECT_EQ(file.FileSize(path), file.FileSize(path_copy));
}
TEST_F(FileTest, ListEmptyDirectory) {
std::vector<std::string> files;
File file;
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
EXPECT_EQ(0u, files.size());
}
TEST_F(FileTest, ListFiles) {
File file;
std::string path = test_vectors::kTestDir + kTestDirName;
EXPECT_TRUE(file.CreateDirectory(path));
path = test_vectors::kTestDir + kTestFileName;
std::string write_data = GenerateRandomData(600);
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
file.Close();
EXPECT_TRUE(file.Exists(path));
path = test_vectors::kTestDir + kTestFileName2;
write_data = GenerateRandomData(600);
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
file.Close();
EXPECT_TRUE(file.Exists(path));
std::vector<std::string> files;
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
EXPECT_EQ(3u, files.size());
for (size_t i = 0; i < files.size(); ++i) {
EXPECT_TRUE(files[i] == kTestDirName || files[i] == kTestFileName ||
files[i] == kTestFileName2);
}
}
} // namespace wvcdm

View File

@@ -0,0 +1,416 @@
// Copyright 2016 Google Inc. All Rights Reserved.
// These tests are for the generic crypto operations. They call on the
// CdmEngine class and exercise the classes below it as well. In
// particular, we assume that the OEMCrypo layer works, and has a valid keybox.
// This is because we need a valid RSA certificate, and will attempt to connect
// to the provisioning server to request one if we don't.
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include <string>
#include "cdm_engine.h"
#include "license_request.h"
#include "log.h"
#include "oec_session_util.h"
#include "../../oemcrypto/mock/src/oemcrypto_key_mock.h"
#include "string_conversions.h"
#include "url_request.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
namespace {
const std::string kKeySystem = "com.widevine.alpha";
} // namespace
namespace wvcdm {
class WvGenericOperationsTest : public testing::Test {
public:
virtual void SetUp() {
::testing::Test::SetUp();
// Load test keybox. This keybox will be used by any CryptoSession
// created by the CDM under test.
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox());
// Perform CdmEngine setup
cdm_engine_ = new CdmEngine(&file_system_);
CdmResponseType status =
cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
Provision();
status = cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
}
ASSERT_EQ(NO_ERROR, status);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
ASSERT_TRUE(cdm_engine_->IsOpenSession(session_id_));
// Get OEMCrypto session ID from the CDM
CdmQueryMap query;
cdm_engine_->QueryOemCryptoSessionId(session_id_, &query);
std::istringstream parse_int;
parse_int.str(query[QUERY_KEY_OEMCRYPTO_SESSION_ID]);
parse_int >> oec_session_id_;
// Construct and install keys into the CDM's OEMCrypto session.
OecSessionSetup(oec_session_id_);
EncryptAndLoadKeys();
}
virtual void TearDown() {
oec_util_session_.close();
cdm_engine_->CloseSession(session_id_);
// OEMCrypto_Terminate() will be performed during the test class's
// destruction (specifically by the CryptoSession destructor)
}
void OecSessionSetup(uint32_t oec_session_id) {
buffer_size_ = 160;
oec_util_session_.SetSessionId(oec_session_id);
oec_util_session_.GenerateTestSessionKeys();
MakeFourKeys();
}
enum GenericKeyType {
kGenericEncrypt = 0,
kGenericDecrypt = 1,
kGenericSign = 2,
kGenericVerify = 3
};
virtual void MakeFourKeys(
uint32_t duration = wvoec::kDuration, uint32_t control = 0,
uint32_t nonce = 0, const std::string& pst = "") {
ASSERT_NO_FATAL_FAILURE(
oec_util_session_.FillSimpleMessage(duration, control, nonce, pst));
oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |=
htonl(wvoec_mock::kControlAllowEncrypt);
oec_util_session_.license().keys[kGenericDecrypt].control.control_bits |=
htonl(wvoec_mock::kControlAllowDecrypt);
oec_util_session_.license().keys[kGenericSign].control.control_bits |=
htonl(wvoec_mock::kControlAllowSign);
oec_util_session_.license().keys[kGenericVerify].control.control_bits |=
htonl(wvoec_mock::kControlAllowVerify);
oec_util_session_.license().keys[kGenericSign].key_data_length =
wvcdm::MAC_KEY_SIZE;
oec_util_session_.license().keys[kGenericVerify].key_data_length =
wvcdm::MAC_KEY_SIZE;
clear_buffer_.assign(buffer_size_, 0);
for (size_t i = 0; i < clear_buffer_.size(); i++) {
clear_buffer_[i] = 1 + i % 250;
}
for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) {
iv_[i] = i;
}
}
std::string GetKeyId(GenericKeyType type) {
std::string key_id;
size_t key_id_length = oec_util_session_.license().keys[0].key_id_length;
key_id.assign(
&(oec_util_session_.license().keys[type].key_id[0]),
&(oec_util_session_.license().keys[type].key_id[key_id_length]));
return key_id;
}
std::string GetClearBuffer() {
std::string buffer;
size_t buffer_length = clear_buffer_.size();
buffer.assign(&clear_buffer_[0], &clear_buffer_[buffer_length]);
return buffer;
}
std::string GetEncryptedBuffer() {
std::string buffer;
size_t buffer_length = encrypted_buffer_.size();
buffer.assign(&encrypted_buffer_[0], &encrypted_buffer_[buffer_length]);
return buffer;
}
std::string GetIvBlock() {
std::string buffer;
size_t buffer_length = wvcdm::KEY_IV_SIZE;
buffer.assign(&iv_[0], &iv_[buffer_length]);
return buffer;
}
std::string GetSignatureBuffer() {
std::string buffer;
buffer.resize(SHA256_DIGEST_LENGTH);
return buffer;
}
void EncryptAndLoadKeys() {
ASSERT_NO_FATAL_FAILURE(oec_util_session_.EncryptAndSign());
oec_util_session_.LoadTestKeys();
}
protected:
void Provision() {
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR,
cdm_engine_->GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
EXPECT_TRUE(ok);
ASSERT_EQ(NO_ERROR,
cdm_engine_->HandleProvisioningResponse(message, &cert,
&wrapped_key));
}
// This CryptoSession object handles Initialization and Termination
// calls on OEMCrypto for the duration of the test. CryptoSessions
// created by the CDM will share the OEMCrypto state of this CryptoSession,
// including, for example, a test keybox.
CryptoSession crypto_session_;
FileSystem file_system_;
CdmEngine* cdm_engine_;
std::string key_msg_;
std::string session_id_;
std::string server_url_;
uint32_t oec_session_id_;
wvoec::Session oec_util_session_;
size_t buffer_size_;
vector<uint8_t> clear_buffer_;
vector<uint8_t> encrypted_buffer_;
uint8_t iv_[wvcdm::KEY_IV_SIZE];
};
TEST_F(WvGenericOperationsTest, NormalSessionOpenClose) {
wvoec::Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.close());
}
TEST_F(WvGenericOperationsTest, GenerateSessionKeys) {
wvoec::Session s;
ASSERT_NO_FATAL_FAILURE(s.open());
ASSERT_NO_FATAL_FAILURE(s.GenerateTestSessionKeys());
ASSERT_NO_FATAL_FAILURE(s.close());
}
TEST_F(WvGenericOperationsTest, GenericEncryptNoKey) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// No key
KeyId key_id("xyz");
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(KEY_ERROR_1, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericEncryptKeyNotAllowed) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// Wrong key
std::string key_id = GetKeyId(kGenericDecrypt);
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericEncryptGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// Good key
std::string key_id = GetKeyId(kGenericEncrypt);
cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericDecryptKeyNotAllowed) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// Wrong key
std::string key_id = GetKeyId(kGenericEncrypt);
cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericDecryptGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string out_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// Good key
std::string key_id = GetKeyId(kGenericDecrypt);
cdm_sts = cdm_engine_->GenericDecrypt(session_id_, in_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128,
&out_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericSignKeyNotAllowed) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer;
// Wrong key
std::string key_id = GetKeyId(kGenericVerify);
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
&signature_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericSignGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer;
// Good key
std::string key_id = GetKeyId(kGenericSign);
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
&signature_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericVerifyKeyNotAllowed) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer = GetSignatureBuffer();
// Wrong key
std::string key_id = GetKeyId(kGenericSign);
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature_buffer);
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
}
TEST_F(WvGenericOperationsTest, GenericVerifyGood) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer = GetSignatureBuffer();
// Good key - signature not set.
std::string key_id = GetKeyId(kGenericVerify);
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature_buffer);
// OEMCrypto error is OEMCrypto_ERROR_SIGNATURE_FAILURE
EXPECT_EQ(UNKNOWN_ERROR, cdm_sts);
}
class WvGenericOperationsDataTest : public WvGenericOperationsTest {
public:
// Construct keys for encrypt/decrypt and for sign/verify
virtual void MakeFourKeys(
uint32_t duration = wvoec::kDuration, uint32_t control = 0,
uint32_t nonce = 0, const std::string& pst = "") {
ASSERT_NO_FATAL_FAILURE(
oec_util_session_.FillSimpleMessage(duration, control, nonce, pst));
oec_util_session_.license().keys[kGenericEncrypt].control.control_bits |=
htonl(wvoec_mock::kControlAllowEncrypt |
wvoec_mock::kControlAllowDecrypt);
oec_util_session_.license().keys[kGenericSign].control.control_bits |=
htonl(wvoec_mock::kControlAllowSign | wvoec_mock::kControlAllowVerify);
oec_util_session_.license().keys[kGenericSign].key_data_length =
wvcdm::MAC_KEY_SIZE;
clear_buffer_.assign(buffer_size_, 0);
for (size_t i = 0; i < clear_buffer_.size(); i++) {
clear_buffer_[i] = 1 + i % 250;
}
for (size_t i = 0; i < wvcdm::KEY_IV_SIZE; i++) {
iv_[i] = i;
}
}
};
TEST_F(WvGenericOperationsDataTest, GenericEncryptDecrypt) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string encrypted_buffer = GetEncryptedBuffer();
std::string iv = GetIvBlock();
// Encrypt
std::string key_id = GetKeyId(kGenericEncrypt);
cdm_sts = cdm_engine_->GenericEncrypt(
session_id_, in_buffer, key_id, iv, wvcdm::kEncryptionAlgorithmAesCbc128,
&encrypted_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
// Decrypt, use same key as encrypt.
key_id = GetKeyId(kGenericEncrypt);
std::string final_buffer;
final_buffer.resize(in_buffer.size());
cdm_sts = cdm_engine_->GenericDecrypt(
session_id_, encrypted_buffer, key_id, iv,
wvcdm::kEncryptionAlgorithmAesCbc128, &final_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
EXPECT_EQ(0, in_buffer.compare(final_buffer));
}
TEST_F(WvGenericOperationsDataTest, GenericSignVerify) {
CdmResponseType cdm_sts;
std::string in_buffer = GetClearBuffer();
std::string signature_buffer = GetSignatureBuffer();
// Signing key
std::string key_id = GetKeyId(kGenericSign);
cdm_sts = cdm_engine_->GenericSign(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
&signature_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
// Verify signature, use same key as sign.
key_id = GetKeyId(kGenericSign);
cdm_sts = cdm_engine_->GenericVerify(session_id_, in_buffer, key_id,
wvcdm::kSigningAlgorithmHmacSha256,
signature_buffer);
EXPECT_EQ(NO_ERROR, cdm_sts);
}
} // namespace wvcdm

View File

@@ -41,9 +41,12 @@ SSL_CTX* InitSslContext() {
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv3_client_method();
method = TLSv1_2_client_method();
ctx = SSL_CTX_new(method);
if (!ctx) LOGE("failed to create SSL context");
int ret = SSL_CTX_set_cipher_list(
ctx, "ALL:!RC4-MD5:!RC4-SHA:!ECDHE-ECDSA-RC4-SHA:!ECDHE-RSA-RC4-SHA");
if (0 != ret) LOGE("error disabling vulnerable ciphers");
return ctx;
}
@@ -294,6 +297,10 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) {
int total_read = 0;
int to_read = len;
if (socket_fd_ == -1) {
LOGE("Socket to %s not open. Cannot read.", domain_name_.c_str());
return -1;
}
while (to_read > 0) {
if (!SocketWait(socket_fd_, /* for_read */ true, timeout_in_ms)) {
LOGE("unable to read from %s", domain_name_.c_str());
@@ -329,6 +336,10 @@ int HttpSocket::Write(const char* data, int len, int timeout_in_ms) {
int total_sent = 0;
int to_send = len;
if (socket_fd_ == -1) {
LOGE("Socket to %s not open. Cannot write.", domain_name_.c_str());
return -1;
}
while (to_send > 0) {
int sent;
if (secure_connect_)

View File

@@ -1,19 +1,44 @@
// Copyright 2015 Google Inc. All Rights Reserved.
#include <arpa/inet.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "initialization_data.h"
#include "license_protocol.pb.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
// References:
// [1] http://dashif.org/identifiers/content-protection/
// [2] http://www.w3.org/TR/encrypted-media/cenc-format.html#common-system
// [3] https://tools.ietf.org/html/draft-pantos-http-live-streaming-18
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::WidevineCencHeader;
namespace {
// Constants for JSON formatting
const std::string kLeftBrace = "{";
const std::string kRightBrace = "}";
const std::string kLeftBracket = "[";
const std::string kRightBracket = "]";
const std::string kComma = ",";
const std::string kColon = ":";
const std::string kDoubleQuote = "\"";
const std::string kNewline = "\n";
const std::string kFourSpaceIndent = " ";
const std::string kJsonProvider = "provider";
const std::string kJsonContentId = "content_id";
const std::string kJsonKeyIds = "key_ids";
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;
const std::string kWidevinePssh = a2bs_hex(
// Widevine PSSH box
"00000042" // atom size
@@ -139,8 +164,245 @@ const std::string kZeroSizedPsshBox = a2bs_hex(
// data:
"08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
// HLS test attribute key and values
const std::string kHlsIvHexValue = "6DF49213A781E338628D0E9C812D328E";
const std::string kHlsIvValue = "0x" + kHlsIvHexValue;
const std::string kHlsKeyFormatValue = "com.widevine.alpha";
const std::string kHlsKeyFormatValueOther = "com.example";
const std::string kHlsTestKey1 = "TESTKEY1";
const std::string kHlsTestValue1 = "testvalue1";
const std::string kHlsTestKey2 = "TESTKEY2";
const std::string kHlsTestValue2 = "testvalue2";
const std::string kHlsTestInvalidLowercaseKey = "testkey3";
const std::string kHlsTestKeyWithDash = "TEST-KEY4";
const std::string kHlsTestInvalidNonAlphanumKey = "TEST;KEY4";
const std::string kHlsTestValueWithEmbeddedQuote = "test\"value1";
const std::string kHlsTestEmptyHexValue = "";
const std::string kHlsTestNoHexValue = "0x";
const std::string kHlsTestHexValueWithOddBytes = kHlsIvHexValue + "7";
const std::string kHlsTestInvalidHexValue = kHlsIvHexValue + "g7";
char kHlsTestKeyFormatVersionsSeparator = '/';
const std::string kHlsTestUriDataFormat = "data:text/plain;base64,";
const std::string kHlsTestProvider = "youtube";
const std::string kHlsTestContentId = "MjAxNV9UZWFycw==";
const std::string kHlsTestKeyId1 = "371E135E1A985D75D198A7F41020DC23";
const std::string kHlsTestKeyId2 = "E670D9B60AE61583E01BC9253FA19261";
const std::string kHlsTestKeyId3 = "78094E72165DF39721B8A354D6A71390";
const std::string kHlsTestInvalidKeyId = "B8A354D6A71390";
const std::string kHlsTestKeyFormatVersion1 = "1";
const std::string kHlsTestKeyFormatVersion3 = "3";
const std::string kHlsTestKeyFormatVersion5 = "5";
const std::string kHlsTestKeyFormatVersion13 = "13";
const std::string kHlsTestKeyFormatVersion21 = "21";
const std::string kHlsTestKeyFormatVersion37 = "37";
// HLS attribute helper functions
std::string QuoteString(const std::string& value) {
return "\"" + value + "\"";
}
std::string GenerateJsonInitData(const std::string& provider,
const std::string& content_id,
const std::vector<std::string>& key_ids) {
std::string json = kLeftBrace + kNewline;
if (provider.size() > 0) {
json += kFourSpaceIndent + kDoubleQuote + kJsonProvider + kDoubleQuote +
kColon + kDoubleQuote + provider + kDoubleQuote + kComma + kNewline;
}
if (content_id.size() > 0) {
json += kFourSpaceIndent + kDoubleQuote + kJsonContentId + kDoubleQuote +
kColon + kDoubleQuote + content_id + kDoubleQuote + kComma +
kNewline;
}
if (key_ids.size() > 0) {
json += kFourSpaceIndent + kDoubleQuote + kJsonKeyIds + kDoubleQuote +
kColon + kNewline;
json += kFourSpaceIndent + kLeftBracket + kNewline;
for (size_t i = 0; i < key_ids.size(); ++i) {
json += kFourSpaceIndent + kFourSpaceIndent + kDoubleQuote + key_ids[i] +
kDoubleQuote;
if (i != key_ids.size() - 1) {
json += kComma;
}
json += kNewline;
}
json += kFourSpaceIndent + kRightBracket + kNewline;
}
json += kRightBrace + kNewline;
return json;
}
class VectorOfStrings {
public:
VectorOfStrings(const std::string& str) { vec_.push_back(str); }
VectorOfStrings& Add(const std::string& str) {
vec_.push_back(str);
return *this;
}
const std::vector<std::string> Generate() { return vec_; }
private:
std::vector<std::string> vec_;
};
std::string GenerateHlsUriData(const std::string& provider,
const std::string& content_id,
const std::vector<std::string>& key_ids) {
std::string json = GenerateJsonInitData(provider, content_id, key_ids);
std::vector<uint8_t> json_init_data(
reinterpret_cast<const uint8_t*>(json.data()),
reinterpret_cast<const uint8_t*>(json.data() + json.size()));
return kHlsTestUriDataFormat + Base64Encode(json_init_data);
}
std::string CreateHlsAttributeList(const std::string& method,
const std::string& provider,
const std::string& content_id,
const std::vector<std::string>& key_ids,
const std::string& iv,
const std::string& key_format,
const std::string& key_format_version) {
return "EXT-X-KEY: " + HLS_METHOD_ATTRIBUTE + "=" + method + "," +
HLS_URI_ATTRIBUTE + "=" +
QuoteString(GenerateHlsUriData(provider, content_id, key_ids)) + "," +
HLS_IV_ATTRIBUTE + "=" + iv + "," + HLS_KEYFORMAT_ATTRIBUTE + "=" +
QuoteString(key_format) + "," + HLS_KEYFORMAT_VERSIONS_ATTRIBUTE +
"=" + QuoteString(key_format_version);
}
// HLS attribute list for testing
const std::string kHlsAttributeList = CreateHlsAttributeList(
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListKeyFormatUnknown = CreateHlsAttributeList(
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue,
kHlsKeyFormatValueOther, HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListKeyFormatVersionUnsupported =
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider,
kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(),
kHlsIvValue, kHlsKeyFormatValue, "2");
const std::string kHlsAttributeListKeyFormatVersionMultiple =
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider,
kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(),
kHlsIvValue, kHlsKeyFormatValue, "1/2/5");
const std::string kHlsAttributeListMethodAes128 = CreateHlsAttributeList(
HLS_METHOD_AES_128, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListMethodNone = CreateHlsAttributeList(
HLS_METHOD_NONE, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListMethodInvalid = CreateHlsAttributeList(
kHlsTestValue1, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsIvValue, kHlsKeyFormatValue,
HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListInvalidUriNoProvider =
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, "", kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(),
kHlsIvValue, kHlsKeyFormatValue,
HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListInvalidUriNoContentId =
CreateHlsAttributeList(HLS_METHOD_SAMPLE_AES, kHlsTestProvider, "",
VectorOfStrings(kHlsTestKeyId1).Generate(),
kHlsIvValue, kHlsKeyFormatValue,
HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListInvalidUriNoKeyId = CreateHlsAttributeList(
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings("").Generate(), kHlsIvValue, kHlsKeyFormatValue,
HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListValidUriThreeKeyIds = CreateHlsAttributeList(
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1)
.Add(kHlsTestKeyId2)
.Add(kHlsTestKeyId3)
.Generate(),
kHlsIvValue, kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListNoIv = CreateHlsAttributeList(
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestNoHexValue,
kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1);
const std::string kHlsAttributeListInvalidIv = CreateHlsAttributeList(
HLS_METHOD_SAMPLE_AES, kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate(), kHlsTestHexValueWithOddBytes,
kHlsKeyFormatValue, HLS_KEYFORMAT_VERSION_VALUE_1);
std::string InsertHlsAttributeInList(const std::string key,
const std::string& value) {
return kHlsAttributeList + "," + key + "=" + value + "," + kHlsTestKey2 +
"=" + kHlsTestValue2;
}
struct HlsInitDataVariant {
HlsInitDataVariant(CdmHlsMethod method, const std::string& provider,
const std::string& content_id, const std::string& key_id,
bool success)
: method_(method), provider_(provider), content_id_(content_id),
success_(success) {
if (key_id.size() > 0) key_ids_.push_back(key_id);
}
HlsInitDataVariant& AddKeyId(const std::string& key_id) {
key_ids_.push_back(key_id);
return *this;
}
const CdmHlsMethod method_;
const std::string provider_;
const std::string content_id_;
std::vector<std::string> key_ids_;
const bool success_;
};
struct HlsAttributeVariant {
HlsAttributeVariant(const std::string& attribute_list, const std::string& key,
const std::string& value, bool success)
: attribute_list_(attribute_list),
key_(key),
value_(value),
success_(success) {}
const std::string attribute_list_;
const std::string key_;
const std::string value_;
const bool success_;
};
class InitializationDataTest : public ::testing::TestWithParam<std::string> {};
class HlsAttributeExtractionTest
: public ::testing::TestWithParam<HlsAttributeVariant> {};
class HlsHexAttributeExtractionTest
: public ::testing::TestWithParam<HlsAttributeVariant> {};
class HlsQuotedAttributeExtractionTest
: public ::testing::TestWithParam<HlsAttributeVariant> {};
class HlsKeyFormatVersionsExtractionTest
: public ::testing::TestWithParam<std::vector<std::string> > {};
class HlsConstructionTest
: public ::testing::TestWithParam<HlsInitDataVariant> {};
class HlsInitDataConstructionTest : public ::testing::Test {};
class HlsParseTest : public ::testing::TestWithParam<HlsAttributeVariant> {};
class HlsTest : public ::testing::Test {};
} // namespace
TEST_P(InitializationDataTest, Parse) {
@@ -148,17 +410,343 @@ TEST_P(InitializationDataTest, Parse) {
EXPECT_FALSE(init_data.IsEmpty());
}
INSTANTIATE_TEST_CASE_P(ParsePssh, InitializationDataTest,
::testing::Values(kWidevinePssh, kWidevinePsshFirst,
kWidevinePsshAfterV0Pssh,
kWidevinePsshAfterNonZeroFlags,
kWidevinePsshAfterV1Pssh,
kWidevineV1Pssh, kOtherBoxFirst,
kZeroSizedPsshBox));
TEST_P(HlsKeyFormatVersionsExtractionTest, ExtractKeyFormatVersions) {
std::vector<std::string> versions = GetParam();
std::string key_format_versions;
for (size_t i = 0; i < versions.size(); ++i) {
key_format_versions += versions[i] + kHlsTestKeyFormatVersionsSeparator;
}
key_format_versions.resize(key_format_versions.size() -
sizeof(kHlsTestKeyFormatVersionsSeparator));
std::vector<std::string> extracted_versions =
InitializationData::ExtractKeyFormatVersions(key_format_versions);
EXPECT_EQ(versions.size(), extracted_versions.size());
for (size_t i = 0; i < versions.size(); ++i) {
bool found = false;
for (size_t j = 0; j < extracted_versions.size(); ++j) {
if (versions[i] == extracted_versions[j]) {
found = true;
break;
}
}
EXPECT_TRUE(found);
}
}
INSTANTIATE_TEST_CASE_P(
ParsePssh, InitializationDataTest,
HlsTest, HlsKeyFormatVersionsExtractionTest,
::testing::Values(VectorOfStrings(kHlsTestKeyFormatVersion1).Generate(),
VectorOfStrings(kHlsTestKeyFormatVersion21).Generate(),
VectorOfStrings(kHlsTestKeyFormatVersion1)
.Add(kHlsTestKeyFormatVersion3)
.Generate(),
VectorOfStrings(kHlsTestKeyFormatVersion1)
.Add(kHlsTestKeyFormatVersion3)
.Add(kHlsTestKeyFormatVersion13)
.Generate(),
VectorOfStrings(kHlsTestKeyFormatVersion13)
.Add(kHlsTestKeyFormatVersion5)
.Add(kHlsTestKeyFormatVersion21)
.Add(kHlsTestKeyFormatVersion37)
.Generate()));
TEST_P(HlsAttributeExtractionTest, ExtractAttribute) {
HlsAttributeVariant param = GetParam();
std::string value;
if (param.success_) {
EXPECT_TRUE(InitializationData::ExtractAttribute(param.attribute_list_,
param.key_, &value));
EXPECT_EQ(param.value_, value);
} else {
EXPECT_FALSE(InitializationData::ExtractAttribute(param.attribute_list_,
param.key_, &value));
}
}
INSTANTIATE_TEST_CASE_P(
HlsTest, HlsAttributeExtractionTest,
::testing::Values(
kWidevinePssh,
kWidevinePsshFirst,
kWidevinePsshAfterV0Pssh,
kWidevinePsshAfterNonZeroFlags,
kWidevinePsshAfterV1Pssh,
kWidevineV1Pssh,
kOtherBoxFirst,
kZeroSizedPsshBox
));
HlsAttributeVariant(kHlsAttributeList, HLS_METHOD_ATTRIBUTE,
HLS_METHOD_SAMPLE_AES, true),
HlsAttributeVariant(kHlsAttributeList, HLS_URI_ATTRIBUTE,
QuoteString(GenerateHlsUriData(
kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate())),
true),
HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvValue,
true),
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE,
QuoteString(kHlsKeyFormatValue), true),
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
QuoteString(HLS_KEYFORMAT_VERSION_VALUE_1), true),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
kHlsTestValue1),
kHlsTestKey1, kHlsTestValue1, true),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
kHlsTestValue1),
kHlsTestKey2, kHlsTestValue2, true),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "\t",
kHlsTestValue1),
kHlsTestKey1, kHlsTestValue1, false),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
" " + kHlsTestValue1),
kHlsTestKey1, kHlsTestValue1, false),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
kHlsTestValue1 + " "),
kHlsTestKey1, kHlsTestValue1, false),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1 + "3",
kHlsTestValue1),
kHlsTestKey1, kHlsTestValue1, false),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1, ""),
kHlsTestKey1, "", true),
HlsAttributeVariant(InsertHlsAttributeInList(
kHlsTestInvalidLowercaseKey, kHlsTestValue1),
kHlsTestInvalidLowercaseKey, kHlsTestValue1, false),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKeyWithDash,
kHlsTestValue1),
kHlsTestKeyWithDash, kHlsTestValue1, true),
HlsAttributeVariant(InsertHlsAttributeInList(
kHlsTestInvalidNonAlphanumKey, kHlsTestValue1),
kHlsTestInvalidNonAlphanumKey, kHlsTestValue1,
false),
HlsAttributeVariant(
InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)),
kHlsTestKey1, QuoteString(kHlsTestValue1), true),
HlsAttributeVariant(
InsertHlsAttributeInList(
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)),
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote), true)));
TEST_P(HlsHexAttributeExtractionTest, ExtractHexAttribute) {
HlsAttributeVariant param = GetParam();
std::vector<uint8_t> value;
if (param.success_) {
EXPECT_TRUE(InitializationData::ExtractHexAttribute(param.attribute_list_,
param.key_, &value));
EXPECT_EQ(param.value_, b2a_hex(value));
} else {
EXPECT_FALSE(InitializationData::ExtractHexAttribute(param.attribute_list_,
param.key_, &value));
}
}
INSTANTIATE_TEST_CASE_P(
HlsTest, HlsHexAttributeExtractionTest,
::testing::Values(
HlsAttributeVariant(kHlsAttributeList, HLS_IV_ATTRIBUTE, kHlsIvHexValue,
true),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
kHlsTestEmptyHexValue),
kHlsTestKey1, kHlsTestEmptyHexValue, false),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
kHlsTestNoHexValue),
kHlsTestKey1, kHlsTestNoHexValue, false),
HlsAttributeVariant(InsertHlsAttributeInList(
kHlsTestKey1, kHlsTestHexValueWithOddBytes),
kHlsTestKey1, kHlsTestHexValueWithOddBytes, false),
HlsAttributeVariant(InsertHlsAttributeInList(kHlsTestKey1,
kHlsTestInvalidHexValue),
kHlsTestKey1, kHlsTestInvalidHexValue, false)));
TEST_P(HlsQuotedAttributeExtractionTest, ExtractQuotedAttribute) {
HlsAttributeVariant param = GetParam();
std::string value;
if (param.success_) {
EXPECT_TRUE(InitializationData::ExtractQuotedAttribute(
param.attribute_list_, param.key_, &value));
EXPECT_EQ(param.value_, value);
} else {
EXPECT_FALSE(InitializationData::ExtractQuotedAttribute(
param.attribute_list_, param.key_, &value));
}
}
INSTANTIATE_TEST_CASE_P(
HlsTest, HlsQuotedAttributeExtractionTest,
::testing::Values(
HlsAttributeVariant(
kHlsAttributeList, HLS_URI_ATTRIBUTE,
GenerateHlsUriData(kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate()),
true),
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_ATTRIBUTE,
kHlsKeyFormatValue, true),
HlsAttributeVariant(kHlsAttributeList, HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
HLS_KEYFORMAT_VERSION_VALUE_1, true),
HlsAttributeVariant(
InsertHlsAttributeInList(kHlsTestKey1, QuoteString(kHlsTestValue1)),
kHlsTestKey1, kHlsTestValue1, true),
HlsAttributeVariant(
InsertHlsAttributeInList(
kHlsTestKey1, QuoteString(kHlsTestValueWithEmbeddedQuote)),
kHlsTestKey1, kHlsTestValueWithEmbeddedQuote, false)));
TEST_P(HlsConstructionTest, InitData) {
HlsInitDataVariant param = GetParam();
std::string uri =
GenerateHlsUriData(param.provider_, param.content_id_, param.key_ids_);
std::string value;
EXPECT_EQ(param.success_, InitializationData::ConstructWidevineInitData(
param.method_, uri, &value));
if (param.success_) {
WidevineCencHeader cenc_header;
EXPECT_TRUE(cenc_header.ParseFromString(value));
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
cenc_header.algorithm());
for (size_t i = 0; i < param.key_ids_.size(); ++i) {
bool key_id_found = false;
if (param.key_ids_[i].size() != 32) continue;
for (int j = 0; j < cenc_header.key_id_size(); ++j) {
if (param.key_ids_[i] == b2a_hex(cenc_header.key_id(j))) {
key_id_found = true;
break;
}
}
EXPECT_TRUE(key_id_found);
}
EXPECT_EQ(param.provider_, cenc_header.provider());
std::vector<uint8_t> param_content_id_vec(Base64Decode(param.content_id_));
EXPECT_EQ(
std::string(param_content_id_vec.begin(), param_content_id_vec.end()),
cenc_header.content_id());
uint32_t protection_scheme = 0;
switch (param.method_) {
case kHlsMethodAes128: protection_scheme = kFourCcCbc1; break;
case kHlsMethodSampleAes: protection_scheme = kFourCcCbcs; break;
default: break;
}
EXPECT_EQ(protection_scheme, ntohl(cenc_header.protection_scheme()));
}
}
INSTANTIATE_TEST_CASE_P(
HlsTest, HlsConstructionTest,
::testing::Values(
HlsInitDataVariant(kHlsMethodAes128, "", kHlsTestContentId,
kHlsTestKeyId1, false),
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
"", kHlsTestKeyId1, false),
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
kHlsTestContentId, "", false),
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
kHlsTestContentId, kHlsTestInvalidKeyId, false),
HlsInitDataVariant(kHlsMethodNone, kHlsTestProvider, kHlsTestContentId,
kHlsTestKeyId1, false),
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
kHlsTestContentId, kHlsTestKeyId1, true),
HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider,
kHlsTestContentId, kHlsTestKeyId1, true),
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
kHlsTestContentId, kHlsTestKeyId1, true)
.AddKeyId(kHlsTestKeyId2)
.AddKeyId(kHlsTestKeyId3),
HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider,
kHlsTestContentId, kHlsTestKeyId1, true)
.AddKeyId(kHlsTestKeyId2)
.AddKeyId(kHlsTestKeyId3),
HlsInitDataVariant(kHlsMethodAes128, kHlsTestProvider,
kHlsTestContentId, kHlsTestInvalidKeyId, true)
.AddKeyId(kHlsTestKeyId1),
HlsInitDataVariant(kHlsMethodSampleAes, kHlsTestProvider,
kHlsTestContentId, kHlsTestInvalidKeyId, true)
.AddKeyId(kHlsTestKeyId1)));
TEST_F(HlsInitDataConstructionTest, InvalidUriDataFormat) {
std::string json =
GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate());
std::vector<uint8_t> json_init_data(
reinterpret_cast<const uint8_t*>(json.data()),
reinterpret_cast<const uint8_t*>(json.data() + json.size()));
std::string value;
EXPECT_FALSE(InitializationData::ConstructWidevineInitData(
kHlsMethodAes128, Base64Encode(json_init_data), &value));
}
TEST_F(HlsInitDataConstructionTest, InvalidUriBase64Encode) {
std::string json =
GenerateJsonInitData(kHlsTestProvider, kHlsTestContentId,
VectorOfStrings(kHlsTestKeyId1).Generate());
std::string value;
EXPECT_FALSE(InitializationData::ConstructWidevineInitData(
kHlsMethodSampleAes, kHlsTestUriDataFormat + json, &value));
}
TEST_P(HlsParseTest, Parse) {
HlsAttributeVariant param = GetParam();
InitializationData init_data(HLS_INIT_DATA_FORMAT, param.attribute_list_);
if (param.success_) {
EXPECT_TRUE(init_data.is_hls());
EXPECT_FALSE(init_data.IsEmpty());
if (param.key_.compare(HLS_METHOD_ATTRIBUTE) == 0) {
if (param.value_.compare(HLS_METHOD_SAMPLE_AES) == 0) {
EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method());
} else if (param.value_.compare(HLS_METHOD_AES_128) == 0) {
EXPECT_EQ(kHlsMethodAes128, init_data.hls_method());
} else if (param.value_.compare(HLS_METHOD_NONE) == 0) {
EXPECT_EQ(kHlsMethodNone, init_data.hls_method());
}
} else {
EXPECT_EQ(kHlsMethodSampleAes, init_data.hls_method());
}
WidevineCencHeader cenc_header;
EXPECT_TRUE(cenc_header.ParseFromString(init_data.data()));
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
cenc_header.algorithm());
if (param.key_.compare(kJsonProvider) == 0) {
EXPECT_EQ(param.value_, cenc_header.provider());
} else if (param.key_.compare(kJsonContentId) == 0) {
EXPECT_EQ(param.value_, cenc_header.content_id());
} else if (param.key_.compare(kJsonKeyIds) == 0) {
EXPECT_EQ(param.value_, b2a_hex(cenc_header.key_id(0)));
}
EXPECT_EQ(kHlsIvHexValue, b2a_hex(init_data.hls_iv()));
} else {
EXPECT_TRUE(init_data.is_hls());
EXPECT_TRUE(init_data.IsEmpty());
}
}
INSTANTIATE_TEST_CASE_P(
HlsTest, HlsParseTest,
::testing::Values(
HlsAttributeVariant(kHlsAttributeList, "", "", true),
HlsAttributeVariant(kHlsAttributeListKeyFormatUnknown,
HLS_KEYFORMAT_ATTRIBUTE, kHlsKeyFormatValueOther,
false),
HlsAttributeVariant(kHlsAttributeListKeyFormatVersionUnsupported,
HLS_KEYFORMAT_VERSIONS_ATTRIBUTE, "2", false),
HlsAttributeVariant(kHlsAttributeListMethodAes128, HLS_METHOD_ATTRIBUTE,
HLS_METHOD_AES_128, true),
HlsAttributeVariant(kHlsAttributeListMethodNone, HLS_METHOD_ATTRIBUTE,
HLS_METHOD_NONE, false),
HlsAttributeVariant(kHlsAttributeListKeyFormatVersionMultiple,
HLS_KEYFORMAT_VERSIONS_ATTRIBUTE,
HLS_KEYFORMAT_VERSION_VALUE_1, true),
HlsAttributeVariant(kHlsAttributeListMethodInvalid,
HLS_METHOD_ATTRIBUTE, kHlsTestValue1, false),
HlsAttributeVariant(kHlsAttributeListInvalidUriNoProvider,
kJsonProvider, kHlsTestProvider, false),
HlsAttributeVariant(kHlsAttributeListInvalidUriNoContentId,
kJsonContentId, kHlsTestContentId, false),
HlsAttributeVariant(kHlsAttributeListInvalidUriNoKeyId, kJsonKeyIds,
kHlsTestKeyId1, false),
HlsAttributeVariant(kHlsAttributeListValidUriThreeKeyIds, kJsonKeyIds,
kHlsTestKeyId1, true),
HlsAttributeVariant(kHlsAttributeListNoIv, HLS_IV_ATTRIBUTE,
kHlsTestNoHexValue, false),
HlsAttributeVariant(kHlsAttributeListInvalidIv, HLS_IV_ATTRIBUTE,
kHlsTestHexValueWithOddBytes, false)));
} // namespace wvcdm

View File

@@ -0,0 +1,888 @@
// Copyright 2016 Google Inc. All Rights Reserved.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <vector>
#include "license.h"
#include "license_key_status.h"
namespace wvcdm {
namespace {
static const uint32_t dev_lo_res = 200;
static const uint32_t dev_hi_res = 400;
static const uint32_t dev_top_res = 800;
static const uint32_t key_lo_res_min = 151;
static const uint32_t key_lo_res_max = 300;
static const uint32_t key_hi_res_min = 301;
static const uint32_t key_hi_res_max = 450;
static const uint32_t key_top_res_min = 451;
static const uint32_t key_top_res_max = 650;
// Content Keys
static const KeyId ck_sw_crypto = "c_key_SW_SECURE_CRYPTO";
static const KeyId ck_sw_decode = "c_key_SW_SECURE_DECODE";
static const KeyId ck_hw_crypto = "c_key_HW_SECURE_CRYPTO";
static const KeyId ck_hw_decode = "c_key_HW_SECURE_DECODE";
static const KeyId ck_hw_secure = "c_key_HW_SECURE_ALL";
// Operator Session Keys
static const KeyId osk_decrypt = "os_key_generic_decrypt";
static const KeyId osk_encrypt = "os_key_generic_encrypt";
static const KeyId osk_sign = "os_key_generic_sign";
static const KeyId osk_verify = "os_key_generic_verify";
static const KeyId osk_encrypt_decrypt = "os_key_generic_encrypt_decrypt";
static const KeyId osk_sign_verify = "os_key_generic_sign_verify";
static const KeyId osk_all = "os_key_generic_all";
// HDCP test keys
static const KeyId ck_sw_crypto_NO_HDCP = "ck_sw_crypto_NO_HDCP";
static const KeyId ck_hw_secure_NO_HDCP = "ck_hw_secure_NO_HDCP";
static const KeyId ck_sw_crypto_HDCP_V2_1 = "ck_sw_crypto_HDCP_V2_1";
static const KeyId ck_hw_secure_HDCP_V2_1 = "ck_hw_secure_HDCP_V2_1";
static const KeyId ck_sw_crypto_HDCP_NO_OUTPUT = "ck_sw_crypto_HDCP_NO_OUT";
static const KeyId ck_hw_secure_HDCP_NO_OUTPUT = "ck_hw_secure_HDCP_NO_OUT";
// Constraint test keys
static const KeyId ck_NO_HDCP_lo_res = "ck_NO_HDCP_lo_res";
static const KeyId ck_HDCP_NO_OUTPUT_hi_res = "ck_HDCP_NO_OUTPUT_hi_res";
static const KeyId ck_HDCP_V2_1_max_res = "ck_HDCP_V2_1_max_res";
static const KeyId ck_NO_HDCP_dual_res = "ck_NO_HDCP_dual_res";
} // namespace
// protobuf generated classes.
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::LicenseIdentification;
using video_widevine_server::sdk::STREAMING;
using video_widevine_server::sdk::OFFLINE;
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
class LicenseKeysTest : public ::testing::Test {
protected:
enum KeyFlag {
kKeyFlagNull,
kKeyFlagFalse,
kKeyFlagTrue
};
static const KeyFlag kEncryptNull = kKeyFlagNull;
static const KeyFlag kEncryptFalse = kKeyFlagFalse;
static const KeyFlag kEncryptTrue = kKeyFlagTrue;
static const KeyFlag kDecryptNull = kKeyFlagNull;
static const KeyFlag kDecryptFalse = kKeyFlagFalse;
static const KeyFlag kDecryptTrue = kKeyFlagTrue;
static const KeyFlag kSignNull = kKeyFlagNull;
static const KeyFlag kSignFalse = kKeyFlagFalse;
static const KeyFlag kSignTrue = kKeyFlagTrue;
static const KeyFlag kVerifyNull = kKeyFlagNull;
static const KeyFlag kVerifyFalse = kKeyFlagFalse;
static const KeyFlag kVerifyTrue = kKeyFlagTrue;
static const KeyFlag kContentSecureFalse = kKeyFlagFalse;
static const KeyFlag kContentSecureTrue = kKeyFlagTrue;
static const KeyFlag kContentClearFalse = kKeyFlagFalse;
static const KeyFlag kContentClearTrue = kKeyFlagTrue;
virtual void SetUp() {
LicenseIdentification* id = license_.mutable_id();
id->set_version(1);
id->set_type(STREAMING);
}
virtual void AddContentKey(
const KeyId& key_id, bool set_level = false,
KeyContainer::SecurityLevel level = KeyContainer::SW_SECURE_CRYPTO,
bool set_hdcp = false, KeyContainer::OutputProtection::HDCP hdcp_value =
KeyContainer::OutputProtection::HDCP_NONE,
bool set_constraints = false,
std::vector<VideoResolutionConstraint>* constraints = NULL) {
KeyContainer* key = license_.add_key();
key->set_type(KeyContainer::CONTENT);
if (set_level) {
key->set_level(level);
}
if (set_hdcp) {
KeyContainer::OutputProtection* pro = key->mutable_required_protection();
pro->set_hdcp(hdcp_value);
}
if (set_constraints) {
for (std::vector<VideoResolutionConstraint>::iterator
it = constraints->begin(); it != constraints->end(); ++it) {
VideoResolutionConstraint* constraint =
key->add_video_resolution_constraints();
constraint->set_min_resolution_pixels(it->min_resolution_pixels());
constraint->set_max_resolution_pixels(it->max_resolution_pixels());
constraint->mutable_required_protection()->
set_hdcp(it->required_protection().hdcp());
}
}
key->set_id(key_id);
}
virtual void AddOperatorSessionKey(
const KeyId& key_id, bool set_perms = false,
KeyFlag encrypt = kKeyFlagNull, KeyFlag decrypt = kKeyFlagNull,
KeyFlag sign = kKeyFlagNull, KeyFlag verify = kKeyFlagNull) {
KeyContainer* non_content_key = license_.add_key();
non_content_key->set_type(KeyContainer::OPERATOR_SESSION);
non_content_key->set_id(key_id);
if (set_perms) {
KeyContainer::OperatorSessionKeyPermissions* permissions =
non_content_key->mutable_operator_session_key_permissions();
if (encrypt != kKeyFlagNull) {
permissions->set_allow_encrypt(encrypt == kKeyFlagTrue);
}
if (decrypt != kKeyFlagNull) {
permissions->set_allow_decrypt(decrypt == kKeyFlagTrue);
}
if (sign != kKeyFlagNull) {
permissions->set_allow_sign(sign == kKeyFlagTrue);
}
if (verify != kKeyFlagNull) {
permissions->set_allow_signature_verify(verify == kKeyFlagTrue);
}
}
}
virtual void AddSigningKey(const KeyId& key_id) {
KeyContainer* key = license_.add_key();
key->set_type(KeyContainer::SIGNING);
key->set_id(key_id);
}
virtual void ExpectAllowedUsageContent(
const CdmKeyAllowedUsage& key_usage, KeyFlag secure, KeyFlag clear) {
EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue);
EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue);
EXPECT_FALSE(key_usage.generic_encrypt);
EXPECT_FALSE(key_usage.generic_decrypt);
EXPECT_FALSE(key_usage.generic_sign);
EXPECT_FALSE(key_usage.generic_verify);
}
virtual void ExpectAllowedUsageOperator(
const CdmKeyAllowedUsage& key_usage, KeyFlag encrypt, KeyFlag decrypt,
KeyFlag sign, KeyFlag verify) {
EXPECT_FALSE(key_usage.decrypt_to_secure_buffer);
EXPECT_FALSE(key_usage.decrypt_to_clear_buffer);
EXPECT_EQ(key_usage.generic_encrypt, encrypt == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_decrypt, decrypt == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_sign, sign == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue);
}
virtual int NumContentKeys() {
return content_key_count_;
}
virtual void StageContentKeys() {
content_key_count_ = 0;
AddContentKey(ck_sw_crypto, true, KeyContainer::SW_SECURE_CRYPTO);
content_key_count_++;
AddContentKey(ck_sw_decode, true, KeyContainer::SW_SECURE_DECODE);
content_key_count_++;
AddContentKey(ck_hw_crypto, true, KeyContainer::HW_SECURE_CRYPTO);
content_key_count_++;
AddContentKey(ck_hw_decode, true, KeyContainer::HW_SECURE_DECODE);
content_key_count_++;
AddContentKey(ck_hw_secure, true, KeyContainer::HW_SECURE_ALL);
content_key_count_++;
license_keys_.SetFromLicense(license_);
}
virtual void StageOperatorSessionKeys() {
AddOperatorSessionKey(osk_decrypt, true,
kEncryptNull, kDecryptTrue, kSignNull, kVerifyNull);
AddOperatorSessionKey(osk_encrypt, true,
kEncryptTrue, kDecryptNull, kSignNull, kVerifyNull);
AddOperatorSessionKey(osk_sign, true,
kEncryptNull, kDecryptNull, kSignTrue, kVerifyNull);
AddOperatorSessionKey(osk_verify, true,
kEncryptNull, kDecryptNull, kSignNull, kVerifyTrue);
AddOperatorSessionKey(osk_encrypt_decrypt, true,
kEncryptTrue, kDecryptTrue, kSignNull, kVerifyNull);
AddOperatorSessionKey(osk_sign_verify, true,
kEncryptNull, kDecryptNull, kSignTrue, kVerifyTrue);
AddOperatorSessionKey(osk_all, true,
kEncryptTrue, kDecryptTrue, kSignTrue, kVerifyTrue);
license_keys_.SetFromLicense(license_);
}
virtual void StageHdcpKeys() {
content_key_count_ = 0;
AddContentKey(ck_sw_crypto_NO_HDCP, true, KeyContainer::SW_SECURE_CRYPTO,
true, KeyContainer::OutputProtection::HDCP_NONE);
content_key_count_++;
AddContentKey(ck_hw_secure_NO_HDCP, true, KeyContainer::HW_SECURE_ALL, true,
KeyContainer::OutputProtection::HDCP_NONE);
content_key_count_++;
AddContentKey(ck_sw_crypto_HDCP_V2_1, true, KeyContainer::SW_SECURE_CRYPTO,
true, KeyContainer::OutputProtection::HDCP_V2_1);
content_key_count_++;
AddContentKey(ck_hw_secure_HDCP_V2_1, true, KeyContainer::HW_SECURE_ALL,
true, KeyContainer::OutputProtection::HDCP_V2_1);
content_key_count_++;
AddContentKey(ck_sw_crypto_HDCP_NO_OUTPUT, true,
KeyContainer::SW_SECURE_CRYPTO, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
content_key_count_++;
AddContentKey(ck_hw_secure_HDCP_NO_OUTPUT, true,
KeyContainer::HW_SECURE_ALL, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
content_key_count_++;
license_keys_.SetFromLicense(license_);
}
virtual void AddConstraint(
std::vector<VideoResolutionConstraint>& constraints, uint32_t min_res,
uint32_t max_res, bool set_hdcp = false,
KeyContainer::OutputProtection::HDCP hdcp =
KeyContainer::OutputProtection::HDCP_NONE) {
VideoResolutionConstraint constraint;
constraint.set_min_resolution_pixels(min_res);
constraint.set_max_resolution_pixels(max_res);
if (set_hdcp) {
constraint.mutable_required_protection()->set_hdcp(hdcp);
}
constraints.push_back(constraint);
}
virtual void StageConstraintKeys() {
content_key_count_ = 0;
std::vector<VideoResolutionConstraint> constraints;
AddConstraint(constraints, key_lo_res_min, key_lo_res_max);
AddContentKey(ck_NO_HDCP_lo_res, true, KeyContainer::SW_SECURE_CRYPTO, true,
KeyContainer::OutputProtection::HDCP_NONE, true,
&constraints);
content_key_count_++;
constraints.clear();
AddConstraint(constraints, key_hi_res_min, key_hi_res_max);
AddContentKey(
ck_HDCP_NO_OUTPUT_hi_res, true, KeyContainer::SW_SECURE_CRYPTO, true,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT, true,
&constraints);
content_key_count_++;
constraints.clear();
AddConstraint(constraints, key_top_res_min, key_top_res_max);
AddContentKey(ck_HDCP_V2_1_max_res, true, KeyContainer::SW_SECURE_CRYPTO,
true, KeyContainer::OutputProtection::HDCP_V2_1, true,
&constraints);
content_key_count_++;
constraints.clear();
AddConstraint(constraints, key_lo_res_min, key_lo_res_max);
AddConstraint(constraints, key_hi_res_min, key_hi_res_max,
KeyContainer::OutputProtection::HDCP_NO_DIGITAL_OUTPUT);
AddContentKey(ck_NO_HDCP_dual_res, true, KeyContainer::HW_SECURE_ALL, true,
KeyContainer::OutputProtection::HDCP_NONE, true,
&constraints);
content_key_count_++;
license_keys_.SetFromLicense(license_);
}
virtual void ExpectKeyStatusesEqual(CdmKeyStatusMap& key_status_map,
CdmKeyStatus expected_status) {
for (CdmKeyStatusMap::iterator it = key_status_map.begin();
it != key_status_map.end(); ++it) {
EXPECT_TRUE(it->second == expected_status);
}
}
virtual void ExpectKeyStatusEqual(CdmKeyStatusMap& key_status_map,
const KeyId& key_id,
CdmKeyStatus expected_status) {
for (CdmKeyStatusMap::iterator it = key_status_map.begin();
it != key_status_map.end(); ++it) {
if (key_id == it->first) {
EXPECT_TRUE(it->second == expected_status);
}
}
}
int content_key_count_;
LicenseKeys license_keys_;
License license_;
};
TEST_F(LicenseKeysTest, Empty) {
EXPECT_TRUE(license_keys_.Empty());
}
TEST_F(LicenseKeysTest, NotEmpty) {
const KeyId c_key = "content_key";
AddContentKey(c_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.Empty());
}
TEST_F(LicenseKeysTest, BadKeyId) {
const KeyId c_key = "content_key";
const KeyId os_key = "op_sess_key";
const KeyId unk_key = "unknown_key";
CdmKeyAllowedUsage allowed_usage;
AddContentKey(c_key);
AddOperatorSessionKey(os_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.IsContentKey(unk_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(unk_key));
EXPECT_TRUE(license_keys_.MeetsConstraints(unk_key));
EXPECT_FALSE(license_keys_.GetAllowedUsage(unk_key, &allowed_usage));
}
TEST_F(LicenseKeysTest, SigningKey) {
const KeyId c_key = "content_key";
const KeyId os_key = "op_sess_key";
const KeyId sign_key = "signing_key";
CdmKeyAllowedUsage allowed_usage;
AddSigningKey(sign_key);
AddContentKey(c_key);
AddOperatorSessionKey(os_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.IsContentKey(sign_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(sign_key));
EXPECT_TRUE(license_keys_.MeetsConstraints(sign_key));
EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &allowed_usage));
}
TEST_F(LicenseKeysTest, ContentKey) {
const KeyId c_key = "content_key";
AddContentKey(c_key);
EXPECT_FALSE(license_keys_.IsContentKey(c_key));
license_keys_.SetFromLicense(license_);
EXPECT_TRUE(license_keys_.IsContentKey(c_key));
}
TEST_F(LicenseKeysTest, OperatorSessionKey) {
const KeyId os_key = "op_sess_key";
EXPECT_FALSE(license_keys_.IsContentKey(os_key));
AddOperatorSessionKey(os_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.IsContentKey(os_key));
}
TEST_F(LicenseKeysTest, CanDecrypt) {
const KeyId os_key = "op_sess_key";
const KeyId c_key = "content_key";
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
AddOperatorSessionKey(os_key);
AddContentKey(c_key);
license_keys_.SetFromLicense(license_);
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
bool new_usable_keys = false;
bool any_change = false;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
}
TEST_F(LicenseKeysTest, AllowedUsageNull) {
const KeyId os_key = "op_sess_key";
const KeyId c_key = "content_key";
const KeyId sign_key = "signing_key";
AddOperatorSessionKey(os_key);
AddContentKey(c_key);
AddSigningKey(sign_key);
license_keys_.SetFromLicense(license_);
CdmKeyAllowedUsage usage_1;
EXPECT_FALSE(license_keys_.GetAllowedUsage(sign_key, &usage_1));
CdmKeyAllowedUsage usage_2;
EXPECT_TRUE(license_keys_.GetAllowedUsage(c_key, &usage_2));
ExpectAllowedUsageContent(usage_2, kContentClearTrue, kContentSecureTrue);
CdmKeyAllowedUsage usage_3;
EXPECT_TRUE(license_keys_.GetAllowedUsage(os_key, &usage_3));
ExpectAllowedUsageContent(usage_3, kContentClearFalse, kContentSecureFalse);
}
TEST_F(LicenseKeysTest, AllowedUsageContent) {
StageContentKeys();
CdmKeyAllowedUsage u_sw_crypto;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_crypto, &u_sw_crypto));
ExpectAllowedUsageContent(u_sw_crypto, kContentSecureTrue, kContentClearTrue);
CdmKeyAllowedUsage u_sw_decode;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_sw_decode, &u_sw_decode));
ExpectAllowedUsageContent(u_sw_decode, kContentSecureTrue, kContentClearTrue);
CdmKeyAllowedUsage u_hw_crypto;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_crypto, &u_hw_crypto));
ExpectAllowedUsageContent(u_hw_crypto, kContentSecureTrue, kContentClearTrue);
CdmKeyAllowedUsage u_hw_decode;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_decode, &u_hw_decode));
ExpectAllowedUsageContent(u_hw_decode, kContentSecureTrue,
kContentClearFalse);
CdmKeyAllowedUsage u_hw_secure;
EXPECT_TRUE(license_keys_.GetAllowedUsage(ck_hw_secure, &u_hw_secure));
ExpectAllowedUsageContent(u_hw_secure, kContentSecureTrue,
kContentClearFalse);
}
TEST_F(LicenseKeysTest, AllowedUsageOperatorSession) {
StageOperatorSessionKeys();
CdmKeyAllowedUsage u_encrypt;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_encrypt, &u_encrypt));
ExpectAllowedUsageOperator(u_encrypt, kEncryptTrue, kDecryptFalse, kSignFalse,
kVerifyFalse);
CdmKeyAllowedUsage u_decrypt;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_decrypt, &u_decrypt));
ExpectAllowedUsageOperator(u_decrypt, kEncryptFalse, kDecryptTrue, kSignFalse,
kVerifyFalse);
CdmKeyAllowedUsage u_sign;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign, &u_sign));
ExpectAllowedUsageOperator(u_sign, kEncryptFalse, kDecryptFalse, kSignTrue,
kVerifyFalse);
CdmKeyAllowedUsage u_verify;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_verify, &u_verify));
ExpectAllowedUsageOperator(u_verify, kEncryptFalse, kDecryptFalse, kSignFalse,
kVerifyTrue);
CdmKeyAllowedUsage u_encrypt_decrypt;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_encrypt_decrypt,
&u_encrypt_decrypt));
ExpectAllowedUsageOperator(u_encrypt_decrypt, kEncryptTrue, kDecryptTrue,
kSignFalse, kVerifyFalse);
CdmKeyAllowedUsage u_sign_verify;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_sign_verify, &u_sign_verify));
ExpectAllowedUsageOperator(u_sign_verify, kEncryptFalse, kDecryptFalse,
kSignTrue, kVerifyTrue);
CdmKeyAllowedUsage u_all;
EXPECT_TRUE(license_keys_.GetAllowedUsage(osk_all, &u_all));
ExpectAllowedUsageOperator(u_all, kEncryptTrue, kDecryptTrue, kSignTrue,
kVerifyTrue);
}
TEST_F(LicenseKeysTest, ExtractKeyStatuses) {
CdmKeyStatusMap key_status_map;
StageOperatorSessionKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(0, key_status_map.size());
StageContentKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
}
TEST_F(LicenseKeysTest, KeyStatusChanges) {
bool new_usable_keys = false;
bool any_change = false;
CdmKeyStatusMap key_status_map;
StageOperatorSessionKeys();
StageContentKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
// change to pending
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to pending (again)
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending,
&new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to usable
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to usable (again)
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to expired
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure));
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());
ExpectKeyStatusesEqual(key_status_map, kKeyStatusExpired);
}
TEST_F(LicenseKeysTest, HdcpChanges) {
bool new_usable_keys = false;
bool any_change = false;
CdmKeyStatusMap key_status_map;
StageHdcpKeys();
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ApplyConstraints(100, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_V1);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_V2_2);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_NO_DIGITAL_OUTPUT);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusUsable);
license_keys_.ApplyConstraints(100, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_hw_secure_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_NO_HDCP));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_NO_HDCP));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_V2_1));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
}
TEST_F(LicenseKeysTest, ConstraintChanges) {
bool new_usable_keys = false;
bool any_change = false;
CdmKeyStatusMap key_status_map;
StageConstraintKeys();
// No constraints set by device
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
// Low-res device, no HDCP support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
// Hi-res device, HDCP_V1 support
license_keys_.ApplyConstraints(dev_hi_res, HDCP_V1);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_NO_HDCP, kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
// Lo-res device, HDCP V2.2 support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_V2_2);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
// Hi-res device, Maximal HDCP support
license_keys_.ApplyConstraints(dev_hi_res, HDCP_NO_DIGITAL_OUTPUT);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusUsable);
// Lo-res device, no HDCP support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
// Too-high-res -- all keys rejected
license_keys_.ApplyConstraints(dev_top_res, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_HDCP_V2_1_max_res));
EXPECT_FALSE(license_keys_.CanDecryptContent(ck_NO_HDCP_dual_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_lo_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_NO_OUTPUT_hi_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_HDCP_V2_1_max_res));
EXPECT_FALSE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
license_keys_.ExtractKeyStatuses(&key_status_map);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_NO_HDCP,
kKeyStatusUsable);
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
}
} // namespace wvcdm

View File

@@ -55,30 +55,4 @@ void LicenseRequest::GetDrmMessage(const std::string& response,
}
}
// Returns heartbeat url in heartbeat_url.
// The heartbeat url is stored as meta data in the response message.
void LicenseRequest::GetHeartbeatUrl(const std::string& response,
std::string& heartbeat_url) {
if (response.empty()) {
heartbeat_url.clear();
return;
}
size_t header_end_pos = FindHeaderEndPosition(response);
if (header_end_pos != std::string::npos) {
header_end_pos += kTwoBlankLines.size(); // points to response body
heartbeat_url.clear();
size_t heartbeat_url_pos = response.find("Heartbeat-Url: ", header_end_pos);
if (heartbeat_url_pos != std::string::npos) {
heartbeat_url_pos += sizeof("Heartbeat-Url: ");
heartbeat_url.assign(response.substr(heartbeat_url_pos));
} else {
LOGE("heartbeat url not found");
}
} else {
LOGE("response body not found");
}
}
} // namespace wvcdm

View File

@@ -17,7 +17,6 @@ class LicenseRequest {
~LicenseRequest() {};
void GetDrmMessage(const std::string& response, std::string& drm_msg);
void GetHeartbeatUrl(const std::string& response, std::string& heartbeat_url);
private:
size_t FindHeaderEndPosition(const std::string& response) const;

View File

@@ -1,323 +0,0 @@
// Copyright 2012 Google Inc. All Rights Reserved.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "crypto_session.h"
#include "license.h"
#include "max_res_engine.h"
#include "mock_clock.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
namespace wvcdm {
typedef ::video_widevine_server::sdk::License License;
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
OutputProtection;
typedef ::video_widevine_server::sdk::License::KeyContainer::
VideoResolutionConstraint VideoResolutionConstraint;
typedef ::google::protobuf::RepeatedPtrField<KeyContainer> KeyList;
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
ConstraintList;
using namespace testing;
namespace {
const KeyId kKeyId1 = "357adc89f1673433c36c621f1b5c41ee";
const KeyId kKeyId2 = "3d25f819250789ecfc9ed48cc99af164";
const KeyId kKeyId3 = "fe3cf6b69e76c9a1c877922e1a661707";
const KeyId kKeyId4 = "29a321b9886658078f916fdd41d6f570";
const KeyId kKeyId5 = "cc5b031bcde371031c06822d935b9a63";
const KeyId kKeyId6 = "90ac1332e4efc8acbaf929c8d321f50c";
const uint32_t kMinRes1 = 0;
const uint32_t kMaxRes1 = 2000;
const uint32_t kTargetRes1 = (kMinRes1 + kMaxRes1) / 2;
const uint32_t kMinRes2 = kMaxRes1;
const uint32_t kMaxRes2 = 4000;
const uint32_t kTargetRes2 = (kMinRes2 + kMaxRes2) / 2;
const uint32_t kTargetRes3 = kMaxRes2 + 1000;
const OutputProtection::HDCP kHdcpDefault = OutputProtection::HDCP_V2;
const OutputProtection::HDCP kHdcpConstraint = OutputProtection::HDCP_V2_1;
const int64_t kHdcpInterval = 10;
class HdcpOnlyMockCryptoSession : public CryptoSession {
public:
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
};
} // namespace
ACTION_P2(IncrementAndReturnPointee, p, a) {
*p += a;
return *p;
}
class MaxResEngineTest : public Test {
protected:
virtual void SetUp() {
mock_clock_ = new NiceMock<MockClock>();
current_time_ = 0;
ON_CALL(*mock_clock_, GetCurrentTime())
.WillByDefault(
IncrementAndReturnPointee(&current_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

View File

@@ -0,0 +1,430 @@
// Copyright 2016 Google Inc. All Rights Reserved.
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "crypto_session.h"
#include "license.h"
#include "policy_engine.h"
#include "mock_clock.h"
#include "scoped_ptr.h"
#include "wv_cdm_event_listener.h"
#include "wv_cdm_types.h"
// protobuf generated classes.
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_Policy;
using video_widevine_server::sdk::STREAMING;
namespace wvcdm {
typedef ::video_widevine_server::sdk::License License;
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
OutputProtection;
typedef ::video_widevine_server::sdk::License::KeyContainer::
VideoResolutionConstraint VideoResolutionConstraint;
typedef ::google::protobuf::RepeatedPtrField<KeyContainer> KeyList;
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
ConstraintList;
using namespace testing;
namespace {
const CdmSessionId kSessionId = "mock_session_id";
const KeyId kKeyId1 = "1111kKeyId1";
const KeyId kKeyId2 = "2222kKeyId2";
const KeyId kKeyId3 = "3333kKeyId3";
const KeyId kKeyId4 = "4444kKeyId4";
const KeyId kKeyId5 = "5555kKeyId5";
const KeyId kKeyId6 = "6666kKeyId6";
const uint32_t kMinRes1 = 0;
const uint32_t kMaxRes1 = 2000;
const uint32_t kMinRes2 = kMaxRes1;
const uint32_t kMaxRes2 = 4000;
const uint32_t kTargetRes1 = (kMinRes1 + kMaxRes1) / 2;
const uint32_t kTargetRes2 = (kMinRes2 + kMaxRes2) / 2;
const uint32_t kTargetRes3 = kMaxRes2 + 1000;
const int64_t kRentalDuration = 604800; // 7 days
const int64_t kPlaybackDuration = 172800; // 48 hours
const int64_t kStreamingLicenseDuration = 300;
const OutputProtection::HDCP kHdcpV2 = OutputProtection::HDCP_V2;
const OutputProtection::HDCP kHdcpV2_1 = OutputProtection::HDCP_V2_1;
// should match kHdcpCheckInterval in policy_engine.cpp
const int64_t kHdcpInterval = 10;
class HdcpOnlyMockCryptoSession : public CryptoSession {
public:
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
};
class MockCdmEventListener : public WvCdmEventListener {
public:
MOCK_METHOD1(OnSessionRenewalNeeded, void(const CdmSessionId& session_id));
MOCK_METHOD3(OnSessionKeysChange, void(const CdmSessionId& session_id,
const CdmKeyStatusMap& keys_status,
bool has_new_usable_key));
MOCK_METHOD2(OnExpirationUpdate, void(const CdmSessionId& session_id,
int64_t new_expiry_time_seconds));
};
} // namespace
class PolicyEngineConstraintsTest : public Test {
protected:
virtual void SetUp() {
mock_clock_ = new NiceMock<MockClock>();
current_time_ = 0;
policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_,
&crypto_session_));
InjectMockClock();
SetupLicense();
}
void AddConstraints(ConstraintList* constraints) {
// Constraint 1 - Low-res and no HDCP
{
VideoResolutionConstraint* c_lo_res_no_hdcp = constraints->Add();
c_lo_res_no_hdcp->set_min_resolution_pixels(kMinRes1);
c_lo_res_no_hdcp->set_max_resolution_pixels(kMaxRes1);
}
// Constraint 2 - High-res and stricter HDCP
{
VideoResolutionConstraint* c_hi_res_hdcp_v2 = constraints->Add();
c_hi_res_hdcp_v2->set_min_resolution_pixels(kMinRes2);
c_hi_res_hdcp_v2->set_max_resolution_pixels(kMaxRes2);
c_hi_res_hdcp_v2->mutable_required_protection()->set_hdcp(kHdcpV2_1);
}
}
void SetupLicense() {
license_.set_license_start_time(current_time_);
LicenseIdentification* id = license_.mutable_id();
id->set_version(1);
id->set_type(STREAMING);
License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy();
policy->set_can_play(true);
policy->set_can_persist(false);
policy->set_rental_duration_seconds(kRentalDuration);
policy->set_playback_duration_seconds(kPlaybackDuration);
policy->set_license_duration_seconds(kStreamingLicenseDuration);
KeyList* keys = license_.mutable_key();
// Key 1 - Content key w/ ID, no HDCP, no constraints
{
KeyContainer* key1 = keys->Add();
key1->set_type(KeyContainer::CONTENT);
key1->set_id(kKeyId1);
}
// Key 2 - Content key w/ ID, HDCP, no constraints
{
KeyContainer* key2 = keys->Add();
key2->set_type(KeyContainer::CONTENT);
key2->set_id(kKeyId2);
key2->mutable_required_protection()->set_hdcp(kHdcpV2);
}
// Key 3 - Content key w/ ID, no HDCP, constraints
{
KeyContainer* key3 = keys->Add();
key3->set_type(KeyContainer::CONTENT);
key3->set_id(kKeyId3);
AddConstraints(key3->mutable_video_resolution_constraints());
}
// Key 4 - Content key w/ ID, HDCP, constraints
{
KeyContainer* key4 = keys->Add();
key4->set_type(KeyContainer::CONTENT);
key4->set_id(kKeyId4);
key4->mutable_required_protection()->set_hdcp(kHdcpV2);
AddConstraints(key4->mutable_video_resolution_constraints());
}
// Key 5 - Content key w/o ID, HDCP, constraints
{
KeyContainer* key5 = keys->Add();
key5->set_type(KeyContainer::CONTENT);
key5->mutable_required_protection()->set_hdcp(kHdcpV2);
AddConstraints(key5->mutable_video_resolution_constraints());
}
// Key 6 - Non-content key
{
KeyContainer* key6 = keys->Add();
key6->set_type(KeyContainer::OPERATOR_SESSION);
}
}
void InjectMockClock() {
mock_clock_ = new MockClock();
policy_engine_->set_clock(mock_clock_);
}
void ExpectSessionKeysChange(CdmKeyStatus expected_key_status,
bool expected_has_new_usable_key) {
EXPECT_CALL(mock_event_listener_,
OnSessionKeysChange(
kSessionId, UnorderedElementsAre(
Pair(kKeyId1, expected_key_status),
Pair(kKeyId2, expected_key_status),
Pair(kKeyId3, expected_key_status),
Pair(kKeyId4, expected_key_status)),
expected_has_new_usable_key));
}
void ExpectSessionKeysChanges(const KeyId& k1, CdmKeyStatus expected_1,
const KeyId& k2, CdmKeyStatus expected_2,
const KeyId& k3, CdmKeyStatus expected_3,
const KeyId& k4, CdmKeyStatus expected_4,
bool expected_has_new_usable_key) {
EXPECT_CALL(mock_event_listener_,
OnSessionKeysChange(
kSessionId, UnorderedElementsAre(
Pair(k1, expected_1),
Pair(k2, expected_2),
Pair(k3, expected_3),
Pair(k4, expected_4)),
expected_has_new_usable_key));
}
scoped_ptr<PolicyEngine> policy_engine_;
MockClock* mock_clock_;
int64_t current_time_;
StrictMock<HdcpOnlyMockCryptoSession> crypto_session_;
StrictMock<MockCdmEventListener> mock_event_listener_;
License license_;
};
TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()).Times(2);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
ExpectSessionKeysChange(kKeyStatusUsable, true);
policy_engine_->SetLicense(license_);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
}
TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
{
Sequence time;
for (int i=0; i<4; ++i) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
.WillOnce(Return(i * 10));
}
}
{
Sequence key_change;
ExpectSessionKeysChange(kKeyStatusUsable, true);
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusUsable,
kKeyId3, kKeyStatusOutputNotAllowed,
kKeyId4, kKeyStatusOutputNotAllowed, false);
}
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(true)));
policy_engine_->SetLicense(license_);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
policy_engine_->NotifyResolution(1, kTargetRes2);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
policy_engine_->NotifyResolution(1, kTargetRes3);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
}
TEST_F(PolicyEngineConstraintsTest,
RequestsHdcpImmediatelyAndOnlyAfterInterval) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(0))
.WillOnce(Return(5));
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
ExpectSessionKeysChange(kKeyStatusUsable, true);
int64_t start_time = current_time_ + 5;
{
InSequence calls;
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillOnce(
DoAll(SetArgPointee<0>(HDCP_V2_2),
Return(true)));
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(start_time + kHdcpInterval / 2))
.WillOnce(Return(start_time + kHdcpInterval));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillOnce(
DoAll(SetArgPointee<0>(HDCP_V2_2),
Return(true)));
}
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->SetLicense(license_);
policy_engine_->OnTimerEvent();
policy_engine_->OnTimerEvent();
policy_engine_->OnTimerEvent();
}
TEST_F(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(0));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
policy_engine_->OnTimerEvent();
}
TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
{
Sequence time;
for (int i=0; i<3; ++i) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
.WillOnce(Return(i * 10));
}
}
{
Sequence key_change;
ExpectSessionKeysChange(kKeyStatusUsable, true);
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusUsable,
kKeyId3, kKeyStatusOutputNotAllowed,
kKeyId4, kKeyStatusOutputNotAllowed, false);
}
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_V2),
Return(true)));
policy_engine_->SetLicense(license_);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
policy_engine_->NotifyResolution(1, kTargetRes2);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
}
TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
{
Sequence time;
for (int i=0; i<3; ++i) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
.WillOnce(Return(i * 10));
}
}
{
Sequence key_change;
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusOutputNotAllowed,
kKeyId3, kKeyStatusUsable,
kKeyId4, kKeyStatusOutputNotAllowed, false);
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusOutputNotAllowed,
kKeyId3, kKeyStatusOutputNotAllowed,
kKeyId4, kKeyStatusOutputNotAllowed, false);
}
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NONE),
Return(true)));
policy_engine_->SetLicense(license_);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
policy_engine_->NotifyResolution(1, kTargetRes2);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
}
TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
{
Sequence time;
for (int i=0; i<2; ++i) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()).InSequence(time)
.WillOnce(Return(i * 10));
}
}
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
policy_engine_->SetLicense(license_);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
}
} // namespace wvcdm

View File

@@ -42,6 +42,7 @@ const char* kRenewalServerUrl =
const KeyId kKeyId = "357adc89f1673433c36c621f1b5c41ee";
const KeyId kAnotherKeyId = "another_key_id";
const KeyId kSomeRandomKeyId = "some_random_key_id";
const KeyId kUnknownKeyId = "some_random_unknown_key_id";
const CdmSessionId kSessionId = "mock_session_id";
int64_t GetLicenseRenewalDelay(int64_t license_duration) {
@@ -60,13 +61,6 @@ class MockCdmEventListener : public WvCdmEventListener {
int64_t new_expiry_time_seconds));
};
class MockMaxResEngine : public MaxResEngine {
public:
MockMaxResEngine() : MaxResEngine(NULL) {}
MOCK_METHOD1(CanDecrypt, bool(const KeyId& key_id));
};
} // namespace
// protobuf generated classes.
@@ -138,11 +132,6 @@ class PolicyEngineTest : public ::testing::Test {
policy_engine_->set_clock(mock_clock_);
}
void InjectMockMaxResEngine() {
mock_max_res_engine_ = new MockMaxResEngine();
policy_engine_->set_max_res_engine(mock_max_res_engine_);
}
void ExpectSessionKeysChange(CdmKeyStatus expected_key_status,
bool expected_has_new_usable_key) {
EXPECT_CALL(mock_event_listener_,
@@ -165,7 +154,6 @@ class PolicyEngineTest : public ::testing::Test {
StrictMock<MockCdmEventListener> mock_event_listener_;
MockClock* mock_clock_;
MockMaxResEngine* mock_max_res_engine_;
scoped_ptr<PolicyEngine> policy_engine_;
License license_;
MockFunction<void(int i)> check_;
@@ -1036,56 +1024,204 @@ TEST_F(PolicyEngineTest, MultipleKeysInLicense) {
EXPECT_FALSE(policy_engine_->CanDecrypt(kSomeRandomKeyId));
}
TEST_F(PolicyEngineTest, KeysOutputNotAllowedByMaxResEngine) {
class PolicyEngineKeyAllowedUsageTest : public PolicyEngineTest {
protected:
enum KeyFlag {
kKeyFlagNull,
kKeyFlagFalse,
kKeyFlagTrue
};
static const KeyFlag kEncryptNull = kKeyFlagNull;
static const KeyFlag kEncryptFalse = kKeyFlagFalse;
static const KeyFlag kEncryptTrue = kKeyFlagTrue;
static const KeyFlag kDecryptNull = kKeyFlagNull;
static const KeyFlag kDecryptFalse = kKeyFlagFalse;
static const KeyFlag kDecryptTrue = kKeyFlagTrue;
static const KeyFlag kSignNull = kKeyFlagNull;
static const KeyFlag kSignFalse = kKeyFlagFalse;
static const KeyFlag kSignTrue = kKeyFlagTrue;
static const KeyFlag kVerifyNull = kKeyFlagNull;
static const KeyFlag kVerifyFalse = kKeyFlagFalse;
static const KeyFlag kVerifyTrue = kKeyFlagTrue;
static const KeyFlag kContentSecureFalse = kKeyFlagFalse;
static const KeyFlag kContentSecureTrue = kKeyFlagTrue;
static const KeyFlag kContentClearFalse = kKeyFlagFalse;
static const KeyFlag kContentClearTrue = kKeyFlagTrue;
void ExpectAllowedContentKeySettings(const CdmKeyAllowedUsage& key_usage,
KeyFlag secure, KeyFlag clear) {
EXPECT_EQ(key_usage.decrypt_to_secure_buffer, secure == kKeyFlagTrue);
EXPECT_EQ(key_usage.decrypt_to_clear_buffer, clear == kKeyFlagTrue);
EXPECT_FALSE(key_usage.generic_encrypt);
EXPECT_FALSE(key_usage.generic_decrypt);
EXPECT_FALSE(key_usage.generic_sign);
EXPECT_FALSE(key_usage.generic_verify);
}
void ExpectAllowedOperatorKeySettings(const CdmKeyAllowedUsage& key_usage,
KeyFlag encrypt, KeyFlag decrypt,
KeyFlag sign, KeyFlag verify) {
EXPECT_FALSE(key_usage.decrypt_to_secure_buffer);
EXPECT_FALSE(key_usage.decrypt_to_clear_buffer);
EXPECT_EQ(key_usage.generic_encrypt, encrypt == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_decrypt, decrypt == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_sign, sign == kKeyFlagTrue);
EXPECT_EQ(key_usage.generic_verify, verify == kKeyFlagTrue);
}
void ExpectSecureContentKey(const KeyId& key_id) {
CdmKeyAllowedUsage key_usage;
EXPECT_EQ(NO_ERROR,
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue,
kContentSecureFalse);
}
void ExpectLessSecureContentKey(const KeyId& key_id) {
CdmKeyAllowedUsage key_usage;
EXPECT_EQ(NO_ERROR,
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
ExpectAllowedContentKeySettings(key_usage, kContentSecureTrue,
kContentSecureTrue);
}
void ExpectOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt,
KeyFlag decrypt, KeyFlag sign, KeyFlag verify) {
CdmKeyAllowedUsage key_usage;
EXPECT_EQ(NO_ERROR,
policy_engine_->QueryKeyAllowedUsage(key_id, &key_usage));
ExpectAllowedOperatorKeySettings(key_usage, encrypt, decrypt, sign, verify);
}
void AddOperatorSessionKey(const KeyId& key_id, KeyFlag encrypt,
KeyFlag decrypt, KeyFlag sign, KeyFlag verify) {
License::KeyContainer* non_content_key = license_.add_key();
non_content_key->set_type(License::KeyContainer::OPERATOR_SESSION);
non_content_key->set_id(key_id);
License::KeyContainer::OperatorSessionKeyPermissions* permissions =
non_content_key->mutable_operator_session_key_permissions();
if (encrypt != kKeyFlagNull) {
permissions->set_allow_encrypt(encrypt == kKeyFlagTrue);
}
if (decrypt != kKeyFlagNull) {
permissions->set_allow_decrypt(decrypt == kKeyFlagTrue);
}
if (sign != kKeyFlagNull) {
permissions->set_allow_sign(sign == kKeyFlagTrue);
}
if (verify != kKeyFlagNull) {
permissions->set_allow_signature_verify(verify == kKeyFlagTrue);
}
}
};
TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageBasic) {
const KeyId kGenericKeyId = "oper_session_key";
license_.clear_key();
// most secure
License::KeyContainer* content_key = license_.add_key();
content_key->set_type(License::KeyContainer::CONTENT);
content_key->set_id(kKeyId);
content_key->set_level(License::KeyContainer::HW_SECURE_ALL);
// generic operator session key (sign)
AddOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull, kSignTrue,
kVerifyNull);
License::KeyContainer* content_key_without_id = license_.add_key();
content_key_without_id->set_type(License::KeyContainer::CONTENT);
// default level - less secure
License::KeyContainer* another_content_key = license_.add_key();
another_content_key->set_type(License::KeyContainer::CONTENT);
another_content_key->set_id(kAnotherKeyId);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + 8))
.WillOnce(Return(kLicenseStartTime + 10));
.WillOnce(Return(kLicenseStartTime + 1));
InjectMockMaxResEngine();
EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kKeyId))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kAnotherKeyId))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(true));
InSequence s;
ExpectSessionKeysChange(kKeyStatusOutputNotAllowed,
kKeyStatusOutputNotAllowed, false);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusOutputNotAllowed, true);
ExpectSessionKeysChange(kKeyStatusOutputNotAllowed, kKeyStatusUsable, true);
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
policy_engine_->SetLicense(license_);
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId));
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId));
ExpectSecureContentKey(kKeyId);
ExpectLessSecureContentKey(kAnotherKeyId);
ExpectOperatorSessionKey(kGenericKeyId, kEncryptNull, kDecryptNull,
kSignTrue, kVerifyNull);
policy_engine_->OnTimerEvent();
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId));
CdmKeyAllowedUsage key_usage;
EXPECT_EQ(KEY_NOT_FOUND_1,
policy_engine_->QueryKeyAllowedUsage(kUnknownKeyId, &key_usage));
}
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId));
TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) {
const KeyId kGenericEncryptKeyId = "oper_session_key_1";
const KeyId kGenericDecryptKeyId = "oper_session_key_2";
const KeyId kGenericSignKeyId = "oper_session_key_3";
const KeyId kGenericVerifyKeyId = "oper_session_key_4";
const KeyId kGenericFullKeyId = "oper_session_key_5";
const KeyId kGenericExplicitKeyId = "oper_session_key_6";
license_.clear_key();
// more secure
License::KeyContainer* content_key = license_.add_key();
content_key->set_type(License::KeyContainer::CONTENT);
content_key->set_id(kKeyId);
content_key->set_level(License::KeyContainer::HW_SECURE_DECODE);
// less secure
License::KeyContainer* another_content_key = license_.add_key();
another_content_key->set_type(License::KeyContainer::CONTENT);
another_content_key->set_id(kAnotherKeyId);
another_content_key->set_level(License::KeyContainer::HW_SECURE_CRYPTO);
// generic operator session keys
AddOperatorSessionKey(kGenericSignKeyId, kEncryptNull, kDecryptNull,
kSignTrue, kVerifyNull);
AddOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptNull,
kSignNull, kVerifyNull);
AddOperatorSessionKey(kGenericDecryptKeyId, kEncryptNull, kDecryptTrue,
kSignNull, kVerifyNull);
AddOperatorSessionKey(kGenericVerifyKeyId, kEncryptNull, kDecryptNull,
kSignNull, kVerifyTrue);
AddOperatorSessionKey(kGenericFullKeyId, kEncryptTrue, kDecryptTrue,
kSignTrue, kVerifyTrue);
AddOperatorSessionKey(kGenericExplicitKeyId, kEncryptFalse, kDecryptTrue,
kSignFalse, kVerifyTrue);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1));
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
policy_engine_->SetLicense(license_);
ExpectSecureContentKey(kKeyId);
ExpectLessSecureContentKey(kAnotherKeyId);
ExpectOperatorSessionKey(kGenericEncryptKeyId, kEncryptTrue, kDecryptFalse,
kSignFalse, kVerifyFalse);
ExpectOperatorSessionKey(kGenericDecryptKeyId, kEncryptFalse, kDecryptTrue,
kSignFalse, kVerifyFalse);
ExpectOperatorSessionKey(kGenericSignKeyId, kEncryptFalse, kDecryptFalse,
kSignTrue, kVerifyFalse);
ExpectOperatorSessionKey(kGenericVerifyKeyId, kEncryptFalse, kDecryptFalse,
kSignFalse, kVerifyTrue);
ExpectOperatorSessionKey(kGenericFullKeyId, kEncryptTrue, kDecryptTrue,
kSignTrue, kVerifyTrue);
ExpectOperatorSessionKey(kGenericExplicitKeyId, kEncryptFalse, kDecryptTrue,
kSignFalse, kVerifyTrue);
}
class PolicyEngineQueryTest : public PolicyEngineTest {

View File

@@ -26,59 +26,74 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case DEVICE_REVOKED: *os << "DEVICE_REVOKED";
break;
case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES";
case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES";
break;
case ADD_KEY_ERROR: *os << "ADD_KEY_ERROR";
break;
case CERT_PROVISIONING_GET_KEYBOX_ERROR_1: *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_1";
case CERT_PROVISIONING_GET_KEYBOX_ERROR_1:
*os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_1";
break;
case CERT_PROVISIONING_GET_KEYBOX_ERROR_2: *os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_2";
case CERT_PROVISIONING_GET_KEYBOX_ERROR_2:
*os << "CERT_PROVISIONING_GET_KEYBOX_ERROR_2";
break;
case CERT_PROVISIONING_INVALID_CERT_TYPE: *os << "CERT_PROVISIONING_INVALID_CERT_TYPE";
case CERT_PROVISIONING_INVALID_CERT_TYPE:
*os << "CERT_PROVISIONING_INVALID_CERT_TYPE";
break;
case CERT_PROVISIONING_REQUEST_ERROR_1: *os << "CERT_PROVISIONING_REQUEST_ERROR_1";
case CERT_PROVISIONING_REQUEST_ERROR_1:
*os << "CERT_PROVISIONING_REQUEST_ERROR_1";
break;
case CERT_PROVISIONING_REQUEST_ERROR_2: *os << "CERT_PROVISIONING_REQUEST_ERROR_2";
case CERT_PROVISIONING_REQUEST_ERROR_2:
*os << "CERT_PROVISIONING_REQUEST_ERROR_2";
break;
case CERT_PROVISIONING_REQUEST_ERROR_3: *os << "CERT_PROVISIONING_REQUEST_ERROR_3";
case CERT_PROVISIONING_REQUEST_ERROR_3:
*os << "CERT_PROVISIONING_REQUEST_ERROR_3";
break;
case CERT_PROVISIONING_REQUEST_ERROR_4: *os << "CERT_PROVISIONING_REQUEST_ERROR_4";
case CERT_PROVISIONING_REQUEST_ERROR_4:
*os << "CERT_PROVISIONING_REQUEST_ERROR_4";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_1: *os << "CERT_PROVISIONING_RESPONSE_ERROR_1";
case CERT_PROVISIONING_RESPONSE_ERROR_1:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_1";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_2: *os << "CERT_PROVISIONING_RESPONSE_ERROR_2";
case CERT_PROVISIONING_RESPONSE_ERROR_2:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_2";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_3: *os << "CERT_PROVISIONING_RESPONSE_ERROR_3";
case CERT_PROVISIONING_RESPONSE_ERROR_3:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_3";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_4: *os << "CERT_PROVISIONING_RESPONSE_ERROR_4";
case CERT_PROVISIONING_RESPONSE_ERROR_4:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_4";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_5: *os << "CERT_PROVISIONING_RESPONSE_ERROR_5";
case CERT_PROVISIONING_RESPONSE_ERROR_5:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_5";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_6: *os << "CERT_PROVISIONING_RESPONSE_ERROR_6";
case CERT_PROVISIONING_RESPONSE_ERROR_6:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_6";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_7: *os << "CERT_PROVISIONING_RESPONSE_ERROR_7";
case CERT_PROVISIONING_RESPONSE_ERROR_7:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_7";
break;
case CERT_PROVISIONING_RESPONSE_ERROR_8: *os << "CERT_PROVISIONING_RESPONSE_ERROR_8";
case CERT_PROVISIONING_RESPONSE_ERROR_8:
*os << "CERT_PROVISIONING_RESPONSE_ERROR_8";
break;
case CRYPTO_SESSION_OPEN_ERROR_1: *os << "CRYPTO_SESSION_OPEN_ERROR_1";
case CRYPTO_SESSION_OPEN_ERROR_1: *os << "CRYPTO_SESSION_OPEN_ERROR_1";
break;
case CRYPTO_SESSION_OPEN_ERROR_2: *os << "CRYPTO_SESSION_OPEN_ERROR_2";
case CRYPTO_SESSION_OPEN_ERROR_2: *os << "CRYPTO_SESSION_OPEN_ERROR_2";
break;
case CRYPTO_SESSION_OPEN_ERROR_3: *os << "CRYPTO_SESSION_OPEN_ERROR_3";
case CRYPTO_SESSION_OPEN_ERROR_3: *os << "CRYPTO_SESSION_OPEN_ERROR_3";
break;
case CRYPTO_SESSION_OPEN_ERROR_4: *os << "CRYPTO_SESSION_OPEN_ERROR_4";
case CRYPTO_SESSION_OPEN_ERROR_4: *os << "CRYPTO_SESSION_OPEN_ERROR_4";
break;
case CRYPTO_SESSION_OPEN_ERROR_5: *os << "CRYPTO_SESSION_OPEN_ERROR_5";
case CRYPTO_SESSION_OPEN_ERROR_5: *os << "CRYPTO_SESSION_OPEN_ERROR_5";
break;
case DECRYPT_NOT_READY: *os << "DECRYPT_NOT_READY";
break;
case DEVICE_CERTIFICATE_ERROR_1: *os << "DEVICE_CERTIFICATE_ERROR_1";
case DEVICE_CERTIFICATE_ERROR_1: *os << "DEVICE_CERTIFICATE_ERROR_1";
break;
case DEVICE_CERTIFICATE_ERROR_2: *os << "DEVICE_CERTIFICATE_ERROR_2";
case DEVICE_CERTIFICATE_ERROR_2: *os << "DEVICE_CERTIFICATE_ERROR_2";
break;
case DEVICE_CERTIFICATE_ERROR_3: *os << "DEVICE_CERTIFICATE_ERROR_3";
case DEVICE_CERTIFICATE_ERROR_3: *os << "DEVICE_CERTIFICATE_ERROR_3";
break;
case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4";
case DEVICE_CERTIFICATE_ERROR_4: *os << "DEVICE_CERTIFICATE_ERROR_4";
break;
case EMPTY_KEY_DATA_1: *os << "EMPTY_KEY_DATA_1";
break;
@@ -96,25 +111,27 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case EMPTY_LICENSE_RENEWAL: *os << "EMPTY_LICENSE_RENEWAL";
break;
case EMPTY_LICENSE_RESPONSE_1: *os << "EMPTY_LICENSE_RESPONSE_1";
case EMPTY_LICENSE_RESPONSE_1: *os << "EMPTY_LICENSE_RESPONSE_1";
break;
case EMPTY_LICENSE_RESPONSE_2: *os << "EMPTY_LICENSE_RESPONSE_2";
case EMPTY_LICENSE_RESPONSE_2: *os << "EMPTY_LICENSE_RESPONSE_2";
break;
case EMPTY_PROVISIONING_CERTIFICATE_1: *os << "EMPTY_PROVISIONING_CERTIFICATE_1";
case EMPTY_PROVISIONING_CERTIFICATE_1:
*os << "EMPTY_PROVISIONING_CERTIFICATE_1";
break;
case EMPTY_PROVISIONING_RESPONSE: *os << "EMPTY_PROVISIONING_RESPONSE";
case EMPTY_PROVISIONING_RESPONSE: *os << "EMPTY_PROVISIONING_RESPONSE";
break;
case EMPTY_SESSION_ID: *os << "EMPTY_SESSION_ID";
break;
case GENERATE_DERIVED_KEYS_ERROR: *os << "GENERATE_DERIVED_KEYS_ERROR";
case GENERATE_DERIVED_KEYS_ERROR: *os << "GENERATE_DERIVED_KEYS_ERROR";
break;
case LICENSE_RENEWAL_NONCE_GENERATION_ERROR: *os << "LICENSE_RENEWAL_NONCE_GENERATION_ERROR";
case LICENSE_RENEWAL_NONCE_GENERATION_ERROR:
*os << "LICENSE_RENEWAL_NONCE_GENERATION_ERROR";
break;
case GENERATE_USAGE_REPORT_ERROR: *os << "GENERATE_USAGE_REPORT_ERROR";
case GENERATE_USAGE_REPORT_ERROR: *os << "GENERATE_USAGE_REPORT_ERROR";
break;
case GET_LICENSE_ERROR: *os << "GET_LICENSE_ERROR";
break;
case GET_RELEASED_LICENSE_ERROR: *os << "GET_RELEASED_LICENSE_ERROR";
case GET_RELEASED_LICENSE_ERROR: *os << "GET_RELEASED_LICENSE_ERROR";
break;
case GET_USAGE_INFO_ERROR_1: *os << "GET_USAGE_INFO_ERROR_1";
break;
@@ -126,51 +143,60 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case INIT_DATA_NOT_FOUND: *os << "INIT_DATA_NOT_FOUND";
break;
case INVALID_CRYPTO_SESSION_1: *os << "INVALID_CRYPTO_SESSION_1";
case INVALID_CRYPTO_SESSION_1: *os << "INVALID_CRYPTO_SESSION_1";
break;
case INVALID_CRYPTO_SESSION_2: *os << "INVALID_CRYPTO_SESSION_2";
case INVALID_CRYPTO_SESSION_2: *os << "INVALID_CRYPTO_SESSION_2";
break;
case INVALID_CRYPTO_SESSION_3: *os << "INVALID_CRYPTO_SESSION_3";
case INVALID_CRYPTO_SESSION_3: *os << "INVALID_CRYPTO_SESSION_3";
break;
case INVALID_CRYPTO_SESSION_4: *os << "INVALID_CRYPTO_SESSION_4";
case INVALID_CRYPTO_SESSION_4: *os << "INVALID_CRYPTO_SESSION_4";
break;
case INVALID_CRYPTO_SESSION_5: *os << "INVALID_CRYPTO_SESSION_5";
case INVALID_CRYPTO_SESSION_5: *os << "INVALID_CRYPTO_SESSION_5";
break;
case INVALID_DECRYPT_PARAMETERS_ENG_1: *os << "INVALID_DECRYPT_PARAMETERS_ENG_1";
case INVALID_DECRYPT_PARAMETERS_ENG_1:
*os << "INVALID_DECRYPT_PARAMETERS_ENG_1";
break;
case INVALID_DECRYPT_PARAMETERS_ENG_2: *os << "INVALID_DECRYPT_PARAMETERS_ENG_2";
case INVALID_DECRYPT_PARAMETERS_ENG_2:
*os << "INVALID_DECRYPT_PARAMETERS_ENG_2";
break;
case INVALID_DECRYPT_PARAMETERS_ENG_3: *os << "INVALID_DECRYPT_PARAMETERS_ENG_3";
case INVALID_DECRYPT_PARAMETERS_ENG_3:
*os << "INVALID_DECRYPT_PARAMETERS_ENG_3";
break;
case INVALID_DECRYPT_PARAMETERS_ENG_4: *os << "INVALID_DECRYPT_PARAMETERS_ENG_4";
case INVALID_DECRYPT_PARAMETERS_ENG_4:
*os << "INVALID_DECRYPT_PARAMETERS_ENG_4";
break;
case INVALID_DEVICE_CERTIFICATE_TYPE: *os << "INVALID_DEVICE_CERTIFICATE_TYPE";
case INVALID_DEVICE_CERTIFICATE_TYPE:
*os << "INVALID_DEVICE_CERTIFICATE_TYPE";
break;
case INVALID_KEY_SYSTEM: *os << "INVALID_KEY_SYSTEM";
break;
case INVALID_LICENSE_RESPONSE: *os << "INVALID_LICENSE_RESPONSE";
case INVALID_LICENSE_RESPONSE: *os << "INVALID_LICENSE_RESPONSE";
break;
case INVALID_LICENSE_TYPE: *os << "INVALID_LICENSE_TYPE";
break;
case INVALID_PARAMETERS_ENG_1: *os << "INVALID_PARAMETERS_ENG_1";
case INVALID_PARAMETERS_ENG_1: *os << "INVALID_PARAMETERS_ENG_1";
break;
case INVALID_PARAMETERS_ENG_2: *os << "INVALID_PARAMETERS_ENG_2";
case INVALID_PARAMETERS_ENG_2: *os << "INVALID_PARAMETERS_ENG_2";
break;
case INVALID_PARAMETERS_ENG_3: *os << "INVALID_PARAMETERS_ENG_3";
case INVALID_PARAMETERS_ENG_3: *os << "INVALID_PARAMETERS_ENG_3";
break;
case INVALID_PARAMETERS_ENG_4: *os << "INVALID_PARAMETERS_ENG_4";
case INVALID_PARAMETERS_ENG_4: *os << "INVALID_PARAMETERS_ENG_4";
break;
case INVALID_PARAMETERS_LIC_1: *os << "INVALID_PARAMETERS_LIC_1";
case INVALID_PARAMETERS_LIC_1: *os << "INVALID_PARAMETERS_LIC_1";
break;
case INVALID_PARAMETERS_LIC_2: *os << "INVALID_PARAMETERS_LIC_2";
case INVALID_PARAMETERS_LIC_2: *os << "INVALID_PARAMETERS_LIC_2";
break;
case INVALID_PROVISIONING_PARAMETERS_1: *os << "INVALID_PROVISIONING_PARAMETERS_1";
case INVALID_PROVISIONING_PARAMETERS_1:
*os << "INVALID_PROVISIONING_PARAMETERS_1";
break;
case INVALID_PROVISIONING_PARAMETERS_2: *os << "INVALID_PROVISIONING_PARAMETERS_2";
case INVALID_PROVISIONING_PARAMETERS_2:
*os << "INVALID_PROVISIONING_PARAMETERS_2";
break;
case INVALID_PROVISIONING_REQUEST_PARAM_1: *os << "INVALID_PROVISIONING_REQUEST_PARAM_1";
case INVALID_PROVISIONING_REQUEST_PARAM_1:
*os << "INVALID_PROVISIONING_REQUEST_PARAM_1";
break;
case INVALID_PROVISIONING_REQUEST_PARAM_2: *os << "INVALID_PROVISIONING_REQUEST_PARAM_2";
case INVALID_PROVISIONING_REQUEST_PARAM_2:
*os << "INVALID_PROVISIONING_REQUEST_PARAM_2";
break;
case INVALID_QUERY_KEY: *os << "INVALID_QUERY_KEY";
break;
@@ -188,22 +214,28 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case LICENSE_ID_NOT_FOUND: *os << "LICENSE_ID_NOT_FOUND";
break;
case LICENSE_PARSER_INIT_ERROR: *os << "LICENSE_PARSER_INIT_ERROR";
case LICENSE_PARSER_INIT_ERROR: *os << "LICENSE_PARSER_INIT_ERROR";
break;
case LICENSE_PARSER_NOT_INITIALIZED_1: *os << "LICENSE_PARSER_NOT_INITIALIZED_1";
case LICENSE_PARSER_NOT_INITIALIZED_1:
*os << "LICENSE_PARSER_NOT_INITIALIZED_1";
break;
case LICENSE_PARSER_NOT_INITIALIZED_2: *os << "LICENSE_PARSER_NOT_INITIALIZED_2";
case LICENSE_PARSER_NOT_INITIALIZED_2:
*os << "LICENSE_PARSER_NOT_INITIALIZED_2";
break;
case LICENSE_PARSER_NOT_INITIALIZED_3: *os << "LICENSE_PARSER_NOT_INITIALIZED_3";
case LICENSE_PARSER_NOT_INITIALIZED_3:
*os << "LICENSE_PARSER_NOT_INITIALIZED_3";
break;
case LICENSE_RESPONSE_NOT_SIGNED: *os << "LICENSE_RESPONSE_NOT_SIGNED";
case LICENSE_RESPONSE_NOT_SIGNED: *os << "LICENSE_RESPONSE_NOT_SIGNED";
break;
break;
case LICENSE_RESPONSE_PARSE_ERROR_1: *os << "LICENSE_RESPONSE_PARSE_ERROR_1";
case LICENSE_RESPONSE_PARSE_ERROR_1:
*os << "LICENSE_RESPONSE_PARSE_ERROR_1";
break;
case LICENSE_RESPONSE_PARSE_ERROR_2: *os << "LICENSE_RESPONSE_PARSE_ERROR_2";
case LICENSE_RESPONSE_PARSE_ERROR_2:
*os << "LICENSE_RESPONSE_PARSE_ERROR_2";
break;
case LICENSE_RESPONSE_PARSE_ERROR_3: *os << "LICENSE_RESPONSE_PARSE_ERROR_3";
case LICENSE_RESPONSE_PARSE_ERROR_3:
*os << "LICENSE_RESPONSE_PARSE_ERROR_3";
break;
case LOAD_KEY_ERROR: *os << "LOAD_KEY_ERROR";
break;
@@ -211,35 +243,34 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case REFRESH_KEYS_ERROR: *os << "REFRESH_KEYS_ERROR";
break;
case RELEASE_ALL_USAGE_INFO_ERROR_1: *os << "RELEASE_ALL_USAGE_INFO_ERROR_1";
case RELEASE_ALL_USAGE_INFO_ERROR_1:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_1";
break;
case RELEASE_ALL_USAGE_INFO_ERROR_2: *os << "RELEASE_ALL_USAGE_INFO_ERROR_2";
case RELEASE_ALL_USAGE_INFO_ERROR_2:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_2";
break;
case RELEASE_KEY_ERROR: *os << "RELEASE_KEY_ERROR";
break;
case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR";
case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR";
break;
case RELEASE_LICENSE_ERROR_1: *os << "RELEASE_LICENSE_ERROR_1";
break;
case RELEASE_LICENSE_ERROR_2: *os << "RELEASE_LICENSE_ERROR_2";
break;
case RELEASE_USAGE_INFO_ERROR: *os << "RELEASE_USAGE_INFO_ERROR";
case RELEASE_USAGE_INFO_ERROR: *os << "RELEASE_USAGE_INFO_ERROR";
break;
case RENEW_KEY_ERROR_1: *os << "RENEW_KEY_ERROR_1";
break;
case RENEW_KEY_ERROR_2: *os << "RENEW_KEY_ERROR_2";
break;
case LICENSE_RENEWAL_SIGNING_ERROR: *os << "LICENSE_RENEWAL_SIGNING_ERROR";
case LICENSE_RENEWAL_SIGNING_ERROR: *os << "LICENSE_RENEWAL_SIGNING_ERROR";
break;
case RESTORE_OFFLINE_LICENSE_ERROR_1: *os << "RESTORE_OFFLINE_LICENSE_ERROR_1";
break;
case RESTORE_OFFLINE_LICENSE_ERROR_2: *os << "RESTORE_OFFLINE_LICENSE_ERROR_2";
break;
case SESSION_INIT_ERROR_1: *os << "SESSION_INIT_ERROR_1";
case RESTORE_OFFLINE_LICENSE_ERROR_2:
*os << "RESTORE_OFFLINE_LICENSE_ERROR_2";
break;
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
break;
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR";
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR";
break;
case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1";
break;
@@ -261,7 +292,7 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case SESSION_NOT_FOUND_10: *os << "SESSION_NOT_FOUND_10";
break;
case SESSION_NOT_FOUND_FOR_DECRYPT: *os << "SESSION_NOT_FOUND_FOR_DECRYPT";
case SESSION_NOT_FOUND_FOR_DECRYPT: *os << "SESSION_NOT_FOUND_FOR_DECRYPT";
break;
case SESSION_KEYS_NOT_FOUND: *os << "SESSION_KEYS_NOT_FOUND";
break;
@@ -271,8 +302,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case STORE_LICENSE_ERROR_2: *os << "STORE_LICENSE_ERROR_2";
break;
case STORE_LICENSE_ERROR_3: *os << "STORE_LICENSE_ERROR_3";
break;
case STORE_USAGE_INFO_ERROR: *os << "STORE_USAGE_INFO_ERROR";
break;
case UNPROVISION_ERROR_1: *os << "UNPROVISION_ERROR_1";
@@ -290,58 +319,134 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR:
*os << "LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR";
break;
case EMPTY_PROVISIONING_CERTIFICATE_2: *os << "EMPTY_PROVISIONING_CERTIFICATE_2";
case EMPTY_PROVISIONING_CERTIFICATE_2:
*os << "EMPTY_PROVISIONING_CERTIFICATE_2";
break;
case PARSE_SERVICE_CERTIFICATE_ERROR: *os << "PARSE_SERVICE_CERTIFICATE_ERROR";
case PARSE_SERVICE_CERTIFICATE_ERROR:
*os << "PARSE_SERVICE_CERTIFICATE_ERROR";
break;
case SERVICE_CERTIFICATE_TYPE_ERROR: *os << "SERVICE_CERTIFICATE_TYPE_ERROR";
case SERVICE_CERTIFICATE_TYPE_ERROR:
*os << "SERVICE_CERTIFICATE_TYPE_ERROR";
break;
case CLIENT_ID_GENERATE_RANDOM_ERROR: *os << "CLIENT_ID_GENERATE_RANDOM_ERROR";
case CLIENT_ID_GENERATE_RANDOM_ERROR:
*os << "CLIENT_ID_GENERATE_RANDOM_ERROR";
break;
case CLIENT_ID_AES_INIT_ERROR: *os << "CLIENT_ID_AES_INIT_ERROR";
case CLIENT_ID_AES_INIT_ERROR: *os << "CLIENT_ID_AES_INIT_ERROR";
break;
case CLIENT_ID_AES_ENCRYPT_ERROR: *os << "CLIENT_ID_AES_ENCRYPT_ERROR";
case CLIENT_ID_AES_ENCRYPT_ERROR: *os << "CLIENT_ID_AES_ENCRYPT_ERROR";
break;
case CLIENT_ID_RSA_INIT_ERROR: *os << "CLIENT_ID_RSA_INIT_ERROR";
case CLIENT_ID_RSA_INIT_ERROR: *os << "CLIENT_ID_RSA_INIT_ERROR";
break;
case CLIENT_ID_RSA_ENCRYPT_ERROR: *os << "CLIENT_ID_RSA_ENCRYPT_ERROR";
case CLIENT_ID_RSA_ENCRYPT_ERROR: *os << "CLIENT_ID_RSA_ENCRYPT_ERROR";
break;
case INVALID_QUERY_STATUS: *os << "INVALID_QUERY_STATUS";
break;
case LICENSE_PARSER_NOT_INITIALIZED_4: *os << "LICENSE_PARSER_NOT_INITIALIZED_4";
case LICENSE_PARSER_NOT_INITIALIZED_4:
*os << "LICENSE_PARSER_NOT_INITIALIZED_4";
break;
case INVALID_PARAMETERS_LIC_3: *os << "INVALID_PARAMETERS_LIC_3";
case INVALID_PARAMETERS_LIC_3: *os << "INVALID_PARAMETERS_LIC_3";
break;
case INVALID_PARAMETERS_LIC_4: *os << "INVALID_PARAMETERS_LIC_4";
case INVALID_PARAMETERS_LIC_4: *os << "INVALID_PARAMETERS_LIC_4";
break;
case INVALID_PARAMETERS_LIC_6: *os << "INVALID_PARAMETERS_LIC_6";
case INVALID_PARAMETERS_LIC_6: *os << "INVALID_PARAMETERS_LIC_6";
break;
case INVALID_PARAMETERS_LIC_7: *os << "INVALID_PARAMETERS_LIC_7";
case INVALID_PARAMETERS_LIC_7: *os << "INVALID_PARAMETERS_LIC_7";
break;
case LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR:
*os << "LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR";
break;
case CENC_INIT_DATA_UNAVAILABLE: *os << "CENC_INIT_DATA_UNAVAILABLE";
case CENC_INIT_DATA_UNAVAILABLE: *os << "CENC_INIT_DATA_UNAVAILABLE";
break;
case PREPARE_CENC_CONTENT_ID_FAILED: *os << "PREPARE_CENC_CONTENT_ID_FAILED";
case PREPARE_CENC_CONTENT_ID_FAILED:
*os << "PREPARE_CENC_CONTENT_ID_FAILED";
break;
case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE";
case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE";
break;
case PREPARE_WEBM_CONTENT_ID_FAILED: *os << "PREPARE_WEBM_CONTENT_ID_FAILED";
case PREPARE_WEBM_CONTENT_ID_FAILED:
*os << "PREPARE_WEBM_CONTENT_ID_FAILED";
break;
case UNSUPPORTED_INIT_DATA_FORMAT: *os << "UNSUPPORTED_INIT_DATA_FORMAT";
case UNSUPPORTED_INIT_DATA_FORMAT: *os << "UNSUPPORTED_INIT_DATA_FORMAT";
break;
case LICENSE_REQUEST_NONCE_GENERATION_ERROR: *os << "LICENSE_REQUEST_NONCE_GENERATION_ERROR";
case LICENSE_REQUEST_NONCE_GENERATION_ERROR:
*os << "LICENSE_REQUEST_NONCE_GENERATION_ERROR";
break;
case LICENSE_REQUEST_SIGNING_ERROR: *os << "LICENSE_REQUEST_SIGNING_ERROR";
case LICENSE_REQUEST_SIGNING_ERROR: *os << "LICENSE_REQUEST_SIGNING_ERROR";
break;
case EMPTY_LICENSE_REQUEST: *os << "EMPTY_LICENSE_REQUEST";
break;
case DUPLICATE_SESSION_ID_SPECIFIED: *os << "DUPLICATE_SESSION_ID_SPECIFIED";
case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED";
case DUPLICATE_SESSION_ID_SPECIFIED:
*os << "DUPLICATE_SESSION_ID_SPECIFIED";
break;
case LICENSE_RENEWAL_PROHIBITED: *os << "LICENSE_RENEWAL_PROHIBITED";
break;
case SESSION_FILE_HANDLE_INIT_ERROR:
*os << "SESSION_FILE_HANDLE_INIT_ERROR";
break;
case INCORRECT_CRYPTO_MODE: *os << "INCORRECT_CRYPTO_MODE";
break;
case INVALID_PARAMETERS_ENG_5: *os << "INVALID_PARAMETERS_ENG_5";
break;
case DECRYPT_ERROR: *os << "DECRYPT_ERROR";
break;
case INSUFFICIENT_OUTPUT_PROTECTION:
*os << "INSUFFICIENT_OUTPUT_PROTECTION";
break;
case SESSION_NOT_FOUND_12: *os << "SESSION_NOT_FOUND_12";
break;
case KEY_NOT_FOUND_1: *os << "KEY_NOT_FOUND_1";
break;
case KEY_NOT_FOUND_2: *os << "KEY_NOT_FOUND_2";
break;
case KEY_CONFLICT_1: *os << "KEY_CONFLICT_1";
break;
case INVALID_PARAMETERS_ENG_6: *os << "INVALID_PARAMETERS_ENG_6";
break;
case INVALID_PARAMETERS_ENG_7: *os << "INVALID_PARAMETERS_ENG_7";
break;
case INVALID_PARAMETERS_ENG_8: *os << "INVALID_PARAMETERS_ENG_8";
break;
case INVALID_PARAMETERS_ENG_9: *os << "INVALID_PARAMETERS_ENG_9";
break;
case INVALID_PARAMETERS_ENG_10: *os << "INVALID_PARAMETERS_ENG_10";
break;
case INVALID_PARAMETERS_ENG_11: *os << "INVALID_PARAMETERS_ENG_11";
break;
case INVALID_PARAMETERS_ENG_12: *os << "INVALID_PARAMETERS_ENG_12";
break;
case SESSION_NOT_FOUND_13: *os << "SESSION_NOT_FOUND_13";
break;
case SESSION_NOT_FOUND_14: *os << "SESSION_NOT_FOUND_14";
break;
case SESSION_NOT_FOUND_15: *os << "SESSION_NOT_FOUND_15";
break;
case SESSION_NOT_FOUND_16: *os << "SESSION_NOT_FOUND_16";
break;
case KEY_NOT_FOUND_3: *os << "KEY_NOT_FOUND_3";
break;
case KEY_NOT_FOUND_4: *os << "KEY_NOT_FOUND_4";
break;
case KEY_NOT_FOUND_5: *os << "KEY_NOT_FOUND_5";
break;
case KEY_NOT_FOUND_6: *os << "KEY_NOT_FOUND_6";
break;
case KEY_ERROR_1: *os << "KEY_ERROR_1";
break;
case KEY_ERROR_2: *os << "KEY_ERROR_2";
break;
case KEY_ERROR_3: *os << "KEY_ERROR_3";
break;
case KEY_ERROR_4: *os << "KEY_ERROR_4";
break;
case INVALID_PARAMETERS_ENG_13: *os << "INVALID_PARAMETERS_ENG_13";
break;
case INVALID_PARAMETERS_ENG_14: *os << "INVALID_PARAMETERS_ENG_14";
break;
case INVALID_PARAMETERS_ENG_15: *os << "INVALID_PARAMETERS_ENG_15";
break;
case INVALID_PARAMETERS_ENG_16: *os << "INVALID_PARAMETERS_ENG_16";
break;
default:
*os << "Unknown CdmResponseType";
*os << "Unknown CdmResponseType";
break;
}
}
@@ -364,7 +469,7 @@ void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) {
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) {
switch (value) {
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
break;
case kSecurityLevelL1: *os << "kSecurityLevelL1";
break;
@@ -387,7 +492,7 @@ void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) {
case kCertificateX509: *os << "kCertificateX509";
break;
default:
*os << "Unknown CdmCertificateType";
*os << "Unknown CdmCertificateType";
break;
}
};