Merge "Merge Changes from CDM repository"
This commit is contained in:
committed by
Android (Google) Code Review
commit
2c0b1d6142
@@ -13,9 +13,9 @@ class CdmClientPropertySet {
|
||||
public:
|
||||
virtual ~CdmClientPropertySet() {}
|
||||
|
||||
virtual std::string security_level() const = 0;
|
||||
virtual const std::string& security_level() const = 0;
|
||||
virtual bool use_privacy_mode() const = 0;
|
||||
virtual std::vector<uint8_t> service_certificate() const = 0;
|
||||
virtual const std::string& service_certificate() const = 0;
|
||||
virtual bool is_session_sharing_enabled() const = 0;
|
||||
virtual uint32_t session_sharing_id() const = 0;
|
||||
virtual void set_session_sharing_id(uint32_t id) = 0;
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
#ifndef WVCDM_CORE_CDM_ENGINE_H_
|
||||
#define WVCDM_CORE_CDM_ENGINE_H_
|
||||
|
||||
#include "cdm_session.h"
|
||||
#include "certificate_provisioning.h"
|
||||
#include "initialization_data.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class CdmClientPropertySet;
|
||||
class CdmSession;
|
||||
class CryptoEngine;
|
||||
class WvCdmEventListener;
|
||||
|
||||
@@ -24,98 +25,94 @@ class CdmEngine {
|
||||
virtual ~CdmEngine();
|
||||
|
||||
// Session related methods
|
||||
CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
const CdmClientPropertySet* property_set,
|
||||
CdmSessionId* session_id);
|
||||
CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||
|
||||
CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
|
||||
CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id);
|
||||
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
|
||||
// License related methods
|
||||
// Construct a valid license request
|
||||
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,
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
|
||||
// Accept license response and extract key info.
|
||||
CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data,
|
||||
CdmKeySetId* key_set_id);
|
||||
|
||||
CdmResponseType RestoreKey(const CdmSessionId& session_id,
|
||||
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id,
|
||||
const CdmKeySetId& key_set_id);
|
||||
|
||||
CdmResponseType CancelKeyRequest(const CdmSessionId& session_id);
|
||||
virtual CdmResponseType CancelKeyRequest(const CdmSessionId& session_id);
|
||||
|
||||
// Construct valid renewal request for the current session keys.
|
||||
CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
|
||||
virtual CdmResponseType GenerateRenewalRequest(const CdmSessionId& session_id,
|
||||
CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
|
||||
// Accept renewal response and update key info.
|
||||
CdmResponseType RenewKey(const CdmSessionId& session_id,
|
||||
virtual CdmResponseType RenewKey(const CdmSessionId& session_id,
|
||||
const CdmKeyResponse& key_data);
|
||||
|
||||
// Query system information
|
||||
CdmResponseType QueryStatus(CdmQueryMap* info);
|
||||
virtual CdmResponseType QueryStatus(CdmQueryMap* info);
|
||||
|
||||
// Query session information
|
||||
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Query license information
|
||||
CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
||||
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Query seesion control information
|
||||
CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
virtual CdmResponseType QueryKeyControlInfo(const CdmSessionId& session_id,
|
||||
CdmQueryMap* key_info);
|
||||
|
||||
// Provisioning related methods
|
||||
CdmResponseType GetProvisioningRequest(
|
||||
CdmCertificateType cert_type,
|
||||
const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
virtual CdmResponseType GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request, std::string* default_url);
|
||||
|
||||
CdmResponseType HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
virtual CdmResponseType HandleProvisioningResponse(
|
||||
CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
|
||||
|
||||
// Usage related methods for streaming licenses
|
||||
CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
|
||||
CdmResponseType ReleaseUsageInfo(const CdmUsageInfoReleaseMessage& message);
|
||||
virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
|
||||
virtual CdmResponseType ReleaseUsageInfo(
|
||||
const CdmUsageInfoReleaseMessage& message);
|
||||
|
||||
// Decryption and key related methods
|
||||
// Accept encrypted buffer and return decrypted data.
|
||||
CdmResponseType Decrypt(const CdmSessionId& session_id,
|
||||
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
|
||||
const CdmDecryptionParameters& parameters);
|
||||
|
||||
size_t SessionSize() const { return sessions_.size(); }
|
||||
virtual size_t SessionSize() const { return sessions_.size(); }
|
||||
|
||||
// Is the key known to any session?
|
||||
bool IsKeyLoaded(const KeyId& key_id);
|
||||
bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
||||
|
||||
// Event listener related methods
|
||||
bool AttachEventListener(const CdmSessionId& session_id,
|
||||
virtual bool AttachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener);
|
||||
bool DetachEventListener(const CdmSessionId& session_id,
|
||||
virtual bool DetachEventListener(const CdmSessionId& session_id,
|
||||
WvCdmEventListener* listener);
|
||||
|
||||
// Timer expiration method
|
||||
void OnTimerEvent();
|
||||
virtual void OnTimerEvent();
|
||||
|
||||
private:
|
||||
// private methods
|
||||
// Cancel all sessions
|
||||
bool CancelSessions();
|
||||
bool ValidateKeySystem(const CdmKeySystem& key_system);
|
||||
|
||||
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
@@ -125,8 +122,11 @@ class CdmEngine {
|
||||
CdmReleaseKeySetMap release_key_sets_;
|
||||
CertificateProvisioning cert_provisioning_;
|
||||
SecurityLevel cert_provisioning_requested_security_level_;
|
||||
CdmSession* usage_session_;
|
||||
|
||||
static bool seeded_;
|
||||
|
||||
// usage related variables
|
||||
scoped_ptr<CdmSession> usage_session_;
|
||||
int64_t last_usage_information_update_time;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
|
||||
|
||||
@@ -22,84 +22,83 @@ class WvCdmEventListener;
|
||||
class CdmSession {
|
||||
public:
|
||||
explicit CdmSession(const CdmClientPropertySet* cdm_client_property_set);
|
||||
~CdmSession();
|
||||
virtual ~CdmSession();
|
||||
|
||||
CdmResponseType Init();
|
||||
virtual CdmResponseType Init();
|
||||
|
||||
CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
const CdmLicenseType license_type);
|
||||
CdmResponseType RestoreUsageSession(const CdmKeyMessage& key_request,
|
||||
const CdmKeyResponse& key_response);
|
||||
virtual CdmResponseType RestoreOfflineSession(
|
||||
const CdmKeySetId& key_set_id, const CdmLicenseType license_type);
|
||||
virtual CdmResponseType RestoreUsageSession(
|
||||
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response);
|
||||
|
||||
void set_key_system(const CdmKeySystem& ksystem) { key_system_ = ksystem; }
|
||||
const CdmKeySystem& key_system() { return key_system_; }
|
||||
virtual void set_key_system(const CdmKeySystem& ksystem) {
|
||||
key_system_ = ksystem;
|
||||
}
|
||||
virtual const CdmKeySystem& key_system() { return key_system_; }
|
||||
|
||||
const CdmSessionId& session_id() { return session_id_; }
|
||||
virtual const CdmSessionId& session_id() { return session_id_; }
|
||||
|
||||
bool VerifySession(const CdmKeySystem& key_system,
|
||||
const InitializationData& init_data);
|
||||
|
||||
CdmResponseType GenerateKeyRequest(const InitializationData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* key_request,
|
||||
virtual CdmResponseType GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
|
||||
// AddKey() - Accept license response and extract key info.
|
||||
CdmResponseType AddKey(const CdmKeyResponse& key_response,
|
||||
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response,
|
||||
CdmKeySetId* key_set_id);
|
||||
|
||||
// CancelKeyRequest() - Cancel session.
|
||||
CdmResponseType CancelKeyRequest();
|
||||
virtual CdmResponseType CancelKeyRequest();
|
||||
|
||||
// Query session status
|
||||
CdmResponseType QueryStatus(CdmQueryMap* key_info);
|
||||
virtual CdmResponseType QueryStatus(CdmQueryMap* key_info);
|
||||
|
||||
// Query license information
|
||||
CdmResponseType QueryKeyStatus(CdmQueryMap* key_info);
|
||||
virtual CdmResponseType QueryKeyStatus(CdmQueryMap* key_info);
|
||||
|
||||
// Query session control info
|
||||
CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info);
|
||||
virtual CdmResponseType QueryKeyControlInfo(CdmQueryMap* key_info);
|
||||
|
||||
// Decrypt() - Accept encrypted buffer and return decrypted data.
|
||||
CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
|
||||
// License renewal
|
||||
// GenerateRenewalRequest() - Construct valid renewal request for the current
|
||||
// session keys.
|
||||
CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
virtual CdmResponseType GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
CdmResponseType RenewKey(const CdmKeyResponse& key_response);
|
||||
virtual CdmResponseType RenewKey(const CdmKeyResponse& key_response);
|
||||
|
||||
// License release
|
||||
// GenerateReleaseRequest() - Construct valid release request for the current
|
||||
// session keys.
|
||||
CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
virtual CdmResponseType GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url);
|
||||
|
||||
// ReleaseKey() - Accept response and release key.
|
||||
CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
|
||||
virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
|
||||
|
||||
bool IsKeyLoaded(const KeyId& key_id);
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
|
||||
bool AttachEventListener(WvCdmEventListener* listener);
|
||||
bool DetachEventListener(WvCdmEventListener* listener);
|
||||
virtual bool AttachEventListener(WvCdmEventListener* listener);
|
||||
virtual bool DetachEventListener(WvCdmEventListener* listener);
|
||||
|
||||
void OnTimerEvent();
|
||||
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
virtual void OnTimerEvent();
|
||||
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
|
||||
SecurityLevel GetRequestedSecurityLevel();
|
||||
CdmSecurityLevel GetSecurityLevel();
|
||||
virtual SecurityLevel GetRequestedSecurityLevel();
|
||||
virtual CdmSecurityLevel GetSecurityLevel();
|
||||
|
||||
CdmResponseType UpdateUsageInformation();
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
|
||||
bool is_usage_update_needed() { return is_usage_update_needed_; }
|
||||
void reset_is_usage_update_needed() { is_usage_update_needed_ = false; }
|
||||
virtual bool is_usage_update_needed() { return is_usage_update_needed_; }
|
||||
virtual void reset_is_usage_update_needed() {
|
||||
is_usage_update_needed_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Generate unique ID for each new session.
|
||||
CdmSessionId GenerateSessionId();
|
||||
bool GenerateKeySetId(CdmKeySetId* key_set_id);
|
||||
@@ -115,10 +114,10 @@ class CdmSession {
|
||||
scoped_ptr<CryptoSession> crypto_session_;
|
||||
PolicyEngine policy_engine_;
|
||||
bool license_received_;
|
||||
bool reinitialize_session_;
|
||||
bool is_offline_;
|
||||
bool is_release_;
|
||||
bool is_usage_update_needed_;
|
||||
bool is_initial_decryption_;
|
||||
|
||||
// information useful for offline and usage scenarios
|
||||
CdmKeyMessage key_request_;
|
||||
@@ -133,10 +132,6 @@ class CdmSession {
|
||||
// license type release and offline related information
|
||||
CdmKeySetId key_set_id_;
|
||||
|
||||
// Used for certificate based licensing
|
||||
std::string wrapped_key_;
|
||||
bool is_certificate_loaded_;
|
||||
|
||||
std::set<WvCdmEventListener*> listeners_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession);
|
||||
|
||||
@@ -19,42 +19,44 @@ typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
|
||||
class CryptoSession {
|
||||
public:
|
||||
CryptoSession();
|
||||
~CryptoSession();
|
||||
virtual ~CryptoSession();
|
||||
|
||||
bool ValidateKeybox();
|
||||
bool GetToken(std::string* token);
|
||||
CdmSecurityLevel GetSecurityLevel();
|
||||
bool GetDeviceUniqueId(std::string* device_id);
|
||||
bool GetSystemId(uint32_t* system_id);
|
||||
bool GetProvisioningId(std::string* provisioning_id);
|
||||
virtual bool ValidateKeybox();
|
||||
virtual bool GetToken(std::string* token);
|
||||
virtual CdmSecurityLevel GetSecurityLevel();
|
||||
virtual bool GetDeviceUniqueId(std::string* device_id);
|
||||
virtual bool GetApiVersion(uint32_t* version);
|
||||
virtual bool GetSystemId(uint32_t* system_id);
|
||||
virtual bool GetProvisioningId(std::string* provisioning_id);
|
||||
|
||||
CdmResponseType Open() { return Open(kLevelDefault); }
|
||||
CdmResponseType Open(SecurityLevel requested_security_level);
|
||||
void Close();
|
||||
virtual CdmResponseType Open() { return Open(kLevelDefault); }
|
||||
virtual CdmResponseType Open(SecurityLevel requested_security_level);
|
||||
virtual void Close();
|
||||
|
||||
bool IsOpen() { return open_; }
|
||||
CryptoSessionId oec_session_id() { return oec_session_id_; }
|
||||
virtual bool IsOpen() { return open_; }
|
||||
virtual CryptoSessionId oec_session_id() { return oec_session_id_; }
|
||||
|
||||
// Key request/response
|
||||
void GenerateRequestId(std::string& req_id_str);
|
||||
bool PrepareRequest(const std::string& key_deriv_message,
|
||||
virtual void GenerateRequestId(std::string& req_id_str);
|
||||
virtual bool PrepareRequest(const std::string& key_deriv_message,
|
||||
bool is_provisioning, std::string* signature);
|
||||
bool PrepareRenewalRequest(const std::string& message,
|
||||
virtual bool PrepareRenewalRequest(const std::string& message,
|
||||
std::string* signature);
|
||||
CdmResponseType LoadKeys(const std::string& message,
|
||||
virtual CdmResponseType LoadKeys(const std::string& message,
|
||||
const std::string& signature,
|
||||
const std::string& mac_key_iv,
|
||||
const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& key_array,
|
||||
const std::string& provider_session_token);
|
||||
bool LoadCertificatePrivateKey(std::string& wrapped_key);
|
||||
bool RefreshKeys(const std::string& message, const std::string& signature,
|
||||
int num_keys, const CryptoKey* key_array);
|
||||
bool GenerateNonce(uint32_t* nonce);
|
||||
bool GenerateDerivedKeys(const std::string& message);
|
||||
bool GenerateDerivedKeys(const std::string& message,
|
||||
virtual bool LoadCertificatePrivateKey(std::string& wrapped_key);
|
||||
virtual bool RefreshKeys(const std::string& message,
|
||||
const std::string& signature, int num_keys,
|
||||
const CryptoKey* key_array);
|
||||
virtual bool GenerateNonce(uint32_t* nonce);
|
||||
virtual bool GenerateDerivedKeys(const std::string& message);
|
||||
virtual bool GenerateDerivedKeys(const std::string& message,
|
||||
const std::string& session_key);
|
||||
bool RewrapDeviceRSAKey(const std::string& message,
|
||||
virtual bool RewrapDeviceRSAKey(const std::string& message,
|
||||
const std::string& signature,
|
||||
const std::string& nonce,
|
||||
const std::string& enc_rsa_key,
|
||||
@@ -62,18 +64,16 @@ class CryptoSession {
|
||||
std::string* wrapped_rsa_key);
|
||||
|
||||
// Media data path
|
||||
CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
|
||||
|
||||
CdmResponseType UpdateUsageInformation();
|
||||
CdmResponseType GenerateUsageReport(
|
||||
const std::string& provider_session_token,
|
||||
std::string* usage_report);
|
||||
CdmResponseType ReleaseUsageInformation(
|
||||
const std::string& message,
|
||||
const std::string& signature,
|
||||
virtual CdmResponseType UpdateUsageInformation();
|
||||
virtual CdmResponseType GenerateUsageReport(
|
||||
const std::string& provider_session_token, std::string* usage_report);
|
||||
virtual CdmResponseType ReleaseUsageInformation(
|
||||
const std::string& message, const std::string& signature,
|
||||
const std::string& provider_session_token);
|
||||
|
||||
bool GetRandom(size_t data_length, uint8_t* random_data);
|
||||
virtual bool GetRandom(size_t data_length, uint8_t* random_data);
|
||||
|
||||
private:
|
||||
void Init();
|
||||
@@ -82,8 +82,8 @@ class CryptoSession {
|
||||
std::string* deriv_context);
|
||||
void GenerateEncryptContext(const std::string& input_context,
|
||||
std::string* deriv_context);
|
||||
bool GenerateSignature(const std::string& message, bool use_rsa,
|
||||
std::string* signature);
|
||||
bool GenerateSignature(const std::string& message, std::string* signature);
|
||||
bool GenerateRsaSignature(const std::string& message, std::string* signature);
|
||||
size_t GetOffset(std::string message, std::string field);
|
||||
bool SetDestinationBufferType();
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
#include <gtest/gtest_prod.h>
|
||||
#endif
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class File;
|
||||
@@ -57,23 +61,38 @@ class DeviceFiles {
|
||||
virtual bool RetrieveUsageInfo(
|
||||
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
|
||||
|
||||
// For testing only
|
||||
static std::string GetCertificateFileName();
|
||||
static std::string GetLicenseFileNameExtension();
|
||||
static std::string GetUsageInfoFileName();
|
||||
void SetTestFile(File* file);
|
||||
|
||||
protected:
|
||||
bool Hash(const std::string& data, std::string* hash);
|
||||
private:
|
||||
bool StoreFile(const char* name, const std::string& serialized_file);
|
||||
bool RetrieveFile(const char* name, std::string* serialized_file);
|
||||
|
||||
private:
|
||||
// Certificate and offline licenses are now stored in security
|
||||
// level specific directories. In an earlier version they were
|
||||
// stored in a common directory and need to be copied over.
|
||||
virtual void SecurityLevelPathBackwardCompatibility();
|
||||
|
||||
// For testing only:
|
||||
static std::string GetCertificateFileName();
|
||||
static std::string GetLicenseFileNameExtension();
|
||||
static std::string GetUsageInfoFileName();
|
||||
void SetTestFile(File* file);
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreCertificate);
|
||||
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, DeleteLicense);
|
||||
FRIEND_TEST(DeviceFilesTest, ReadCertificate);
|
||||
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility);
|
||||
FRIEND_TEST(DeviceFilesTest, StoreLicenses);
|
||||
FRIEND_TEST(DeviceFilesTest, UpdateLicenseState);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Delete);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Read);
|
||||
FRIEND_TEST(DeviceFilesUsageInfoTest, Store);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest);
|
||||
FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test);
|
||||
FRIEND_TEST(WvCdmUsageInfoTest, DISABLED_UsageInfo);
|
||||
#endif
|
||||
|
||||
File* file_;
|
||||
CdmSecurityLevel security_level_;
|
||||
bool initialized_;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// File class. The implementation is platform dependent.
|
||||
|
||||
@@ -23,32 +23,32 @@ class CdmLicense {
|
||||
public:
|
||||
|
||||
CdmLicense() : session_(NULL), initialized_(false) {}
|
||||
~CdmLicense() {}
|
||||
virtual ~CdmLicense() {}
|
||||
|
||||
bool Init(const std::string& token, CryptoSession* session,
|
||||
virtual bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
|
||||
bool PrepareKeyRequest(const InitializationData& init_data,
|
||||
virtual bool PrepareKeyRequest(const InitializationData& init_data,
|
||||
const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
const CdmSessionId& session_id,
|
||||
CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request,
|
||||
virtual bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
||||
CdmResponseType HandleKeyUpdateResponse(
|
||||
virtual CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
|
||||
virtual CdmResponseType HandleKeyUpdateResponse(
|
||||
bool is_renewal, const CdmKeyResponse& license_response);
|
||||
|
||||
bool RestoreOfflineLicense(const CdmKeyMessage& license_request,
|
||||
virtual bool RestoreOfflineLicense(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response);
|
||||
bool RestoreUsageLicense(const CdmKeyMessage& license_request,
|
||||
virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response);
|
||||
bool HasInitData() { return !stored_init_data_.empty(); }
|
||||
bool IsKeyLoaded(const KeyId& key_id);
|
||||
virtual bool HasInitData() { return !stored_init_data_.empty(); }
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
|
||||
std::string provider_session_token() { return provider_session_token_; }
|
||||
virtual std::string provider_session_token() { return provider_session_token_; }
|
||||
|
||||
private:
|
||||
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
|
||||
@@ -17,6 +17,8 @@ typedef enum {
|
||||
LOG_VERBOSE
|
||||
} LogPriority;
|
||||
|
||||
extern LogPriority g_cutoff;
|
||||
|
||||
// Enable/disable verbose logging (LOGV).
|
||||
// This function is supplied for cases where the system layer does not
|
||||
// initialize logging. This is also needed to initialize logging in
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// oemcrypto_adapter.h
|
||||
// This interface allows CDM to open a Level 3 session instead of
|
||||
// letting the wrapper function choose between level 1 or level 3.
|
||||
//
|
||||
#ifndef WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
#define WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
|
||||
class Clock;
|
||||
class PolicyEngineTest;
|
||||
|
||||
@@ -18,53 +20,46 @@ class PolicyEngineTest;
|
||||
class PolicyEngine {
|
||||
public:
|
||||
PolicyEngine();
|
||||
~PolicyEngine();
|
||||
virtual ~PolicyEngine();
|
||||
|
||||
// The value returned should be taken as a hint rather than an absolute
|
||||
// status. It is computed during the last call to either SetLicense/
|
||||
// UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync
|
||||
// depending on the amount of time elapsed. The current decryption
|
||||
// status is not calculated to avoid overhead in the decryption path.
|
||||
inline bool can_decrypt() { return can_decrypt_; }
|
||||
virtual bool can_decrypt() { return can_decrypt_; }
|
||||
|
||||
// OnTimerEvent is called when a timer fires. It notifies the Policy Engine
|
||||
// that the timer has fired and that it should check whether any events have
|
||||
// occurred since the last timer event. If so, it sets event_occurred to true
|
||||
// and sets event to point to the event that occurred. If not, it sets
|
||||
// event_occurred to false.
|
||||
void OnTimerEvent(bool* event_occurred, CdmEventType* event);
|
||||
virtual void OnTimerEvent(bool* event_occurred, CdmEventType* event);
|
||||
|
||||
// SetLicense is used in handling the initial license response. It stores
|
||||
// an exact copy of the policy information stored in the license.
|
||||
// The license state transitions to kLicenseStateCanPlay if the license
|
||||
// permits playback.
|
||||
void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
virtual void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
|
||||
// Call this on first decrypt to set the start of playback. This is
|
||||
// for cases where usage begins not when the license is received,
|
||||
// but at the start of playback
|
||||
void BeginDecryption(void);
|
||||
// Call this on first decrypt to set the start of playback.
|
||||
virtual void BeginDecryption(void);
|
||||
|
||||
// UpdateLicense is used in handling a license response for a renewal request.
|
||||
// The response may only contain any policy fields that have changed. In this
|
||||
// case an exact copy is not what we want to happen. We also will receive an
|
||||
// updated license_start_time from the server. The license will transition to
|
||||
// kLicenseStateCanPlay if the license permits playback.
|
||||
void UpdateLicense(const video_widevine_server::sdk::License& license);
|
||||
virtual void UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
|
||||
CdmResponseType Query(CdmQueryMap* key_info);
|
||||
virtual CdmResponseType Query(CdmQueryMap* key_info);
|
||||
|
||||
const video_widevine_server::sdk::LicenseIdentification& license_id() {
|
||||
return license_id_;
|
||||
}
|
||||
|
||||
bool IsLicenseDurationExpired(int64_t current_time);
|
||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||
virtual const LicenseIdentification& license_id() { return license_id_; }
|
||||
|
||||
private:
|
||||
typedef enum {
|
||||
kLicenseStateInitial,
|
||||
kLicenseStateInitialPendingUsage,
|
||||
kLicenseStateCanPlay,
|
||||
kLicenseStateNeedRenewal,
|
||||
kLicenseStateWaitingLicenseUpdate,
|
||||
@@ -73,6 +68,11 @@ class PolicyEngine {
|
||||
|
||||
void Init(Clock* clock);
|
||||
|
||||
bool IsLicenseDurationExpired(int64_t current_time);
|
||||
int64_t GetLicenseDurationRemaining(int64_t current_time);
|
||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||
int64_t GetPlaybackDurationRemaining(int64_t current_time);
|
||||
|
||||
bool IsRenewalDelayExpired(int64_t current_time);
|
||||
bool IsRenewalRecoveryDurationExpired(int64_t current_time);
|
||||
bool IsRenewalRetryIntervalExpired(int64_t current_time);
|
||||
|
||||
@@ -24,30 +24,27 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/rsa.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class AesCbcKey {
|
||||
public:
|
||||
AesCbcKey() : initialized_(false) {};
|
||||
~AesCbcKey() {};
|
||||
AesCbcKey();
|
||||
~AesCbcKey();
|
||||
|
||||
bool Init(const std::string& key);
|
||||
bool Encrypt(const std::string& in, std::string* out, std::string* iv);
|
||||
|
||||
private:
|
||||
EVP_CIPHER_CTX ctx_;
|
||||
bool initialized_;
|
||||
std::string key_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(AesCbcKey);
|
||||
};
|
||||
|
||||
class RsaPublicKey {
|
||||
public:
|
||||
RsaPublicKey() : key_(NULL) {}
|
||||
RsaPublicKey();
|
||||
~RsaPublicKey();
|
||||
|
||||
// Initializes an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey
|
||||
@@ -64,7 +61,7 @@ class RsaPublicKey {
|
||||
const std::string& signature);
|
||||
|
||||
private:
|
||||
RSA* key_;
|
||||
std::string serialized_key_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(RsaPublicKey);
|
||||
};
|
||||
|
||||
@@ -26,12 +26,6 @@ class Properties {
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
static inline bool begin_license_usage_when_received() {
|
||||
return begin_license_usage_when_received_;
|
||||
}
|
||||
static inline bool require_explicit_renew_request() {
|
||||
return require_explicit_renew_request_;
|
||||
}
|
||||
static inline bool oem_crypto_use_secure_buffers() {
|
||||
return oem_crypto_use_secure_buffers_;
|
||||
}
|
||||
@@ -45,9 +39,6 @@ class Properties {
|
||||
static inline bool use_certificates_as_identification() {
|
||||
return use_certificates_as_identification_;
|
||||
}
|
||||
static inline bool decrypt_with_empty_session_support() {
|
||||
return decrypt_with_empty_session_support_;
|
||||
}
|
||||
static inline bool security_level_path_backward_compatibility_support() {
|
||||
return security_level_path_backward_compatibility_support_;
|
||||
}
|
||||
@@ -62,9 +53,10 @@ class Properties {
|
||||
static bool GetFactoryKeyboxPath(std::string* keybox);
|
||||
static bool GetOEMCryptoPath(std::string* library_name);
|
||||
static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs);
|
||||
static const std::string GetSecurityLevel(const CdmSessionId& session_id);
|
||||
static const std::vector<uint8_t> GetServiceCertificate(
|
||||
const CdmSessionId& session_id);
|
||||
static bool GetSecurityLevel(const CdmSessionId& session_id,
|
||||
std::string* security_level);
|
||||
static bool GetServiceCertificate(const CdmSessionId& session_id,
|
||||
std::string* service_certificate);
|
||||
static bool UsePrivacyMode(const CdmSessionId& session_id);
|
||||
static uint32_t GetSessionSharingId(const CdmSessionId& session_id);
|
||||
|
||||
@@ -75,12 +67,6 @@ class Properties {
|
||||
private:
|
||||
static const CdmClientPropertySet* GetCdmClientPropertySet(
|
||||
const CdmSessionId& session_id);
|
||||
static void set_begin_license_usage_when_received(bool flag) {
|
||||
begin_license_usage_when_received_ = flag;
|
||||
}
|
||||
static void set_require_explicit_renew_request(bool flag) {
|
||||
require_explicit_renew_request_ = flag;
|
||||
}
|
||||
static void set_oem_crypto_use_secure_buffers(bool flag) {
|
||||
oem_crypto_use_secure_buffers_ = flag;
|
||||
}
|
||||
@@ -96,23 +82,17 @@ class Properties {
|
||||
static void set_use_certificates_as_identification(bool flag) {
|
||||
use_certificates_as_identification_ = flag;
|
||||
}
|
||||
static void set_decrypt_with_empty_session_support(bool flag) {
|
||||
decrypt_with_empty_session_support_ = flag;
|
||||
}
|
||||
static void set_security_level_path_backward_compatibility_support(
|
||||
bool flag) {
|
||||
security_level_path_backward_compatibility_support_ = flag;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool begin_license_usage_when_received_;
|
||||
static bool require_explicit_renew_request_;
|
||||
static bool oem_crypto_use_secure_buffers_;
|
||||
static bool oem_crypto_use_fifo_;
|
||||
static bool oem_crypto_use_userspace_buffers_;
|
||||
static bool oem_crypto_require_usage_tables_;
|
||||
static bool use_certificates_as_identification_;
|
||||
static bool decrypt_with_empty_session_support_;
|
||||
static bool security_level_path_backward_compatibility_support_;
|
||||
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;
|
||||
|
||||
|
||||
@@ -28,11 +28,6 @@ typedef std::string CdmUsageInfoReleaseMessage;
|
||||
typedef std::string CdmProvisioningRequest;
|
||||
typedef std::string CdmProvisioningResponse;
|
||||
|
||||
// Types for shared host/cdm interface pairs used to shared vendor data.
|
||||
typedef std::pair<std::string, std::string> kStringPairs;
|
||||
typedef std::vector<uint8_t> kVectorBytes;
|
||||
typedef std::pair<std::string, kVectorBytes> kVectorPairs;
|
||||
|
||||
enum CdmResponseType {
|
||||
NO_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "cdm_engine.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
@@ -18,27 +20,30 @@
|
||||
|
||||
namespace {
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kMinNoncesPerSession = 4;
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
} // unnamed namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
bool CdmEngine::seeded_ = false;
|
||||
|
||||
CdmEngine::CdmEngine()
|
||||
: cert_provisioning_requested_security_level_(kLevelDefault),
|
||||
usage_session_(NULL),
|
||||
last_usage_information_update_time(0) {
|
||||
Properties::Init();
|
||||
if (!seeded_) {
|
||||
Clock clock;
|
||||
srand(clock.GetCurrentTime());
|
||||
seeded_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
CancelSessions();
|
||||
|
||||
if (NULL != usage_session_)
|
||||
delete usage_session_;
|
||||
|
||||
CdmSessionMap::iterator i(sessions_.begin());
|
||||
for (; i != sessions_.end(); ++i)
|
||||
for (; i != sessions_.end(); ++i) {
|
||||
delete i->second;
|
||||
}
|
||||
sessions_.clear();
|
||||
}
|
||||
|
||||
@@ -286,9 +291,6 @@ CdmResponseType CdmEngine::RestoreKey(
|
||||
CdmResponseType CdmEngine::CancelKeyRequest(const CdmSessionId& session_id) {
|
||||
LOGI("CdmEngine::CancelKeyRequest");
|
||||
|
||||
//TODO(gmorgan): Issue: what is semantics of canceling a key request. Should
|
||||
//this call cancel all keys for the session?
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s",
|
||||
@@ -494,11 +496,23 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
wrapped_key);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
if (NULL == usage_session_) {
|
||||
usage_session_ = new CdmSession(NULL);
|
||||
CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::Unprovision: unable to initialize device files");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!handle.DeleteAllFiles()) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete files");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
usage_session_.reset(new CdmSession(NULL));
|
||||
|
||||
CdmResponseType status = usage_session_->Init();
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: session init error");
|
||||
@@ -523,35 +537,32 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
|
||||
}
|
||||
|
||||
std::string server_url;
|
||||
// rate limit secure stop messages based on minimum nonce
|
||||
// table size per session
|
||||
usage_info->resize(license_info.size() >= kMinNoncesPerSession - 1
|
||||
? kMinNoncesPerSession - 1
|
||||
: license_info.size());
|
||||
for (size_t i = 0; i < usage_info->size(); ++i) {
|
||||
status = usage_session_->RestoreUsageSession(license_info[i].first,
|
||||
license_info[i].second);
|
||||
usage_info->resize(kUsageReportsPerRequest);
|
||||
|
||||
uint32_t index = rand() % license_info.size();
|
||||
status = usage_session_->RestoreUsageSession(license_info[index].first,
|
||||
license_info[index].second);
|
||||
if (KEY_ADDED != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: restore usage session error: %ld",
|
||||
status);
|
||||
LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld",
|
||||
index, status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
status = usage_session_->GenerateReleaseRequest(&(*usage_info)[i],
|
||||
&server_url);
|
||||
|
||||
status = usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld",
|
||||
status);
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseUsageInfo(
|
||||
const CdmUsageInfoReleaseMessage& message) {
|
||||
if (NULL == usage_session_) {
|
||||
if (NULL == usage_session_.get()) {
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: cdm session not initialized");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -559,9 +570,8 @@ CdmResponseType CdmEngine::ReleaseUsageInfo(
|
||||
CdmResponseType status = usage_session_->ReleaseKey(message);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::ReleaseUsageInfo: release key error: %ld", status);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::Decrypt(
|
||||
@@ -588,23 +598,23 @@ CdmResponseType CdmEngine::Decrypt(
|
||||
LOGE("CdmEngine::Decrypt: no dest decrypt buffer");
|
||||
return KEY_ERROR;
|
||||
} // else we must be level 1 direct and we don't need to return a buffer.
|
||||
// TODO:(eschacker) look at renaming Properties::oem_crypto_use_fifo()
|
||||
// to something like Properties::oem_crypto_use_direct_rendering().
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter;
|
||||
if (session_id.empty()) {
|
||||
if (!Properties::decrypt_with_empty_session_support()) return KEY_ERROR;
|
||||
|
||||
// Loop through the sessions to find the session containing the key_id.
|
||||
for (iter = sessions_.begin(); iter != sessions_.end(); ++iter) {
|
||||
if (iter->second->IsKeyLoaded(*parameters.key_id)) break;
|
||||
if (iter->second->IsKeyLoaded(*parameters.key_id)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
iter = sessions_.find(session_id);
|
||||
}
|
||||
if (iter == sessions_.end()) {
|
||||
LOGE("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str());
|
||||
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
|
||||
session_id.c_str(),
|
||||
session_id.size());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -679,11 +689,6 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
|
||||
return (key_system.find("widevine") != std::string::npos);
|
||||
}
|
||||
|
||||
bool CdmEngine::CancelSessions() {
|
||||
// TODO(gmorgan) Implement CancelSessions()
|
||||
return true;
|
||||
}
|
||||
|
||||
void CdmEngine::OnTimerEvent() {
|
||||
Clock clock;
|
||||
uint64_t current_time = clock.GetCurrentTime();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
// Author: jfore@google.com (Jeff Fore), rkuroiwa@google.com (Rintaro Kuroiwa)
|
||||
|
||||
#include "cdm_session.h"
|
||||
|
||||
@@ -30,11 +29,10 @@ CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set)
|
||||
: session_id_(GenerateSessionId()),
|
||||
crypto_session_(NULL),
|
||||
license_received_(false),
|
||||
reinitialize_session_(false),
|
||||
is_offline_(false),
|
||||
is_release_(false),
|
||||
is_usage_update_needed_(false),
|
||||
is_certificate_loaded_(false) {
|
||||
is_initial_decryption_(true) {
|
||||
if (cdm_client_property_set) {
|
||||
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
|
||||
}
|
||||
@@ -51,8 +49,10 @@ CdmResponseType CdmSession::Init() {
|
||||
std::string token;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
DeviceFiles handle;
|
||||
std::string wrapped_key;
|
||||
if (!handle.Init(session.get()->GetSecurityLevel()) ||
|
||||
!handle.RetrieveCertificate(&token, &wrapped_key_)) {
|
||||
!handle.RetrieveCertificate(&token, &wrapped_key) ||
|
||||
!session->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
} else {
|
||||
@@ -64,7 +64,7 @@ CdmResponseType CdmSession::Init() {
|
||||
|
||||
crypto_session_.reset(session.release());
|
||||
license_received_ = false;
|
||||
reinitialize_session_ = false;
|
||||
is_initial_decryption_ = true;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -94,15 +94,6 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (is_certificate_loaded_ ||
|
||||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
|
||||
is_certificate_loaded_ = true;
|
||||
} else {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
|
||||
if (!license_parser_.RestoreOfflineLicense(key_request_, key_response_,
|
||||
offline_key_renewal_response_)) {
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -120,15 +111,6 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
|
||||
key_request_ = key_request;
|
||||
key_response_ = key_response;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (is_certificate_loaded_ ||
|
||||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
|
||||
is_certificate_loaded_ = true;
|
||||
} else {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
|
||||
if (!license_parser_.RestoreUsageLicense(key_request_, key_response_)) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -139,25 +121,10 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
bool CdmSession::VerifySession(const CdmKeySystem& key_system,
|
||||
const InitializationData& init_data) {
|
||||
// TODO(gmorgan): Compare key_system and init_data with value received
|
||||
// during session startup - they should be the same.
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (reinitialize_session_) {
|
||||
CdmResponseType sts = Init();
|
||||
if (sts != NO_ERROR) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Reinitialization failed");
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
|
||||
if (crypto_session_.get() == NULL) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -181,9 +148,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
if (is_release_) {
|
||||
return GenerateReleaseRequest(key_request, server_url);
|
||||
} else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request()
|
||||
? UNKNOWN_ERROR
|
||||
: GenerateRenewalRequest(key_request, server_url);
|
||||
return GenerateRenewalRequest(key_request, server_url);
|
||||
} else {
|
||||
if (!init_data.is_supported()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
|
||||
@@ -195,16 +160,6 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (is_certificate_loaded_ ||
|
||||
crypto_session_->LoadCertificatePrivateKey(wrapped_key_)) {
|
||||
is_certificate_loaded_ = true;
|
||||
} else {
|
||||
reinitialize_session_ = true;
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
}
|
||||
|
||||
if (!license_parser_.PrepareKeyRequest(init_data, license_type,
|
||||
app_parameters, session_id_,
|
||||
key_request, server_url)) {
|
||||
@@ -235,11 +190,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
}
|
||||
|
||||
if (is_release_) {
|
||||
return ReleaseKey(key_response);
|
||||
CdmResponseType sts = ReleaseKey(key_response);
|
||||
return (NO_ERROR == sts) ? KEY_ADDED : sts;
|
||||
} else if (license_received_) { // renewal
|
||||
return Properties::require_explicit_renew_request()
|
||||
? UNKNOWN_ERROR
|
||||
: RenewKey(key_response);
|
||||
return RenewKey(key_response);
|
||||
} else {
|
||||
CdmResponseType sts = license_parser_.HandleKeyResponse(key_response);
|
||||
|
||||
@@ -313,7 +267,6 @@ CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
|
||||
// CancelKeyRequest() - Cancel session.
|
||||
CdmResponseType CdmSession::CancelKeyRequest() {
|
||||
// TODO(gmorgan): cancel and clean up session
|
||||
crypto_session_->Close();
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -324,17 +277,12 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
CdmResponseType status = crypto_session_->Decrypt(params);
|
||||
// TODO(rfrias): Remove after support for OEMCrypto_ERROR_KEY_EXPIRED is in
|
||||
if (UNKNOWN_ERROR == status) {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
if (policy_engine_.IsLicenseDurationExpired(current_time) ||
|
||||
policy_engine_.IsPlaybackDurationExpired(current_time)) {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
if (NO_ERROR == status) {
|
||||
if (is_initial_decryption_) {
|
||||
policy_engine_.BeginDecryption();
|
||||
is_initial_decryption_ = false;
|
||||
}
|
||||
if (!is_usage_update_needed_) {
|
||||
is_usage_update_needed_ =
|
||||
!license_parser_.provider_session_token().empty();
|
||||
@@ -390,7 +338,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false,
|
||||
key_response);
|
||||
if (NO_ERROR != sts)
|
||||
if (KEY_ADDED != sts)
|
||||
return sts;
|
||||
|
||||
if (is_offline_ || !license_parser_.provider_session_token().empty()) {
|
||||
@@ -405,8 +353,6 @@ bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
|
||||
|
||||
CdmSessionId CdmSession::GenerateSessionId() {
|
||||
static int session_num = 1;
|
||||
// TODO(rkuroiwa): Want this to be unique. Probably doing Hash(time+init_data)
|
||||
// to get something that is reasonably unique.
|
||||
return SESSION_ID_PREFIX + IntToString(++session_num);
|
||||
}
|
||||
|
||||
@@ -539,8 +485,9 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
}
|
||||
|
||||
SecurityLevel CdmSession::GetRequestedSecurityLevel() {
|
||||
if (Properties::GetSecurityLevel(session_id_)
|
||||
.compare(QUERY_VALUE_SECURITY_LEVEL_L3) == 0) {
|
||||
std::string security_level;
|
||||
if (Properties::GetSecurityLevel(session_id_, &security_level) &&
|
||||
security_level == QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
return kLevel3;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace {
|
||||
|
||||
// URL for Google Provisioning Server.
|
||||
// This server supplies the certificate that is needed
|
||||
// The provisioning server supplies the certificate that is needed
|
||||
// to communicate with the License Server.
|
||||
const std::string kProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
@@ -28,10 +28,9 @@ using video_widevine_server::sdk::ProvisioningResponse;
|
||||
using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
|
||||
/*
|
||||
* This function converts SignedProvisioningRequest into base64 string.
|
||||
* It then wraps it in JSON format expected by the Apiary frontend.
|
||||
* Apiary requires the base64 encoding to replace '+' with minus '-',
|
||||
* and '/' with underscore '_'; opposite to stubby's.
|
||||
* This function converts SignedProvisioningRequest into base64 string. It then
|
||||
* wraps it in JSON format expected by the frontend. This server requires a
|
||||
* "web-safe" base 64 encoding, where '+' becomes '-' and '/' becomes '_'.
|
||||
*
|
||||
* Returns the JSON formated string in *request. The JSON string will be
|
||||
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
|
||||
|
||||
@@ -159,6 +159,25 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetApiVersion(uint32_t* version) {
|
||||
if (!version) {
|
||||
LOGE("CryptoSession::GetApiVersion: No buffer passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
CdmSecurityLevel level = GetSecurityLevel();
|
||||
SecurityLevel security_level = kLevelDefault;
|
||||
if (kSecurityLevelL3 == level) security_level = kLevel3;
|
||||
|
||||
LOGV("CryptoSession::GetApiVersion: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
*version = OEMCrypto_APIVersion(security_level);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetSystemId(uint32_t* system_id) {
|
||||
if (!system_id) {
|
||||
LOGE("CryptoSession::GetSystemId : No buffer passed to method.");
|
||||
@@ -270,9 +289,9 @@ bool CryptoSession::PrepareRequest(const std::string& message,
|
||||
if (!Properties::use_certificates_as_identification() || is_provisioning) {
|
||||
if (!GenerateDerivedKeys(message)) return false;
|
||||
|
||||
if (!GenerateSignature(message, false, signature)) return false;
|
||||
if (!GenerateSignature(message, signature)) return false;
|
||||
} else {
|
||||
if (!GenerateSignature(message, true, signature)) return false;
|
||||
if (!GenerateRsaSignature(message, signature)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -290,7 +309,7 @@ bool CryptoSession::PrepareRenewalRequest(const std::string& message,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GenerateSignature(message, false, signature)) {
|
||||
if (!GenerateSignature(message, signature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -508,55 +527,77 @@ bool CryptoSession::GenerateDerivedKeys(const std::string& message,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GenerateSignature(const std::string& message, bool use_rsa,
|
||||
bool CryptoSession::GenerateSignature(const std::string& message,
|
||||
std::string* signature) {
|
||||
LOGV("GenerateSignature: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) return false;
|
||||
|
||||
size_t length = 0;
|
||||
OEMCryptoResult sts = OEMCrypto_SUCCESS;
|
||||
if (use_rsa) {
|
||||
sts = OEMCrypto_GenerateRSASignature(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(), NULL, &length, kSign_RSASSA_PSS);
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
length = kSignatureSize;
|
||||
// TODO(gmorgan,kqyang): Use OEMCrypto_GenerateSignature to determine
|
||||
// length after marvell fixes their implementation.
|
||||
/*
|
||||
sts = OEMCrypto_GenerateSignature(
|
||||
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
|
||||
message.size(), NULL, &length);
|
||||
*/
|
||||
}
|
||||
|
||||
signature->resize(length);
|
||||
|
||||
if (use_rsa) {
|
||||
sts = OEMCrypto_GenerateRSASignature(
|
||||
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, kSign_RSASSA_PSS);
|
||||
} else {
|
||||
&length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retry with proper-sized signature buffer
|
||||
signature->resize(length);
|
||||
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) {
|
||||
LOGD("GenerateSignature: OEMCrypto_GenerateSignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(fredgc): b/8878371
|
||||
// remove in K, when L1 library reports correct length.
|
||||
// Trim signature buffer
|
||||
signature->resize(length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CryptoSession::GenerateRsaSignature(const std::string& message,
|
||||
std::string* signature) {
|
||||
LOGV("GenerateRsaSignature: id=%ld", (uint32_t)oec_session_id_);
|
||||
if (!signature) return false;
|
||||
|
||||
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) {
|
||||
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retry with proper-sized signature buffer
|
||||
signature->resize(length);
|
||||
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) {
|
||||
LOGD("GenerateRsaSignature: OEMCrypto_GenerateRSASignature err=%d", sts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Trim signature buffer
|
||||
signature->resize(length);
|
||||
|
||||
return true;
|
||||
@@ -588,7 +629,8 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
buffer_descriptor.buffer.clear.address =
|
||||
static_cast<uint8_t*>(params.decrypt_buffer) +
|
||||
params.decrypt_buffer_offset;
|
||||
buffer_descriptor.buffer.clear.max_length = params.decrypt_buffer_length;
|
||||
buffer_descriptor.buffer.clear.max_length =
|
||||
params.decrypt_buffer_length - params.decrypt_buffer_offset;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
buffer_descriptor.buffer.secure.handle = params.decrypt_buffer;
|
||||
@@ -768,8 +810,6 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
|
||||
reinterpret_cast<uint8_t*>(&(*wrapped_rsa_key)[0]),
|
||||
&wrapped_rsa_key_length);
|
||||
|
||||
// TODO(fredgc): b/8878371
|
||||
// remove in K, when L1 library reports correct length.
|
||||
wrapped_rsa_key->resize(wrapped_rsa_key_length);
|
||||
|
||||
if (OEMCrypto_SUCCESS != status) {
|
||||
|
||||
@@ -2,13 +2,20 @@
|
||||
|
||||
#include "device_files.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <CommonCrypto/CommonDigest.h>
|
||||
# define SHA256 CC_SHA256
|
||||
# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
|
||||
#else
|
||||
# include <openssl/sha.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "device_files.pb.h"
|
||||
#include "file_store.h"
|
||||
#include "log.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
|
||||
@@ -22,6 +29,7 @@ using video_widevine_client::sdk::UsageInfo;
|
||||
using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
const char kUsageInfoFileName[] = "usage.bin";
|
||||
const char kLicenseFileNameExt[] = ".lic";
|
||||
@@ -31,6 +39,18 @@ const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
|
||||
size_t kSecurityLevelPathCompatibilityExclusionListSize =
|
||||
sizeof(kSecurityLevelPathCompatibilityExclusionList) /
|
||||
sizeof(*kSecurityLevelPathCompatibilityExclusionList);
|
||||
|
||||
bool Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash) return false;
|
||||
hash->resize(SHA256_DIGEST_LENGTH);
|
||||
|
||||
const unsigned char* input =
|
||||
reinterpret_cast<const unsigned char*>(data.data());
|
||||
unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
|
||||
SHA256(input, data.size(), output);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -343,18 +363,13 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UsageInfo* updated_info = file.mutable_usage_info();
|
||||
UsageInfo info(*(const_cast<const UsageInfo*>(updated_info)));
|
||||
updated_info->clear_sessions();
|
||||
UsageInfo* usage_info = file.mutable_usage_info();
|
||||
int index = 0;
|
||||
bool found = false;
|
||||
for (int i = 0; i < info.sessions_size(); ++i) {
|
||||
if (info.sessions(i).token().compare(provider_session_token) == 0) {
|
||||
for (; index < usage_info->sessions_size(); ++index) {
|
||||
if (usage_info->sessions(index).token().compare(provider_session_token) == 0) {
|
||||
found = true;
|
||||
} else {
|
||||
updated_info->add_sessions()->set_token(info.sessions(i).token());
|
||||
updated_info->add_sessions()->set_license_request(
|
||||
info.sessions(i).license_request());
|
||||
updated_info->add_sessions()->set_license(info.sessions(i).license());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +379,13 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
google::protobuf::RepeatedPtrField<UsageInfo_ProviderSession>* sessions =
|
||||
usage_info->mutable_sessions();
|
||||
if (index < usage_info->sessions_size() - 1) {
|
||||
sessions->SwapElements(index, usage_info->sessions_size() - 1);
|
||||
}
|
||||
sessions->RemoveLast();
|
||||
|
||||
file.SerializeToString(&serialized_file);
|
||||
return StoreFile(kUsageInfoFileName, serialized_file);
|
||||
}
|
||||
@@ -430,17 +452,6 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector<
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::Hash(const std::string& data, std::string* hash) {
|
||||
if (!hash) return false;
|
||||
|
||||
hash->resize(SHA256_DIGEST_LENGTH);
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
SHA256_Update(&sha256, data.data(), data.size());
|
||||
SHA256_Final(reinterpret_cast<unsigned char*>(&(*hash)[0]), &sha256);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::StoreFile(const char* name,
|
||||
const std::string& serialized_file) {
|
||||
if (!file_) {
|
||||
|
||||
@@ -47,7 +47,7 @@ message File {
|
||||
enum FileType {
|
||||
DEVICE_CERTIFICATE = 1;
|
||||
LICENSE = 2;
|
||||
USAGE_INFO = 2;
|
||||
USAGE_INFO = 3;
|
||||
}
|
||||
|
||||
enum FileVersion {
|
||||
@@ -63,5 +63,6 @@ message File {
|
||||
|
||||
message HashedFile {
|
||||
optional bytes file = 1;
|
||||
// A raw (not hex-encoded) SHA256, taken over the bytes of 'file'.
|
||||
optional bytes hash = 2;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ std::string kDeviceNameKey = "device_name";
|
||||
std::string kProductNameKey = "product_name";
|
||||
std::string kBuildInfoKey = "build_info";
|
||||
std::string kDeviceIdKey = "device_id";
|
||||
std::string kOemCryptoApiVersion = "oemcrypto_api_version";
|
||||
const unsigned char kServiceCertificateCAPublicKey[] = {
|
||||
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81,
|
||||
0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03,
|
||||
@@ -193,10 +194,10 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
}
|
||||
|
||||
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id);
|
||||
std::vector<uint8_t> cert = Properties::GetServiceCertificate(session_id);
|
||||
std::string serialized_service_certificate(cert.begin(), cert.end());
|
||||
|
||||
if (serialized_service_certificate.empty())
|
||||
std::string serialized_service_certificate;
|
||||
if (!Properties::GetServiceCertificate(session_id,
|
||||
&serialized_service_certificate) ||
|
||||
serialized_service_certificate.empty())
|
||||
serialized_service_certificate = service_certificate_;
|
||||
|
||||
if (privacy_mode_enabled && serialized_service_certificate.empty()) {
|
||||
@@ -254,12 +255,17 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
client_info->set_name(kBuildInfoKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
|
||||
if (session_->GetDeviceUniqueId(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
uint32_t version = 0;
|
||||
if (session_->GetApiVersion(&version)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kOemCryptoApiVersion);
|
||||
client_info->set_value(UintToString(version));
|
||||
}
|
||||
|
||||
if (privacy_mode_enabled) {
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
@@ -353,7 +359,7 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(jfore): The time field will be updated once the cdm wrapper
|
||||
// The time field will be updated once the cdm wrapper
|
||||
// has been updated to pass us in the time.
|
||||
license_request.set_request_time(0);
|
||||
|
||||
@@ -432,7 +438,8 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
|
||||
if (!is_renewal) {
|
||||
if (license_id.has_provider_session_token()) {
|
||||
std::string usage_report;
|
||||
if (!session_->GenerateUsageReport(license_id.provider_session_token(),
|
||||
if (NO_ERROR !=
|
||||
session_->GenerateUsageReport(license_id.provider_session_token(),
|
||||
&usage_report)) {
|
||||
return false;
|
||||
}
|
||||
@@ -560,7 +567,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
std::string provider_session_token;
|
||||
if (license.id().has_provider_session_token())
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
|
||||
@@ -568,9 +574,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
|
||||
// TODO(kqyang, jfore, gmorgan): change SetLicense function signature to
|
||||
// be able to return true/false to accept/reject the license. (Pending code
|
||||
// merge from Eureka)
|
||||
policy_engine_->SetLicense(license);
|
||||
|
||||
CdmResponseType resp = session_->LoadKeys(signed_response.msg(),
|
||||
@@ -578,7 +581,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
mac_key_iv,
|
||||
mac_key,
|
||||
key_array,
|
||||
provider_session_token);
|
||||
provider_session_token_);
|
||||
|
||||
if (KEY_ADDED == resp) {
|
||||
loaded_keys_.clear();
|
||||
@@ -630,27 +633,23 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (is_renewal) {
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
if (!is_renewal) {
|
||||
if (!license.id().has_provider_session_token()) return KEY_ADDED;
|
||||
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
CdmResponseType status =
|
||||
session_->ReleaseUsageInformation(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
provider_session_token_);
|
||||
return (NO_ERROR == status) ? KEY_ADDED : status;
|
||||
}
|
||||
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
license.policy().renewal_server_url().size() > 0) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (license.id().has_provider_session_token()) {
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
session_->ReleaseUsageInformation(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
provider_session_token_);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kqyang, jfore, gmorgan): change UpdateLicense function signature to
|
||||
// be able to return true/false to accept/reject the license. (Pending code
|
||||
// merge from Eureka)
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
if (!is_renewal) return KEY_ADDED;
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ class Adapter {
|
||||
if (level1_.InstallKeybox(keybox, size) != OEMCrypto_SUCCESS) {
|
||||
LOGE("Could NOT install keybox from %s. Falling Back to L3.",
|
||||
filename.c_str());
|
||||
false;
|
||||
return false;
|
||||
}
|
||||
LOGI("Installed keybox from %s", filename.c_str());
|
||||
return true;
|
||||
|
||||
@@ -76,7 +76,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys,
|
||||
const OEMCrypto_KeyObject* key_array,
|
||||
const uint8_t* pst, size_t pst_length) {
|
||||
return OEMCrypto_LoadKeys_V8(pair.session, message, message_length, signature,
|
||||
return OEMCrypto_LoadKeys_V8(session, message, message_length, signature,
|
||||
signature_length, enc_mac_key_iv, enc_mac_key,
|
||||
num_keys, key_array);
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
#include "policy_engine.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "clock.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "clock.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -59,7 +59,6 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
|
||||
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateInitialPendingUsage:
|
||||
case kLicenseStateCanPlay: {
|
||||
if (IsRenewalDelayExpired(current_time))
|
||||
renewal_needed = true;
|
||||
@@ -115,11 +114,6 @@ void PolicyEngine::UpdateLicense(
|
||||
|
||||
policy_.MergeFrom(license.policy());
|
||||
|
||||
if (!policy_.can_play()) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
return;
|
||||
}
|
||||
|
||||
// some basic license validation
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
// license start time needs to be present in the initial response
|
||||
@@ -158,35 +152,23 @@ void PolicyEngine::UpdateLicense(
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
if (Properties::begin_license_usage_when_received())
|
||||
playback_start_time_ = current_time;
|
||||
if (!policy_.can_play()) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsLicenseDurationExpired(current_time)) return;
|
||||
if (IsPlaybackDurationExpired(current_time)) return;
|
||||
|
||||
// Update state
|
||||
if (Properties::begin_license_usage_when_received()) {
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
license_state_ = kLicenseStateInitialPendingUsage;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::BeginDecryption() {
|
||||
if ((playback_start_time_ == 0) &&
|
||||
(!Properties::begin_license_usage_when_received())) {
|
||||
if (playback_start_time_ == 0) {
|
||||
switch (license_state_) {
|
||||
case kLicenseStateInitialPendingUsage:
|
||||
case kLicenseStateCanPlay:
|
||||
case kLicenseStateNeedRenewal:
|
||||
case kLicenseStateWaitingLicenseUpdate:
|
||||
playback_start_time_ = clock_->GetCurrentTime();
|
||||
@@ -194,12 +176,7 @@ void PolicyEngine::BeginDecryption() {
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
break;
|
||||
case kLicenseStateCanPlay:
|
||||
case kLicenseStateInitial:
|
||||
case kLicenseStateExpired:
|
||||
default:
|
||||
@@ -224,17 +201,10 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*key_info)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ?
|
||||
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
int64_t remaining_time = policy_max_duration_seconds_ +
|
||||
license_received_time_ - current_time;
|
||||
if (remaining_time < 0)
|
||||
remaining_time = 0;
|
||||
ss << remaining_time;
|
||||
ss << GetLicenseDurationRemaining(current_time);
|
||||
(*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
|
||||
remaining_time = policy_.playback_duration_seconds() + playback_start_time_ -
|
||||
current_time;
|
||||
if (remaining_time < 0)
|
||||
remaining_time = 0;
|
||||
ss << remaining_time;
|
||||
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();
|
||||
|
||||
@@ -255,6 +225,16 @@ bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
|
||||
current_time;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_max_duration_seconds_) return LLONG_MAX;
|
||||
|
||||
int64_t remaining_time = policy_max_duration_seconds_
|
||||
+ license_received_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0) remaining_time = 0;
|
||||
return remaining_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
|
||||
return (policy_.playback_duration_seconds() > 0) &&
|
||||
playback_start_time_ &&
|
||||
@@ -262,6 +242,17 @@ bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
|
||||
current_time;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
|
||||
if (0 == policy_.playback_duration_seconds()) return LLONG_MAX;
|
||||
if (0 == playback_start_time_) return policy_.playback_duration_seconds();
|
||||
|
||||
int64_t remaining_time = policy_.playback_duration_seconds()
|
||||
+ playback_start_time_ - current_time;
|
||||
|
||||
if (remaining_time < 0) remaining_time = 0;
|
||||
return remaining_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
|
||||
return policy_.can_renew() &&
|
||||
(policy_.renewal_delay_seconds() > 0) &&
|
||||
|
||||
@@ -7,38 +7,60 @@
|
||||
|
||||
#include "privacy_crypto.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/err.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/sha.h"
|
||||
|
||||
namespace {
|
||||
const int kPssSaltLength = 20;
|
||||
const int kRsaPkcs1OaepPaddingLength = 41;
|
||||
|
||||
RSA* GetKey(const std::string& serialized_key) {
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
||||
serialized_key.size());
|
||||
if (bio == NULL) {
|
||||
LOGE("GetKey: BIO_new_mem_buf returned NULL");
|
||||
return NULL;
|
||||
}
|
||||
RSA* key = d2i_RSAPublicKey_bio(bio, NULL);
|
||||
BIO_free(bio);
|
||||
|
||||
if (key == NULL) {
|
||||
LOGE("GetKey: RSA key deserialization failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void FreeKey(RSA* key) {
|
||||
if (key != NULL) {
|
||||
RSA_free(key);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
AesCbcKey::AesCbcKey() {}
|
||||
|
||||
AesCbcKey::~AesCbcKey() {}
|
||||
|
||||
bool AesCbcKey::Init(const std::string& key) {
|
||||
if (key.empty()) {
|
||||
LOGE("AesCbcKey::Init: no key provided");
|
||||
return false;
|
||||
}
|
||||
if (key.size() != AES_BLOCK_SIZE) {
|
||||
LOGE("AesCbcKey::Init: unexpected key size: %d", key.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_init(&ctx_);
|
||||
if (EVP_EncryptInit(&ctx_, EVP_aes_128_cbc(),
|
||||
reinterpret_cast<const uint8_t*>(&key[0]), NULL) == 0) {
|
||||
LOGE("AesCbcKey::Init: AES CBC key setup failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
}
|
||||
initialized_ = true;
|
||||
key_ = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,14 +82,16 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
LOGE("AesCbcKey::Encrypt: crypttext destination not provided");
|
||||
return false;
|
||||
}
|
||||
if (!initialized_) {
|
||||
if (key_.empty()) {
|
||||
LOGE("AesCbcKey::Encrypt: AES key not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_EncryptInit(&ctx_, NULL, NULL,
|
||||
reinterpret_cast<const uint8_t*>(iv->data())) == 0) {
|
||||
LOGE("AesCbcKey::Encrypt: AES CBC iv setup failure: %s",
|
||||
EVP_CIPHER_CTX ctx;
|
||||
if (EVP_EncryptInit(&ctx, EVP_aes_128_cbc(),
|
||||
reinterpret_cast<uint8_t*>(&key_[0]),
|
||||
reinterpret_cast<uint8_t*>(&(*iv)[0])) == 0) {
|
||||
LOGE("AesCbcKey::Encrypt: AES CBC setup failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
}
|
||||
@@ -75,7 +99,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
out->resize(in.size() + AES_BLOCK_SIZE);
|
||||
int out_length = out->size();
|
||||
if (EVP_EncryptUpdate(
|
||||
&ctx_, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
|
||||
&ctx, reinterpret_cast<uint8_t*>(&(*out)[0]), &out_length,
|
||||
reinterpret_cast<uint8_t*>(const_cast<char*>(in.data())),
|
||||
in.size()) == 0) {
|
||||
LOGE("AesCbcKey::Encrypt: encryption failure: %s",
|
||||
@@ -84,7 +108,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
}
|
||||
|
||||
int padding = 0;
|
||||
if (EVP_EncryptFinal(&ctx_, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
|
||||
if (EVP_EncryptFinal(&ctx, reinterpret_cast<uint8_t*>(&(*out)[out_length]),
|
||||
&padding) == 0) {
|
||||
LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
@@ -95,34 +119,17 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
return true;
|
||||
}
|
||||
|
||||
RsaPublicKey::~RsaPublicKey() {
|
||||
if (key_ != NULL) {
|
||||
RSA_free(key_);
|
||||
}
|
||||
}
|
||||
RsaPublicKey::RsaPublicKey() {}
|
||||
|
||||
RsaPublicKey::~RsaPublicKey() {}
|
||||
|
||||
bool RsaPublicKey::Init(const std::string& serialized_key) {
|
||||
|
||||
if (serialized_key.empty()) {
|
||||
LOGE("RsaPublicKey::Init: no serialized key provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
BIO* bio = BIO_new_mem_buf(const_cast<char*>(serialized_key.data()),
|
||||
serialized_key.size());
|
||||
if (bio == NULL) {
|
||||
LOGE("RsaPublicKey::Init: BIO_new_mem_buf returned NULL");
|
||||
return false;
|
||||
}
|
||||
key_ = d2i_RSAPublicKey_bio(bio, NULL);
|
||||
BIO_free(bio);
|
||||
|
||||
if (key_ == NULL) {
|
||||
LOGE("RsaPublicKey::Init: RSA key deserialization failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
serialized_key_ = serialized_key;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,36 +143,46 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
LOGE("RsaPublicKey::Encrypt: no encrypt message buffer provided");
|
||||
return false;
|
||||
}
|
||||
if (key_ == NULL) {
|
||||
if (serialized_key_.empty()) {
|
||||
LOGE("RsaPublicKey::Encrypt: RSA key not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
int rsa_size = RSA_size(key_);
|
||||
RSA* key = GetKey(serialized_key_);
|
||||
if (key == NULL) {
|
||||
// Error already logged by GetKey.
|
||||
return false;
|
||||
}
|
||||
|
||||
int rsa_size = RSA_size(key);
|
||||
if (static_cast<int>(clear_message.size()) >
|
||||
rsa_size - kRsaPkcs1OaepPaddingLength) {
|
||||
LOGE("RsaPublicKey::Encrypt: message too large to be encrypted (actual %d",
|
||||
" max allowed %d)", clear_message.size(),
|
||||
rsa_size - kRsaPkcs1OaepPaddingLength);
|
||||
FreeKey(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
encrypted_message->assign(rsa_size, 0);
|
||||
if (RSA_public_encrypt(
|
||||
clear_message.size(),
|
||||
const_cast<unsigned char*>(
|
||||
reinterpret_cast<const unsigned char*>(clear_message.data())),
|
||||
reinterpret_cast<unsigned char*>(&(*encrypted_message)[0]), key_,
|
||||
reinterpret_cast<unsigned char*>(&(*encrypted_message)[0]), key,
|
||||
RSA_PKCS1_OAEP_PADDING) != rsa_size) {
|
||||
LOGE("RsaPublicKey::Encrypt: encrypt failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
FreeKey(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
const std::string& signature) {
|
||||
if (key_ == NULL) {
|
||||
if (serialized_key_.empty()) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA key not initialized");
|
||||
return false;
|
||||
}
|
||||
@@ -173,25 +190,33 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
LOGE("RsaPublicKey::VerifySignature: signed message is empty");
|
||||
return false;
|
||||
}
|
||||
RSA* key = GetKey(serialized_key_);
|
||||
if (key == NULL) {
|
||||
// Error already logged by GetKey.
|
||||
return false;
|
||||
}
|
||||
|
||||
int rsa_size = RSA_size(key_);
|
||||
int rsa_size = RSA_size(key);
|
||||
if (static_cast<int>(signature.size()) != rsa_size) {
|
||||
LOGE(
|
||||
"RsaPublicKey::VerifySignature: message signature is of the wrong "
|
||||
"size (expected %d, actual %d)",
|
||||
rsa_size, signature.size());
|
||||
FreeKey(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decrypt the signature.
|
||||
std::string padded_digest(signature.size(), 0);
|
||||
if (RSA_public_decrypt(
|
||||
signature.size(),
|
||||
const_cast<unsigned char*>(
|
||||
reinterpret_cast<const unsigned char*>(signature.data())),
|
||||
reinterpret_cast<unsigned char*>(&padded_digest[0]), key_,
|
||||
reinterpret_cast<unsigned char*>(&padded_digest[0]), key,
|
||||
RSA_NO_PADDING) != rsa_size) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA public decrypt failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
FreeKey(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -202,12 +227,13 @@ bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
|
||||
// Verify PSS padding.
|
||||
if (RSA_verify_PKCS1_PSS(
|
||||
key_, reinterpret_cast<const unsigned char*>(message_digest.data()),
|
||||
key, reinterpret_cast<const unsigned char*>(message_digest.data()),
|
||||
EVP_sha1(),
|
||||
reinterpret_cast<const unsigned char*>(padded_digest.data()),
|
||||
kPssSaltLength) == 0) {
|
||||
LOGE("RsaPublicKey::VerifySignature: RSA verify failure: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
FreeKey(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
43
libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp
Normal file
43
libwvdrmengine/cdm/core/src/privacy_crypto_dummy.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Dummy version of privacy crypto classes for systems which
|
||||
// can't tolerate OpenSSL as a dependency.
|
||||
//
|
||||
|
||||
#include "privacy_crypto.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
AesCbcKey::AesCbcKey() {}
|
||||
|
||||
AesCbcKey::~AesCbcKey() {}
|
||||
|
||||
bool AesCbcKey::Init(const std::string& key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
|
||||
std::string* iv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RsaPublicKey::RsaPublicKey() {}
|
||||
|
||||
RsaPublicKey::~RsaPublicKey() {}
|
||||
|
||||
bool RsaPublicKey::Init(const std::string& serialized_key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RsaPublicKey::Encrypt(const std::string& clear_message,
|
||||
std::string* encrypted_message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RsaPublicKey::VerifySignature(const std::string& message,
|
||||
const std::string& signature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
@@ -9,27 +9,21 @@ const char* kSecurityLevelDirs[] = {"L1/", "L3/"};
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
bool Properties::begin_license_usage_when_received_;
|
||||
bool Properties::require_explicit_renew_request_;
|
||||
bool Properties::oem_crypto_use_secure_buffers_;
|
||||
bool Properties::oem_crypto_use_fifo_;
|
||||
bool Properties::oem_crypto_use_userspace_buffers_;
|
||||
bool Properties::oem_crypto_require_usage_tables_;
|
||||
bool Properties::use_certificates_as_identification_;
|
||||
bool Properties::decrypt_with_empty_session_support_;
|
||||
bool Properties::security_level_path_backward_compatibility_support_;
|
||||
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;
|
||||
|
||||
void Properties::Init() {
|
||||
begin_license_usage_when_received_ = kPropertyBeginLicenseUsageWhenReceived;
|
||||
require_explicit_renew_request_ = kPropertyRequireExplicitRenewRequest;
|
||||
oem_crypto_use_secure_buffers_ = kPropertyOemCryptoUseSecureBuffers;
|
||||
oem_crypto_use_fifo_ = kPropertyOemCryptoUseFifo;
|
||||
oem_crypto_use_userspace_buffers_ = kPropertyOemCryptoUseUserSpaceBuffers;
|
||||
oem_crypto_require_usage_tables_ = kPropertyOemCryptoRequireUsageTable;
|
||||
use_certificates_as_identification_ =
|
||||
kPropertyUseCertificatesAsIdentification;
|
||||
decrypt_with_empty_session_support_ = kDecryptWithEmptySessionSupport;
|
||||
security_level_path_backward_compatibility_support_ =
|
||||
kSecurityLevelPathBackwardCompatibilitySupport;
|
||||
session_property_set_.reset(new CdmClientPropertySetMap());
|
||||
@@ -66,27 +60,30 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const std::string Properties::GetSecurityLevel(const CdmSessionId& session_id) {
|
||||
bool Properties::GetSecurityLevel(const CdmSessionId& session_id,
|
||||
std::string* security_level) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
LOGE("Properties::GetSecurityLevel: cannot find property set for %s",
|
||||
session_id.c_str());
|
||||
return "";
|
||||
return false;
|
||||
}
|
||||
return property_set->security_level();
|
||||
*security_level = property_set->security_level();
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> Properties::GetServiceCertificate(
|
||||
const CdmSessionId& session_id) {
|
||||
bool Properties::GetServiceCertificate(const CdmSessionId& session_id,
|
||||
std::string* service_certificate) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
LOGE("Properties::GetServiceCertificate: cannot find property set for %s",
|
||||
session_id.c_str());
|
||||
return std::vector<uint8_t>();
|
||||
return false;
|
||||
}
|
||||
return property_set->service_certificate();
|
||||
*service_certificate = property_set->service_certificate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
|
||||
|
||||
@@ -77,11 +77,11 @@ std::string b2a_hex(const std::string& byte) {
|
||||
|
||||
// Filename-friendly base64 encoding (RFC4648), commonly referred to
|
||||
// as Base64WebSafeEncode.
|
||||
// This is the encoding required to interface with the provisioning
|
||||
// server's Apiary interface as well as for certain license server
|
||||
// transactions. It is also used for logging certain strings.
|
||||
// The difference between web safe encoding vs regular encoding is that
|
||||
// the web safe version replaces '+' with '-' and '/' with '_'.
|
||||
//
|
||||
// This is the encoding required to interface with the provisioning server, as
|
||||
// well as for certain license server transactions. It is also used for logging
|
||||
// certain strings. The difference between web safe encoding vs regular encoding
|
||||
// is that the web safe version replaces '+' with '-' and '/' with '_'.
|
||||
std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
|
||||
if (bin_input.empty()) {
|
||||
return std::string();
|
||||
|
||||
@@ -33,13 +33,13 @@ wvcdm::KeyId g_key_id_pssh;
|
||||
wvcdm::KeyId g_key_id_unwrapped;
|
||||
wvcdm::CdmKeySystem g_key_system;
|
||||
std::string g_license_server;
|
||||
std::string g_port;
|
||||
wvcdm::KeyId g_wrong_key_id;
|
||||
int g_use_full_path = 0; // cannot use boolean in getopt_long
|
||||
|
||||
// This is the RSA certificate from the provisioning server. The client
|
||||
// sends this certificate to a license server as verification in the
|
||||
// provisioning test case.
|
||||
// This is an RSA certificate message from the provisioning server.
|
||||
// The client sends this certificate to a license server for device
|
||||
// authentication by the license server.
|
||||
// This certificate is used to test the CDM engine's provisioning
|
||||
// response handling.
|
||||
static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
|
||||
"{\"signedResponse\": {"
|
||||
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
|
||||
@@ -136,16 +136,16 @@ class WvCdmEngineTest : public testing::Test {
|
||||
std::string GetKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
// Use secure connection and chunk transfer coding.
|
||||
UrlRequest url_request(server_url + client_auth, g_port, true, true);
|
||||
UrlRequest url_request(server_url + client_auth);
|
||||
if (!url_request.is_connected()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
url_request.PostRequest(key_msg_);
|
||||
std::string response;
|
||||
int resp_bytes = url_request.GetResponse(&response);
|
||||
LOGD("response:\r\n%s", response.c_str());
|
||||
LOGD("end %d bytes response dump", resp_bytes);
|
||||
bool ok = url_request.GetResponse(&response);
|
||||
LOGD("response: %s\n", response.c_str());
|
||||
EXPECT_TRUE(ok);
|
||||
|
||||
int status_code = url_request.GetStatusCode(response);
|
||||
EXPECT_EQ(kHttpOk, status_code);
|
||||
@@ -252,41 +252,38 @@ int main(int argc, char **argv) {
|
||||
// The following variables are configurable through command line options.
|
||||
g_license_server.assign(config.license_server());
|
||||
g_key_id_pssh.assign(config.key_id());
|
||||
g_port.assign(config.port());
|
||||
std::string license_server(g_license_server);
|
||||
|
||||
int show_usage = 0;
|
||||
static const struct option long_options[] = {
|
||||
{ "use_full_path", no_argument, &g_use_full_path, 0 },
|
||||
{ "keyid", required_argument, NULL, 'k' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "server", required_argument, NULL, 's' },
|
||||
{ "vmodule", required_argument, NULL, 0 },
|
||||
{ "v", required_argument, NULL, 0 },
|
||||
{ NULL, 0, NULL, '\0' }
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
int opt = 0;
|
||||
while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "k:s:v", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'k': {
|
||||
g_key_id_pssh.clear();
|
||||
g_key_id_pssh.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
g_port.clear();
|
||||
g_port.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
g_license_server.clear();
|
||||
g_license_server.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
g_use_full_path = 1;
|
||||
case 'v': {
|
||||
// This option _may_ have already been consumed by wvcdm::InitLogging()
|
||||
// above, depending on the platform-specific logging implementation.
|
||||
// We only tell getopt about it so that it is not an error. We ignore
|
||||
// the option here when seen.
|
||||
// TODO: Stop passing argv to InitLogging, and instead set the log
|
||||
// level here through the logging API. We should keep all command-line
|
||||
// parsing at the application level, rather than split between various
|
||||
// apps and various platform-specific logging implementations.
|
||||
break;
|
||||
}
|
||||
case '?': {
|
||||
@@ -302,11 +299,6 @@ int main(int argc, char **argv) {
|
||||
std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl;
|
||||
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --port=<connection port>";
|
||||
std::cout << "specifies the port number, in decimal format" << std::endl;
|
||||
std::cout << std::setw(30) << std::left << " ";
|
||||
std::cout << "default: " << g_port << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --server=<server_url>";
|
||||
std::cout << "configure the license server url, please include http[s] in the url" << std::endl;
|
||||
std::cout << std::setw(30) << std::left << " ";
|
||||
@@ -316,21 +308,15 @@ int main(int argc, char **argv) {
|
||||
std::cout << "configure the key id or pssh, in hex format" << std::endl;
|
||||
std::cout << std::setw(30) << std::left << " default keyid:";
|
||||
std::cout << g_key_id_pssh << std::endl;
|
||||
|
||||
std::cout << std::setw(30) << std::left << " --use_full_path";
|
||||
std::cout << "specify server url is not a proxy server" << std::endl;
|
||||
std::cout << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "Server: " << g_license_server << std::endl;
|
||||
std::cout << "Port: " << g_port << std::endl;
|
||||
std::cout << "KeyID: " << g_key_id_pssh << std::endl << std::endl;
|
||||
|
||||
g_key_id_pssh = wvcdm::a2bs_hex(g_key_id_pssh);
|
||||
config.set_license_server(g_license_server);
|
||||
config.set_port(g_port);
|
||||
config.set_key_id(g_key_id_pssh);
|
||||
|
||||
// Extract the key ID from the PSSH box.
|
||||
|
||||
@@ -5,42 +5,50 @@
|
||||
namespace {
|
||||
const std::string kWidevineKeySystem = "com.widevine.alpha";
|
||||
|
||||
// Youtube Content Protection license server data
|
||||
const std::string kYtCpLicenseServer =
|
||||
// Content Protection license server data
|
||||
const std::string kCpLicenseServer =
|
||||
"http://wv-ref-eme-player.appspot.com/proxy";
|
||||
const std::string kYtCpClientAuth = "";
|
||||
const std::string kYtCpKeyId =
|
||||
"000000427073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
||||
"08011a0d7769646576696e655f7465737422" // pssh data (streaming)
|
||||
"0f73747265616d696e675f636c697031";
|
||||
|
||||
const std::string kYtCpOfflineKeyId =
|
||||
"000000407073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id
|
||||
"08011a0d7769646576696e655f7465737422" //pssh data (offline)
|
||||
"0d6f66666c696e655f636c697031";
|
||||
|
||||
// Youtube license server data
|
||||
const std::string kYtLicenseServer =
|
||||
"https://www.youtube.com/api/drm/"
|
||||
"widevine?video_id=03681262dc412c06&source=YOUTUBE";
|
||||
const std::string kYtClientAuth = "";
|
||||
const std::string kYtKeyId =
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
||||
"0801121093789920E8D6520098577DF8F2DD5546"; // pssh data
|
||||
const std::string kCpClientAuth = "";
|
||||
const std::string kCpKeyId =
|
||||
"00000042" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000022" // pssh data size
|
||||
// pssh data:
|
||||
"08011a0d7769646576696e655f746573"
|
||||
"74220f73747265616d696e675f636c69"
|
||||
"7031";
|
||||
const std::string kCpOfflineKeyId =
|
||||
"00000040" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000020" // pssh data size
|
||||
// pssh data:
|
||||
"08011a0d7769646576696e655f746573"
|
||||
"74220d6f66666c696e655f636c697031";
|
||||
|
||||
// Google Play license server data
|
||||
const std::string kGpLicenseServer =
|
||||
"https://jmt17.google.com/video/license/GetCencLicense";
|
||||
|
||||
// Test client authorization string.
|
||||
// NOTE: Append a userdata attribute to place a unique marker that the
|
||||
// server team can use to track down specific requests during debugging
|
||||
// e.g., "<existing-client-auth-string>&userdata=<your-ldap>.<your-tag>"
|
||||
// "<existing-client-auth-string>&userdata=jbmr2.dev"
|
||||
const std::string kGpClientAuth =
|
||||
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
|
||||
const std::string kGpKeyId =
|
||||
"00000034" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000014" // pssh data size
|
||||
// pssh data:
|
||||
"08011210e02562e04cd55351b14b3d74"
|
||||
"8d36ed8e";
|
||||
const std::string kGpOfflineKeyId = kGpKeyId;
|
||||
|
||||
const std::string kGpClientOfflineQueryParameters =
|
||||
"&offline=true";
|
||||
@@ -49,33 +57,30 @@ const std::string kGpClientOfflineRenewalQueryParameters =
|
||||
const std::string kGpClientOfflineReleaseQueryParameters =
|
||||
"&offline=true&release=true";
|
||||
|
||||
const std::string kGpKeyId =
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
|
||||
"08011210e02562e04cd55351b14b3d748d36ed8e"; // pssh data
|
||||
|
||||
// An invalid key id, expected to fail
|
||||
const std::string kWrongKeyId =
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
||||
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
|
||||
"00000034" // blob size
|
||||
"70737368" // "pssh"
|
||||
"00000000" // flags
|
||||
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
|
||||
"00000014" // pssh data size
|
||||
// pssh data:
|
||||
"0901121094889920e8d6520098577df8"
|
||||
"f2dd5546";
|
||||
|
||||
// Url returned by GetProvisioningRequest()
|
||||
// URL of provisioning server (returned by GetProvisioningRequest())
|
||||
const std::string kProductionProvisioningServerUrl =
|
||||
"https://www.googleapis.com/"
|
||||
"certificateprovisioning/v1/devicecertificates/create"
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
const std::string kServerSdkLicenseServer =
|
||||
"http://kir03fcpg174.widevine.net/widevine/cgi-bin/drm.cgi";
|
||||
|
||||
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
|
||||
{ wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
|
||||
kGpKeyId, kDefaultHttpsPort, true, true },
|
||||
{ wvcdm::kYouTubeContentProtectionServer, kYtCpLicenseServer,
|
||||
kYtCpClientAuth, kYtCpKeyId, kYtCpOfflineKeyId, kDefaultHttpPort,
|
||||
false, false }
|
||||
{ wvcdm::kGooglePlayServer, kGpLicenseServer,
|
||||
kGpClientAuth, kGpKeyId, kGpOfflineKeyId },
|
||||
{ wvcdm::kContentProtectionServer, kCpLicenseServer,
|
||||
kCpClientAuth, kCpKeyId, kCpOfflineKeyId },
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -113,11 +118,7 @@ void ConfigTestEnv::Init(LicenseServerId server_id) {
|
||||
key_id_ = license_servers[server_id].key_id;
|
||||
key_system_ = kWidevineKeySystem;
|
||||
license_server_ = license_servers[server_id].url;
|
||||
port_ = license_servers[server_id].port;
|
||||
provisioning_server_url_ = kProductionProvisioningServerUrl;
|
||||
server_sdk_license_server_ = kServerSdkLicenseServer;
|
||||
use_chunked_transfer_ = license_servers[server_id].use_chunked_transfer;
|
||||
use_secure_transfer_ = license_servers[server_id].use_secure_transfer;
|
||||
wrong_key_id_= kWrongKeyId;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,10 @@
|
||||
#include <string>
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace {
|
||||
const std::string kDefaultHttpsPort = "443";
|
||||
const std::string kDefaultHttpPort = "80";
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
typedef enum {
|
||||
kGooglePlayServer,
|
||||
kYouTubeContentProtectionServer
|
||||
kContentProtectionServer,
|
||||
} LicenseServerId;
|
||||
|
||||
// Configures default test environment.
|
||||
@@ -26,9 +21,6 @@ class ConfigTestEnv {
|
||||
std::string client_tag;
|
||||
std::string key_id;
|
||||
std::string offline_key_id;
|
||||
std::string port;
|
||||
bool use_chunked_transfer;
|
||||
bool use_secure_transfer;
|
||||
} LicenseServerConfiguration;
|
||||
|
||||
explicit ConfigTestEnv(LicenseServerId server_id);
|
||||
@@ -41,15 +33,9 @@ class ConfigTestEnv {
|
||||
const KeyId& key_id() const { return key_id_; }
|
||||
const CdmKeySystem& key_system() const { return key_system_; }
|
||||
const std::string& license_server() const { return license_server_; }
|
||||
const std::string& port() const { return port_; }
|
||||
const std::string& provisioning_server_url() const {
|
||||
return provisioning_server_url_;
|
||||
}
|
||||
const std::string& server_sdk_license_server() const {
|
||||
return server_sdk_license_server_;
|
||||
}
|
||||
bool use_chunked_transfer() { return use_chunked_transfer_; }
|
||||
bool use_secure_transfer() { return use_secure_transfer_; }
|
||||
const KeyId& wrong_key_id() const { return wrong_key_id_; }
|
||||
|
||||
void set_key_id(KeyId& key_id) { key_id_.assign(key_id); }
|
||||
@@ -59,7 +45,6 @@ class ConfigTestEnv {
|
||||
void set_license_server(std::string& license_server) {
|
||||
license_server_.assign(license_server);
|
||||
}
|
||||
void set_port(std::string& port) { port_.assign(port); }
|
||||
|
||||
private:
|
||||
void Init(LicenseServerId server_id);
|
||||
@@ -68,11 +53,7 @@ class ConfigTestEnv {
|
||||
KeyId key_id_;
|
||||
CdmKeySystem key_system_;
|
||||
std::string license_server_;
|
||||
std::string port_;
|
||||
std::string provisioning_server_url_;
|
||||
std::string server_sdk_license_server_;
|
||||
bool use_chunked_transfer_;
|
||||
bool use_secure_transfer_;
|
||||
KeyId wrong_key_id_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);
|
||||
|
||||
@@ -26,10 +26,15 @@ using ::testing::StrEq;
|
||||
namespace {
|
||||
const uint32_t kCertificateLen = 700;
|
||||
const uint32_t kWrappedKeyLen = 500;
|
||||
|
||||
const uint32_t kProtobufEstimatedOverhead = 75;
|
||||
const uint32_t kLicenseRequestLen = 300;
|
||||
const uint32_t kLicenseLen = 500;
|
||||
const uint32_t kProviderSessionTokenLen = 128;
|
||||
|
||||
// Structurally valid test certificate.
|
||||
// The data elements in this module are used to test the storage and
|
||||
// retrieval of certificates and licenses
|
||||
const std::string kTestCertificate =
|
||||
"124B035F3D256A656F0E505A085E7A6C482B61035E0C4A540F7803137F4C3B45206B7F33"
|
||||
"347F4D7A005E56400F0955011F4E07072D0D46781817460974326A516E3944385760280E"
|
||||
@@ -51,6 +56,10 @@ const std::string kTestCertificate =
|
||||
"523B102906195E003C2D111A7D4740122C6941003726602B59263B5C09473D4E025E3541"
|
||||
"701B122D340A3D145436137002687E4C470D2F6F4C357A3245384D737B734E2274301179"
|
||||
"402473486311156E5A0C78644C593273";
|
||||
|
||||
// A Wrapped Private Key
|
||||
// The data elements in this module are used to test the storage and
|
||||
// retrieval of certificates and licenses
|
||||
const std::string kTestWrappedPrivateKey =
|
||||
"4F724B065326371A2F5F6F51467C2E26555C453B5C7C1B4F2738454B782E3E7B5340435A"
|
||||
"66374D0612052C521A233D7A67194871751C78575E5177070130264C4F037633320E667B"
|
||||
@@ -66,6 +75,10 @@ const std::string kTestWrappedPrivateKey =
|
||||
"5F7F3D6B64525E7227165948101540243C19495C4C702F37490F26613353797825624143"
|
||||
"263043020E1E6760123D51056F2F1E482F2E3D021B27677D3E7E3C0C11757C3448275E08"
|
||||
"382E111263644C6D224714706D760A054A586E17505C3429575A41043F184209";
|
||||
|
||||
// The test certificate in file storage format.
|
||||
// The data elements in this module are used to test the storage and
|
||||
// retrieval of certificates and licenses
|
||||
const std::string kTestCertificateFileData =
|
||||
"0ABD09080110011AB6090ABC05124B035F3D256A656F0E505A085E7A6C482B61035E0C4A"
|
||||
"540F7803137F4C3B45206B7F33347F4D7A005E56400F0955011F4E07072D0D4678181746"
|
||||
@@ -115,10 +128,13 @@ struct LicenseInfo {
|
||||
std::string file_data;
|
||||
};
|
||||
|
||||
// Sample data to test storage and retrieval of license-related data
|
||||
// The license data and URLs in this test are not real.
|
||||
size_t kNumberOfLicenses = 3;
|
||||
|
||||
// Sample license data and related data for storage and use for offline
|
||||
// playback. The license data and URLs in this test are not real. Test
|
||||
// storage and retrieval of license-related data.
|
||||
LicenseInfo license_test_data[] = {
|
||||
|
||||
// license 0
|
||||
{"ksid54C57C966E23CEF5", DeviceFiles::kLicenseStateActive,
|
||||
wvcdm::a2bs_hex("0801121030313233343536373839414243444546"),
|
||||
@@ -310,6 +326,7 @@ LicenseInfo license_test_data[] = {
|
||||
"652E636F6D2F766964656F2D6465762F6C6963656E73652F47657443656E"
|
||||
"634C6963656E73651220F6974C1CFFD00E3144488FC092D3DF4F6007A3CA"
|
||||
"C4756EB046DC74B1C2E512CC")},
|
||||
|
||||
// license 1
|
||||
{"ksidC8EAA2579A282EB0", DeviceFiles::kLicenseStateReleasing,
|
||||
wvcdm::a2bs_hex("0801121030313233343536373839414243444546"),
|
||||
@@ -502,6 +519,7 @@ LicenseInfo license_test_data[] = {
|
||||
"3D3033363831323632646334313263303626736F757263653D594F555455"
|
||||
"42451220EC449C6B026C43004743061B3A3DCB7208B2AD11600254841B96"
|
||||
"1CFA1AD57172")},
|
||||
|
||||
// license 2
|
||||
{"ksidE8C37662C88DC673", DeviceFiles::kLicenseStateReleasing,
|
||||
wvcdm::a2bs_hex("0801121030313233343536373839414243444546"),
|
||||
@@ -693,6 +711,9 @@ LicenseInfo license_test_data[] = {
|
||||
"72702E676F6F676C652E636F6D3A383838382F64726D12205CD2C43C618C"
|
||||
"CA27BBCB2EEBDE32B57CBD51B424FD85DAB715B7F5A87546FD40")}};
|
||||
|
||||
// Sample license data and related data for storage and use for offline
|
||||
// playback. The license data and URLs in this test are not real.
|
||||
// The data is used to test license-related functions.
|
||||
LicenseInfo license_update_test_data[] = {
|
||||
// active license
|
||||
{"key_set_id_: ksid2A048BC7FAEC885A", DeviceFiles::kLicenseStateActive,
|
||||
|
||||
@@ -45,10 +45,10 @@ class FileTest : public testing::Test {
|
||||
|
||||
TEST_F(FileTest, FileExists) {
|
||||
File file;
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kFileExists));
|
||||
EXPECT_TRUE(file.Exists(test_vectors::kDirExists));
|
||||
EXPECT_FALSE(file.Exists(test_vectors::kFileDoesNotExist));
|
||||
EXPECT_FALSE(file.Exists(test_vectors::kDirDoesNotExist));
|
||||
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) {
|
||||
|
||||
@@ -5,17 +5,35 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "openssl/bio.h"
|
||||
#include "openssl/err.h"
|
||||
#include "openssl/x509.h"
|
||||
|
||||
namespace wvcdm {
|
||||
namespace {
|
||||
|
||||
SSL_CTX* HttpSocket::InitSslContext(void) {
|
||||
// Helper function to tokenize a string. This makes it easier to avoid silly
|
||||
// parsing bugs that creep in easily when each part of the string is parsed
|
||||
// with its own piece of code.
|
||||
bool Tokenize(const std::string& source, const std::string& delim,
|
||||
const size_t offset, std::string* substring_output,
|
||||
size_t* next_offset) {
|
||||
size_t start_of_delim = source.find(delim, offset);
|
||||
if (start_of_delim == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
substring_output->assign(source, offset, start_of_delim - offset);
|
||||
*next_offset = start_of_delim + delim.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
SSL_CTX* InitSslContext() {
|
||||
const SSL_METHOD* method;
|
||||
SSL_CTX* ctx;
|
||||
|
||||
@@ -23,21 +41,18 @@ SSL_CTX* HttpSocket::InitSslContext(void) {
|
||||
SSL_load_error_strings();
|
||||
method = SSLv3_client_method();
|
||||
ctx = SSL_CTX_new(method);
|
||||
if (NULL == ctx) {
|
||||
if (!ctx)
|
||||
LOGE("failed to create SSL context");
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void HttpSocket::ShowServerCertificate(const SSL* ssl) {
|
||||
X509* cert;
|
||||
char* line;
|
||||
|
||||
// unused, may be useful for debugging SSL-related issues.
|
||||
void ShowServerCertificate(const SSL* ssl) {
|
||||
// gets the server certificate
|
||||
cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert != NULL) {
|
||||
X509* cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert) {
|
||||
char* line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||
LOGV("server certificate:");
|
||||
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||
LOGV("subject: %s", line);
|
||||
free(line);
|
||||
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
||||
@@ -49,183 +64,252 @@ void HttpSocket::ShowServerCertificate(const SSL* ssl) {
|
||||
}
|
||||
}
|
||||
|
||||
HttpSocket::HttpSocket()
|
||||
: secure_connect_(true),
|
||||
socket_fd_(-1),
|
||||
// Wait for a socket to be ready for reading or writing.
|
||||
// Establishing a connection counts as "ready for write".
|
||||
// Returns false on select error or timeout.
|
||||
// Returns true when the socket is ready.
|
||||
bool SocketWait(int fd, bool for_read, int timeout_in_ms) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout_in_ms / 1000;
|
||||
tv.tv_usec = (timeout_in_ms % 1000) * 1000;
|
||||
|
||||
fd_set *read_fds = NULL;
|
||||
fd_set *write_fds = NULL;
|
||||
if (for_read) {
|
||||
read_fds = &fds;
|
||||
} else {
|
||||
write_fds = &fds;
|
||||
}
|
||||
|
||||
int ret = select(fd + 1, read_fds, write_fds, NULL, &tv);
|
||||
if (ret == 0) {
|
||||
LOGE("socket timed out");
|
||||
return false;
|
||||
} else if (ret == -1) {
|
||||
LOGE("select failed, errno = %d", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// socket ready.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Parses the URL and extracts all relevant information.
|
||||
// static
|
||||
bool HttpSocket::ParseUrl(const std::string& url,
|
||||
std::string* scheme,
|
||||
bool* secure_connect,
|
||||
std::string* domain_name,
|
||||
int* port,
|
||||
std::string* path) {
|
||||
size_t offset = 0;
|
||||
|
||||
if (!Tokenize(url, "://", offset, scheme, &offset)) {
|
||||
LOGE("Invalid URL, scheme not found: %s", url.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the scheme is http or https, set secure_connect and port accordingly.
|
||||
// Otherwise, consider the scheme unsupported and fail.
|
||||
if (*scheme == "http") {
|
||||
*secure_connect = false;
|
||||
*port = 80;
|
||||
} else if (*scheme == "https") {
|
||||
*secure_connect = true;
|
||||
*port = 443;
|
||||
} else {
|
||||
LOGE("Invalid URL, scheme not supported: %s", url.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Tokenize(url, "/", offset, domain_name, &offset)) {
|
||||
// The rest of the URL belongs to the domain name.
|
||||
domain_name->assign(url, offset, std::string::npos);
|
||||
// No explicit path after the domain name.
|
||||
path->assign("/");
|
||||
} else {
|
||||
// The rest of the URL, including the preceding slash, belongs to the path.
|
||||
path->assign(url, offset - 1, std::string::npos);
|
||||
}
|
||||
|
||||
// The domain name may optionally contain a port which overrides the default.
|
||||
std::string domain_name_without_port;
|
||||
size_t port_offset;
|
||||
if (Tokenize(*domain_name, ":", 0, &domain_name_without_port,
|
||||
&port_offset)) {
|
||||
*port = atoi(domain_name->c_str() + port_offset);
|
||||
if (*port <= 0 || *port >= 65536) {
|
||||
LOGE("Invalid URL, port not valid: %s", url.c_str());
|
||||
return false;
|
||||
}
|
||||
domain_name->assign(domain_name_without_port);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HttpSocket::HttpSocket(const std::string& url)
|
||||
: socket_fd_(-1),
|
||||
ssl_(NULL),
|
||||
ssl_ctx_(NULL),
|
||||
timeout_enabled_(false) {
|
||||
ssl_ctx_(NULL) {
|
||||
valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_,
|
||||
&resource_path_);
|
||||
SSL_library_init();
|
||||
}
|
||||
|
||||
HttpSocket::~HttpSocket() { CloseSocket(); }
|
||||
HttpSocket::~HttpSocket() {
|
||||
CloseSocket();
|
||||
}
|
||||
|
||||
void HttpSocket::CloseSocket() {
|
||||
if (socket_fd_ != -1) {
|
||||
close(socket_fd_);
|
||||
socket_fd_ = -1;
|
||||
}
|
||||
if (secure_connect_) {
|
||||
if (ssl_) {
|
||||
SSL_free(ssl_);
|
||||
ssl_ = NULL;
|
||||
}
|
||||
if (ssl_ctx_) {
|
||||
CloseSslContext(ssl_ctx_);
|
||||
SSL_CTX_free(ssl_ctx_);
|
||||
ssl_ctx_ = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts the domain name and resource path from the input url parameter.
|
||||
// The results are put in domain_name and resource_path respectively.
|
||||
// The format of the url can begin with <protocol/scheme>:://domain server/...
|
||||
// or dowmain server/resource_path
|
||||
void HttpSocket::GetDomainNameAndPathFromUrl(const std::string& url,
|
||||
std::string& domain_name,
|
||||
std::string& resource_path) {
|
||||
domain_name.clear();
|
||||
resource_path.clear();
|
||||
|
||||
size_t start = url.find("//");
|
||||
size_t end = url.npos;
|
||||
if (start != url.npos) {
|
||||
end = url.find("/", start + 2);
|
||||
if (end != url.npos) {
|
||||
domain_name.assign(url, start + 2, end - start - 2);
|
||||
resource_path.assign(url, end + 1, url.npos);
|
||||
} else {
|
||||
domain_name.assign(url, start + 2, url.npos);
|
||||
bool HttpSocket::Connect(int timeout_in_ms) {
|
||||
if (!valid_url_) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// no scheme/protocol in url
|
||||
end = url.find("/");
|
||||
if (end != url.npos) {
|
||||
domain_name.assign(url, 0, end);
|
||||
resource_path.assign(url, end + 1, url.npos);
|
||||
} else {
|
||||
domain_name.assign(url);
|
||||
}
|
||||
}
|
||||
// strips port number if present, e.g. https://www.domain.com:8888/...
|
||||
end = domain_name.find(":");
|
||||
if (end != domain_name.npos) {
|
||||
domain_name.erase(end);
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpSocket::Connect(const char* url, const std::string& port,
|
||||
bool enable_timeout, bool secure_connection) {
|
||||
secure_connect_ = secure_connection;
|
||||
if (secure_connect_) ssl_ctx_ = InitSslContext();
|
||||
|
||||
GetDomainNameAndPathFromUrl(url, domain_name_, resource_path_);
|
||||
|
||||
// get a socket
|
||||
socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socket_fd_ < 0) {
|
||||
LOGE("cannot open socket %d", errno);
|
||||
LOGE("cannot open socket, errno = %d", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
int reuse = 1;
|
||||
if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) ==
|
||||
-1) {
|
||||
// set the socket in non-blocking mode
|
||||
int original_flags = fcntl(socket_fd_, F_GETFL, 0);
|
||||
if (original_flags == -1) {
|
||||
LOGE("fcntl error, errno = %d", errno);
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) {
|
||||
LOGE("fcntl error, errno = %d", errno);
|
||||
CloseSocket();
|
||||
LOGE("setsockopt error %d", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
// lookup the server IP
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
struct addrinfo* addr_info = NULL;
|
||||
bool status = true;
|
||||
int ret = getaddrinfo(domain_name_.c_str(), port.c_str(), &hints, &addr_info);
|
||||
int ret = getaddrinfo(domain_name_.c_str(), NULL, &hints, &addr_info);
|
||||
if (ret != 0) {
|
||||
CloseSocket();
|
||||
LOGE("getaddrinfo failed with %d", ret);
|
||||
status = false;
|
||||
} else {
|
||||
if (connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen) == -1) {
|
||||
CloseSocket();
|
||||
LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(),
|
||||
errno);
|
||||
status = false;
|
||||
LOGE("getaddrinfo failed, errno = %d", ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
timeout_enabled_ = enable_timeout;
|
||||
if (addr_info != NULL) {
|
||||
|
||||
// set the port
|
||||
struct sockaddr_in* addr_ipv4 = reinterpret_cast<struct sockaddr_in*>(
|
||||
addr_info->ai_addr);
|
||||
addr_ipv4->sin_port = htons(port_);
|
||||
|
||||
// connect to the server
|
||||
ret = connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen);
|
||||
freeaddrinfo(addr_info);
|
||||
|
||||
if (ret == 0) {
|
||||
// connected right away.
|
||||
} else {
|
||||
if (errno != EINPROGRESS) {
|
||||
// failed right away.
|
||||
LOGE("cannot connect to %s, errno = %d", domain_name_.c_str(), errno);
|
||||
CloseSocket();
|
||||
return false;
|
||||
} else {
|
||||
// in progress. block until timeout expired or connection established.
|
||||
if (!SocketWait(socket_fd_, /* for_read */ false, timeout_in_ms)) {
|
||||
LOGE("cannot connect to %s", domain_name_.c_str());
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!status) return false;
|
||||
// set up SSL if needed
|
||||
if (secure_connect_) {
|
||||
ssl_ctx_ = InitSslContext();
|
||||
if (!ssl_ctx_) {
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
|
||||
// secures connection
|
||||
if (secure_connect_ && ssl_ctx_) {
|
||||
ssl_ = SSL_new(ssl_ctx_);
|
||||
if (!ssl_) {
|
||||
LOGE("failed SSL_new");
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
|
||||
BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE);
|
||||
if (!a_bio) {
|
||||
LOGE("BIO_new_socket error");
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
|
||||
SSL_set_bio(ssl_, a_bio, a_bio);
|
||||
int ret = SSL_connect(ssl_);
|
||||
if (1 != ret) {
|
||||
do {
|
||||
ret = SSL_connect(ssl_);
|
||||
if (ret != 1) {
|
||||
int ssl_err = SSL_get_error(ssl_, ret);
|
||||
if (ssl_err != SSL_ERROR_WANT_READ &&
|
||||
ssl_err != SSL_ERROR_WANT_WRITE) {
|
||||
char buf[256];
|
||||
LOGE("SSL_connect error:%s", ERR_error_string(ERR_get_error(), buf));
|
||||
LOGE("SSL_connect error: %s", ERR_error_string(ERR_get_error(), buf));
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
bool for_read = ssl_err == SSL_ERROR_WANT_READ;
|
||||
if (!SocketWait(socket_fd_, for_read, timeout_in_ms)) {
|
||||
LOGE("cannot connect to %s", domain_name_.c_str());
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (ret != 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int HttpSocket::Read(char* data, int len) { return (Read(data, len, 0)); }
|
||||
|
||||
// makes non-blocking mode only during read, it supports timeout for read
|
||||
// returns -1 for error, number of bytes read for success
|
||||
// Returns -1 for error, number of bytes read for success.
|
||||
// The timeout here only applies to the span between packets of data, for the
|
||||
// sake of simplicity.
|
||||
int HttpSocket::Read(char* data, int len, int timeout_in_ms) {
|
||||
bool use_timeout = (timeout_enabled_ && (timeout_in_ms > 0));
|
||||
int original_flags = 0;
|
||||
if (use_timeout) {
|
||||
original_flags = fcntl(socket_fd_, F_GETFL, 0);
|
||||
if (original_flags == -1) {
|
||||
LOGE("fcntl error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) {
|
||||
LOGE("fcntl error %d", errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int total_read = 0;
|
||||
int read = 0;
|
||||
int to_read = len;
|
||||
|
||||
while (to_read > 0) {
|
||||
if (use_timeout) {
|
||||
fd_set read_fds;
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout_in_ms / 1000;
|
||||
tv.tv_usec = (timeout_in_ms % 1000) * 1000;
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(socket_fd_, &read_fds);
|
||||
if (select(socket_fd_ + 1, &read_fds, NULL, NULL, &tv) == -1) {
|
||||
LOGE("select failed");
|
||||
break;
|
||||
}
|
||||
if (!FD_ISSET(socket_fd_, &read_fds)) {
|
||||
LOGD("socket read timeout");
|
||||
break;
|
||||
}
|
||||
if (!SocketWait(socket_fd_, /* for_read */ true, timeout_in_ms)) {
|
||||
LOGE("unable to read from %s", domain_name_.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read;
|
||||
if (secure_connect_)
|
||||
read = SSL_read(ssl_, data, to_read);
|
||||
else
|
||||
@@ -236,27 +320,26 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) {
|
||||
data += read;
|
||||
total_read += read;
|
||||
} else if (read == 0) {
|
||||
// in blocking mode, zero read mean's peer closed.
|
||||
// in non-blocking mode, select said that there is data. so it should not
|
||||
// happen
|
||||
// The connection has been closed. No more data.
|
||||
break;
|
||||
} else {
|
||||
LOGE("recv returned %d, error = %d", read, errno);
|
||||
break;
|
||||
LOGE("recv returned %d, errno = %d", read, errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_timeout) {
|
||||
fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
|
||||
int HttpSocket::Write(const char* data, int len) {
|
||||
// Returns -1 for error, number of bytes written for success.
|
||||
// The timeout here only applies to the span between packets of data, for the
|
||||
// sake of simplicity.
|
||||
int HttpSocket::Write(const char* data, int len, int timeout_in_ms) {
|
||||
int total_sent = 0;
|
||||
int sent = 0;
|
||||
int to_send = len;
|
||||
|
||||
while (to_send > 0) {
|
||||
int sent;
|
||||
if (secure_connect_)
|
||||
sent = SSL_write(ssl_, data, to_send);
|
||||
else
|
||||
@@ -267,11 +350,17 @@ int HttpSocket::Write(const char* data, int len) {
|
||||
data += sent;
|
||||
total_sent += sent;
|
||||
} else if (sent == 0) {
|
||||
usleep(10); // retry later
|
||||
// We filled up the pipe. Wait for room to write.
|
||||
if (!SocketWait(socket_fd_, /* for_read */ false, timeout_in_ms)) {
|
||||
LOGE("unable to write to %s", domain_name_.c_str());
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
LOGE("send returned error %d", errno);
|
||||
LOGE("send returned %d, errno = %d", sent, errno);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return total_sent;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,43 +4,52 @@
|
||||
#define CDM_TEST_HTTP_SOCKET_H_
|
||||
|
||||
#include <string>
|
||||
#include "openssl/ssl.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
#include <gtest/gtest_prod.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include "wv_cdm_types.h" // CORE_DISALLOW_COPY_AND_ASSIGN
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Provides basic Linux based TCP socket interface.
|
||||
class HttpSocket {
|
||||
public:
|
||||
HttpSocket();
|
||||
// A scheme (http:// or https://) is required for the URL.
|
||||
explicit HttpSocket(const std::string& url);
|
||||
~HttpSocket();
|
||||
|
||||
bool Connect(int timeout_in_ms);
|
||||
void CloseSocket();
|
||||
bool Connect(const char* url, const std::string& port, bool enable_timeout,
|
||||
bool secure_connection);
|
||||
void GetDomainNameAndPathFromUrl(const std::string& url,
|
||||
std::string& domain_name,
|
||||
std::string& resource_path);
|
||||
const std::string& domain_name() const { return domain_name_; };
|
||||
const std::string& resource_path() const { return resource_path_; };
|
||||
int Read(char* data, int len);
|
||||
|
||||
const std::string& scheme() const { return scheme_; }
|
||||
bool secure_connect() const { return secure_connect_; }
|
||||
const std::string& domain_name() const { return domain_name_; }
|
||||
int port() const { return port_; }
|
||||
const std::string& resource_path() const { return resource_path_; }
|
||||
|
||||
int Read(char* data, int len, int timeout_in_ms);
|
||||
int Write(const char* data, int len);
|
||||
int Write(const char* data, int len, int timeout_in_ms);
|
||||
|
||||
private:
|
||||
void CloseSslContext(SSL_CTX* ctx) const {
|
||||
if (ctx) SSL_CTX_free(ctx);
|
||||
}
|
||||
SSL_CTX* InitSslContext(void);
|
||||
void ShowServerCertificate(const SSL* ssl);
|
||||
static bool ParseUrl(const std::string& url,
|
||||
std::string* scheme,
|
||||
bool* secure_connect,
|
||||
std::string* domain_name,
|
||||
int* port,
|
||||
std::string* path);
|
||||
FRIEND_TEST(HttpSocketTest, ParseUrlTest);
|
||||
|
||||
std::string domain_name_;
|
||||
std::string scheme_;
|
||||
bool secure_connect_;
|
||||
std::string domain_name_;
|
||||
int port_;
|
||||
std::string resource_path_;
|
||||
bool valid_url_;
|
||||
|
||||
int socket_fd_;
|
||||
SSL* ssl_;
|
||||
SSL_CTX* ssl_ctx_;
|
||||
bool timeout_enabled_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(HttpSocket);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <errno.h>
|
||||
#include "gtest/gtest.h"
|
||||
#include "http_socket.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "log.h"
|
||||
#include "string_conversions.h"
|
||||
#include "url_request.h"
|
||||
@@ -10,10 +11,14 @@
|
||||
namespace {
|
||||
// Arbitrary URL for tests.
|
||||
const std::string kHttpsTestServer("https://www.google.com");
|
||||
const std::string kHttpTestServer("http://www.google.com");
|
||||
// This URL and data are used by RoundTripTest, and can be overridden on the
|
||||
// command line.
|
||||
std::string gTestServer(kHttpsTestServer);
|
||||
std::string gTestData("Hello");
|
||||
// Arbitrary buffer size and timeout settings.
|
||||
const int kHttpBufferSize = 4096;
|
||||
char gBuffer[kHttpBufferSize];
|
||||
const int kTimeout = 3000;
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -21,153 +26,179 @@ namespace wvcdm {
|
||||
class HttpSocketTest : public testing::Test {
|
||||
public:
|
||||
HttpSocketTest() {}
|
||||
~HttpSocketTest() { socket_.CloseSocket(); }
|
||||
~HttpSocketTest() {}
|
||||
|
||||
protected:
|
||||
bool Connect(const std::string& server_url, bool secure_connection) {
|
||||
bool Connect(const std::string& server_url) {
|
||||
socket_.reset(new HttpSocket(server_url));
|
||||
|
||||
std::string port = secure_connection ? "443" : "80";
|
||||
if (socket_.Connect(server_url.c_str(), port, true, secure_connection)) {
|
||||
LOGD("connected to %s", socket_.domain_name().c_str());
|
||||
if (socket_->Connect(kTimeout)) {
|
||||
LOGD("connected to %s", socket_->domain_name().c_str());
|
||||
return true;
|
||||
} else {
|
||||
LOGE("failed to connect to %s", socket_.domain_name().c_str());
|
||||
LOGE("failed to connect to %s", server_url.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PostRequest(const std::string& data) {
|
||||
std::string request("POST ");
|
||||
if (socket_.resource_path().empty())
|
||||
request.append(socket_.domain_name());
|
||||
else
|
||||
request.append(socket_.resource_path());
|
||||
request.append(socket_->resource_path());
|
||||
request.append(" HTTP/1.1\r\n");
|
||||
|
||||
request.append("Host: ");
|
||||
request.append(socket_.domain_name());
|
||||
request.append("\r\nUser-Agent: httpSocketTest/1.0\r\n");
|
||||
request.append(socket_->domain_name());
|
||||
request.append("\r\n");
|
||||
|
||||
// Important! Otherwise, the HTTP 1.1 default behavior for a server is to
|
||||
// keep the connection open for a subsequent request.
|
||||
request.append("Connection: close\r\n");
|
||||
|
||||
request.append("User-Agent: httpSocketTest/1.0\r\n");
|
||||
|
||||
char buffer[32] = {0};
|
||||
snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(data.size()));
|
||||
request.append("Content-Length: ");
|
||||
memset(gBuffer, 0, kHttpBufferSize);
|
||||
snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast<int>(data.size()));
|
||||
request.append(gBuffer);
|
||||
request.append(buffer);
|
||||
request.append("\r\n");
|
||||
|
||||
request.append("Content-Type: multipart/form-data\r\n");
|
||||
|
||||
// newline terminates header
|
||||
// an extra newline terminates HTTP headers.
|
||||
request.append("\r\n");
|
||||
|
||||
// append data
|
||||
request.append(data);
|
||||
socket_.Write(request.c_str(), request.size());
|
||||
socket_->Write(request.c_str(), request.size(), kTimeout);
|
||||
|
||||
LOGD("request: %s", request.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetResponse() {
|
||||
int bytes = socket_.Read(gBuffer, kHttpBufferSize, 1000);
|
||||
bool GetResponse(std::string* response) {
|
||||
char buffer[kHttpBufferSize];
|
||||
int bytes = socket_->Read(buffer, sizeof(buffer), kTimeout);
|
||||
if (bytes < 0) {
|
||||
LOGE("read error = ", errno);
|
||||
LOGE("read error, errno = %d", errno);
|
||||
return false;
|
||||
} else {
|
||||
LOGD("read %d bytes", bytes);
|
||||
std::string response(gBuffer, bytes);
|
||||
LOGD("response: %s", response.c_str());
|
||||
LOGD("end response dump");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
HttpSocket socket_;
|
||||
LOGD("read %d bytes", bytes);
|
||||
response->assign(buffer, bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
scoped_ptr<HttpSocket> socket_;
|
||||
std::string domain_name_;
|
||||
std::string resource_path_;
|
||||
};
|
||||
|
||||
TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) {
|
||||
socket_.GetDomainNameAndPathFromUrl(
|
||||
"https://code.google.com/p/googletest/wiki/Primer", domain_name_,
|
||||
resource_path_);
|
||||
EXPECT_STREQ("code.google.com", domain_name_.c_str());
|
||||
EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str());
|
||||
struct ParseUrlTests {
|
||||
const char* url;
|
||||
const char* scheme;
|
||||
bool secure_connect;
|
||||
const char* domain_name;
|
||||
int port;
|
||||
const char* path;
|
||||
};
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl(
|
||||
"http://code.google.com/p/googletest/wiki/Primer/", domain_name_,
|
||||
resource_path_);
|
||||
EXPECT_STREQ("code.google.com", domain_name_.c_str());
|
||||
EXPECT_STREQ("p/googletest/wiki/Primer/", resource_path_.c_str());
|
||||
ParseUrlTests parse_url_tests[] = {
|
||||
{
|
||||
"https://code.google.com/p/googletest/wiki/Primer", // url
|
||||
"https", // scheme
|
||||
true, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
443, // port
|
||||
"/p/googletest/wiki/Primer", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com/p/googletest/wiki/Primer/", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/p/googletest/wiki/Primer/", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com/", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"http://code.google.com", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"code.google.com", // domain_name
|
||||
80, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"http://10.11.12.13:8888/drm", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/drm", // path
|
||||
},
|
||||
{
|
||||
"http://10.11.12.13:8888", // url
|
||||
"http", // scheme
|
||||
false, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/", // path
|
||||
},
|
||||
{
|
||||
"https://10.11.12.13:8888", // url
|
||||
"https", // scheme
|
||||
true, // secure_connect
|
||||
"10.11.12.13", // domain_name
|
||||
8888, // port
|
||||
"/", // path
|
||||
},
|
||||
{ NULL } // list terminator
|
||||
};
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl("http://code.google.com/", domain_name_,
|
||||
resource_path_);
|
||||
EXPECT_STREQ("code.google.com", domain_name_.c_str());
|
||||
EXPECT_STREQ("", resource_path_.c_str());
|
||||
TEST_F(HttpSocketTest, ParseUrlTest) {
|
||||
std::string scheme;
|
||||
bool secure_connect;
|
||||
std::string domain_name;
|
||||
int port;
|
||||
std::string path;
|
||||
ParseUrlTests* test = NULL;
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl("http://code.google.com", domain_name_,
|
||||
resource_path_);
|
||||
EXPECT_STREQ("code.google.com", domain_name_.c_str());
|
||||
EXPECT_STREQ("", resource_path_.c_str());
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl(
|
||||
"code.google.com/p/googletest/wiki/Primer", domain_name_, resource_path_);
|
||||
EXPECT_STREQ("code.google.com", domain_name_.c_str());
|
||||
EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str());
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl("code.google.com", domain_name_,
|
||||
resource_path_);
|
||||
EXPECT_STREQ("code.google.com", domain_name_.c_str());
|
||||
EXPECT_STREQ("", resource_path_.c_str());
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl("code.google.com/", domain_name_,
|
||||
resource_path_);
|
||||
EXPECT_STREQ("code.google.com", domain_name_.c_str());
|
||||
EXPECT_STREQ("", resource_path_.c_str());
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl("", domain_name_, resource_path_);
|
||||
EXPECT_TRUE(domain_name_.empty());
|
||||
EXPECT_TRUE(resource_path_.empty());
|
||||
|
||||
// Test with arbitrary numeric URL
|
||||
socket_.GetDomainNameAndPathFromUrl("http://10.11.12.13:8888/drm",
|
||||
domain_name_, resource_path_);
|
||||
EXPECT_STREQ("10.11.12.13", domain_name_.c_str());
|
||||
EXPECT_STREQ("drm", resource_path_.c_str());
|
||||
|
||||
socket_.GetDomainNameAndPathFromUrl("http://10.11.12.13:8888", domain_name_,
|
||||
resource_path_);
|
||||
EXPECT_STREQ("10.11.12.13", domain_name_.c_str());
|
||||
EXPECT_TRUE(resource_path_.empty());
|
||||
for (test = &parse_url_tests[0]; test->url != NULL; ++test) {
|
||||
bool ok = HttpSocket::ParseUrl(test->url, &scheme, &secure_connect,
|
||||
&domain_name, &port, &path);
|
||||
EXPECT_TRUE(ok);
|
||||
if (ok) {
|
||||
EXPECT_EQ(test->scheme, scheme);
|
||||
EXPECT_EQ(test->secure_connect, secure_connect);
|
||||
EXPECT_EQ(test->domain_name, domain_name);
|
||||
EXPECT_EQ(test->port, port);
|
||||
EXPECT_EQ(test->path, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(HttpSocketTest, ConnectTest) {
|
||||
const bool kUseSecureConnection = true;
|
||||
|
||||
if (gTestServer.find("https") != std::string::npos) {
|
||||
EXPECT_TRUE(Connect(gTestServer, kUseSecureConnection));
|
||||
socket_.CloseSocket();
|
||||
|
||||
// https connection allows insecure connection through port 80 as well
|
||||
EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection));
|
||||
socket_.CloseSocket();
|
||||
} else {
|
||||
EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection));
|
||||
socket_.CloseSocket();
|
||||
|
||||
// Test for the case that non-https connection must not use port 443
|
||||
EXPECT_FALSE(Connect(gTestServer, kUseSecureConnection));
|
||||
socket_.CloseSocket();
|
||||
}
|
||||
|
||||
EXPECT_FALSE(Connect("ww.g.c", kUseSecureConnection));
|
||||
socket_.CloseSocket();
|
||||
|
||||
EXPECT_FALSE(Connect("ww.g.c", !kUseSecureConnection));
|
||||
socket_.CloseSocket();
|
||||
EXPECT_TRUE(Connect(kHttpsTestServer));
|
||||
EXPECT_TRUE(Connect(kHttpTestServer));
|
||||
EXPECT_FALSE(Connect("ww.g.c"));
|
||||
EXPECT_FALSE(Connect("http://ww.g.c"));
|
||||
EXPECT_FALSE(Connect("https://ww.g.c"));
|
||||
}
|
||||
|
||||
TEST_F(HttpSocketTest, RoundTripTest) {
|
||||
int secure_connection =
|
||||
(gTestServer.find("https") != std::string::npos) ? true : false;
|
||||
ASSERT_TRUE(Connect(gTestServer, secure_connection));
|
||||
ASSERT_TRUE(Connect(gTestServer));
|
||||
EXPECT_TRUE(PostRequest(gTestData));
|
||||
GetResponse();
|
||||
socket_.CloseSocket();
|
||||
|
||||
std::string response;
|
||||
EXPECT_TRUE(GetResponse(&response));
|
||||
LOGD("response: %s", response.c_str());
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -44,9 +44,6 @@ void LicenseRequest::GetDrmMessage(const std::string& response,
|
||||
if (drm_msg_pos != std::string::npos) {
|
||||
drm_msg = response.substr(drm_msg_pos);
|
||||
} else {
|
||||
// TODO(edwinwong, rfrias): hack to get HTTP message body out for
|
||||
// non-Google Play webservers. Need to clean this up. Possibly test
|
||||
// for GLS and decide which part is the drm message
|
||||
drm_msg = response.substr(header_end_pos);
|
||||
}
|
||||
} else {
|
||||
@@ -59,7 +56,7 @@ void LicenseRequest::GetDrmMessage(const std::string& response,
|
||||
void LicenseRequest::GetHeartbeatUrl(const std::string& response,
|
||||
std::string& heartbeat_url) {
|
||||
if (response.empty()) {
|
||||
heartbeat_url.clear(); // TODO: assign default heartbeat url
|
||||
heartbeat_url.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,51 +10,15 @@
|
||||
#include "string_conversions.h"
|
||||
|
||||
namespace {
|
||||
const int kMaxReadAttempts = 4;
|
||||
const int kSingleReadAttempt = 1;
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
UrlRequest::UrlRequest(const std::string& url, const std::string& port,
|
||||
bool secure_connection, bool chunk_transfer_mode)
|
||||
: chunk_transfer_mode_(chunk_transfer_mode),
|
||||
is_connected_(false),
|
||||
port_("80"),
|
||||
request_(""),
|
||||
server_url_(url) {
|
||||
if (!port.empty()) {
|
||||
port_.assign(port);
|
||||
}
|
||||
if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) {
|
||||
is_connected_ = true;
|
||||
} else {
|
||||
LOGE("failed to connect to %s, port=%s", socket_.domain_name().c_str(),
|
||||
port.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
UrlRequest::~UrlRequest() { socket_.CloseSocket(); }
|
||||
|
||||
void UrlRequest::AppendChunkToUpload(const std::string& data) {
|
||||
// format of chunk:
|
||||
// size of chunk in hex\r\n
|
||||
// data\r\n
|
||||
// . . .
|
||||
// 0\r\n
|
||||
|
||||
// buffer to store length of chunk
|
||||
memset(buffer_, 0, kHttpBufferSize);
|
||||
snprintf(buffer_, kHttpBufferSize, "%zx\r\n", data.size());
|
||||
request_.append(buffer_); // appends size of chunk
|
||||
LOGD("...\r\n%s", request_.c_str());
|
||||
request_.append(data);
|
||||
request_.append("\r\n"); // marks end of data
|
||||
}
|
||||
const int kReadBufferSize = 1024;
|
||||
const int kConnectTimeoutMs = 5000;
|
||||
const int kWriteTimeoutMs = 3000;
|
||||
const int kReadTimeoutMs = 3000;
|
||||
|
||||
// Concatenate all chunks into one blob and returns the response with
|
||||
// header information.
|
||||
void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
|
||||
void ConcatenateChunkedResponse(const std::string http_response,
|
||||
std::string* modified_response) {
|
||||
if (http_response.empty()) return;
|
||||
|
||||
@@ -103,33 +67,52 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
|
||||
}
|
||||
}
|
||||
|
||||
int UrlRequest::GetResponse(std::string* message) {
|
||||
message->clear();
|
||||
} // namespace
|
||||
|
||||
std::string response;
|
||||
const int kTimeoutInMs = 3000;
|
||||
int bytes = 0;
|
||||
for (int attempts = kMaxReadAttempts; attempts > 0; --attempts) {
|
||||
memset(buffer_, 0, kHttpBufferSize);
|
||||
bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs);
|
||||
if (bytes > 0) {
|
||||
response.append(buffer_, bytes);
|
||||
if (bytes < static_cast<int>(kHttpBufferSize)) {
|
||||
attempts = kSingleReadAttempt;
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
UrlRequest::UrlRequest(const std::string& url)
|
||||
: is_connected_(false),
|
||||
socket_(url) {
|
||||
if (socket_.Connect(kConnectTimeoutMs)) {
|
||||
is_connected_ = true;
|
||||
} else {
|
||||
if (bytes < 0) LOGE("read error = ", errno);
|
||||
// bytes == 0 indicates nothing to read
|
||||
LOGE("failed to connect to %s, port=%d", socket_.domain_name().c_str(),
|
||||
socket_.port());
|
||||
}
|
||||
}
|
||||
|
||||
UrlRequest::~UrlRequest() {}
|
||||
|
||||
bool UrlRequest::GetResponse(std::string* message) {
|
||||
std::string response;
|
||||
|
||||
// Keep reading until end of stream (0 bytes read) or timeout. Partial
|
||||
// buffers worth of data can and do happen, especially with OpenSSL in
|
||||
// non-blocking mode.
|
||||
while (true) {
|
||||
char read_buffer[kReadBufferSize];
|
||||
int bytes = socket_.Read(read_buffer, sizeof(read_buffer), kReadTimeoutMs);
|
||||
if (bytes > 0) {
|
||||
response.append(read_buffer, bytes);
|
||||
} else if (bytes < 0) {
|
||||
LOGE("read error, errno = %d", errno);
|
||||
return false;
|
||||
} else {
|
||||
// end of stream.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ConcatenateChunkedResponse(response, message);
|
||||
LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());
|
||||
return message->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
int UrlRequest::GetStatusCode(const std::string& response) {
|
||||
const std::string kHttpVersion("HTTP/1.1");
|
||||
const std::string kHttpVersion("HTTP/1.1 ");
|
||||
|
||||
int status_code = -1;
|
||||
size_t pos = response.find(kHttpVersion);
|
||||
@@ -140,79 +123,48 @@ int UrlRequest::GetStatusCode(const std::string& response) {
|
||||
return status_code;
|
||||
}
|
||||
|
||||
bool UrlRequest::PostRequestChunk(const std::string& data) {
|
||||
request_.assign("POST /");
|
||||
request_.append(socket_.resource_path());
|
||||
request_.append(" HTTP/1.1\r\n");
|
||||
request_.append("Host: ");
|
||||
request_.append(socket_.domain_name());
|
||||
request_.append("\r\nConnection: Keep-Alive\r\n");
|
||||
request_.append("Transfer-Encoding: chunked\r\n");
|
||||
request_.append("User-Agent: Widevine CDM v1.0\r\n");
|
||||
request_.append("Accept-Encoding: gzip,deflate\r\n");
|
||||
request_.append("Accept-Language: en-us,fr\r\n");
|
||||
request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n");
|
||||
request_.append("\r\n"); // empty line to terminate header
|
||||
bool UrlRequest::PostRequestWithPath(const std::string& path,
|
||||
const std::string& data) {
|
||||
std::string request;
|
||||
|
||||
// calls AppendChunkToUpload repeatedly for multiple chunks
|
||||
AppendChunkToUpload(data);
|
||||
request.append("POST ");
|
||||
request.append(path);
|
||||
request.append(" HTTP/1.1\r\n");
|
||||
|
||||
// terminates last chunk with 0\r\n, then ends header with an empty line
|
||||
request_.append("0\r\n\r\n");
|
||||
request.append("Host: ");
|
||||
request.append(socket_.domain_name());
|
||||
request.append("\r\n");
|
||||
|
||||
socket_.Write(request_.c_str(), request_.size());
|
||||
return true;
|
||||
request.append("Connection: close\r\n");
|
||||
request.append("User-Agent: Widevine CDM v1.0\r\n");
|
||||
|
||||
// buffer to store length of data as a string
|
||||
char data_size_buffer[32] = {0};
|
||||
snprintf(data_size_buffer, sizeof(data_size_buffer), "%zd", data.size());
|
||||
|
||||
request.append("Content-Length: ");
|
||||
request.append(data_size_buffer); // appends size of data
|
||||
request.append("\r\n");
|
||||
|
||||
request.append("\r\n"); // empty line to terminate headers
|
||||
|
||||
request.append(data);
|
||||
|
||||
int ret = socket_.Write(request.c_str(), request.size(), kWriteTimeoutMs);
|
||||
LOGD("HTTP request: (%d): %s", request.size(), b2a_hex(request).c_str());
|
||||
return ret != -1;
|
||||
}
|
||||
|
||||
bool UrlRequest::PostRequest(const std::string& data) {
|
||||
if (chunk_transfer_mode_) {
|
||||
return PostRequestChunk(data);
|
||||
}
|
||||
request_.assign("POST /");
|
||||
request_.append(socket_.resource_path());
|
||||
request_.append(" HTTP/1.1\r\n");
|
||||
request_.append("Host: ");
|
||||
request_.append(socket_.domain_name());
|
||||
request_.append("\r\nConnection: Keep-Alive\r\n");
|
||||
request_.append("User-Agent: Widevine CDM v1.0\r\n");
|
||||
request_.append("Accept-Encoding: gzip,deflate\r\n");
|
||||
request_.append("Accept-Language: en-us,fr\r\n");
|
||||
request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n");
|
||||
std::ostringstream ss;
|
||||
ss << data.size();
|
||||
request_.append("Content-Length: ");
|
||||
request_.append(ss.str());
|
||||
request_.append("\r\n\r\n");
|
||||
request_.append(data);
|
||||
|
||||
// terminates with \r\n, then ends with an empty line
|
||||
request_.append("\r\n\r\n");
|
||||
|
||||
socket_.Write(request_.c_str(), request_.size());
|
||||
LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str());
|
||||
LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str());
|
||||
return true;
|
||||
return PostRequestWithPath(socket_.resource_path(), data);
|
||||
}
|
||||
|
||||
bool UrlRequest::PostCertRequestInQueryString(const std::string& data) {
|
||||
request_.assign("POST /");
|
||||
request_.append(socket_.resource_path());
|
||||
request_.append("&signedRequest=");
|
||||
request_.append(data);
|
||||
request_.append(" HTTP/1.1\r\n");
|
||||
request_.append("User-Agent: Widevine CDM v1.0\r\n");
|
||||
request_.append("Host: ");
|
||||
request_.append(socket_.domain_name());
|
||||
request_.append("\r\nAccept: */*");
|
||||
request_.append("\r\nContent-Type: application/json");
|
||||
request_.append("\r\nContent-Length: 0");
|
||||
request_.append("\r\n"); // empty line to terminate header
|
||||
request_.append("\r\n"); // terminates the request
|
||||
std::string path = socket_.resource_path();
|
||||
path.append("&signedRequest=");
|
||||
path.append(data);
|
||||
|
||||
socket_.Write(request_.c_str(), request_.size());
|
||||
LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str());
|
||||
LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str());
|
||||
return true;
|
||||
return PostRequestWithPath(path, "");
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -13,29 +13,22 @@ namespace wvcdm {
|
||||
// Only POST request method is implemented.
|
||||
class UrlRequest {
|
||||
public:
|
||||
UrlRequest(const std::string& url, const std::string& port,
|
||||
bool secure_connect, bool chunk_transfer_mode);
|
||||
explicit UrlRequest(const std::string& url);
|
||||
~UrlRequest();
|
||||
|
||||
void AppendChunkToUpload(const std::string& data);
|
||||
void ConcatenateChunkedResponse(const std::string http_response,
|
||||
std::string* modified_response);
|
||||
int GetResponse(std::string* message);
|
||||
int GetStatusCode(const std::string& response);
|
||||
bool is_connected() const { return is_connected_; }
|
||||
|
||||
bool PostRequest(const std::string& data);
|
||||
bool PostRequestChunk(const std::string& data);
|
||||
bool PostCertRequestInQueryString(const std::string& data);
|
||||
|
||||
bool GetResponse(std::string* message);
|
||||
static int GetStatusCode(const std::string& response);
|
||||
|
||||
private:
|
||||
static const unsigned int kHttpBufferSize = 4096;
|
||||
char buffer_[kHttpBufferSize];
|
||||
bool chunk_transfer_mode_;
|
||||
bool PostRequestWithPath(const std::string& path, const std::string& data);
|
||||
|
||||
bool is_connected_;
|
||||
std::string port_;
|
||||
std::string request_;
|
||||
HttpSocket socket_;
|
||||
std::string server_url_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(UrlRequest);
|
||||
};
|
||||
|
||||
@@ -8,13 +8,6 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// If false begin license usage on first playback
|
||||
const bool kPropertyBeginLicenseUsageWhenReceived = false;
|
||||
|
||||
// If false, calls to Generate Key request, after the first one,
|
||||
// will result in a renewal request being generated
|
||||
const bool kPropertyRequireExplicitRenewRequest = false;
|
||||
|
||||
// Set only one of the three below to true. If secure buffer
|
||||
// is selected, fallback to userspace buffers may occur
|
||||
// if L1/L2 OEMCrypto APIs fail
|
||||
@@ -29,10 +22,6 @@ const bool kPropertyOemCryptoRequireUsageTable = true;
|
||||
// and passed as the token in the license request
|
||||
const bool kPropertyUseCertificatesAsIdentification = true;
|
||||
|
||||
// If true, session_id parameter to CdmEngine::Decrypt can be empty; the
|
||||
// function will try to find out the session_id from the key_id.
|
||||
const bool kDecryptWithEmptySessionSupport = false;
|
||||
|
||||
// If true, device files will be moved to the directory specified by
|
||||
// Properties::GetDeviceFilesBasePath
|
||||
const bool kSecurityLevelPathBackwardCompatibilitySupport = true;
|
||||
|
||||
@@ -81,6 +81,8 @@ class WvContentDecryptionModule : public TimerHandler {
|
||||
std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel level);
|
||||
|
||||
// Secure stop related methods
|
||||
virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info);
|
||||
virtual CdmResponseType ReleaseUsageInfo(
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
LogPriority g_cutoff = LOG_VERBOSE;
|
||||
|
||||
void InitLogging(int argc, const char* const* argv) {}
|
||||
|
||||
void Log(const char* file, int line, LogPriority level, const char* fmt, ...) {
|
||||
|
||||
@@ -149,6 +149,11 @@ CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse(
|
||||
return cdm_engine_->HandleProvisioningResponse(response, cert, wrapped_key);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::Unprovision(
|
||||
CdmSecurityLevel level) {
|
||||
return cdm_engine_->Unprovision(level);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::GetUsageInfo(
|
||||
CdmUsageInfo* usage_info) {
|
||||
return cdm_engine_->GetUsageInfo(usage_info);
|
||||
|
||||
@@ -34,13 +34,9 @@ wvcdm::ConfigTestEnv* g_config = NULL;
|
||||
wvcdm::KeyId g_key_id;
|
||||
wvcdm::CdmKeySystem g_key_system;
|
||||
std::string g_license_server;
|
||||
std::string g_port;
|
||||
wvcdm::KeyId g_wrong_key_id;
|
||||
bool g_use_chunked_transfer = false;
|
||||
bool g_use_full_path = false;
|
||||
bool g_use_secure_transfer = false;
|
||||
wvcdm::LicenseServerId g_license_server_id =
|
||||
wvcdm::kYouTubeContentProtectionServer;
|
||||
wvcdm::kContentProtectionServer;
|
||||
|
||||
std::string kServiceCertificate =
|
||||
"0803121028703454C008F63618ADE7443DB6C4C8188BE7F99005228E023082010"
|
||||
@@ -393,8 +389,8 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
session_sharing_id_(0) {}
|
||||
virtual ~TestWvCdmClientPropertySet() {}
|
||||
|
||||
virtual std::string security_level() const { return security_level_; }
|
||||
virtual std::vector<uint8_t> service_certificate() const {
|
||||
virtual const std::string& security_level() const { return security_level_; }
|
||||
virtual const std::string& service_certificate() const {
|
||||
return service_certificate_;
|
||||
}
|
||||
virtual bool use_privacy_mode() const { return use_privacy_mode_; }
|
||||
@@ -409,8 +405,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
security_level_ = security_level;
|
||||
}
|
||||
}
|
||||
void set_service_certificate(
|
||||
const std::vector<uint8_t>& service_certificate) {
|
||||
void set_service_certificate(const std::string& service_certificate) {
|
||||
service_certificate_ = service_certificate;
|
||||
}
|
||||
void set_use_privacy_mode(bool use_privacy_mode) {
|
||||
@@ -423,7 +418,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
|
||||
private:
|
||||
std::string security_level_;
|
||||
std::vector<uint8_t> service_certificate_;
|
||||
std::string service_certificate_;
|
||||
bool use_privacy_mode_;
|
||||
bool is_session_sharing_enabled_;
|
||||
uint32_t session_sharing_id_;
|
||||
@@ -450,6 +445,19 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
~WvCdmRequestLicenseTest() {}
|
||||
|
||||
protected:
|
||||
void GetOfflineConfiguration(std::string* key_id, std::string* client_auth) {
|
||||
ConfigTestEnv config(g_license_server_id, false);
|
||||
if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0)
|
||||
key_id->assign(wvcdm::a2bs_hex(config.key_id()));
|
||||
else
|
||||
key_id->assign(g_key_id);
|
||||
|
||||
if (g_client_auth.compare(g_config->client_auth()) == 0)
|
||||
client_auth->assign(config.client_auth());
|
||||
else
|
||||
client_auth->assign(g_client_auth);
|
||||
}
|
||||
|
||||
void GenerateKeyRequest(const std::string& init_data,
|
||||
CdmLicenseType license_type) {
|
||||
wvcdm::CdmAppParameterMap app_parameters;
|
||||
@@ -492,8 +500,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
std::string GetKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
// Use secure connection and chunk transfer coding.
|
||||
UrlRequest url_request(server_url + client_auth, g_port,
|
||||
g_use_secure_transfer, g_use_chunked_transfer);
|
||||
UrlRequest url_request(server_url + client_auth);
|
||||
if (!url_request.is_connected()) {
|
||||
return "";
|
||||
}
|
||||
@@ -517,7 +524,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
// the HTTP response.
|
||||
std::string GetCertRequestResponse(const std::string& server_url) {
|
||||
// Use secure connection and chunk transfer coding.
|
||||
UrlRequest url_request(server_url, kDefaultHttpsPort, true, true);
|
||||
UrlRequest url_request(server_url);
|
||||
if (!url_request.is_connected()) {
|
||||
return "";
|
||||
}
|
||||
@@ -538,8 +545,7 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
const std::string& client_auth,
|
||||
const std::string& usage_info_request) {
|
||||
// Use secure connection and chunk transfer coding.
|
||||
UrlRequest url_request(server_url + client_auth, g_port,
|
||||
g_use_secure_transfer, g_use_chunked_transfer);
|
||||
UrlRequest url_request(server_url + client_auth);
|
||||
if (!url_request.is_connected()) {
|
||||
return "";
|
||||
}
|
||||
@@ -587,6 +593,20 @@ class WvCdmRequestLicenseTest : public testing::Test {
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
|
||||
CdmSecurityLevel GetDefaultSecurityLevel() {
|
||||
std::string level = GetSecurityLevel(NULL).c_str();
|
||||
CdmSecurityLevel security_level = kSecurityLevelUninitialized;
|
||||
if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) {
|
||||
security_level = kSecurityLevelL1;
|
||||
} else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) {
|
||||
security_level = kSecurityLevelL3;
|
||||
} else {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
return security_level;
|
||||
}
|
||||
|
||||
wvcdm::WvContentDecryptionModule decryptor_;
|
||||
CdmKeyMessage key_msg_;
|
||||
CdmSessionId session_id_;
|
||||
@@ -614,6 +634,20 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, UnprovisionTest) {
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
DeviceFiles handle;
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
File file;
|
||||
handle.SetTestFile(&file);
|
||||
std::string certificate;
|
||||
std::string wrapped_private_key;
|
||||
EXPECT_TRUE(handle.RetrieveCertificate(&certificate, &wrapped_private_key));
|
||||
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Unprovision(security_level));
|
||||
EXPECT_FALSE(handle.RetrieveCertificate(&certificate, &wrapped_private_key));
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningRetryTest) {
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
std::string provisioning_server_url;
|
||||
@@ -710,16 +744,20 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
|
||||
property_set_Ln.set_security_level("");
|
||||
decryptor_.OpenSession(g_key_system, &property_set_Ln, &session_id_Ln);
|
||||
|
||||
std::string security_level = Properties::GetSecurityLevel(session_id_L1);
|
||||
std::string security_level;
|
||||
EXPECT_TRUE(Properties::GetSecurityLevel(session_id_L1, &security_level));
|
||||
EXPECT_TRUE(!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
|
||||
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3));
|
||||
EXPECT_TRUE(Properties::UsePrivacyMode(session_id_L1));
|
||||
EXPECT_EQ(Properties::GetSecurityLevel(session_id_L3),
|
||||
QUERY_VALUE_SECURITY_LEVEL_L3);
|
||||
|
||||
EXPECT_TRUE(Properties::GetSecurityLevel(session_id_L3, &security_level));
|
||||
EXPECT_EQ(security_level, QUERY_VALUE_SECURITY_LEVEL_L3);
|
||||
EXPECT_FALSE(Properties::UsePrivacyMode(session_id_L3));
|
||||
security_level = Properties::GetSecurityLevel(session_id_Ln);
|
||||
|
||||
EXPECT_TRUE(Properties::GetSecurityLevel(session_id_Ln, &security_level));
|
||||
EXPECT_TRUE(security_level.empty() ||
|
||||
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3));
|
||||
|
||||
decryptor_.CloseSession(session_id_L1);
|
||||
decryptor_.CloseSession(session_id_L3);
|
||||
decryptor_.CloseSession(session_id_Ln);
|
||||
@@ -776,7 +814,7 @@ TEST_F(WvCdmRequestLicenseTest, PrivacyModeWithServiceCertificateTest) {
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
|
||||
property_set.set_use_privacy_mode(true);
|
||||
property_set.set_service_certificate(a2b_hex(kServiceCertificate));
|
||||
property_set.set_service_certificate(a2bs_hex(kServiceCertificate));
|
||||
decryptor_.OpenSession(g_key_system, &property_set, &session_id_);
|
||||
GenerateKeyRequest(g_key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, false);
|
||||
@@ -797,8 +835,7 @@ TEST_F(WvCdmRequestLicenseTest, WrongMessageTest) {
|
||||
GenerateKeyRequest(wrong_message, kLicenseTypeStreaming);
|
||||
// We should receive a response with no license, i.e. the extracted license
|
||||
// response message should be empty or an HTTP error
|
||||
UrlRequest url_request(g_license_server + g_client_auth, g_port,
|
||||
g_use_secure_transfer, g_use_chunked_transfer);
|
||||
UrlRequest url_request(g_license_server + g_client_auth);
|
||||
if (!url_request.is_connected()) {
|
||||
return;
|
||||
}
|
||||
@@ -828,14 +865,9 @@ TEST_F(WvCdmRequestLicenseTest, AddStreamingKeyTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id = g_key_id;
|
||||
std::string client_auth = g_client_auth;
|
||||
|
||||
ConfigTestEnv config(g_license_server_id, false);
|
||||
if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0)
|
||||
key_id.assign(wvcdm::a2bs_hex(config.key_id()));
|
||||
if (g_client_auth.compare(g_config->client_auth()) == 0)
|
||||
client_auth.assign(config.client_auth());
|
||||
std::string key_id;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &client_auth);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
||||
@@ -845,14 +877,9 @@ TEST_F(WvCdmRequestLicenseTest, AddKeyOfflineTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id = g_key_id;
|
||||
std::string client_auth = g_client_auth;
|
||||
|
||||
ConfigTestEnv config(g_license_server_id, false);
|
||||
if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0)
|
||||
key_id.assign(wvcdm::a2bs_hex(config.key_id()));
|
||||
if (g_client_auth.compare(g_config->client_auth()) == 0)
|
||||
client_auth.assign(config.client_auth());
|
||||
std::string key_id;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &client_auth);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
||||
@@ -870,14 +897,9 @@ TEST_F(WvCdmRequestLicenseTest, RestoreOfflineKeyTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id = g_key_id;
|
||||
std::string client_auth = g_client_auth;
|
||||
|
||||
ConfigTestEnv config(g_license_server_id, false);
|
||||
if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0)
|
||||
key_id.assign(wvcdm::a2bs_hex(config.key_id()));
|
||||
if (g_client_auth.compare(g_config->client_auth()) == 0)
|
||||
client_auth.assign(config.client_auth());
|
||||
std::string key_id;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &client_auth);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
||||
@@ -902,14 +924,9 @@ TEST_F(WvCdmRequestLicenseTest, ReleaseOfflineKeyTest) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id = g_key_id;
|
||||
std::string client_auth = g_client_auth;
|
||||
|
||||
ConfigTestEnv config(g_license_server_id, false);
|
||||
if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0)
|
||||
key_id.assign(wvcdm::a2bs_hex(config.key_id()));
|
||||
if (g_client_auth.compare(g_config->client_auth()) == 0)
|
||||
client_auth.assign(config.client_auth());
|
||||
std::string key_id;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &client_auth);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
||||
@@ -954,14 +971,9 @@ TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewal) {
|
||||
// override default settings unless configured through the command line
|
||||
std::string key_id = g_key_id;
|
||||
std::string client_auth = g_client_auth;
|
||||
|
||||
ConfigTestEnv config(g_license_server_id, false);
|
||||
if (g_key_id.compare(a2bs_hex(g_config->key_id())) == 0)
|
||||
key_id.assign(wvcdm::a2bs_hex(config.key_id()));
|
||||
if (g_client_auth.compare(g_config->client_auth()) == 0)
|
||||
client_auth.assign(config.client_auth());
|
||||
std::string key_id;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &client_auth);
|
||||
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
||||
@@ -979,19 +991,10 @@ class WvCdmUsageInfoTest
|
||||
public ::testing::WithParamInterface<UsageInfoSubSampleInfo*> {};
|
||||
|
||||
TEST_P(WvCdmUsageInfoTest, DISABLED_UsageInfo) {
|
||||
File file;
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
DeviceFiles handle;
|
||||
|
||||
std::string level = GetSecurityLevel(NULL).c_str();
|
||||
CdmSecurityLevel security_level = kSecurityLevelUninitialized;
|
||||
if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1) == 0) {
|
||||
security_level = kSecurityLevelL1;
|
||||
} else if (level.compare(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0) {
|
||||
security_level = kSecurityLevelL3;
|
||||
} else {
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
File file;
|
||||
handle.SetTestFile(&file);
|
||||
EXPECT_TRUE(handle.DeleteUsageInfo());
|
||||
|
||||
@@ -1037,7 +1040,11 @@ TEST_P(WvCdmUsageInfoTest, DISABLED_UsageInfo) {
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg));
|
||||
}
|
||||
status = decryptor_.GetUsageInfo(&usage_info);
|
||||
EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status);
|
||||
switch (status) {
|
||||
case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); break;
|
||||
case NO_ERROR: EXPECT_TRUE(usage_info.empty()); break;
|
||||
default: FAIL() << "GetUsageInfo failed with error " << status ; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1499,14 +1506,14 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) {
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, DecryptionKeyExpiredTest) {
|
||||
const std::string kYtCpKeyId = a2bs_hex(
|
||||
const std::string kCpKeyId = a2bs_hex(
|
||||
"000000347073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
|
||||
"0801121030313233343536373839616263646566"); // pssh data
|
||||
SubSampleInfo* data = &single_encrypted_sub_sample_short_expiry;
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
if (data->retrieve_key) {
|
||||
GenerateKeyRequest(kYtCpKeyId, kLicenseTypeStreaming);
|
||||
GenerateKeyRequest(kCpKeyId, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, false);
|
||||
}
|
||||
|
||||
@@ -1579,10 +1586,6 @@ void show_menu(char* prog_name) {
|
||||
std::cout << " or adb shell '" << prog_name << " -u\"url\"'" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
std::cout << std::setw(35) << std::left << " -c/--chunked_transfer";
|
||||
std::cout << "specifies chunked transfer encoding in request" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
std::cout << std::setw(35) << std::left << " -f/--use_full_path";
|
||||
std::cout << "specify server url is not a proxy server" << std::endl;
|
||||
std::cout << std::endl;
|
||||
@@ -1592,7 +1595,7 @@ void show_menu(char* prog_name) {
|
||||
std::cout << std::setw(35) << std::left << " ";
|
||||
std::cout << "gp (case sensitive) for GooglePlay server" << std::endl;
|
||||
std::cout << std::setw(35) << std::left << " ";
|
||||
std::cout << "cp (case sensitive) for Youtube Content Protection server"
|
||||
std::cout << "cp (case sensitive) for Content Protection server"
|
||||
<< std::endl << std::endl;
|
||||
|
||||
std::cout << std::setw(35) << std::left << " -k/--keyid=<key_id>";
|
||||
@@ -1616,34 +1619,22 @@ int main(int argc, char** argv) {
|
||||
|
||||
bool show_usage = false;
|
||||
static const struct option long_options[] = {
|
||||
{"chunked_transfer", no_argument, NULL, 'c'},
|
||||
{"keyid", required_argument, NULL, 'k'},
|
||||
{"license_server_id", required_argument, NULL, 'i'},
|
||||
{"license_server_url", required_argument, NULL, 'u'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"secure_transfer", no_argument, NULL, 's'},
|
||||
{"use_full_path", no_argument, NULL, 'f'},
|
||||
{NULL, 0, NULL, '\0'}};
|
||||
|
||||
int option_index = 0;
|
||||
int opt = 0;
|
||||
while ((opt = getopt_long(argc, argv, "cfi:k:p:su:", long_options,
|
||||
while ((opt = getopt_long(argc, argv, "i:k:u:", long_options,
|
||||
&option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'c': {
|
||||
g_use_chunked_transfer = true;
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
g_use_full_path = true;
|
||||
break;
|
||||
}
|
||||
case 'i': {
|
||||
std::string license_id(optarg);
|
||||
if (!license_id.compare("gp")) {
|
||||
g_license_server_id = wvcdm::kGooglePlayServer;
|
||||
} else if (!license_id.compare("cp")) {
|
||||
g_license_server_id = wvcdm::kYouTubeContentProtectionServer;
|
||||
g_license_server_id = wvcdm::kContentProtectionServer;
|
||||
} else {
|
||||
std::cout << "Invalid license server id" << optarg << std::endl;
|
||||
show_usage = true;
|
||||
@@ -1655,15 +1646,6 @@ int main(int argc, char** argv) {
|
||||
g_key_id.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
g_port.clear();
|
||||
g_port.assign(optarg);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
g_use_secure_transfer = true;
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
g_license_server.clear();
|
||||
g_license_server.assign(optarg);
|
||||
@@ -1688,32 +1670,21 @@ int main(int argc, char** argv) {
|
||||
|
||||
// The following variables are configurable through command line
|
||||
// options. If the command line arguments are absent, use the settings
|
||||
// in license_servers[] pointed to by g_config.
|
||||
// in kLicenseServers[] pointed to by g_config.
|
||||
if (g_key_id.empty()) {
|
||||
g_key_id.assign(g_config->key_id());
|
||||
}
|
||||
if (g_license_server.empty()) {
|
||||
g_license_server.assign(g_config->license_server());
|
||||
}
|
||||
if (g_port.empty()) {
|
||||
g_port.assign(g_config->port());
|
||||
}
|
||||
if (!g_use_chunked_transfer) {
|
||||
g_use_chunked_transfer = g_config->use_chunked_transfer();
|
||||
}
|
||||
if (!g_use_secure_transfer) {
|
||||
g_use_secure_transfer = g_config->use_secure_transfer();
|
||||
}
|
||||
|
||||
// Displays server url, port and key Id being used
|
||||
std::cout << std::endl;
|
||||
std::cout << "Server: " << g_license_server << std::endl;
|
||||
std::cout << "Port: " << g_port << std::endl;
|
||||
std::cout << "KeyID: " << g_key_id << std::endl << std::endl;
|
||||
|
||||
g_key_id = wvcdm::a2bs_hex(g_key_id);
|
||||
g_config->set_license_server(g_license_server);
|
||||
g_config->set_port(g_port);
|
||||
|
||||
int status = RUN_ALL_TESTS();
|
||||
delete g_config;
|
||||
|
||||
@@ -8,10 +8,10 @@ namespace wvcdm {
|
||||
namespace test_vectors {
|
||||
|
||||
// for FileStore unit tests
|
||||
static const std::string kFileExists = "/system/bin/sh";
|
||||
static const std::string kDirExists = "/system/bin";
|
||||
static const std::string kFileDoesNotExist = "/system/bin/enoext";
|
||||
static const std::string kDirDoesNotExist = "/system/bin_enoext";
|
||||
static const std::string kExistentFile = "/system/bin/sh";
|
||||
static const std::string kExistentDir = "/system/bin";
|
||||
static const std::string kNonExistentFile = "/system/bin/enoext";
|
||||
static const std::string kNonExistentDir = "/system/bin_enoext";
|
||||
static const std::string kTestDir = "/data/mediadrm/IDM0/";
|
||||
|
||||
} // namespace test_vectors
|
||||
|
||||
@@ -51,6 +51,8 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libstlport \
|
||||
libutils
|
||||
|
||||
LOCAL_CFLAGS += -DUNIT_TEST
|
||||
|
||||
# Needed to use gMock 1.7.0 on Android
|
||||
LOCAL_CFLAGS += \
|
||||
-DGTEST_HAS_TR1_TUPLE \
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -175,7 +175,7 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
|
||||
virtual ~WVClientPropertySet() {}
|
||||
|
||||
virtual std::string security_level() const {
|
||||
virtual const std::string& security_level() const {
|
||||
return mSecurityLevel;
|
||||
}
|
||||
|
||||
@@ -191,11 +191,11 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
mUsePrivacyMode = usePrivacyMode;
|
||||
}
|
||||
|
||||
virtual std::vector<uint8_t> service_certificate() const {
|
||||
virtual const std::string& service_certificate() const {
|
||||
return mServiceCertificate;
|
||||
}
|
||||
|
||||
void set_service_certificate(const std::vector<uint8_t>& serviceCertificate) {
|
||||
void set_service_certificate(const std::string& serviceCertificate) {
|
||||
mServiceCertificate = serviceCertificate;
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
|
||||
std::string mSecurityLevel;
|
||||
bool mUsePrivacyMode;
|
||||
std::vector<uint8_t> mServiceCertificate;
|
||||
std::string mServiceCertificate;
|
||||
bool mShareKeys;
|
||||
uint32_t mSessionSharingId;
|
||||
} mPropertySet;
|
||||
|
||||
@@ -498,9 +498,9 @@ status_t WVDrmPlugin::getPropertyByteArray(const String8& name,
|
||||
value.appendArray(reinterpret_cast<const uint8_t*>(uniqueId.data()),
|
||||
uniqueId.size());
|
||||
} else if (name == "serviceCertificate") {
|
||||
vector<uint8_t> cert = mPropertySet.service_certificate();
|
||||
std::string cert = mPropertySet.service_certificate();
|
||||
value.clear();
|
||||
value.appendArray(&cert[0], cert.size());
|
||||
value.appendArray(reinterpret_cast<const uint8_t*>(&cert[0]), cert.size());
|
||||
} else {
|
||||
ALOGE("App requested unknown byte array property %s", name.string());
|
||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||
@@ -577,7 +577,7 @@ status_t WVDrmPlugin::setPropertyString(const String8& name,
|
||||
status_t WVDrmPlugin::setPropertyByteArray(const String8& name,
|
||||
const Vector<uint8_t>& value) {
|
||||
if (name == "serviceCertificate") {
|
||||
vector<uint8_t> cert(value.begin(), value.end());
|
||||
std::string cert(value.begin(), value.end());
|
||||
mPropertySet.set_service_certificate(cert);
|
||||
} else {
|
||||
ALOGE("App set unknown byte array property %s", name.string());
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#ifndef OEMCRYPTO_CENC_H_
|
||||
#define OEMCRYPTO_CENC_H_
|
||||
|
||||
#include<stdbool.h>
|
||||
#include<stddef.h>
|
||||
#include<stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
65
libwvdrmengine/oemcrypto/include/oemcrypto_logging.h
Normal file
65
libwvdrmengine/oemcrypto/include/oemcrypto_logging.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#ifndef WVOEC_OEMCRYPTO_LOGGING_H_
|
||||
#define WVOEC_OEMCRYPTO_LOGGING_H_
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include "log.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
// The constants below represent integers with a single "on" bit that
|
||||
// represents categories of logging This allows users to specify with
|
||||
// more precision what they want to log. LogCategoryEnabled(category)
|
||||
// is used to see if the category passed in the parameters is to
|
||||
// be logged based on the current settings. Categories can be combines
|
||||
// using the | (or) bitwise operator. For example
|
||||
// LogCategoryEnabled(category1 | category2) will return true if
|
||||
// category1 and/or category2 are set to logging.
|
||||
|
||||
const int kLoggingTraceOEMCryptoCalls = 0x01;
|
||||
const int kLoggingDumpContentKeys = 0x02;
|
||||
const int kLoggingDumpKeyControlBlocks = 0x04;
|
||||
const int kLoggingDumpDerivedKeys = 0x08;
|
||||
const int kLoggingTraceNonce = 0x10;
|
||||
const int kLoggingTraceDecryption = 0x20;
|
||||
const int kLoggingTraceUsageTable = 0x40;
|
||||
const int kLoggingDumpTraceAll = 0xFF;
|
||||
|
||||
void SetLoggingSettings(int level, int categories);
|
||||
|
||||
// set level of logging
|
||||
void SetLoggingLevel(int level);
|
||||
|
||||
void TurnOffLoggingForAllCategories();
|
||||
|
||||
// Returns true if the category passed is set to logging.
|
||||
// Returns false otherwise. The category constant declared
|
||||
// above are passed.
|
||||
bool LogCategoryEnabled(int category);
|
||||
|
||||
// Turn on logging for the categories passed.
|
||||
void AddLoggingForCategories(int categories);
|
||||
|
||||
// Turn off logging for the categories passed.
|
||||
void RemoveLoggingForCategories(int categories);
|
||||
|
||||
void dump_hex_helper(std::string& buffer, std::string name,
|
||||
const uint8_t* vector, size_t length);
|
||||
|
||||
void dump_hex(std::string name, const uint8_t* vector, size_t length);
|
||||
|
||||
void dump_array_part_helper(std::string& buffer, std::string array,
|
||||
size_t index, std::string name,
|
||||
const uint8_t* vector, size_t length);
|
||||
|
||||
void dump_array_part(std::string array, size_t index,
|
||||
std::string name, const uint8_t* vector, size_t length);
|
||||
|
||||
} // namespace wvoec_mock
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
#
|
||||
# Builds liboemcrypto_mock.so
|
||||
#
|
||||
|
||||
#PROJECTS_ROOT = ~projects
|
||||
#
|
||||
ifndef PROJECTS_ROOT
|
||||
PROJECTS_ROOT = ../../../..
|
||||
endif
|
||||
|
||||
CDM_ROOT = $(PROJECTS_ROOT)/cdm
|
||||
CDM_SRC_PATH = $(CDM_ROOT)/cdm
|
||||
CDM_INCLUDE_PATH = $(CDM_SRC_PATH)/include
|
||||
|
||||
EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka/src
|
||||
CHROME_ROOT = $(EUREKA_ROOT)/chromium/src
|
||||
#
|
||||
# build outputs should go into Chrome repository, such as ../chromium/src/out
|
||||
# or some local equivalent.
|
||||
# WARNING: splitting outputs from CHROME_ROOT can lead to build errors
|
||||
ifndef CHROME_ROOT
|
||||
CHROME_ROOT = $(CDM_ROOT)/out
|
||||
endif
|
||||
|
||||
# TARGET_PLATFORM from {x86,eureka}
|
||||
ifndef TARGET_PLATFORM
|
||||
TARGET_PLATFORM = x86
|
||||
endif
|
||||
|
||||
# TARGET_BUILD from {debug,release}
|
||||
ifndef TARGET_BUILD
|
||||
TARGET_BUILD = debug
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_PLATFORM),x86)
|
||||
BUILDPLATFORM = out_x86_linux
|
||||
else ifeq ($(TARGET_PLATFORM),eureka)
|
||||
BUILDPLATFORM = out_arm_eureka
|
||||
else
|
||||
BUILDPLATFORM = UNKNOWN
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BUILD),debug)
|
||||
BUILDTYPE = Debug
|
||||
else ifeq ($(TARGET_BUILD),release)
|
||||
BUILDTYPE = Release
|
||||
else
|
||||
BUILDTYPE = UNKNOWN
|
||||
endif
|
||||
|
||||
BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE)
|
||||
OBJPATH = $(BUILDPATH)/obj
|
||||
|
||||
#primary build target
|
||||
TARGET_LIB = liboemcrypto_mock.so
|
||||
|
||||
LIB_OBJECTS = \
|
||||
oemcrypto_mock.o \
|
||||
oemcrypto_engine_mock.o \
|
||||
oemcrypto_key_mock.o \
|
||||
oemcrypto_keybox_mock.o \
|
||||
oemcrypto_usage_table_mock.o
|
||||
|
||||
INCLUDES = \
|
||||
-I$(CDM_INCLUDE_PATH)
|
||||
|
||||
OBJECTDIR = $(OBJPATH)/mock
|
||||
|
||||
INSTALLDIR = $(BUILDPATH)
|
||||
|
||||
OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(LIB_OBJECTS))
|
||||
|
||||
CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST
|
||||
LINK = $(CXX)
|
||||
MKDIR = mkdir -p
|
||||
|
||||
$(INSTALLDIR)/$(TARGET_LIB): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS)
|
||||
$(LINK) -v -fPIC -m64 -shared -static-libgcc $(SHLIBFLAGS) \
|
||||
$(OBJECTS) \
|
||||
--retain-symbols-file=OECsymbols.txt \
|
||||
-Wl,-Bstatic \
|
||||
-Wl,-Bdynamic -lcrypto \
|
||||
-o $@
|
||||
|
||||
$(OBJECTDIR)/%.o: src/%.cpp
|
||||
$(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
$(OBJECTDIR)/%.o: src/%.cc
|
||||
$(CXX) -c -DCDM_TEST $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
test:
|
||||
make -C test
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(OBJECTDIR)
|
||||
$(RM) -rf $(INSTALLDIR)/$(TARGET_LIB)
|
||||
|
||||
$(OBJECTDIR):
|
||||
@$(MKDIR) $@
|
||||
|
||||
$(INSTALLDIR):
|
||||
@$(MKDIR) $@
|
||||
|
||||
.PHONY: $(OBJECTDIR)
|
||||
|
||||
.PHONY: $(INSTALLDIR)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
.PHONY: test
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "log.h"
|
||||
#include "oemcrypto_key_mock.h"
|
||||
#include "oemcrypto_logging.h"
|
||||
#include "oemcrypto_usage_table_mock.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/bio.h"
|
||||
@@ -34,7 +35,7 @@ void ctr128_inc64(uint8_t* counter) {
|
||||
uint32_t n = 16;
|
||||
do {
|
||||
if (++counter[--n] != 0) return;
|
||||
} while (n>8);
|
||||
} while (n > 8);
|
||||
}
|
||||
void dump_openssl_error() {
|
||||
while (unsigned long err = ERR_get_error()) {
|
||||
@@ -43,7 +44,7 @@ void dump_openssl_error() {
|
||||
err, ERR_error_string(err, buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
@@ -76,7 +77,7 @@ void SessionKeyTable::Remove(const KeyId key_id) {
|
||||
}
|
||||
|
||||
void SessionKeyTable::UpdateDuration(const KeyControlBlock& control) {
|
||||
for(KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) {
|
||||
for (KeyMap::iterator it = keys_.begin(); it != keys_.end(); ++it) {
|
||||
it->second->UpdateDuration(control);
|
||||
}
|
||||
}
|
||||
@@ -157,17 +158,13 @@ bool SessionContext::DeriveKeys(const std::vector<uint8_t>& master_key,
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0 // Print Derived Keys to stdout.
|
||||
std::cout << " mac_key_context = " << wvcdm::b2a_hex(mac_key_context)
|
||||
<< std::endl;
|
||||
std::cout << " enc_key_context = " << wvcdm::b2a_hex(enc_key_context)
|
||||
<< std::endl;
|
||||
std::cout << " mac_key_server = " << wvcdm::b2a_hex(mac_key_server)
|
||||
<< std::endl;
|
||||
std::cout << " mac_key_client = " << wvcdm::b2a_hex(mac_key_client)
|
||||
<< std::endl;
|
||||
std::cout << " enc_key = " << wvcdm::b2a_hex(enc_key) << std::endl;
|
||||
#endif
|
||||
if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) {
|
||||
LOGI((" mac_key_context = " + wvcdm::b2a_hex(mac_key_context)).c_str());
|
||||
LOGI((" enc_key_context = " + wvcdm::b2a_hex(enc_key_context)).c_str());
|
||||
LOGI((" mac_key_server = " + wvcdm::b2a_hex(mac_key_server)).c_str());
|
||||
LOGI((" mac_key_client = " + wvcdm::b2a_hex(mac_key_client)).c_str());
|
||||
LOGI((" enc_key = " + wvcdm::b2a_hex(enc_key)).c_str());
|
||||
}
|
||||
|
||||
set_mac_key_server(mac_key_server);
|
||||
set_mac_key_client(mac_key_client);
|
||||
@@ -183,7 +180,7 @@ bool SessionContext::RSADeriveKeys(const std::vector<uint8_t>& enc_session_key,
|
||||
return false;
|
||||
}
|
||||
if (enc_session_key.size() != static_cast<size_t>(RSA_size(rsa_key_))) {
|
||||
LOGE("[RSADeriveKeys(): encrypted session key is wrong size:%zu, should be %d]",
|
||||
LOGE("[RSADeriveKeys(): encrypted session key wrong size:%zu, expected %d]",
|
||||
enc_session_key.size(), RSA_size(rsa_key_));
|
||||
dump_openssl_error();
|
||||
return false;
|
||||
@@ -214,7 +211,6 @@ bool SessionContext::GenerateSignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
|
||||
if (message == NULL || message_length == 0 ||
|
||||
signature == NULL || signature_length == 0) {
|
||||
LOGE("[OEMCrypto_GenerateSignature(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
@@ -305,7 +301,7 @@ bool SessionContext::GenerateRSASignature(const uint8_t* message,
|
||||
return false;
|
||||
}
|
||||
// Pad the message with PKCS1 padding, and then encrypt.
|
||||
int status = RSA_private_encrypt(message_length, message, signature,
|
||||
size_t status = RSA_private_encrypt(message_length, message, signature,
|
||||
rsa_key_, RSA_PKCS1_PADDING);
|
||||
if (status != *signature_length) {
|
||||
LOGE("[GeneratRSASignature(): error in RSA private encrypt. status=%d]", status);
|
||||
@@ -323,7 +319,6 @@ bool SessionContext::ValidateMessage(const uint8_t* given_message,
|
||||
size_t message_length,
|
||||
const uint8_t* given_signature,
|
||||
size_t signature_length) {
|
||||
|
||||
if (signature_length != SHA256_DIGEST_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
@@ -471,14 +466,14 @@ bool SessionContext::InstallKey(const KeyId& key_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0 // Print content key to stdout.
|
||||
std::cout << " InstallKey: key_id = "
|
||||
<< wvcdm::b2a_hex(key_id) << std::endl;
|
||||
std::cout << " InstallKey: content_key = "
|
||||
<< wvcdm::b2a_hex(content_key) << std::endl;
|
||||
std::cout << " InstallKey: key_control = "
|
||||
<< wvcdm::b2a_hex(key_control_str) << std::endl;
|
||||
#endif
|
||||
if (LogCategoryEnabled(kLoggingDumpContentKeys)) {
|
||||
LOGI((" InstallKey: key_id = " +
|
||||
wvcdm::b2a_hex(key_id)).c_str());
|
||||
LOGI((" InstallKey: content_key = " +
|
||||
wvcdm::b2a_hex(content_key)).c_str());
|
||||
LOGI((" InstallKey: key_control = " +
|
||||
wvcdm::b2a_hex(key_control_str)).c_str());
|
||||
}
|
||||
|
||||
// Key control must be supplied by license server
|
||||
if (key_control.empty()) {
|
||||
@@ -517,7 +512,7 @@ bool SessionContext::RefreshKey(const KeyId& key_id,
|
||||
// Key control is not encrypted if key id is NULL
|
||||
KeyControlBlock key_control_block(key_control);
|
||||
if (!key_control_block.valid()) {
|
||||
LOGD("Parse key control error.");
|
||||
LOGE("Parse key control error.");
|
||||
return false;
|
||||
}
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled) &&
|
||||
@@ -533,12 +528,16 @@ bool SessionContext::RefreshKey(const KeyId& key_id,
|
||||
Key* content_key = session_keys_.Find(key_id);
|
||||
|
||||
if (NULL == content_key) {
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
LOGD("Error: no matching content key.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key_control.empty()) {
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
LOGD("Error: no key_control.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -547,20 +546,28 @@ bool SessionContext::RefreshKey(const KeyId& key_id,
|
||||
// Decrypt encrypted key control block
|
||||
std::vector<uint8_t> control;
|
||||
if (key_control_iv.empty()) {
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
LOGD("Key control block is NOT encrypted.");
|
||||
}
|
||||
control = key_control;
|
||||
} else {
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
LOGD("Key control block is encrypted.");
|
||||
}
|
||||
if (!DecryptMessage(content_key_value, key_control_iv, key_control,
|
||||
&control)) {
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
LOGD("Error decrypting key control block.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
KeyControlBlock key_control_block(control);
|
||||
if (!key_control_block.valid()) {
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
LOGD("Parse key control error.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ((key_control_block.control_bits() & kControlNonceEnabled) &&
|
||||
@@ -606,7 +613,6 @@ bool SessionContext::LoadRSAKey(uint8_t* pkcs8_rsa_key,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
|
||||
// Validate message signature
|
||||
if (!ValidateMessage(message, message_length, signature, signature_length)) {
|
||||
LOGE("[LoadRSAKey(): Could not verify signature]");
|
||||
@@ -627,7 +633,7 @@ bool SessionContext::LoadRSAKey(uint8_t* pkcs8_rsa_key,
|
||||
rsa_key_length -= 8;
|
||||
}
|
||||
BIO *bio = BIO_new_mem_buf(pkcs8_rsa_key, rsa_key_length);
|
||||
if( bio == NULL ) {
|
||||
if ( bio == NULL ) {
|
||||
LOGE("[LoadRSAKey(): Could not allocate bio buffer]");
|
||||
return false;
|
||||
}
|
||||
@@ -690,7 +696,7 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
|
||||
const KeyControlBlock& control = current_content_key()->control();
|
||||
// Set the AES key.
|
||||
if (static_cast<int>(key.size()) != AES_BLOCK_SIZE) {
|
||||
LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d",key.size());
|
||||
LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %d", key.size());
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (!(control.control_bits() & kControlAllowEncrypt)) {
|
||||
@@ -709,11 +715,11 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
if ( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
LOGE("[Generic_Encrypt(): algorithm bad.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if( buffer_length % AES_BLOCK_SIZE != 0 ) {
|
||||
if ( buffer_length % AES_BLOCK_SIZE != 0 ) {
|
||||
LOGE("[Generic_Encrypt(): buffers size bad.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
@@ -767,11 +773,11 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
if ( algorithm != OEMCrypto_AES_CBC_128_NO_PADDING ) {
|
||||
LOGE("[Generic_Decrypt(): bad algorithm.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if( buffer_length % AES_BLOCK_SIZE != 0 ) {
|
||||
if ( buffer_length % AES_BLOCK_SIZE != 0 ) {
|
||||
LOGE("[Generic_Decrypt(): bad buffer size.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
@@ -875,7 +881,7 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if( algorithm != OEMCrypto_HMAC_SHA256 ) {
|
||||
if ( algorithm != OEMCrypto_HMAC_SHA256 ) {
|
||||
LOGE("[Generic_Verify(): bad algorithm.");
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
@@ -896,7 +902,6 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer,
|
||||
|
||||
bool SessionContext::UpdateMacKeys(const std::vector<uint8_t>& enc_mac_keys,
|
||||
const std::vector<uint8_t>& iv) {
|
||||
|
||||
// Decrypt mac key from enc_mac_key using device_keya
|
||||
std::vector<uint8_t> mac_keys;
|
||||
if (!DecryptMessage(encryption_key_, iv, enc_mac_keys, &mac_keys)) {
|
||||
@@ -911,12 +916,14 @@ bool SessionContext::UpdateMacKeys(const std::vector<uint8_t>& enc_mac_keys,
|
||||
|
||||
bool SessionContext::SelectContentKey(const KeyId& key_id) {
|
||||
const Key* content_key = session_keys_.Find(key_id);
|
||||
#if 0
|
||||
std::cout << " Select Key: key_id = "
|
||||
<< wvcdm::b2a_hex(key_id) << std::endl;
|
||||
std::cout << " Select Key: key = "
|
||||
<< wvcdm::b2a_hex(content_key->value()) << std::endl;
|
||||
#endif
|
||||
|
||||
if (LogCategoryEnabled(kLoggingTraceDecryption)){
|
||||
LOGI(( " Select Key: key_id = " +
|
||||
wvcdm::b2a_hex(key_id) ).c_str());
|
||||
LOGI(( " Select Key: key = " +
|
||||
wvcdm::b2a_hex(content_key->value()) ).c_str());
|
||||
}
|
||||
|
||||
if (NULL == content_key) {
|
||||
LOGE("[SelectContentKey(): No key matches key id]");
|
||||
return false;
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
#include "oemcrypto_key_mock.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "log.h"
|
||||
#include "oemcrypto_logging.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
#define FOURCC(c1, c2, c3, c4) \
|
||||
@@ -40,7 +43,7 @@ uint32_t KeyControlBlock::ExtractField(const std::vector<uint8_t>& str,
|
||||
KeyControlBlock::KeyControlBlock(
|
||||
const std::vector<uint8_t>& key_control_string) {
|
||||
if (key_control_string.size() < wvcdm::KEY_CONTROL_SIZE) {
|
||||
LOGE("KCB: BAD Size: %d (not %d)",key_control_string.size(),
|
||||
LOGE("KCB: BAD Size: %d (not %d)", key_control_string.size(),
|
||||
wvcdm::KEY_CONTROL_SIZE);
|
||||
return;
|
||||
}
|
||||
@@ -49,7 +52,7 @@ KeyControlBlock::KeyControlBlock(
|
||||
duration_ = ExtractField(key_control_string, 1);
|
||||
nonce_ = ExtractField(key_control_string, 2);
|
||||
control_bits_ = ExtractField(key_control_string, 3);
|
||||
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
LOGD("KCB:");
|
||||
LOGD(" valid: %d", valid());
|
||||
LOGD(" duration: %d", duration());
|
||||
@@ -68,7 +71,8 @@ KeyControlBlock::KeyControlBlock(
|
||||
break;
|
||||
}
|
||||
LOGD(" bits kControlKDCPVersion 0x%02x.",
|
||||
(control_bits() & kControlHDCPVersionMask) >> kControlHDCPVersionShift);
|
||||
(control_bits() & kControlHDCPVersionMask)
|
||||
>> kControlHDCPVersionShift);
|
||||
LOGD(" bit kControlAllowEncrypt %s.",
|
||||
(control_bits() & kControlAllowEncrypt) ? "set" : "unset");
|
||||
LOGD(" bit kControlAllowDecrypt %s.",
|
||||
@@ -92,6 +96,7 @@ KeyControlBlock::KeyControlBlock(
|
||||
uint32_t cgms_bits = control_bits() & 0x3;
|
||||
const char* cgms_values[4] = {"free", "BAD", "once", "never"};
|
||||
LOGD(" CGMS = %s", cgms_values[cgms_bits]);
|
||||
}
|
||||
Validate();
|
||||
}
|
||||
|
||||
@@ -102,4 +107,4 @@ void Key::UpdateDuration(const KeyControlBlock& control) {
|
||||
control_.set_duration(control.duration());
|
||||
}
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
}; // namespace wvoec_mock
|
||||
|
||||
@@ -104,4 +104,4 @@ bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace wvoec_eng
|
||||
}; // namespace wvoec_mock
|
||||
|
||||
109
libwvdrmengine/oemcrypto/mock/src/oemcrypto_logging.cpp
Normal file
109
libwvdrmengine/oemcrypto/mock/src/oemcrypto_logging.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "oemcrypto_logging.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
int logging_category_setting = 0x00;
|
||||
|
||||
void SetLoggingSettings(int level, int categories) {
|
||||
SetLoggingLevel(level);
|
||||
TurnOffLoggingForAllCategories();
|
||||
AddLoggingForCategories(categories);
|
||||
}
|
||||
|
||||
void TurnOffLoggingForAllCategories() {
|
||||
logging_category_setting = 0;
|
||||
}
|
||||
|
||||
void SetLoggingLevel(int level){;
|
||||
wvcdm::g_cutoff = static_cast<wvcdm::LogPriority>(level);
|
||||
}
|
||||
|
||||
void SetLoggingLevel(wvcdm::LogPriority level) {
|
||||
wvcdm::g_cutoff = level;
|
||||
}
|
||||
|
||||
void AddLoggingForCategories(int categories) {
|
||||
logging_category_setting |= categories;
|
||||
}
|
||||
|
||||
void RemoveLoggingForCategories(int categories) {
|
||||
logging_category_setting &= ~categories;
|
||||
}
|
||||
|
||||
bool LogCategoryEnabled(int categories) {
|
||||
return ( (logging_category_setting & categories) !=0 );
|
||||
}
|
||||
|
||||
void dump_hex_helper(std::string& buffer, std::string name,
|
||||
const uint8_t* vector, size_t length) {
|
||||
buffer += name + " = ";
|
||||
if (vector == NULL) {
|
||||
buffer +="NULL;\n";
|
||||
LOGE(buffer.c_str());
|
||||
return;
|
||||
}
|
||||
int a, b;
|
||||
char int_to_hexcar[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (i == 0) {
|
||||
buffer += "\n wvcdm::a2b_hex(\"";
|
||||
} else if (i % 32 == 0) {
|
||||
buffer += "\"\n \"";
|
||||
}
|
||||
a = vector[i] % 16;
|
||||
b = (vector[i] - a) / 16;
|
||||
buffer += int_to_hexcar[b];
|
||||
buffer += int_to_hexcar[a];
|
||||
}
|
||||
buffer += "\");\n";
|
||||
}
|
||||
|
||||
void dump_hex(std::string name, const uint8_t* vector, size_t length) {
|
||||
std::string buffer="";
|
||||
dump_hex_helper(buffer, name, vector, length);
|
||||
LOGV(buffer.c_str());
|
||||
}
|
||||
|
||||
void dump_array_part_helper(std::string& buffer, std::string array,
|
||||
size_t index, std::string name,
|
||||
const uint8_t* vector, size_t length) {
|
||||
char index_str[256];
|
||||
|
||||
snprintf(index_str, sizeof index_str, "%zu", index);
|
||||
|
||||
if (vector == NULL) {
|
||||
buffer += array.c_str();
|
||||
buffer += "[";
|
||||
buffer += index_str;
|
||||
buffer += "].";
|
||||
buffer += name.c_str();
|
||||
buffer += " = NULL;\n";
|
||||
LOGW(buffer.c_str());
|
||||
return;
|
||||
}
|
||||
buffer += "std::string s";
|
||||
buffer += index_str;
|
||||
buffer+= "_";
|
||||
dump_hex_helper(buffer, name, vector, length);
|
||||
buffer += array.c_str();
|
||||
buffer += "[";
|
||||
buffer += index_str;
|
||||
buffer += "]." + name + " = message_ptr + message.find(s";
|
||||
buffer += index_str;
|
||||
buffer += "_" + name + ".data());\n";
|
||||
}
|
||||
|
||||
void dump_array_part(std::string array, size_t index,
|
||||
std::string name, const uint8_t* vector, size_t length) {
|
||||
std::string buffer ="";
|
||||
dump_array_part_helper(buffer, array, index, name, vector, length);
|
||||
LOGV(buffer.c_str());
|
||||
}
|
||||
|
||||
} // namespace wvoec_mock
|
||||
|
||||
@@ -9,23 +9,23 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "log.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
#include "oemcrypto_logging.h"
|
||||
#include "oemcrypto_usage_table_mock.h"
|
||||
#include "openssl/cmac.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/hmac.h"
|
||||
#include "openssl/rand.h"
|
||||
#include "openssl/sha.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
namespace wvoec_mock {
|
||||
|
||||
static CryptoEngine* crypto_engine = NULL;
|
||||
|
||||
// Set this to true when you are generating test vectors.
|
||||
const bool trace_all_calls = false;
|
||||
|
||||
typedef struct {
|
||||
uint8_t signature[wvcdm::MAC_KEY_SIZE];
|
||||
uint8_t context[wvcdm::MAC_KEY_SIZE];
|
||||
@@ -33,39 +33,10 @@ typedef struct {
|
||||
uint8_t enc_rsa_key[];
|
||||
} WrappedRSAKey;
|
||||
|
||||
static void dump_hex(std::string name, const uint8_t* vector, size_t length) {
|
||||
printf("%s = ", name.c_str());
|
||||
if (vector == NULL) {
|
||||
printf("NULL;\n");
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (i == 0) {
|
||||
printf("\n wvcdm::a2b_hex(\"");
|
||||
} else if (i % 32 == 0) {
|
||||
printf("\"\n \"");
|
||||
}
|
||||
printf("%02X", vector[i]);
|
||||
}
|
||||
printf("\");\n");
|
||||
}
|
||||
|
||||
void dump_array_part(std::string array, size_t index,
|
||||
std::string name, const uint8_t* vector, size_t length) {
|
||||
if (vector == NULL) {
|
||||
printf("%s[%zu].%s = NULL;\n", array.c_str(), index, name.c_str());
|
||||
return;
|
||||
}
|
||||
printf("std::string s%zu_", index);
|
||||
dump_hex(name, vector, length);
|
||||
printf("%s[%zu].%s = message_ptr + message.find(s%zu_%s.data());\n",
|
||||
array.c_str(), index, name.c_str(), index, name.c_str());
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Initialize(void) {
|
||||
if (trace_all_calls) {
|
||||
printf("------------------------- OEMCrypto_Initialize(void)\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("------------------------- OEMCrypto_Initialize(void)\n");
|
||||
}
|
||||
|
||||
crypto_engine = new CryptoEngine;
|
||||
@@ -74,14 +45,16 @@ OEMCryptoResult OEMCrypto_Initialize(void) {
|
||||
LOGE("[OEMCrypto_Initialize(): failed]");
|
||||
return OEMCrypto_ERROR_INIT_FAILED;
|
||||
}
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGD("[OEMCrypto_Initialize(): success]");
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_Terminate(void) {
|
||||
if (trace_all_calls) {
|
||||
printf("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("----------------- OEMCryptoResult OEMCrypto_Terminate(void)\n");
|
||||
}
|
||||
|
||||
if (!crypto_engine) {
|
||||
@@ -95,31 +68,41 @@ OEMCryptoResult OEMCrypto_Terminate(void) {
|
||||
|
||||
delete crypto_engine;
|
||||
crypto_engine = NULL;
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGD("[OEMCrypto_Terminate(): success]");
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION *session)\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_OpenSession"
|
||||
"(OEMCrypto_SESSION *session)\n");
|
||||
}
|
||||
SessionId sid = crypto_engine->CreateSession();
|
||||
*session = (OEMCrypto_SESSION)sid;
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGD("[OEMCrypto_OpenSession(): SID=%08x]", sid);
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session)\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_CloseSession"
|
||||
"(OEMCrypto_SESSION session)\n");
|
||||
}
|
||||
if (!crypto_engine->DestroySession((SessionId)session)) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGD("[OEMCrypto_CloseSession(SID=%08X): failed]", session);
|
||||
}
|
||||
return OEMCrypto_ERROR_CLOSE_SESSION_FAILED;
|
||||
} else {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGD("[OEMCrypto_CloseSession(SID=%08X): success]", session);
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -127,8 +110,9 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session) {
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
||||
uint32_t* nonce) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GenerateNonce"
|
||||
"(OEMCrypto_SESSION session,\n");
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
@@ -161,8 +145,8 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
||||
}
|
||||
session_ctx->AddNonce(nonce_value);
|
||||
*nonce = nonce_value;
|
||||
if (trace_all_calls) {
|
||||
printf("nonce = %08x\n", nonce_value);
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("nonce = %08x\n", nonce_value);
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
@@ -173,13 +157,15 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
|
||||
uint32_t mac_key_context_length,
|
||||
const uint8_t* enc_key_context,
|
||||
uint32_t enc_key_context_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GenerateDerivedKeys(\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("mac_key_context", mac_key_context,
|
||||
(size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context,
|
||||
(size_t)enc_key_context_length);
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
@@ -201,7 +187,8 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
|
||||
mac_ctx_str, enc_ctx_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("mac_key_server", &session_ctx->mac_key_server()[0],
|
||||
session_ctx->mac_key_server().size());
|
||||
dump_hex("mac_key_client", &session_ctx->mac_key_client()[0],
|
||||
@@ -209,6 +196,7 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys(OEMCrypto_SESSION session,
|
||||
dump_hex("enc_key", &session_ctx->encryption_key()[0],
|
||||
session_ctx->encryption_key().size());
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -219,10 +207,12 @@ OEMCryptoResult OEMCrypto_GenerateSignature(
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GenerateSignature(\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("message", message, message_length);
|
||||
}
|
||||
}
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateSignature(): ERROR_KEYBOX_INVALID]");
|
||||
@@ -250,9 +240,11 @@ OEMCryptoResult OEMCrypto_GenerateSignature(
|
||||
message_length,
|
||||
signature,
|
||||
signature_length)) {
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("signature", signature, *signature_length);
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -281,15 +273,16 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
const OEMCrypto_KeyObject* key_array,
|
||||
const uint8_t* pst,
|
||||
size_t pst_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("message", message, message_length);
|
||||
dump_hex("signature", signature, signature_length);
|
||||
dump_hex("enc_mac_key_iv", enc_mac_key_iv, wvcdm::KEY_IV_SIZE);
|
||||
dump_hex("enc_mac_keys", enc_mac_keys, 2*wvcdm::MAC_KEY_SIZE);
|
||||
dump_hex("pst", pst, pst_length);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
printf("key_array[%zu].key_id_length=%zu;\n", i,
|
||||
LOGV("key_array[%zu].key_id_length=%zu;\n", i,
|
||||
key_array[i].key_id_length);
|
||||
dump_array_part("key_array", i, "key_id",
|
||||
key_array[i].key_id, key_array[i].key_id_length);
|
||||
@@ -303,6 +296,9 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
key_array[i].key_control, wvcdm::KEY_IV_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_LoadKeys(): ERROR_KEYBOX_INVALID]");
|
||||
@@ -354,15 +350,16 @@ OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
|
||||
OEMCryptoResult OEMCrypto_RefreshKeys(
|
||||
OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length,
|
||||
size_t num_keys,
|
||||
const OEMCrypto_KeyRefreshObject* key_array) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n",
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_RefreshKeys(num_keys=%zu)\n",
|
||||
num_keys);
|
||||
}
|
||||
|
||||
@@ -415,7 +412,7 @@ OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
|
||||
key_array[i].key_id + key_array[i].key_id_length);
|
||||
key_control.assign(key_array[i].key_control,
|
||||
key_array[i].key_control + wvcdm::KEY_CONTROL_SIZE);
|
||||
if (key_array[i].key_control_iv == NULL ) {
|
||||
if ( key_array[i].key_control_iv == NULL ) {
|
||||
key_control_iv.clear();
|
||||
} else {
|
||||
key_control_iv.assign(key_array[i].key_control_iv,
|
||||
@@ -448,10 +445,13 @@ extern "C"
|
||||
OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
|
||||
const uint8_t* key_id,
|
||||
size_t key_id_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_SelectKey"
|
||||
"(const OEMCrypto_SESSION session,\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("key_id", key_id, key_id_length);
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_SelectKey(): ERROR_KEYBOX_INVALID]");
|
||||
@@ -484,8 +484,9 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
|
||||
size_t block_offset,
|
||||
const OEMCrypto_DestBufferDesc* out_buffer,
|
||||
uint8_t subsample_flags) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_DecryptCTR"
|
||||
"(OEMCrypto_SESSION session,\n");
|
||||
}
|
||||
wvoec_mock::BufferType buffer_type = kBufferTypeDirect;
|
||||
uint8_t* destination = NULL;
|
||||
@@ -541,8 +542,8 @@ OEMCryptoResult OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
size_t keyBoxLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t *keybox,\n");
|
||||
}
|
||||
if (crypto_engine->keybox().InstallKeybox(keybox, keyBoxLength)) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
@@ -552,8 +553,8 @@ OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {\n");
|
||||
}
|
||||
switch(crypto_engine->ValidateKeybox()) {
|
||||
case NO_ERROR: return OEMCrypto_SUCCESS;
|
||||
@@ -567,8 +568,8 @@ OEMCryptoResult OEMCrypto_IsKeyboxValid(void) {
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
|
||||
size_t* idLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,\n");
|
||||
}
|
||||
std::vector<uint8_t> dev_id_string = crypto_engine->keybox().device_id();
|
||||
if (dev_id_string.empty()) {
|
||||
@@ -585,15 +586,17 @@ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
|
||||
memset(deviceID, 0, *idLength);
|
||||
memcpy(deviceID, &dev_id_string[0], dev_id_len);
|
||||
*idLength = dev_id_len;
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGD("[OEMCrypto_GetDeviceId(): success]");
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
|
||||
size_t* keyDataLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,\n");
|
||||
}
|
||||
size_t length = crypto_engine->keybox().key_data_length();
|
||||
if (*keyDataLength < length) {
|
||||
@@ -604,14 +607,17 @@ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
|
||||
memset(keyData, 0, *keyDataLength);
|
||||
memcpy(keyData, crypto_engine->keybox().key_data(), length);
|
||||
*keyDataLength = length;
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGD("[OEMCrypto_GetKeyData(): success]");
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength) {\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GetRandom"
|
||||
"(uint8_t* randomData, size_t dataLength) {\n");
|
||||
}
|
||||
if (!randomData) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -629,8 +635,8 @@ OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t* keybox,
|
||||
size_t* wrappedKeyBoxLength,
|
||||
const uint8_t* transportKey,
|
||||
size_t transportKeyLength) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_WrapKeybox(const uint8_t *keybox,\n");
|
||||
}
|
||||
if (!keybox || !wrappedKeybox || !wrappedKeyBoxLength
|
||||
|| (keyBoxLength != *wrappedKeyBoxLength)) {
|
||||
@@ -654,14 +660,18 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
const uint8_t* enc_rsa_key_iv,
|
||||
uint8_t* wrapped_rsa_key,
|
||||
size_t* wrapped_rsa_key_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey()\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls | kLoggingTraceNonce)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey()\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("message", message, message_length);
|
||||
dump_hex("signature", signature, signature_length);
|
||||
printf("nonce = %08X;\n", *nonce);
|
||||
}
|
||||
LOGI("nonce = %08X;\n", *nonce);
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("enc_rsa_key", enc_rsa_key, enc_rsa_key_length);
|
||||
dump_hex("enc_rsa_key_iv", enc_rsa_key_iv, wvcdm::KEY_IV_SIZE);
|
||||
}
|
||||
}
|
||||
if (wrapped_rsa_key_length == NULL) {
|
||||
LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]");
|
||||
return OEMCrypto_ERROR_INVALID_CONTEXT;
|
||||
@@ -673,7 +683,9 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey);
|
||||
|
||||
if (wrapped_rsa_key == NULL || *wrapped_rsa_key_length < buffer_size) {
|
||||
if (LogCategoryEnabled(kLoggingDumpDerivedKeys)) {
|
||||
LOGW("[OEMCrypto_RewrapDeviceRSAKey(): Wrapped Keybox Short Buffer]");
|
||||
}
|
||||
*wrapped_rsa_key_length = buffer_size;
|
||||
return OEMCrypto_ERROR_SHORT_BUFFER;
|
||||
}
|
||||
@@ -777,12 +789,14 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
result = OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
}
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("wrapped_rsa_key", wrapped_rsa_key, *wrapped_rsa_key_length);
|
||||
dump_hex("signature", wrapped->signature, sizeof(wrapped->signature));
|
||||
dump_hex("context", wrapped->context, sizeof(wrapped->context));
|
||||
dump_hex("iv", wrapped->iv, sizeof(wrapped->iv));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -796,13 +810,15 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
}
|
||||
const WrappedRSAKey* wrapped
|
||||
= reinterpret_cast<const WrappedRSAKey*>(wrapped_rsa_key);
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_LoadDeviceRSAKey()\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("wrapped_rsa_key", wrapped_rsa_key, wrapped_rsa_key_length);
|
||||
dump_hex("signature", wrapped->signature, sizeof(wrapped->signature));
|
||||
dump_hex("context", wrapped->context, sizeof(wrapped->context));
|
||||
dump_hex("iv", wrapped->iv, sizeof(wrapped->iv));
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_LoadDeviceRSAKey(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
@@ -852,15 +868,19 @@ OEMCryptoResult OEMCrypto_LoadDeviceRSAKey(OEMCrypto_SESSION session,
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
|
||||
OEMCryptoResult OEMCrypto_GenerateRSASignature(
|
||||
OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length,
|
||||
RSA_Padding_Scheme padding_scheme) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GenerateRSASignature()\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("message", message, message_length);
|
||||
dump_hex("message", message, message_length);
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateRSASignature(): ERROR_KEYBOX_INVALID]");
|
||||
@@ -895,9 +915,11 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature(OEMCrypto_SESSION session,
|
||||
signature,
|
||||
signature_length,
|
||||
padding_scheme)) {
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("signature", signature, *signature_length);
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;;
|
||||
@@ -912,14 +934,16 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
size_t mac_key_context_length,
|
||||
const uint8_t* enc_key_context,
|
||||
size_t enc_key_context_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("enc_session_key", enc_session_key, enc_session_key_length);
|
||||
dump_hex("mac_key_context", mac_key_context,
|
||||
(size_t)mac_key_context_length);
|
||||
dump_hex("enc_key_context", enc_key_context,
|
||||
(size_t)enc_key_context_length);
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
@@ -947,7 +971,8 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
if (!session_ctx->RSADeriveKeys(ssn_key_str, mac_ctx_str, enc_ctx_str)) {
|
||||
return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
}
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("mac_key_server", &session_ctx->mac_key_server()[0],
|
||||
session_ctx->mac_key_server().size());
|
||||
dump_hex("mac_key", &session_ctx->mac_key_client()[0],
|
||||
@@ -955,6 +980,7 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
dump_hex("enc_key", &session_ctx->encryption_key()[0],
|
||||
session_ctx->encryption_key().size());
|
||||
}
|
||||
}
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -969,10 +995,11 @@ const char* OEMCrypto_SecurityLevel() {
|
||||
}
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current,
|
||||
OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
OEMCrypto_HDCP_Capability *current,
|
||||
OEMCrypto_HDCP_Capability *maximum) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_GetHDCPCapability(%p, %p)\n",
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_GetHDCPCapability(%p, %p)\n",
|
||||
current, maximum);
|
||||
}
|
||||
if (current == NULL) return OEMCrypto_ERROR_UNKNOWN_FAILURE;
|
||||
@@ -989,13 +1016,14 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer) {
|
||||
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_Generic_Encrypt( algorithm=%d\n",
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_Generic_Encrypt( algorithm=%d\n",
|
||||
algorithm);
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("in_buffer", in_buffer, buffer_length);
|
||||
dump_hex("iv", iv, wvcdm::KEY_IV_SIZE);
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Enrypt(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
@@ -1013,9 +1041,11 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
|
||||
OEMCryptoResult sts =
|
||||
session_ctx->Generic_Encrypt(in_buffer, buffer_length, iv, algorithm,
|
||||
out_buffer);
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("out_buffer", out_buffer, buffer_length);
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1026,12 +1056,14 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
|
||||
const uint8_t* iv,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* out_buffer) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_Generic_Decrypt( algorithm=%d\n",
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_Generic_Decrypt( algorithm=%d\n",
|
||||
algorithm);
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("in_buffer", in_buffer, buffer_length);
|
||||
dump_hex("iv", iv, wvcdm::KEY_IV_SIZE);
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Decrypt(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
@@ -1049,9 +1081,11 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
|
||||
OEMCryptoResult sts =
|
||||
session_ctx->Generic_Decrypt(in_buffer, buffer_length, iv, algorithm,
|
||||
out_buffer);
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE){
|
||||
dump_hex("out_buffer", out_buffer, buffer_length);
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1062,11 +1096,13 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_Generic_Sign( algorithm=%d\n",
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_Generic_Sign( algorithm=%d\n",
|
||||
algorithm);
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("in_buffer", in_buffer, buffer_length);
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Sign(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
@@ -1087,9 +1123,11 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
|
||||
OEMCryptoResult sts =
|
||||
session_ctx->Generic_Sign(in_buffer, buffer_length, algorithm,
|
||||
signature, signature_length);
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("signature", signature, *signature_length);
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1100,12 +1138,14 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
|
||||
OEMCrypto_Algorithm algorithm,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_Generic_Verify( algorithm=%d\n",
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_Generic_Verify( algorithm=%d\n",
|
||||
algorithm);
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("in_buffer", in_buffer, buffer_length);
|
||||
dump_hex("signature", signature, signature_length);
|
||||
}
|
||||
}
|
||||
if (NO_ERROR != crypto_engine->ValidateKeybox()) {
|
||||
LOGE("[OEMCrypto_Generic_Verify(): ERROR_KEYBOX_INVALID]");
|
||||
return OEMCrypto_ERROR_KEYBOX_INVALID;
|
||||
@@ -1128,15 +1168,15 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session,
|
||||
|
||||
extern "C"
|
||||
bool OEMCrypto_SupportsUsageTable() {
|
||||
if (trace_all_calls) {
|
||||
printf("-- bool OEMCrypto_SupportsUsageTable(); // returns true.\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- bool OEMCrypto_SupportsUsageTable(); // returns true.\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_UpdateUsageTable() {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_UpdateUsageTable();\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_UpdateUsageTable();\n");
|
||||
}
|
||||
return crypto_engine->usage_table()->UpdateTable();
|
||||
}
|
||||
@@ -1144,10 +1184,12 @@ OEMCryptoResult OEMCrypto_UpdateUsageTable() {
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst,
|
||||
size_t pst_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeactivateUsageEntry(\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_DeactivateUsageEntry(\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("pst", pst, pst_length);
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> pstv(pst, pst + pst_length);
|
||||
return crypto_engine->usage_table()->DeactivateEntry(pstv);
|
||||
}
|
||||
@@ -1158,10 +1200,12 @@ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
|
||||
size_t pst_length,
|
||||
OEMCrypto_PST_Report *buffer,
|
||||
size_t *buffer_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_ReportUsage(\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_ReportUsage(\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("pst", pst, pst_length);
|
||||
}
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_ReportUsage(): ERROR_INVALID_SESSION]");
|
||||
@@ -1176,10 +1220,12 @@ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
|
||||
OEMCryptoResult sts =
|
||||
entry->ReportUsage(session_ctx, pstv, buffer, buffer_length);
|
||||
crypto_engine->usage_table()->UpdateTable();
|
||||
if (trace_all_calls) {
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("usage buffer", reinterpret_cast<uint8_t*>(buffer),
|
||||
*buffer_length);
|
||||
}
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -1191,12 +1237,14 @@ OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session,
|
||||
size_t message_length,
|
||||
const uint8_t *signature,
|
||||
size_t signature_length) {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeleteUsageEntry(\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_DeleteUsageEntry(\n");
|
||||
if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) {
|
||||
dump_hex("pst", pst, pst_length);
|
||||
dump_hex("message", message, message_length);
|
||||
dump_hex("signature", signature, signature_length);
|
||||
}
|
||||
}
|
||||
SessionContext* session_ctx = crypto_engine->FindSession(session);
|
||||
if (!session_ctx || !session_ctx->isValid()) {
|
||||
LOGE("[OEMCrypto_DeleteUsageEntry(): ERROR_INVALID_SESSION]");
|
||||
@@ -1226,8 +1274,8 @@ OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session,
|
||||
|
||||
extern "C"
|
||||
OEMCryptoResult OEMCrypto_DeleteUsageTable() {
|
||||
if (trace_all_calls) {
|
||||
printf("-- OEMCryptoResult OEMCrypto_DeleteUsageTable()\n");
|
||||
if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) {
|
||||
LOGI("-- OEMCryptoResult OEMCrypto_DeleteUsageTable()\n");
|
||||
}
|
||||
if (crypto_engine->usage_table()->Clear()) {
|
||||
return OEMCrypto_SUCCESS;
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
// Mock implementation of OEMCrypto APIs
|
||||
//
|
||||
#include "oemcrypto_usage_table_mock.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "clock.h"
|
||||
#include "log.h"
|
||||
#include "file_store.h"
|
||||
#include "properties.h"
|
||||
#include "oemcrypto_engine_mock.h"
|
||||
#include "oemcrypto_logging.h"
|
||||
#include "openssl/aes.h"
|
||||
#include "openssl/rand.h"
|
||||
#include "openssl/sha.h"
|
||||
@@ -45,6 +48,7 @@ UsageTableEntry::UsageTableEntry(const StoredUsageEntry *buffer) {
|
||||
buffer->mac_key_server + wvcdm::MAC_KEY_SIZE);
|
||||
mac_key_client_.assign(buffer->mac_key_client,
|
||||
buffer->mac_key_client + wvcdm::MAC_KEY_SIZE);
|
||||
session_ = NULL;
|
||||
}
|
||||
|
||||
void UsageTableEntry::SaveToBuffer(StoredUsageEntry *buffer) {
|
||||
@@ -158,7 +162,9 @@ UsageTable::UsageTable(CryptoEngine *ce) {
|
||||
|
||||
std::string filename = path + "UsageTable.dat";
|
||||
if (!file.Exists(filename)) {
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGI("UsageTable: No saved usage table. Creating new table.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
size_t file_size = file.FileSize(filename);
|
||||
@@ -222,9 +228,13 @@ UsageTable::UsageTable(CryptoEngine *ce) {
|
||||
file.Read(reinterpret_cast<char *>(&generation_), sizeof(int64_t));
|
||||
file.Close();
|
||||
if (stored_table->generation == generation_ + 1) {
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGW("UsageTable: File is one generation old. Acceptable rollback.");
|
||||
}
|
||||
} else if (stored_table->generation == generation_ - 1) {
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGW("UsageTable: File is one generation new. Acceptable rollback.");
|
||||
}
|
||||
// This might happen if the generation number was rolled back?
|
||||
} else if (stored_table->generation != generation_) {
|
||||
LOGE("UsageTable: Rollback detected. Clearing Usage Table. %lx -> %lx",
|
||||
@@ -241,7 +251,9 @@ UsageTable::UsageTable(CryptoEngine *ce) {
|
||||
new UsageTableEntry(&stored_table->entries[i].entry);
|
||||
table_[entry->pst_hash()] = entry;
|
||||
}
|
||||
LOGI("UsageTable: loaded %d entryies.", stored_table->count);
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGI("UsageTable: loaded %d entries.", stored_table->count);
|
||||
}
|
||||
}
|
||||
|
||||
bool UsageTable::SaveToFile() {
|
||||
@@ -308,8 +320,10 @@ bool UsageTable::SaveToFile() {
|
||||
|
||||
std::string filename = path + "UsageTable.dat";
|
||||
if (!file.Exists(filename)) {
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
LOGI("UsageTable: No saved usage table. Creating new table.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.Open(filename, wvcdm::File::kCreate | wvcdm::File::kTruncate |
|
||||
wvcdm::File::kBinary)) {
|
||||
|
||||
138
libwvdrmengine/oemcrypto/mock/test/oemcrypto_logging_test.cpp
Normal file
138
libwvdrmengine/oemcrypto/mock/test/oemcrypto_logging_test.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
#include <string>
|
||||
#include <gtest/gtest.h>
|
||||
#include "log.h"
|
||||
#include "oemcrypto_logging.h"
|
||||
#include "oemcrypto_mock.cpp"
|
||||
|
||||
class OEMCryptoLoggingTest : public ::testing::Test {
|
||||
protected:
|
||||
OEMCryptoLoggingTest() {}
|
||||
|
||||
void SetUp() {
|
||||
::testing::Test::SetUp();
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
OEMCrypto_Terminate();
|
||||
::testing::Test::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(OEMCryptoLoggingTest, TestDumpHexFunctions) {
|
||||
uint8_t vector[] = { 0xFA, 0x11 , 0x28 , 0x33 };
|
||||
std::string buffer = "";
|
||||
wvoec_mock::dump_hex_helper(buffer, "name", vector, (size_t)4);
|
||||
ASSERT_EQ(buffer, "name = \n wvcdm::a2b_hex(\"FA112833\");\n");
|
||||
|
||||
buffer = "";
|
||||
uint8_t vector2[] = { 0xFA, 0x11 , 0x28 , 0x33 ,
|
||||
0xFA, 0x11 , 0x28 , 0x33 , 0xFA, 0x11 ,
|
||||
0x28 , 0x33 , 0xFA, 0x11 , 0x28 , 0x33 , 0xFA, 0x11 , 0x28 , 0x33 ,
|
||||
0xFA, 0x11 , 0x28 , 0x33 , 0x01, 0x14 , 0x28 , 0xAB, 0xFA, 0xCD ,
|
||||
0xEF , 0x67, 0x01, 0x14 , 0x28 , 0xAB, 0xFA, 0xCD , 0xEF , 0x67 };
|
||||
wvoec_mock::dump_hex_helper(buffer, "name", vector2, (size_t)40);
|
||||
|
||||
ASSERT_EQ(buffer, "name = \n wvcdm::a2b_hex(\"FA112833FA112833FA112833F"
|
||||
"A112833FA112833FA112833011428ABFACDEF67\"\n \""
|
||||
"011428ABFACDEF67\");\n");
|
||||
|
||||
buffer = "";
|
||||
wvoec_mock::dump_array_part_helper(buffer, "array",
|
||||
(size_t) 5, "name", vector2, (size_t) 40);
|
||||
char* exp = "std::string s5_name = \n wvcdm::a2b_hex(\"FA112833FA112833F"
|
||||
"A112833FA112833FA112833FA112833011428ABFACDEF67\"\n "
|
||||
" \"011428ABFACDEF67\");\narray[5].name = message_ptr + me"
|
||||
"ssage.find(s5_name.data());\n";
|
||||
ASSERT_EQ(buffer, exp);
|
||||
|
||||
buffer = "";
|
||||
wvoec_mock::dump_array_part_helper(buffer, "array", (size_t) 5,
|
||||
"name", NULL, (size_t) 40);
|
||||
ASSERT_EQ(buffer, "array[5].name = NULL;\n");
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoLoggingTest, TestChangeLoggingLevel) {
|
||||
wvcdm::LogPriority default_logging_level = wvcdm::LOG_WARN;
|
||||
wvoec_mock::SetLoggingLevel(1);
|
||||
ASSERT_EQ(wvcdm::g_cutoff, default_logging_level);
|
||||
|
||||
wvoec_mock::SetLoggingLevel(2);
|
||||
ASSERT_EQ(wvcdm::g_cutoff, wvcdm::LOG_INFO);
|
||||
|
||||
wvoec_mock::SetLoggingSettings(
|
||||
wvcdm::LOG_WARN,
|
||||
wvoec_mock::kLoggingDumpTraceAll);
|
||||
ASSERT_EQ(wvcdm::g_cutoff, wvcdm::LOG_WARN);
|
||||
ASSERT_EQ(wvoec_mock::LogCategoryEnabled(
|
||||
wvoec_mock::kLoggingDumpTraceAll), true);
|
||||
wvoec_mock::TurnOffLoggingForAllCategories();
|
||||
|
||||
wvoec_mock::SetLoggingLevel(wvcdm::LOG_VERBOSE);
|
||||
ASSERT_EQ(wvcdm::g_cutoff, wvcdm::LOG_VERBOSE);
|
||||
|
||||
wvoec_mock::SetLoggingLevel(1);
|
||||
}
|
||||
|
||||
namespace wvoec_mock {
|
||||
TEST_F(OEMCryptoLoggingTest, TestChangeLoggingCategories) {
|
||||
TurnOffLoggingForAllCategories();
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingTraceDecryption |
|
||||
kLoggingTraceOEMCryptoCalls), false);
|
||||
|
||||
AddLoggingForCategories(kLoggingDumpKeyControlBlocks |
|
||||
kLoggingDumpDerivedKeys);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpKeyControlBlocks), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingTraceUsageTable), false);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpTraceAll), true);
|
||||
|
||||
RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks |
|
||||
kLoggingTraceUsageTable);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpKeyControlBlocks), false);
|
||||
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpDerivedKeys), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingTraceUsageTable), false);
|
||||
|
||||
TurnOffLoggingForAllCategories();
|
||||
bool flag = false;
|
||||
if (LogCategoryEnabled(kLoggingTraceUsageTable)) {
|
||||
flag = true;
|
||||
}
|
||||
ASSERT_EQ(flag, false);
|
||||
|
||||
AddLoggingForCategories(kLoggingDumpTraceAll);
|
||||
if (LogCategoryEnabled(kLoggingDumpKeyControlBlocks)) {
|
||||
flag = true;
|
||||
}
|
||||
ASSERT_EQ(flag, true);
|
||||
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingTraceOEMCryptoCalls), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpContentKeys), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpKeyControlBlocks), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpDerivedKeys), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingTraceNonce), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingTraceDecryption), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingTraceUsageTable), true);
|
||||
ASSERT_EQ(LogCategoryEnabled(kLoggingDumpTraceAll), true);
|
||||
|
||||
flag= false;
|
||||
RemoveLoggingForCategories(kLoggingDumpKeyControlBlocks);
|
||||
if ( LogCategoryEnabled(kLoggingDumpKeyControlBlocks) ) {
|
||||
flag = true;
|
||||
}
|
||||
ASSERT_EQ(flag, false);
|
||||
}
|
||||
|
||||
} // namespace wvoec_mock
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
wvcdm::InitLogging(argc, argv);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
#
|
||||
# Builds oemcrypto_unittests
|
||||
#
|
||||
#PROJECTS_ROOT = ~projects
|
||||
#
|
||||
ifndef PROJECTS_ROOT
|
||||
PROJECTS_ROOT = ../../../../..
|
||||
endif
|
||||
|
||||
CDM_ROOT = $(PROJECTS_ROOT)/cdm
|
||||
CDM_SRC_PATH = $(CDM_ROOT)/cdm
|
||||
CDM_BASE_INCLUDE_PATH = $(CDM_SRC_PATH)/include
|
||||
|
||||
EUREKA_ROOT = $(PROJECTS_ROOT)/eureka/eureka
|
||||
CHROME_ROOT = $(EUREKA_ROOT)/src/chromium/src
|
||||
#
|
||||
# build outputs should go into Chrome repository, such as ../chromium/src/out
|
||||
# or some local equivalent.
|
||||
# WARNING: splitting outputs from CHROME_ROOT can lead to build errors
|
||||
ifndef CHROME_ROOT
|
||||
CHROME_ROOT = $(CDM_ROOT)/out
|
||||
endif
|
||||
|
||||
# TARGET_PLATFORM from {x86,eureka}
|
||||
ifndef TARGET_PLATFORM
|
||||
TARGET_PLATFORM = x86
|
||||
endif
|
||||
|
||||
# TARGET_BUILD from {debug,release}
|
||||
ifndef TARGET_BUILD
|
||||
TARGET_BUILD = debug
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_PLATFORM),x86)
|
||||
BUILDPLATFORM = out_x86_linux
|
||||
else ifeq ($(TARGET_PLATFORM),eureka)
|
||||
BUILDPLATFORM = out_arm_eureka
|
||||
else
|
||||
BUILDPLATFORM = UNKNOWN
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_BUILD),debug)
|
||||
BUILDTYPE = Debug
|
||||
else ifeq ($(TARGET_BUILD),release)
|
||||
BUILDTYPE = Release
|
||||
else
|
||||
BUILDTYPE = UNKNOWN
|
||||
endif
|
||||
|
||||
BUILDPATH = $(CHROME_ROOT)/$(BUILDPLATFORM)/$(BUILDTYPE)
|
||||
OBJPATH = $(BUILDPATH)/obj
|
||||
|
||||
CHROME_THIRD_PARTY_LIBS = $(BUILDPATH)/obj/third_party
|
||||
|
||||
# target image file name
|
||||
TARGET_TEST_EXE = oemcrypto_unittests
|
||||
|
||||
TARGET_OBJECTS = oemcrypto_test.o
|
||||
|
||||
OBJECTDIR = $(OBJPATH)/oemcrypto_unittests
|
||||
|
||||
INSTALLDIR = $(BUILDPATH)
|
||||
|
||||
LIBGTEST_INCLUDE = $(CDM_SRC_PATH)/prebuilt/gtest/include
|
||||
LIBGTEST_LIBS = $(CDM_SRC_PATH)/prebuilt/gtest/$(BUILDPLATFORM)/$(BUILDTYPE)/lib
|
||||
LIBGTEST_LIBNAME = gtest
|
||||
|
||||
INCLUDES = \
|
||||
-I$(LIBGTEST_INCLUDE) \
|
||||
-I$(CDM_BASE_INCLUDE_PATH)
|
||||
|
||||
LIBDIRS = \
|
||||
-L$(INSTALLDIR) \
|
||||
-L$(LIBGTEST_LIBS)
|
||||
|
||||
OBJECTS := $(patsubst %.o,$(OBJECTDIR)/%.o,$(TARGET_OBJECTS))
|
||||
|
||||
CXXFLAGS = -m64 -fPIC -W -Wall -g -DCDM_TEST
|
||||
LINK = $(CXX)
|
||||
MKDIR = mkdir -p
|
||||
|
||||
$(INSTALLDIR)/$(TARGET_TEST_EXE): $(OBJECTDIR) $(INSTALLDIR) $(OBJECTS)
|
||||
$(CXX) -v -fPIC -m64 $(OBJECTS) $(LIBDIRS) -loemcrypto_mock \
|
||||
-lcrypto -ldl -lrt -lpthread -l$(LIBGTEST_LIBNAME) -o $@
|
||||
@echo "[Unit test image: " $(INSTALLDIR)/$(TARGET_TEST_EXE) "]"
|
||||
|
||||
$(OBJECTDIR)/%.o: %.cpp
|
||||
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
$(OBJECTDIR)/%.o: %.cc
|
||||
$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(OBJECTDIR)
|
||||
$(RM) -rf $(INSTALLDIR)/$(TARGET_TEST_EXE)
|
||||
|
||||
$(OBJECTDIR):
|
||||
@$(MKDIR) $@
|
||||
|
||||
$(INSTALLDIR):
|
||||
@$(MKDIR) $@
|
||||
|
||||
.PHONY: $(OBJECTDIR)
|
||||
|
||||
.PHONY: $(INSTALLDIR)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
.PHONY: test
|
||||
@@ -89,6 +89,9 @@ struct PaddedPSTReport {
|
||||
uint8_t padding[256];
|
||||
};
|
||||
|
||||
// These are test keyboxes. They will not be accepted by production systems.
|
||||
// By using known keyboxes for these tests, the results for a given set of
|
||||
// inputs to a test are predictable and can be compared to the actual results.
|
||||
const wvoec_mock::WidevineKeybox kDefaultKeybox = {
|
||||
// Sample keybox used for test vectors
|
||||
{
|
||||
@@ -185,9 +188,8 @@ static wvoec_mock::WidevineKeybox kValidKeybox03 = {
|
||||
}
|
||||
};
|
||||
|
||||
/* Note: Key 1 was 3072 bits. We are only generating 2048 bit keys,
|
||||
so we do not need to test with 3072 bit keys. */
|
||||
|
||||
// A 2048-bit test RSA Private Key
|
||||
// This is used to verify the functions that manipulate RSA keys.
|
||||
static const uint8_t kTestPKCS1RSAPrivateKey2_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, 0x00, 0x02,
|
||||
0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60,
|
||||
@@ -339,7 +341,8 @@ static const uint8_t kTestPKCS1RSAPrivateKey2_2048[] = {
|
||||
0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, 0x56, 0xfe,
|
||||
0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 };
|
||||
|
||||
// 2048 bit RSA key in PKCS#8 PrivateKeyInfo
|
||||
// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format
|
||||
// Used to verify the functions that manipulate RSA keys.
|
||||
static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30,
|
||||
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
||||
@@ -494,6 +497,8 @@ static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = {
|
||||
0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18,
|
||||
0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 };
|
||||
|
||||
// A 2048 bit RSA Public key
|
||||
// Used to verify the functions that manipulate RSA keys.
|
||||
static const uint8_t kTestRSAPublicKey2_2048[] = {
|
||||
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
|
||||
0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd,
|
||||
@@ -530,6 +535,8 @@ static const uint8_t kTestRSAPublicKey2_2048[] = {
|
||||
0x6b, 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a,
|
||||
0x05, 0x02, 0x03, 0x01, 0x00, 0x01 };
|
||||
|
||||
// A second 2048-bit RSA private key
|
||||
// This is used to verify the functions that manipulate RSA keys.
|
||||
static const uint8_t kTestPKCS1RSAPrivateKey3_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02,
|
||||
0x82, 0x01, 0x01, 0x00, 0xa5, 0xd0, 0xd7, 0x3e,
|
||||
@@ -681,7 +688,8 @@ static const uint8_t kTestPKCS1RSAPrivateKey3_2048[] = {
|
||||
0x98, 0x18, 0x0e, 0x65, 0xb6, 0x4b, 0x69, 0x0b,
|
||||
0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8, 0xee, 0x24 };
|
||||
|
||||
// 2048 bit RSA key in PKCS#8 PrivateKeyInfo
|
||||
// A second 2048 bit RSA key in PKCS#8 PrivateKeyInfo format
|
||||
// Used to verify the functions that manipulate RSA keys.
|
||||
static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_2048[] = {
|
||||
0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30,
|
||||
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
||||
@@ -837,6 +845,8 @@ static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_2048[] = {
|
||||
0x69, 0x0b, 0x21, 0xdc, 0x86, 0x17, 0x6e, 0xc8,
|
||||
0xee, 0x24 };
|
||||
|
||||
// A second 2048 bit RSA Public key
|
||||
// Used to verify the functions that manipulate RSA keys.
|
||||
static const uint8_t kTestRSAPublicKey3_2048[] = {
|
||||
0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
|
||||
0x00, 0xa5, 0xd0, 0xd7, 0x3e, 0x0e, 0x2d, 0xfb,
|
||||
@@ -938,8 +948,11 @@ class Session {
|
||||
|
||||
void FillDefaultContext(vector<uint8_t>* mac_context,
|
||||
vector<uint8_t>* enc_context) {
|
||||
/* These context strings are normally created by the CDM layer above from
|
||||
a license request message. */
|
||||
/* Context strings
|
||||
* These context strings are normally created by the CDM layer
|
||||
* from a license request message.
|
||||
* They are used to test MAC and ENC key generation.
|
||||
*/
|
||||
*mac_context = wvcdm::a2b_hex(
|
||||
"41555448454e5449434154494f4e000a4c08001248000000020000101907d9ff"
|
||||
"de13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e5873"
|
||||
@@ -963,6 +976,9 @@ class Session {
|
||||
OEMCrypto_GenerateDerivedKeys(session_id(), &mac_context[0],
|
||||
mac_context.size(), &enc_context[0],
|
||||
enc_context.size()));
|
||||
|
||||
// Expected MAC and ENC keys generated from context strings
|
||||
// with test keybox "installed".
|
||||
mac_key_server_ = wvcdm::a2b_hex(
|
||||
"3CFD60254786AF350B353B4FBB700AB382558400356866BA16C256BCD8C502BF");
|
||||
mac_key_client_ = wvcdm::a2b_hex(
|
||||
@@ -1047,7 +1063,8 @@ class Session {
|
||||
license_.keys[i].control.control_bits = htonl(control);
|
||||
}
|
||||
memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length()));
|
||||
// For the canned decryption content, The first key is:
|
||||
|
||||
// The first key for the canned decryption content.
|
||||
vector<uint8_t> key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D");
|
||||
memcpy(license_.keys[0].key_data, &key[0], key.size());
|
||||
}
|
||||
@@ -1166,6 +1183,7 @@ class Session {
|
||||
}
|
||||
|
||||
// Set up our expected input and output
|
||||
// This is dummy encrypted data.
|
||||
vector<uint8_t> encryptedData = wvcdm::a2b_hex(
|
||||
"ec261c115f9d5cda1d5cc7d33c4e37362d1397c89efdd1da5f0065c4848b0462"
|
||||
"337ba14693735203c9b4184e362439c0cea5e5d1a628425eddf8a6bf9ba901ca"
|
||||
@@ -1177,6 +1195,7 @@ class Session {
|
||||
"fc14a9ab9647e6e31adabb72d792f0c9ba99dc3e9205657d28fc7771d64e6d4b");
|
||||
vector<uint8_t> encryptionIv =
|
||||
wvcdm::a2b_hex("719dbcb253b2ec702bb8c1b1bc2f3bc6");
|
||||
// This is the expected decrypted data.
|
||||
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
|
||||
"19ef4361e16e6825b336e2012ad8ffc9ce176ab2256e1b98aa15b7877bd8c626"
|
||||
"fa40b2e88373457cbcf4f1b4b9793434a8ac03a708f85974cff01bddcbdd7a8e"
|
||||
@@ -1231,6 +1250,7 @@ class Session {
|
||||
rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048);
|
||||
}
|
||||
|
||||
// Dummy context for testing signature generation.
|
||||
vector<uint8_t> context = wvcdm::a2b_hex(
|
||||
"0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840"
|
||||
"8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202"
|
||||
@@ -1426,6 +1446,8 @@ class Session {
|
||||
&mac_context[0], mac_context.size(), &enc_context[0],
|
||||
enc_context.size()));
|
||||
|
||||
// Expected MAC and ENC keys generated from context strings
|
||||
// with RSA certificate "installed".
|
||||
mac_key_server_ = wvcdm::a2b_hex(
|
||||
"1E451E59CB663DA1646194DD28880788ED8ED2EFF913CBD6A0D535D1D5A90381");
|
||||
mac_key_client_ = wvcdm::a2b_hex(
|
||||
@@ -1815,7 +1837,7 @@ class DISABLED_TestKeybox : public OEMCryptoClientTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DISABLED_TestKeybox, CheckSystemID) {
|
||||
TEST_F(OEMCryptoClientTest, DISABLED_CheckSystemID) {
|
||||
OEMCryptoResult sts;
|
||||
uint8_t key_data[256];
|
||||
size_t key_data_len = sizeof(key_data);
|
||||
@@ -1862,6 +1884,7 @@ TEST_F(DISABLED_TestKeybox, BadCRCKeybox) {
|
||||
InstallKeybox(keybox, false);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
|
||||
InstallKeybox(kDefaultKeybox, true);
|
||||
}
|
||||
|
||||
TEST_F(DISABLED_TestKeybox, BadMagicKeybox) {
|
||||
@@ -1871,6 +1894,7 @@ TEST_F(DISABLED_TestKeybox, BadMagicKeybox) {
|
||||
InstallKeybox(keybox, false);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts);
|
||||
InstallKeybox(kDefaultKeybox, true);
|
||||
}
|
||||
|
||||
TEST_F(DISABLED_TestKeybox, BadDataKeybox) {
|
||||
@@ -1880,6 +1904,7 @@ TEST_F(DISABLED_TestKeybox, BadDataKeybox) {
|
||||
InstallKeybox(keybox, false);
|
||||
sts = OEMCrypto_IsKeyboxValid();
|
||||
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
|
||||
InstallKeybox(kDefaultKeybox, true);
|
||||
}
|
||||
|
||||
TEST_F(DISABLED_TestKeybox, GenerateSignature) {
|
||||
@@ -1889,6 +1914,7 @@ TEST_F(DISABLED_TestKeybox, GenerateSignature) {
|
||||
|
||||
s.GenerateDerivedKeys();
|
||||
|
||||
// Dummy context for testing signature generation.
|
||||
vector<uint8_t> context = wvcdm::a2b_hex(
|
||||
"0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf840"
|
||||
"8f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202"
|
||||
@@ -2354,6 +2380,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
|
||||
// Set up our expected input and output
|
||||
// This is dummy encrypted data.
|
||||
vector<uint8_t> encryptedData = wvcdm::a2b_hex(
|
||||
"c17055d4e3ab8e892b40ca2deed7cd46b406cd41d50f23d5877b36"
|
||||
"ad351887df2b3774dc413904afd958ba766cc6ab51a3ffd8f845296c5d8326ee"
|
||||
@@ -2365,6 +2392,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithOffset) {
|
||||
"4bcc7bd14746304fea100dc6465ab51241355bb19e6c2cfb2bb6bbf709765d13");
|
||||
vector<uint8_t> encryptionIv = wvcdm::a2b_hex(
|
||||
"c09454479a280829c946df3c22f25539");
|
||||
// This is the expected decrypted data.
|
||||
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
|
||||
"f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8"
|
||||
"a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f"
|
||||
@@ -2450,6 +2478,7 @@ TEST_F(DISABLED_TestKeybox, DecryptWithNearWrap) {
|
||||
vector<uint8_t> key = wvcdm::a2b_hex("39AD33E5719656069F9EDE9EBBA7A77D");
|
||||
vector<uint8_t> encryptionIv = wvcdm::a2b_hex(
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
|
||||
// This is dummy decrypted data.
|
||||
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
|
||||
"f344d9cfe336c94cf4e3ea9e3446d1427bc02d2debe6dec5b272b8"
|
||||
"a4004b696c4b37e01d7418510abf32bb071f9a4bc0d2ad7e874b648e50bd0e4f"
|
||||
@@ -2497,6 +2526,7 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
|
||||
// Set up our expected input and output
|
||||
// This is dummy decrypted data.
|
||||
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
|
||||
"1558497b6d994be343ed1c6d6313e0537b843e9a9c0836d1e83fe33154191ce9"
|
||||
"a14d8d95bebaddc03bd471827170f527c0a166b9068b273d1bc57fbb13975ee4"
|
||||
@@ -2535,6 +2565,7 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencryptedNoKey) {
|
||||
// CLear data should be copied even if there is no key selected.
|
||||
|
||||
// Set up our expected input and output
|
||||
// This is dummy decrypted data.
|
||||
vector<uint8_t> unencryptedData = wvcdm::a2b_hex(
|
||||
"1558497b6d994be343ed1c6d6313e0537b843e9a9c0836d1e83fe33154191ce9"
|
||||
"a14d8d95bebaddc03bd471827170f527c0a166b9068b273d1bc57fbb13975ee4"
|
||||
@@ -4597,6 +4628,85 @@ TEST_P(DISABLED_UsageTableTest, EmptyTable) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DISABLED_UsageTableTest, FiftyEntries) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
Session s1;
|
||||
s1.open();
|
||||
s1.GenerateDerivedKeys();
|
||||
std::string pst1 = "pst saved";
|
||||
s1.FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired,
|
||||
s1.get_nonce(), pst1);
|
||||
s1.EncryptAndSign();
|
||||
s1.LoadTestKeys(pst1, new_mac_keys_);
|
||||
sleep(kShortSleep);
|
||||
|
||||
cout << "Making 49\n";
|
||||
const size_t ENTRY_COUNT = 49;// API says should hold at least 50 entries.
|
||||
Session sessions[ENTRY_COUNT];
|
||||
for (int i=0; i<ENTRY_COUNT; i++) {
|
||||
sessions[i].open();
|
||||
sessions[i].GenerateDerivedKeys();
|
||||
std::string pst = "pst ";
|
||||
char c = 'A' + i;
|
||||
pst = pst + c;
|
||||
sessions[i].FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired,
|
||||
sessions[i].get_nonce(), pst);
|
||||
sessions[i].EncryptAndSign();
|
||||
sessions[i].LoadTestKeys(pst, new_mac_keys_);
|
||||
sessions[i].GenerateReport(pst);
|
||||
sessions[i].close();
|
||||
}
|
||||
cout << "Checking 49\n";
|
||||
for (int i=0; i<ENTRY_COUNT; i++) {
|
||||
Session s;
|
||||
s.open();
|
||||
std::string pst = "pst ";
|
||||
char c = 'A' + i;
|
||||
pst = pst + c;
|
||||
s.GenerateReport(pst, true, &sessions[i]);
|
||||
EXPECT_EQ(kUnused, s.pst_report()->status);
|
||||
s.close();
|
||||
}
|
||||
sleep(kShortSleep);
|
||||
cout << "Making another 49\n";
|
||||
// If I add too many entries, it can delete the older ones first, except
|
||||
// it shouldn't delete the one attached to an open session. (s1)
|
||||
for (int i=0; i<ENTRY_COUNT; i++) {
|
||||
sessions[i].open();
|
||||
sessions[i].GenerateDerivedKeys();
|
||||
std::string pst = "newer pst ";
|
||||
char c = 'A' + i;
|
||||
pst = pst + c;
|
||||
sessions[i].FillSimpleMessage(
|
||||
0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired,
|
||||
sessions[i].get_nonce(), pst);
|
||||
sessions[i].EncryptAndSign();
|
||||
sessions[i].LoadTestKeys(pst, new_mac_keys_);
|
||||
sessions[i].GenerateReport(pst);
|
||||
sessions[i].close();
|
||||
}
|
||||
cout << "Checking another 49\n";
|
||||
for (int i=0; i<49; i++) {
|
||||
Session s;
|
||||
s.open();
|
||||
std::string pst = "newer pst ";
|
||||
char c = 'A' + i;
|
||||
pst = pst + c;
|
||||
s.GenerateReport(pst, true, &sessions[i]);
|
||||
EXPECT_EQ(kUnused, s.pst_report()->status);
|
||||
s.close();
|
||||
}
|
||||
s1.close();
|
||||
s1.open(); // Make sure s1's entry is still in the table.
|
||||
s1.GenerateReport(pst1);
|
||||
EXPECT_EQ(kUnused, s1.pst_report()->status);
|
||||
s1.close();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DISABLED_UsageTableTest, DeleteUnusedEntry) {
|
||||
if (OEMCrypto_SupportsUsageTable()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable());
|
||||
@@ -5157,21 +5267,24 @@ TEST_P(DISABLED_UsageTableTest, TimingTest) {
|
||||
Session s1;
|
||||
Session s2;
|
||||
Session s3;
|
||||
time_t loaded = time(NULL);
|
||||
LoadOfflineLicense(s1, pst1);
|
||||
time_t loaded1 = time(NULL);
|
||||
LoadOfflineLicense(s2, pst2);
|
||||
time_t loaded2 = time(NULL);
|
||||
LoadOfflineLicense(s3, pst3);
|
||||
time_t loaded3 = time(NULL);
|
||||
|
||||
sleep(kLongSleep);
|
||||
time_t first_decrypt = time(NULL);
|
||||
s1.open();
|
||||
s1.GenerateDerivedKeys();
|
||||
s1.LoadTestKeys(pst1, new_mac_keys_);
|
||||
time_t first_decrypt1 = time(NULL);
|
||||
s1.TestDecryptCTR();
|
||||
|
||||
s2.open();
|
||||
s2.GenerateDerivedKeys();
|
||||
s2.LoadTestKeys(pst2, new_mac_keys_);
|
||||
time_t first_decrypt2 = time(NULL);
|
||||
s2.TestDecryptCTR();
|
||||
|
||||
sleep(kLongSleep);
|
||||
@@ -5204,34 +5317,36 @@ TEST_P(DISABLED_UsageTableTest, TimingTest) {
|
||||
s2.open();
|
||||
s3.open();
|
||||
sleep(kLongSleep);
|
||||
time_t report_generated = time(NULL);
|
||||
time_t report_generated1 = time(NULL);
|
||||
s1.GenerateReport(pst1);
|
||||
time_t report_generated2 = time(NULL);
|
||||
s2.GenerateReport(pst2);
|
||||
time_t report_generated3 = time(NULL);
|
||||
s3.GenerateReport(pst3);
|
||||
|
||||
EXPECT_EQ(kInactive, s1.pst_report()->status);
|
||||
EXPECT_ALMOST(
|
||||
report_generated - loaded,
|
||||
report_generated1 - loaded1,
|
||||
wvcdm::htonll64(s1.pst_report()->seconds_since_license_received));
|
||||
EXPECT_ALMOST(
|
||||
report_generated - first_decrypt,
|
||||
report_generated1 - first_decrypt1,
|
||||
wvcdm::htonll64(s1.pst_report()->seconds_since_first_decrypt));
|
||||
EXPECT_ALMOST(report_generated - second_decrypt,
|
||||
EXPECT_ALMOST(report_generated1 - second_decrypt,
|
||||
wvcdm::htonll64(s1.pst_report()->seconds_since_last_decrypt));
|
||||
|
||||
EXPECT_EQ(kActive, s2.pst_report()->status);
|
||||
EXPECT_ALMOST(
|
||||
report_generated - loaded,
|
||||
report_generated2 - loaded2,
|
||||
wvcdm::htonll64(s2.pst_report()->seconds_since_license_received));
|
||||
EXPECT_ALMOST(
|
||||
report_generated - first_decrypt,
|
||||
report_generated2 - first_decrypt2,
|
||||
wvcdm::htonll64(s2.pst_report()->seconds_since_first_decrypt));
|
||||
EXPECT_ALMOST(report_generated - third_decrypt,
|
||||
EXPECT_ALMOST(report_generated2 - third_decrypt,
|
||||
wvcdm::htonll64(s2.pst_report()->seconds_since_last_decrypt));
|
||||
|
||||
EXPECT_EQ(kUnused, s3.pst_report()->status);
|
||||
EXPECT_ALMOST(
|
||||
report_generated - loaded,
|
||||
report_generated3 - loaded3,
|
||||
wvcdm::htonll64(s3.pst_report()->seconds_since_license_received));
|
||||
// We don't expect first or last decrypt for unused report.
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,22 +1,12 @@
|
||||
ExoPlayerDemo.apk can be used to do end-to-end verification of Modular DRM.
|
||||
ExoPlayerDemo.apk can be used to do end-to-end verification of your Modular DRM.
|
||||
|
||||
To install, side load ExoPlayerDemo.apk app to your device:
|
||||
|
||||
adb install ExoPlayerDemo.apk
|
||||
|
||||
To run, launch ExoPlayer, then choose the clip to play. The
|
||||
Widevine-encrypted DASH CENC assets are in the "WIDEVINE DASH GTS"
|
||||
section.
|
||||
|
||||
These assets test various configurations of the Key Control Block (KCB)
|
||||
with various protections and expirations:
|
||||
|
||||
WV: HDCP not specified (KCB: Observe_HDCP=false)
|
||||
WV: HDCP not required (KCB: Observe_HDCP=true && HDCP=not required && DataPath=normal)
|
||||
WV: HDCP required (KCB: Observe_HDCP=true && HDCP=required && DataPath=normal)
|
||||
WV: Secure video path required (KCB: Observe_HDCP=true && HDCP=not required && DataPath=secure)
|
||||
WV: HDCP + secure video path required (KCB: Observe_HDCP=true && HDCP=required && DataPath=secure)
|
||||
WV: 30s license duration (KCB: test timer expiration)
|
||||
To run, launch ExoPlayer, then choose the Revenge (DASH CENC) clip, which is encrypted
|
||||
using DASH Common Encryption. Then press "Play" to start playback. The other clips
|
||||
in the list are clear (i.e. not encrypted).
|
||||
|
||||
Notes:
|
||||
|
||||
@@ -34,6 +24,5 @@ Notes:
|
||||
AudioTrack.getTimestamp API to do A/V sync. It will fall back to how it used to do things if
|
||||
the API isn't available.
|
||||
|
||||
- Exoplayer will retrieve a new license when playing "WV: 30s license duration",
|
||||
after the license duration has expired. Integrators should verify by means
|
||||
other than visual inspection that license duration is being enforced.
|
||||
- The apk is still built against API level 18. The features above are accessed via reflection.
|
||||
|
||||
|
||||
@@ -14,6 +14,13 @@ needs a different format of build script, and it needs to build against the OS'
|
||||
modified version of gTest, rather than the packaged version.
|
||||
|
||||
Local Modifications:
|
||||
--Tue Jun 3, 2014 (joeyparrish)--
|
||||
|
||||
Removed DOS newlines for the sake of the git-to-piper bridge. Piper converts
|
||||
newlines automatically to unix style. If this change is not made in git, the
|
||||
bridge detects differences and attempts to sync to piper, even when nothing
|
||||
has changed in git.
|
||||
|
||||
--Mon Feb 4, 2014 (juce)--
|
||||
|
||||
Upgraded to gMock 1.7.0. This is required for compatibility with the upgrade
|
||||
|
||||
Reference in New Issue
Block a user