Merge "Merge Changes from CDM repository"

This commit is contained in:
Fred Gylys-Colwell
2014-07-01 20:05:29 +00:00
committed by Android (Google) Code Review
66 changed files with 2927 additions and 1998 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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_;

View File

@@ -7,6 +7,9 @@
#include "wv_cdm_types.h"
#include <stddef.h>
#include <stdlib.h>
namespace wvcdm {
// File class. The implementation is platform dependent.

View File

@@ -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,

View File

@@ -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

View File

@@ -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_

View File

@@ -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);

View File

@@ -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);
};

View File

@@ -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_;

View File

@@ -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,

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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_) {

View 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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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) &&

View File

@@ -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;
}

View 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

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
};

View File

@@ -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;

View File

@@ -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(

View File

@@ -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, ...) {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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 \

View File

@@ -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;

View File

@@ -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());

View File

@@ -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" {

View 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -104,4 +104,4 @@ bool WvKeybox::InstallKeybox(const uint8_t* buffer, size_t keyBoxLength) {
return true;
}
}; // namespace wvoec_eng
}; // namespace wvoec_mock

View 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

View File

@@ -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;

View File

@@ -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)) {

View 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();
}

View File

@@ -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

View File

@@ -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.
}

View File

@@ -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.

View File

@@ -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