Merges to android Pi release (part: 1)

Below are a set of CLs being merged from the wv cdm repo to the android repo.

* Fix handling of OEM Cert public key.

  Author: Srujan Gaddam <srujzs@google.com>

  [ Merge of http://go/wvgerrit/27921 ]

  This is a potential fix for b/36656190. Set aside public
  key on first call to get the public key, and use it afterwards.
  This gets rid of extra calls to OEMCrypto_GetOEMPublicCertificate(),
  which has side-effect of staging the OEM private key.

  This also fixes a problem where the public cert string was
  not being trimmed to match the size returned by
  OEMCrypto_GetOEMPublicCertificate().

* Complete provisioning request/response for Provisioning 3.0

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27780 ]

  Fix bug on provisioning request path where GenerateDerivedKeys()
  was being called when preparing to generate the signature.

  Add message signature verification, and call correct OEMCrypto
  routine to rewrap the private key (OEMCrypto_RewrapDeviceRSAKey30).

* Implement Cdm::deleteAllUsageRecords()

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27780 ]

  Delete all usage records for current origin.  Removes usage
  records from file system and retains the PSTs.  The deletes
  any usage entries matching those PSTs held by OEMCrypto.

  BUG: 35319024

* Remove stringencoders library from third_party.

  Author: Jacob Trimble <modmaker@google.com>

  [ Merge of http://go/wvgerrit/27585 ]

  We have a fork of the stringencoders library that we use for base64
  encoding.  This reimplements base64 encoding to remove the extra
  dependency and to reduce the amount of code.

* Add Cdm::deleteUsageRecord() based on key_set_id.

  Author: Gene Morgan <gmorgan@google.com>

  [ Merge of http://go/wvgerrit/27605 ]

  Delete specified usage record from file system usage info and
  from OEMCrypto.

  BUG: 35319024

* Modifiable OEMCrypto

  Author: Fred Gylys-Colwell <fredgc@google.com>

  [ Merge of http://go/wvgerrit/24729 ]

  This CL adds a new variant of the OEMCrypto mock code that adjusts its
  behavior based on a configuration file.  This is intended for
  testing.

  For example, a tester can set current_hdcp to 2 in the options.txt
  file, push it to the device, and verify that a license is granted for
  HDCP 2.0.  Then the tester can edit the value of current_hdcp to 1 and
  push the file to the device.  Playback should stop because the license
  is no longer valid.

  This variant uses a real level 1 liboemcrypto.so to push data to a
  secure buffer.  That means we can test playback for a license that
  requires secure buffers on an Android device with real secure buffers.

  BUG: 35141278
  BUG: 37353534

BUG: 71650075
Test: Not currently passing. Will be addressed in a subsequent
      commit in the chain.

Change-Id: I58443c510919e992bb455192e70373490a00e2b6
This commit is contained in:
Rahul Frias
2018-01-05 17:05:18 -08:00
parent e34f83cdce
commit 0419b55222
120 changed files with 5402 additions and 6827 deletions

View File

@@ -3,20 +3,21 @@
#ifndef WVCDM_CORE_CDM_ENGINE_H_
#define WVCDM_CORE_CDM_ENGINE_H_
#include <memory>
#include <string>
#include <vector>
#include "cdm_session_map.h"
#include "certificate_provisioning.h"
#include "clock.h"
#include "crypto_session.h"
#include "file_store.h"
#include "initialization_data.h"
#include "lock.h"
#include "metrics_collections.h"
#include "oemcrypto_adapter.h"
#include "scoped_ptr.h"
#include "timer_metric.h"
#include "service_certificate.h"
#include "timer_metric.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_types.h"
@@ -28,11 +29,9 @@ class CryptoEngine;
class UsagePropertySet;
class WvCdmEventListener;
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
typedef std::map<
CdmKeySetId,
std::pair<CdmSessionId, int64_t /* expiration time in seconds */> >
CdmReleaseKeySetMap;
// Keep expiration time for each key set
typedef std::map<CdmKeySetId,
std::pair<CdmSessionId, int64_t> > CdmReleaseKeySetMap;
class CdmEngine {
public:
@@ -91,26 +90,7 @@ class CdmEngine {
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const InitializationData& init_data, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
// This API may
// (a) accept license response, extract key info and load keys.
// (b) accept a renewal response and update license policy information.
// (c) accept a release response and release an offline license or secure
// stop.
// (d) accept a service certificate and cache that information for the
// the lifetime of the session.
//
// |session_id| identifies the session that generated the request and can
// process the response. Should be empty if a release response.
// |key_data| is the license, renewal, release response or service
// certificate response.
// |key_set_id| should be non-null and specified if license release.
// If offline license or streaming license associated with
// a secure stop, |key_set_id| should be non-null and will
// be filled in on return. Use the |key_set_id| with
// RestoreKeys (to reload offline session) or
// GenerateKeyRequest (to release offline session/secure stop).
// |key_set_id| will be cleared if release or streaming
// (not associated with a secure stop).
// Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId* key_set_id);
@@ -178,25 +158,46 @@ class CdmEngine {
// system. This will force the device to reprovision itself.
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
// Delete OEMCrypto usage tables. Used by Unprovision().
virtual CdmResponseType DeleteUsageTable(CdmSecurityLevel security_level);
// Return the list of key_set_ids stored on the current (origin-specific)
// file system.
virtual CdmResponseType ListStoredLicenses(
CdmSecurityLevel security_level, std::vector<std::string>* key_set_ids);
// Delete OEMCrypto usage tables. Used by Unprovision().
CdmResponseType DeleteUsageTable(CdmSecurityLevel security_level);
// Return the list of key_set_ids stored as usage records on the
// current (origin-specific) file system.
virtual CdmResponseType ListUsageRecords(
const std::string& app_id, CdmSecurityLevel security_level,
std::vector<std::string>* ksids);
// Delete the usage record for the given key_set_id. This removes the
// usage record in the file system and the OEMCrypto usage record.
virtual CdmResponseType DeleteUsageRecord(const std::string& app_id,
CdmSecurityLevel security_level,
const std::string& key_set_id);
// Usage related methods for streaming licenses
// Retrieve a random usage info from the list of all usage infos for this app
// id.
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
CdmUsageInfo* usage_info);
// Retrieve the usage info for the specified pst.
// Returns UNKNOWN_ERROR if no usage info was found.
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info);
// Release all usage records for the current origin.
virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id,
CdmSecurityLevel security_level);
// Release all usage records for the current origin. Span all
// security levels.
virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id);
virtual CdmResponseType ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message);
virtual CdmResponseType LoadUsageSession(const CdmKeySetId& key_set_id,
@@ -236,7 +237,7 @@ class CdmEngine {
CdmSigningAlgorithm algorithm,
const std::string& signature);
virtual size_t SessionSize() const { return sessions_.size(); }
virtual size_t SessionSize() const { return session_map_.Size(); }
// Is the key known to any session?
virtual bool IsKeyLoaded(const KeyId& key_id);
@@ -256,7 +257,7 @@ class CdmEngine {
// dead lock.
virtual void OnTimerEvent();
virtual metrics::EngineMetrics* GetMetrics() { return &metrics_; }
virtual metrics::MetricsGroup* GetMetrics() { return &metrics_; }
private:
// private methods
@@ -279,7 +280,6 @@ class CdmEngine {
// instance variables
/*
* The metrics group must be the first variable declared to ensure
* that it is the last member destroyed so that no child members
@@ -287,10 +287,10 @@ class CdmEngine {
* ensure that all data has been properly recorded in the group before
* it is published.
*/
metrics::EngineMetrics metrics_;
metrics::MetricsGroup metrics_;
metrics::TimerMetric life_span_;
CdmSessionMap sessions_;
CdmSessionMap session_map_;
CdmReleaseKeySetMap release_key_sets_;
scoped_ptr<CertificateProvisioning> cert_provisioning_;
SecurityLevel cert_provisioning_requested_security_level_;
@@ -311,14 +311,7 @@ class CdmEngine {
scoped_ptr<UsagePropertySet> usage_property_set_;
int64_t last_usage_information_update_time_;
// Locks the list of sessions, |sessions_|, for the event timer. It will be
// locked in OpenSession, CloseSession. It is also locked in OnTimerEvent and
// OnKeyReleaseEvent while the list of event listeners is being generated.
// The layer above the CDM implementation is expected to handle thread
// synchronization to make sure other functions that access sessions do not
// occur simultaneously with OpenSession or CloseSession.
Lock session_list_lock_;
// Protect release_key_sets_ from non-thread-safe operations.
Lock release_key_sets_lock_;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);

View File

@@ -12,7 +12,7 @@
#include "file_store.h"
#include "initialization_data.h"
#include "license.h"
#include "metrics_collections.h"
#include "metrics_group.h"
#include "oemcrypto_adapter.h"
#include "policy_engine.h"
#include "scoped_ptr.h"
@@ -24,28 +24,16 @@ namespace wvcdm {
class CdmClientPropertySet;
class ServiceCertificate;
class WvCdmEventListener;
class UsageTableHeader;
class CdmSession {
public:
// Creates a new instance of the CdmSession with the given |file_system|
// and |metrics| parameters. Both parameters are owned by the caller and
// must remain in scope througout the scope of the new instance.
CdmSession(FileSystem* file_system, metrics::SessionMetrics* metrics);
CdmSession(FileSystem* file_system);
virtual ~CdmSession();
// Initializes this instance of CdmSession with the given property set.
// |cdm_client_property_set| MAY be null, is owned by the caller,
// and must remain in scope throughout the scope of this session.
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set);
void Close() { closed_ = true; }
bool IsClosed() { return closed_; }
// Initializes this instance of CdmSession with the given parmeters.
// All parameters are owned by the caller.
// |cdm_client_property_set| is caller owned, may be null, but must be
// in scope as long as the session is in scope.
// |forced_session_id| is caller owned and may be null.
// |event_listener| is caller owned, may be null, but must be in scope
// as long as the session is in scope.
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set);
virtual CdmResponseType Init(ServiceCertificate* service_certificate,
CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id,
@@ -54,7 +42,7 @@ class CdmSession {
virtual CdmResponseType RestoreOfflineSession(
const CdmKeySetId& key_set_id, const CdmLicenseType license_type);
virtual CdmResponseType RestoreUsageSession(
const DeviceFiles::CdmUsageData& usage_data);
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response);
virtual const CdmSessionId& session_id() { return session_id_; }
virtual const CdmKeySetId& key_set_id() { return key_set_id_; }
@@ -98,8 +86,6 @@ class CdmSession {
// ReleaseKey() - Accept response and release key.
virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response);
virtual CdmResponseType DeleteUsageEntry(uint32_t usage_entry_number);
virtual bool IsKeyLoaded(const KeyId& key_id);
virtual int64_t GetDurationRemaining();
@@ -118,8 +104,7 @@ class CdmSession {
// Delete usage information for the list of tokens, |provider_session_tokens|.
virtual CdmResponseType DeleteMultipleUsageInformation(
const std::vector<std::string>& provider_session_tokens);
virtual CdmResponseType UpdateUsageTableInformation();
virtual CdmResponseType UpdateUsageEntryInformation();
virtual CdmResponseType UpdateUsageInformation();
virtual bool is_initial_usage_update() { return is_initial_usage_update_; }
virtual bool is_usage_update_needed() { return is_usage_update_needed_; }
@@ -132,13 +117,6 @@ class CdmSession {
virtual bool is_offline() { return is_offline_; }
virtual bool is_temporary() { return is_temporary_; }
virtual bool license_received() { return license_received_; }
virtual bool has_provider_session_token() {
return (license_parser_.get() != NULL &&
license_parser_->provider_session_token().size() > 0);
}
virtual CdmUsageSupportType get_usage_support_type()
{ return usage_support_type_; }
// ReleaseCrypto() - Closes the underlying crypto session but leaves this
// object alive. It is invalid to call any method that requires a crypto
@@ -182,7 +160,7 @@ class CdmSession {
CdmSigningAlgorithm algorithm,
const std::string& signature);
virtual metrics::SessionMetrics* GetMetrics() { return metrics_; }
virtual metrics::MetricsGroup* GetMetrics() { return &metrics_; }
private:
friend class CdmSessionTest;
@@ -192,8 +170,6 @@ class CdmSession {
CdmResponseType StoreLicense();
bool StoreLicense(DeviceFiles::LicenseState state);
bool UpdateUsageInfo();
// These setters are for testing only. Takes ownership of the pointers.
void set_license_parser(CdmLicense* license_parser);
void set_crypto_session(CryptoSession* crypto_session);
@@ -201,13 +177,20 @@ class CdmSession {
void set_file_handle(DeviceFiles* file_handle);
// instance variables
metrics::SessionMetrics* metrics_;
metrics::CryptoMetrics* crypto_metrics_;
/*
* The metrics group must be the first variable declared to ensure
* that it is the last member destroyed so that no child members
* try to use a reference to it after it is destroyed. This will
* ensure that all data has been properly recorded in the group before
* it is published.
*/
metrics::MetricsGroup metrics_;
metrics::TimerMetric life_span_;
bool initialized_;
bool closed_; // Session closed, but final shared_ptr has not been released.
CdmSessionId session_id_;
FileSystem* file_system_;
scoped_ptr<CdmLicense> license_parser_;
scoped_ptr<CryptoSession> crypto_session_;
scoped_ptr<PolicyEngine> policy_engine_;
@@ -220,18 +203,11 @@ class CdmSession {
SecurityLevel requested_security_level_;
CdmAppParameterMap app_parameters_;
// decryption flags
// decryption and usage flags
bool is_initial_decryption_;
bool has_decrypted_since_last_report_; // ... last report to policy engine.
// Usage related flags and data
bool is_initial_usage_update_;
bool is_usage_update_needed_;
CdmUsageSupportType usage_support_type_;
UsageTableHeader* usage_table_header_;
uint32_t usage_entry_number_;
CdmUsageEntry usage_entry_;
std::string usage_provider_session_token_;
// information useful for offline and usage scenarios
CdmKeyMessage key_request_;

View File

@@ -6,8 +6,8 @@
#include <string>
#include "crypto_session.h"
#include "metrics_collections.h"
#include "license_protocol.pb.h"
#include "metrics_group.h"
#include "oemcrypto_adapter.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
@@ -21,8 +21,8 @@ class ServiceCertificate;
class CertificateProvisioning {
public:
explicit CertificateProvisioning(metrics::CryptoMetrics* metrics,
ServiceCertificate* service_certificate) :
CertificateProvisioning(metrics::MetricsGroup* metrics,
ServiceCertificate* service_certificate) :
crypto_session_(metrics),
cert_type_(kCertificateWidevine),
service_certificate_(service_certificate) {}
@@ -39,10 +39,8 @@ class CertificateProvisioning {
// Process the provisioning response.
CdmResponseType HandleProvisioningResponse(
FileSystem* file_system,
const CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key);
FileSystem* file_system, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key);
private:
bool GetProvisioningTokenType(
@@ -54,12 +52,6 @@ class CertificateProvisioning {
video_widevine::SignedProvisioningMessage::ProtocolVersion
GetProtocolVersion();
void ComposeJsonRequestAsQueryString(const std::string& message,
CdmProvisioningRequest* request);
bool ParseJsonResponse(const CdmProvisioningResponse& json_str,
const std::string& start_substr,
const std::string& end_substr, std::string* result);
CryptoSession crypto_session_;
CdmCertificateType cert_type_;
ServiceCertificate* service_certificate_;

View File

@@ -9,7 +9,7 @@
#include "OEMCryptoCENC.h"
#include "lock.h"
#include "metrics_collections.h"
#include "metrics_group.h"
#include "oemcrypto_adapter.h"
#include "timer_metric.h"
#include "wv_cdm_types.h"
@@ -17,8 +17,6 @@
namespace wvcdm {
class CryptoKey;
class UsageTableHeader;
typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
class CryptoSession {
@@ -36,10 +34,7 @@ class CryptoSession {
bool rsa_cast;
};
// Creates an instance of CryptoSession with the given |crypto_metrics|.
// |crypto_metrics| is owned by the caller, must NOT be null, and must
// exist as long as the new CryptoSession exists.
CryptoSession(metrics::CryptoMetrics* crypto_metrics);
CryptoSession(metrics::MetricsGroup* metrics);
virtual ~CryptoSession();
virtual bool GetClientToken(std::string* client_token);
@@ -83,6 +78,8 @@ class CryptoSession {
virtual bool GenerateDerivedKeys(const std::string& message);
virtual bool GenerateDerivedKeys(const std::string& message,
const std::string& session_key);
virtual bool RewrapCertificate(const std::string& signed_message,
const std::string& signature,
const std::string& nonce,
@@ -96,7 +93,7 @@ class CryptoSession {
// Usage related methods
virtual bool UsageInformationSupport(bool* has_support);
virtual CdmResponseType UpdateUsageInformation(); // only for OEMCrypto v9-12
virtual CdmResponseType UpdateUsageInformation();
virtual CdmResponseType DeactivateUsageInformation(
const std::string& provider_session_token);
virtual CdmResponseType GenerateUsageReport(
@@ -148,9 +145,6 @@ class CryptoSession {
const std::string& signature);
// Usage table header and usage entry related methods
virtual UsageTableHeader* GetUsageTableHeader() {
return usage_table_header_;
}
virtual CdmResponseType GetUsageSupportType(CdmUsageSupportType* type);
virtual CdmResponseType CreateUsageTableHeader(
CdmUsageTableHeader* usage_table_header);
@@ -165,28 +159,15 @@ class CryptoSession {
virtual CdmResponseType ShrinkUsageTableHeader(
uint32_t new_entry_count, CdmUsageTableHeader* usage_table_header);
virtual CdmResponseType MoveUsageEntry(uint32_t new_entry_number);
virtual bool CreateOldUsageEntry(
uint64_t time_since_license_received,
uint64_t time_since_first_decrypt,
uint64_t time_since_last_decrypt,
UsageDurationStatus status,
const std::string& server_mac_key,
const std::string& client_mac_key,
const std::string& provider_session_token);
virtual CdmResponseType CopyOldUsageEntry(
const std::string& provider_session_token);
virtual metrics::CryptoMetrics* GetCryptoMetrics() { return metrics_; }
private:
friend class CryptoSessionForTest;
bool GetProvisioningMethod(CdmClientTokenType* token_type);
void Init();
void Terminate();
bool GetTokenFromKeybox(std::string* token);
bool GetTokenFromOemCert(std::string* token);
static bool ExtractSystemIdFromOemCert(const std::string& oem_cert,
uint32_t* system_id);
void GenerateMacContext(const std::string& input_context,
std::string* deriv_context);
void GenerateEncryptContext(const std::string& input_context,
@@ -234,12 +215,12 @@ class CryptoSession {
static bool initialized_;
static int session_count_;
metrics::CryptoMetrics* metrics_;
metrics::MetricsGroup* metrics_;
metrics::TimerMetric life_span_;
bool open_;
CdmClientTokenType pre_provision_token_type_;
std::string oem_token_; // Cached OEMCrypto Public Key
std::string oem_token_; // Cached OEMCrypto Public Key
bool update_usage_table_after_close_session_;
CryptoSessionId oec_session_id_;
@@ -249,11 +230,7 @@ class CryptoSession {
KeyId cached_key_id_;
bool is_usage_support_type_valid_;
CdmUsageSupportType usage_support_type_;
UsageTableHeader* usage_table_header_;
static UsageTableHeader* usage_table_header_l1_;
static UsageTableHeader* usage_table_header_l3_;
uint64_t request_id_base_;
static uint64_t request_id_index_;

View File

@@ -29,13 +29,17 @@ class DeviceFiles {
kLicenseStateUnknown,
} LicenseState;
struct CdmUsageData {
std::string provider_session_token;
CdmKeyMessage license_request;
CdmKeyResponse license;
std::string key_set_id;
CdmUsageEntry usage_entry;
uint32_t usage_entry_number;
typedef enum {
kStorageLicense, // persistent license
kStorageUsageInfo, // secure stop
} UsageEntryStorage;
struct UsageEntryInfo {
UsageEntryStorage storage_type;
std::string key_set_id; // used when storage_type is kStorageLicense
std::string
provider_session_token; // used when storage_type is kStorageUsageInfo
std::string app_id; // used when storage_type is kStorageUsageInfo
};
DeviceFiles(FileSystem*);
@@ -65,8 +69,7 @@ class DeviceFiles {
int64_t last_playback_time,
int64_t grace_period_end_time,
const CdmAppParameterMap& app_parameters,
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number);
const CdmUsageEntry& usage_entry);
virtual bool RetrieveLicense(
const std::string& key_set_id, LicenseState* state,
CdmInitData* pssh_data, CdmKeyMessage* key_request,
@@ -74,7 +77,7 @@ class DeviceFiles {
CdmKeyResponse* key_renewal_response, std::string* release_server_url,
int64_t* playback_start_time, int64_t* last_playback_time,
int64_t* grace_period_end_time, CdmAppParameterMap* app_parameters,
CdmUsageEntry* usage_entry, uint32_t* usage_entry_number);
CdmUsageEntry* usage_entry);
virtual bool DeleteLicense(const std::string& key_set_id);
virtual bool ListLicenses(std::vector<std::string>* key_set_ids);
virtual bool DeleteAllFiles();
@@ -83,71 +86,54 @@ class DeviceFiles {
virtual bool ReserveLicenseId(const std::string& key_set_id);
virtual bool UnreserveLicenseId(const std::string& key_set_id);
// Use this method to create a |usage_info_file_name| from an |app_id|
static std::string GetUsageInfoFileName(const std::string& app_id);
// The UsageInfo methods have been revised to use |usage_info_file_name|
// rather than |app_id| as a parameter. Use the helper method above to
// translate.
// OEMCrypto API 13 introduced big usage tables which required
// migration from usage tables stored by the TEE to usage table
// header+usage entries stored in unsecured persistent storage. The upgrade
// required creation of reverse lookup tables (CdmUsageEntryInfo).
// |app_id| however was hashed and unextractable, and necessitated the
// switch to |usage_info_file_name|
// Store a usage record to the set of usage information on the file system.
virtual bool StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const std::string& usage_info_file_name,
const std::string& app_id,
const std::string& key_set_id,
const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number);
virtual bool DeleteUsageInfo(const std::string& usage_info_file_name,
const CdmUsageEntry& usage_entry);
// Extract KSIDs from usage information on the file system.
virtual bool ListUsageRecords(const std::string& app_id,
std::vector<std::string>* ksids);
// Get the provider session token for the given key_set_id.
virtual bool GetProviderSessionToken(const std::string& app_id,
const std::string& key_set_id,
std::string* provider_session_token);
// Delete the usage record for the given PST.
virtual bool DeleteUsageInfo(const std::string& app_id,
const std::string& provider_session_token);
// Delete usage information from the file system. Puts a list of all the
// psts that were deleted from the file into |provider_session_tokens|.
virtual bool DeleteAllUsageInfoForApp(
const std::string& usage_info_file_name,
const std::string& app_id,
std::vector<std::string>* provider_session_tokens);
// Retrieve one usage info from the file. Subsequent calls will retrieve
// subsequent entries in the table for this app_id.
virtual bool RetrieveUsageInfo(
const std::string& usage_info_file_name,
const std::string& app_id,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
// Retrieve the usage info entry specified by |provider_session_token|.
// Returns false if the entry could not be found.
virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name,
virtual bool RetrieveUsageInfo(const std::string& app_id,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry,
uint32_t* usage_entry_number);
CdmUsageEntry* usage_entry);
// Retrieve the usage info entry specified by |key_set_id|.
// Returns false if the entry could not be found.
virtual bool RetrieveUsageInfoByKeySetId(
const std::string& usage_info_file_name,
const std::string& key_set_id,
std::string* provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry,
uint32_t* usage_entry_number);
// These APIs support upgrading from usage tables to usage tabler header +
// entries introduced in OEMCrypto V13.
virtual bool ListUsageInfoFiles(std::vector<std::string>* usage_file_names);
virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name,
std::vector<CdmUsageData>* usage_data);
virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
CdmUsageData* usage_data);
// This method overwrites rather than appends data to the usage file
virtual bool StoreUsageInfo(const std::string& usage_info_file_name,
const std::vector<CdmUsageData>& usage_data);
virtual bool UpdateUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
const CdmUsageData& usage_data);
virtual bool RetrieveUsageInfoByKeySetId(const std::string& app_id,
const std::string& key_set_id,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
CdmUsageEntry* usage_entry);
virtual bool StoreHlsAttributes(const std::string& key_set_id,
const CdmHlsMethod method,
@@ -159,11 +145,11 @@ class DeviceFiles {
virtual bool StoreUsageTableInfo(
const CdmUsageTableHeader& usage_table_header,
const std::vector<CdmUsageEntryInfo>& usage_entry_info);
const std::vector<UsageEntryInfo>& usage_entry_info);
virtual bool RetrieveUsageTableInfo(
CdmUsageTableHeader* usage_table_header,
std::vector<CdmUsageEntryInfo>* usage_entry_info);
std::vector<UsageEntryInfo>* usage_entry_info);
private:
// Helpers that wrap the File interface and automatically handle hashing, as
@@ -182,6 +168,7 @@ class DeviceFiles {
static std::string GetCertificateFileName();
static std::string GetHlsAttributesFileNameExtension();
static std::string GetLicenseFileNameExtension();
static std::string GetUsageInfoFileName(const std::string& app_id);
static std::string GetUsageTableFileName();
static std::string GetFileNameSafeHash(const std::string& input);

View File

@@ -20,7 +20,6 @@ namespace wvcdm {
class Clock;
class CryptoSession;
class PolicyEngine;
class CdmSession;
class ServiceCertificate;
class CdmLicense {
@@ -39,8 +38,7 @@ class CdmLicense {
std::string* server_url);
virtual CdmResponseType PrepareKeyUpdateRequest(
bool is_renewal, const CdmAppParameterMap& app_parameters,
CdmSession* cdm_session, CdmKeyMessage* signed_request,
std::string* server_url);
CdmKeyMessage* signed_request, std::string* server_url);
virtual CdmResponseType HandleKeyResponse(
const CdmKeyResponse& license_response);
virtual CdmResponseType HandleKeyUpdateResponse(
@@ -51,7 +49,7 @@ class CdmLicense {
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response,
int64_t playback_start_time, int64_t last_playback_time,
int64_t grace_period_end_time, CdmSession* cdm_session);
int64_t grace_period_end_time);
virtual bool RestoreLicenseForRelease(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response);
virtual bool HasInitData() { return stored_init_data_.get(); }
@@ -65,10 +63,6 @@ class CdmLicense {
return is_offline_;
}
static bool ExtractProviderSessionToken(
const CdmKeyResponse& license_response,
std::string* provider_session_token);
private:
CdmResponseType HandleKeyErrorResponse(

View File

@@ -11,8 +11,6 @@
namespace wvcdm {
static const uint32_t kNoResolution = 0;
class LicenseKeyStatus;
// Holds all content and operator session keys for a session.
@@ -33,17 +31,11 @@ class LicenseKeys {
virtual bool GetAllowedUsage(const KeyId& key_id,
CdmKeyAllowedUsage* allowed_usage);
// Applies a new status, resolution, and/or HDCP level to each content key,
// updating their usability under their constraints. Returns true if any keys
// have changed usability, and sets new_usable_keys to true if the status
// changes resulted in at least one key becoming usable.
//
// Pass in NULL for any of the const-pointer arguments to leave that state
// unchanged.
virtual bool ApplyStatusChange(
const CdmKeyStatus* new_status, const uint32_t* new_resolution,
const CryptoSession::HdcpCapability* new_hdcp_level,
bool* new_usable_keys);
// Applies a new status to each content key.
// Returns true if any statuses changed, and sets new_usable_keys to
// true if the status changes resulted in keys becoming usable.
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Populates the CdmKeyStatusMap with the current content keys.
virtual void ExtractKeyStatuses(CdmKeyStatusMap* content_keys);
@@ -53,6 +45,11 @@ class LicenseKeys {
// to the key, returns true.
virtual bool MeetsConstraints(const KeyId& key_id);
// Applies a resolution and/or hdcp change to each key, updating their
// useability under their constraints.
virtual void ApplyConstraints(uint32_t new_resolution,
CryptoSession::HdcpCapability new_hdcp_level);
// Extracts the keys from a license and makes them available for
// querying usage and constraint settings.
virtual void SetFromLicense(
@@ -88,24 +85,21 @@ class LicenseKeyStatus {
// Returns the current status of the key.
virtual CdmKeyStatus GetKeyStatus() const { return key_status_; }
// Applies a new status, resolution, and/or HDCP level, updating the key's
// usability under its constraints. Returns true if this results in a change
// to the key's usability, and sets newly_usable to true if the status
// changes resulted in the key becoming usable.
//
// Pass in NULL for any of the const-pointer arguments to leave that state
// unchanged.
virtual bool ApplyStatusChange(
const CdmKeyStatus* maybe_new_status,
const uint32_t* maybe_new_resolution,
const CryptoSession::HdcpCapability* maybe_new_hdcp_level,
bool* newly_usable);
// Applies a new status to this key.
// Returns true if the status changed, and sets new_usable_keys to
// true if the status changes resulted in the key becoming usable.
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Returns the current constraint status of this key. The result
// may change due to calls to ApplyConstraints().
// Note: this will return true until the first call to ApplyConstraints().
virtual bool MeetsConstraints() const { return meets_constraints_; }
// Applies the given changes in resolution or HDCP settings.
virtual void ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level);
protected:
typedef ::video_widevine::License::KeyContainer KeyContainer;
typedef KeyContainer::OperatorSessionKeyPermissions
@@ -127,14 +121,11 @@ class LicenseKeyStatus {
bool HasConstraints() {
return is_content_key_ && constraints_.size() != 0;
}
void SetConstraints(const ConstraintList& constraints);
void ApplyConstraints();
bool is_content_key_;
CdmKeyStatus key_status_;
uint32_t resolution_;
CryptoSession::HdcpCapability hdcp_level_;
bool can_check_constraints_;
bool meets_constraints_;
CdmKeyAllowedUsage allowed_usage_;
CryptoSession::HdcpCapability default_hdcp_level_;

View File

@@ -108,15 +108,9 @@ class PolicyEngine {
friend class PolicyEngineConstraintsTest;
void InitDevice(CryptoSession* crypto_session);
// Checks the keys against the current state of the hardware in case the HDCP
// level no longer meets their requirements. Will only check once every
// |kHdcpCheckInterval| seconds. Calls NotifyIfKeysChanged() to propagate any
// resultant changes to the OnKeysChange event.
void CheckDevice(int64_t current_time);
void SetDeviceResolution(uint32_t width, uint32_t height) {
current_resolution_ = width * height;
}
void SetDeviceResolution(uint32_t width, uint32_t height);
void CheckDeviceHdcpStatusOnTimer(int64_t current_time);
void CheckDeviceHdcpStatus();
typedef enum {
kLicenseStateInitial,
@@ -147,22 +141,14 @@ class PolicyEngine {
void UpdateRenewalRequest(int64_t current_time);
// Updates the keys' status to |new_status|. Calls NotifyIfKeysChanged() to
// propagate any resultant changes to the OnKeysChange event.
void UpdateKeyStatus(CdmKeyStatus new_status);
// Helper method that correctly fires the OnKeysChange event - if needed -
// depending on the parameters it is given.
void NotifyIfKeysChanged(bool keys_changed, bool new_usable_keys);
// Notifies updates in keys information and fire OnKeysChange event if
// key changes.
void NotifyKeysChange(CdmKeyStatus new_status);
// Notifies updates in expiry time and fire OnExpirationUpdate event if
// expiry time changes.
void NotifyExpirationUpdate(int64_t current_time);
// Guard against clock rollbacks
int64_t GetCurrentTime();
// set_clock() is for testing only. It alters ownership of the
// passed-in pointer.
void set_clock(Clock* clock);
@@ -194,9 +180,6 @@ class PolicyEngine {
// calculate the time where renewal retries should occur.
int64_t next_renewal_time_;
// to assist in clock rollback checks
int64_t last_recorded_current_time_;
// Used to dispatch CDM events.
CdmSessionId session_id_;
WvCdmEventListener* event_listener_;

View File

@@ -43,6 +43,10 @@ class ServiceCertificate {
virtual bool HasProviderId() { return !provider_id_.empty(); }
virtual const std::string& provider_id() { return provider_id_; }
// Verify the signature for a message.
virtual CdmResponseType VerifySignedMessage(const std::string& message,
const std::string& signature);
// Encrypt the ClientIdentification message for a provisioning or
// licensing request. Encryption is performed using the current
// service certificate. Return a failure if the service certificate is

View File

@@ -64,10 +64,6 @@ static const std::string QUERY_KEY_MAX_NUMBER_OF_SESSIONS =
"MaxNumberOfSessions";
static const std::string QUERY_KEY_OEMCRYPTO_API_VERSION =
"OemCryptoApiVersion";
static const std::string QUERY_KEY_CURRENT_SRM_VERSION = "CurrentSRMVersion";
static const std::string QUERY_KEY_SRM_UPDATE_SUPPORT = "SRMUpdateSupport";
// whether OEM supports SRM update
static const std::string QUERY_KEY_WVCDM_VERSION = "WidevineCdmVersion";
static const std::string QUERY_VALUE_TRUE = "True";
static const std::string QUERY_VALUE_FALSE = "False";
@@ -77,7 +73,6 @@ static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1";
static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2";
static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3";
static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown";
static const std::string QUERY_VALUE_SECURITY_LEVEL_DEFAULT = "Default";
static const std::string QUERY_VALUE_DISCONNECTED = "Disconnected";
static const std::string QUERY_VALUE_UNPROTECTED = "Unprotected";
static const std::string QUERY_VALUE_HDCP_V1 = "HDCP-1.x";
@@ -105,7 +100,10 @@ static const std::string HLS_URI_ATTRIBUTE = "URI";
static const char EMPTY_ORIGIN[] = "";
static const char EMPTY_SPOID[] = "";
static const char EMPTY_APP_PACKAGE_NAME[] = "";
//Policy engine HDCP enforcement
static const uint32_t HDCP_UNSPECIFIED_VIDEO_RESOLUTION = 0;
static const int64_t HDCP_DEVICE_CHECK_INTERVAL = 10;
} // namespace wvcdm
#endif // WVCDM_CORE_WV_CDM_CONSTANTS_H_

View File

@@ -274,8 +274,8 @@ enum CdmResponseType {
MOVE_USAGE_ENTRY_UNKNOWN_ERROR,
COPY_OLD_USAGE_ENTRY_UNKNOWN_ERROR,
INVALID_PARAMETERS_ENG_22,
STORE_LICENSE_ERROR_4,
LIST_LICENSES_ERROR, /* 235 */
LIST_LICENSE_ERROR_1,
LIST_LICENSE_ERROR_2, /* 235 */
INVALID_PARAMETERS_ENG_23,
USAGE_INFORMATION_SUPPORT_FAILED,
USAGE_SUPPORT_GET_API_FAILED,
@@ -290,8 +290,8 @@ enum CdmResponseType {
USAGE_STORE_LICENSE_FAILED,
USAGE_STORE_USAGE_INFO_FAILED,
USAGE_INVALID_LOAD_ENTRY,
RELEASE_ALL_USAGE_INFO_ERROR_4, /* 250 */
RELEASE_ALL_USAGE_INFO_ERROR_5,
RELEASE_ALL_USAGE_INFO_ERROR_3, /* 250 */
RELEASE_ALL_USAGE_INFO_ERROR_4,
RELEASE_USAGE_INFO_FAILED,
INCORRECT_USAGE_SUPPORT_TYPE_1,
INCORRECT_USAGE_SUPPORT_TYPE_2,
@@ -303,16 +303,6 @@ enum CdmResponseType {
DELETE_USAGE_ERROR_1, /* 260 */
DELETE_USAGE_ERROR_2,
DELETE_USAGE_ERROR_3,
PRIVACY_MODE_ERROR_1,
PRIVACY_MODE_ERROR_2,
PRIVACY_MODE_ERROR_3, /* 265 */
EMPTY_RESPONSE_ERROR_1,
INVALID_PARAMETERS_ENG_24,
PARSE_RESPONSE_ERROR_1,
PARSE_RESPONSE_ERROR_2,
PARSE_RESPONSE_ERROR_3, /* 270 */
PARSE_RESPONSE_ERROR_4,
RELEASE_ALL_USAGE_INFO_ERROR_6,
};
enum CdmKeyStatus {
@@ -392,28 +382,12 @@ enum CdmClientTokenType {
// kUsageEntrySupport - usage information (table headers and entries) are
// persisted in non-secure storage but are loaded and unloaded from
// the TEE during use (OEMCrypto v13+)
// kUnknownUsageSupport - usage support type is uninitialized or unavailable
enum CdmUsageSupportType {
kNonSecureUsageSupport,
kUsageTableSupport,
kUsageEntrySupport,
};
enum CdmUsageEntryStorageType {
kStorageLicense,
kStorageUsageInfo,
kStorageUnknown,
};
struct CdmUsageEntryInfo {
CdmUsageEntryStorageType storage_type;
CdmKeySetId key_set_id;
std::string usage_info_file_name;
bool operator==(const CdmUsageEntryInfo& other) const {
return storage_type == other.storage_type &&
key_set_id == other.key_set_id &&
(storage_type != kStorageUsageInfo ||
usage_info_file_name == other.usage_info_file_name);
}
kUnknownUsageSupport,
};
class CdmKeyAllowedUsage {

File diff suppressed because it is too large Load Diff

View File

@@ -12,11 +12,11 @@
#include "clock.h"
#include "file_store.h"
#include "log.h"
#include "metrics_front_end.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
#include "usage_table_header.h"
namespace {
const size_t kKeySetIdLength = 14;
@@ -24,10 +24,10 @@ const size_t kKeySetIdLength = 14;
namespace wvcdm {
CdmSession::CdmSession(FileSystem* file_system,
metrics::SessionMetrics* metrics) :
metrics_(metrics),
CdmSession::CdmSession(FileSystem* file_system) :
initialized_(false),
closed_(false),
crypto_session_(new CryptoSession(&metrics_)),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
is_offline_(false),
@@ -39,14 +39,8 @@ CdmSession::CdmSession(FileSystem* file_system,
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
usage_support_type_(kNonSecureUsageSupport),
usage_table_header_(NULL),
usage_entry_number_(0),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(new CryptoSession(crypto_metrics_));
life_span_.Start();
}
@@ -57,10 +51,7 @@ CdmSession::~CdmSession() {
}
Properties::RemoveSessionPropertySet(session_id_);
if (metrics_) {
M_RECORD(metrics_, cdm_session_life_span_, life_span_.AsMs());
metrics_->SetCompleted();
}
M_RECORD(&metrics_, cdm_session_life_span_, life_span_.AsMs());
}
CdmResponseType CdmSession::Init(
@@ -85,28 +76,23 @@ CdmResponseType CdmSession::Init(
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_,
sts = crypto_session_->Open(
requested_security_level_),
&metrics_,
crypto_session_open_,
sts,
requested_security_level_);
if (NO_ERROR != sts) return sts;
security_level_ = crypto_session_->GetSecurityLevel();
crypto_metrics_->crypto_session_security_level_.Record(security_level_);
M_TIME(
security_level_ = crypto_session_->GetSecurityLevel(),
&metrics_,
crypto_session_get_security_level_,
security_level_);
if (!file_handle_->Init(security_level_)) {
LOGE("CdmSession::Init: Unable to initialize file handle");
return SESSION_FILE_HANDLE_INIT_ERROR;
}
if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) {
if (usage_support_type_ == kUsageEntrySupport)
usage_table_header_ = crypto_session_->GetUsageTableHeader();
} else {
usage_support_type_ = kNonSecureUsageSupport;
}
// Device Provisioning state is not yet known.
// If not using certificates, then Keybox is client token for license
// requests.
@@ -124,9 +110,13 @@ CdmResponseType CdmSession::Init(
LOGW("CdmSession::Init: Properties::use_certificates_as_identification() "
"is not set - using Keybox for license requests (not recommended).");
bool get_client_token_sts = crypto_session_->GetClientToken(&client_token);
crypto_metrics_->crypto_session_get_token_.Increment(
get_client_token_sts);
bool get_client_token_sts;
M_TIME(
get_client_token_sts = crypto_session_->GetClientToken(
&client_token),
&metrics_,
crypto_session_get_token_,
get_client_token_sts);
if (!get_client_token_sts) {
return SESSION_INIT_ERROR_1;
}
@@ -141,7 +131,7 @@ CdmResponseType CdmSession::Init(
M_TIME(
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(
wrapped_key),
crypto_metrics_,
&metrics_,
crypto_session_load_certificate_private_key_,
load_cert_sts);
if(!load_cert_sts) {
@@ -163,7 +153,6 @@ CdmResponseType CdmSession::Init(
session_id_ =
Properties::AlwaysUseKeySetIds() ? key_set_id_ : GenerateSessionId();
metrics_->SetSessionId(session_id_);
if (session_id_.empty()) {
LOGE("CdmSession::Init: empty session ID");
@@ -191,54 +180,33 @@ CdmResponseType CdmSession::Init(
CdmResponseType CdmSession::RestoreOfflineSession(
const CdmKeySetId& key_set_id, const CdmLicenseType license_type) {
if (!key_set_id_.empty()) {
file_handle_->UnreserveLicenseId(key_set_id_);
}
key_set_id_ = key_set_id;
DeviceFiles::LicenseState license_state;
int64_t playback_start_time;
int64_t last_playback_time;
int64_t grace_period_end_time;
std::string usage_entry;
if (!file_handle_->RetrieveLicense(
key_set_id, &license_state, &offline_init_data_, &key_request_,
&key_response_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &grace_period_end_time,
&app_parameters_, &usage_entry_, &usage_entry_number_)) {
LOGE("CdmSession::RestoreOfflineSession: failed to retrieve license. "
"key set id = %s", key_set_id.c_str());
&app_parameters_, &usage_entry)) {
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
key_set_id.c_str());
return GET_LICENSE_ERROR;
}
// Do not restore a released offline license, unless a release retry
if (!(license_type == kLicenseTypeRelease ||
license_state == DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::RestoreOfflineSession: invalid offline license state = "
"%d, type = %d", license_state, license_type);
LOGE("CdmSession::Init invalid offline license state = %d, type = %d",
license_state, license_type);
return GET_RELEASED_LICENSE_ERROR;
}
std::string provider_session_token;
if (!license_parser_->ExtractProviderSessionToken(
key_response_, &provider_session_token)) {
provider_session_token.clear();
}
if (usage_support_type_ == kUsageEntrySupport &&
provider_session_token.size() > 0 &&
usage_table_header_ != nullptr) {
CdmResponseType sts = usage_table_header_->LoadEntry(crypto_session_.get(),
usage_entry_,
usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = %d",
sts);
return sts;
}
}
if (license_type == kLicenseTypeRelease) {
if (!license_parser_->RestoreLicenseForRelease(key_request_,
key_response_)) {
@@ -247,28 +215,11 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} else {
if (!license_parser_->RestoreOfflineLicense(
key_request_, key_response_, offline_key_renewal_response_,
playback_start_time, last_playback_time, grace_period_end_time,
this)) {
playback_start_time, last_playback_time, grace_period_end_time)) {
return RESTORE_OFFLINE_LICENSE_ERROR_2;
}
}
if (usage_support_type_ == kUsageEntrySupport &&
provider_session_token.size() > 0 &&
usage_table_header_ != nullptr) {
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession failed to update usage entry = "
"%d", sts);
return sts;
}
if (!StoreLicense(license_state)) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
license_received_ = true;
is_offline_ = true;
is_release_ = license_type == kLicenseTypeRelease;
@@ -276,47 +227,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
}
CdmResponseType CdmSession::RestoreUsageSession(
const DeviceFiles::CdmUsageData& usage_data) {
if (!key_set_id_.empty()) {
file_handle_->UnreserveLicenseId(key_set_id_);
}
key_set_id_ = usage_data.key_set_id;
key_request_ = usage_data.license_request;
key_response_ = usage_data.license;
usage_entry_ = usage_data.usage_entry;
usage_entry_number_ = usage_data.usage_entry_number;
usage_provider_session_token_ = usage_data.provider_session_token;
if (usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != nullptr) {
CdmResponseType sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to load usage entry = %d",
sts);
return sts;
}
}
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response) {
key_request_ = key_request;
key_response_ = key_response;
if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) {
return RELEASE_LICENSE_ERROR_2;
}
if (usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != nullptr) {
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to update usage entry: %d",
sts);
return sts;
}
if (!UpdateUsageInfo()) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
license_received_ = true;
is_offline_ = false;
is_release_ = true;
@@ -442,57 +360,14 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
} else if (license_received_) { // renewal
return RenewKey(key_response);
} else {
// If usage table header+entries are supported, preprocess the license
// to see if it has a provider session token. If so a new entry needs
// to be created.
CdmResponseType sts;
std::string provider_session_token;
if (usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != nullptr) {
if (license_parser_->ExtractProviderSessionToken(
key_response, &provider_session_token) &&
!provider_session_token.empty()) {
std::string app_id;
GetApplicationId(&app_id);
sts = usage_table_header_->AddEntry(
crypto_session_.get(), is_offline_, key_set_id_,
DeviceFiles::GetUsageInfoFileName(app_id), &usage_entry_number_);
if (sts != NO_ERROR) return sts;
}
}
sts = license_parser_->HandleKeyResponse(key_response);
// Update or delete entry if usage table header+entries are supported
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty() &&
usage_table_header_ != nullptr) {
if (sts != KEY_ADDED) {
CdmResponseType sts =
usage_table_header_->DeleteEntry(usage_entry_number_,
file_handle_.get(),
crypto_metrics_);
if (sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d", sts);
}
}
}
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? ADD_KEY_ERROR : sts;
license_received_ = true;
key_response_ = key_response;
if (is_offline_ || has_provider_session_token()) {
if (has_provider_session_token() &&
usage_support_type_ == kUsageEntrySupport &&
usage_table_header_ != nullptr) {
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
}
if (!is_offline_)
usage_provider_session_token_ =
license_parser_->provider_session_token();
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
sts = StoreLicense();
if (sts != NO_ERROR) return sts;
}
@@ -595,7 +470,8 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
}
has_decrypted_since_last_report_ = true;
if (!is_usage_update_needed_) {
is_usage_update_needed_ = has_provider_session_token();
is_usage_update_needed_ =
!license_parser_->provider_session_token().empty();
}
} else {
Clock clock;
@@ -614,7 +490,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
CdmResponseType CdmSession::GenerateRenewalRequest(
CdmKeyRequest* key_request) {
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
true, app_parameters_, NULL, &key_request->message, &key_request->url);
true, app_parameters_, &key_request->message, &key_request->url);
key_request->type = kKeyRequestTypeRenewal;
@@ -644,32 +520,16 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
CdmKeyRequest* key_request) {
is_release_ = true;
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
false, app_parameters_, usage_table_header_ == NULL ? NULL : this,
&key_request->message, &key_request->url);
false, app_parameters_, &key_request->message,
&key_request->url);
key_request->type = kKeyRequestTypeRelease;
if (KEY_MESSAGE != status) return status;
if (has_provider_session_token() &&
usage_support_type_ == kUsageEntrySupport) {
status = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
if (status != NO_ERROR) {
LOGE("CdmSession::GenerateReleaseRequest: Update usage entry failed = "
"%d", status);
return status;
}
}
if (is_offline_) { // Mark license as being released
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))
return RELEASE_KEY_REQUEST_ERROR;
} else if (!usage_provider_session_token_.empty()) {
if (usage_support_type_ == kUsageEntrySupport) {
if (!UpdateUsageInfo())
return RELEASE_USAGE_INFO_FAILED;
}
}
return KEY_MESSAGE;
}
@@ -680,56 +540,12 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
license_parser_->HandleKeyUpdateResponse(false, key_response);
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RELEASE_KEY_ERROR : sts;
if (is_offline_ || has_provider_session_token()) {
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
DeleteLicense();
if (usage_support_type_ == kUsageEntrySupport &&
has_provider_session_token()) {
sts = DeleteUsageEntry(usage_entry_number_);
if (NO_ERROR != sts) return sts;
}
}
return NO_ERROR;
}
CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
if (usage_support_type_ != kUsageEntrySupport) {
LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d",
usage_support_type_);
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
// The usage entry cannot be deleted if it has a crypto session handling
// it, so close and reopen session.
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(new CryptoSession(crypto_metrics_));
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_,
crypto_session_open_,
sts,
requested_security_level_);
if (sts != NO_ERROR) return sts;
usage_table_header_ = NULL;
if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) {
if (usage_support_type_ == kUsageEntrySupport)
usage_table_header_ = crypto_session_->GetUsageTableHeader();
} else {
usage_support_type_ = kNonSecureUsageSupport;
}
if (usage_table_header_ == NULL) {
LOGE("CdmSession::DeleteUsageEntry: Usage table header unavailable");
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
return usage_table_header_->DeleteEntry(usage_entry_number,
file_handle_.get(),
crypto_metrics_);
}
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
return license_parser_->IsKeyLoaded(key_id);
}
@@ -803,11 +619,10 @@ CdmResponseType CdmSession::StoreLicense() {
std::string app_id;
GetApplicationId(&app_id);
std::string usage_entry;
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
key_response_,
DeviceFiles::GetUsageInfoFileName(app_id),
key_set_id_, usage_entry_,
usage_entry_number_)) {
key_response_, app_id, key_set_id_,
usage_entry)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return STORE_USAGE_INFO_ERROR;
}
@@ -815,13 +630,13 @@ CdmResponseType CdmSession::StoreLicense() {
}
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
std::string usage_entry;
return file_handle_->StoreLicense(
key_set_id_, state, offline_init_data_, key_request_, key_response_,
offline_key_renewal_request_, offline_key_renewal_response_,
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
policy_engine_->GetLastPlaybackTime(),
policy_engine_->GetGracePeriodEndTime(), app_parameters_, usage_entry_,
usage_entry_number_);
policy_engine_->GetGracePeriodEndTime(), app_parameters_, usage_entry);
}
CdmResponseType CdmSession::ReleaseCrypto() {
@@ -830,7 +645,7 @@ CdmResponseType CdmSession::ReleaseCrypto() {
}
bool CdmSession::DeleteLicense() {
if (!is_offline_ && !has_provider_session_token())
if (!is_offline_ && license_parser_->provider_session_token().empty())
return false;
if (is_offline_) {
@@ -839,8 +654,7 @@ bool CdmSession::DeleteLicense() {
std::string app_id;
GetApplicationId(&app_id);
return file_handle_->DeleteUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
license_parser_->provider_session_token());
app_id, license_parser_->provider_session_token());
}
}
@@ -873,48 +687,24 @@ void CdmSession::GetApplicationId(std::string* app_id) {
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
const std::vector<std::string>& provider_session_tokens) {
CdmResponseType sts = crypto_session_->DeleteMultipleUsageInformation(
provider_session_tokens);
crypto_metrics_->crypto_session_delete_multiple_usage_information_
.Increment(sts);
return sts;
}
CdmResponseType CdmSession::UpdateUsageTableInformation() {
CdmResponseType sts;
M_TIME(
sts = crypto_session_->UpdateUsageInformation(),
crypto_metrics_,
crypto_session_update_usage_information_,
sts = crypto_session_->DeleteMultipleUsageInformation(
provider_session_tokens),
&metrics_,
crypto_session_delete_multiple_usage_information_,
sts);
return sts;
}
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (usage_support_type_ != kUsageEntrySupport ||
!has_provider_session_token() ||
usage_table_header_ == nullptr) {
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected state, "
"usage support type: %d, PST present: %s, usage table header available"
": %s", usage_support_type_,
has_provider_session_token() ? "yes" : "no",
usage_table_header_ == nullptr ? "no" : "yes");
return INCORRECT_USAGE_SUPPORT_TYPE_2;
}
CdmResponseType sts = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
if (sts != NO_ERROR) return sts;
if (is_offline_)
StoreLicense(is_release_
? DeviceFiles::kLicenseStateReleasing
: DeviceFiles::kLicenseStateActive);
else if (!usage_provider_session_token_.empty())
UpdateUsageInfo();
return NO_ERROR;
CdmResponseType CdmSession::UpdateUsageInformation() {
CdmResponseType sts;
M_TIME(
sts = crypto_session_->UpdateUsageInformation(),
&metrics_,
crypto_session_update_usage_information_,
sts);
return sts;
}
CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
@@ -934,7 +724,7 @@ CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
iv,
algorithm,
out_buffer),
crypto_metrics_,
&metrics_,
crypto_session_generic_encrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
@@ -959,7 +749,7 @@ CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
iv,
algorithm,
out_buffer),
crypto_metrics_,
&metrics_,
crypto_session_generic_decrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
@@ -982,7 +772,7 @@ CdmResponseType CdmSession::GenericSign(const std::string& message,
key_id,
algorithm,
signature),
crypto_metrics_,
&metrics_,
crypto_session_generic_sign_,
sts,
metrics::Pow2Bucket(message.size()),
@@ -1001,7 +791,7 @@ CdmResponseType CdmSession::GenericVerify(const std::string& message,
key_id,
algorithm,
signature),
crypto_metrics_,
&metrics_,
crypto_session_generic_verify_,
sts,
metrics::Pow2Bucket(message.size()),
@@ -1009,24 +799,6 @@ CdmResponseType CdmSession::GenericVerify(const std::string& message,
return sts;
}
bool CdmSession::UpdateUsageInfo() {
std::string app_id;
GetApplicationId(&app_id);
DeviceFiles::CdmUsageData usage_data;
usage_data.provider_session_token = usage_provider_session_token_;
usage_data.license_request = key_request_;
usage_data.license = key_response_;
usage_data.key_set_id = key_set_id_;
usage_data.usage_entry = usage_entry_;
usage_data.usage_entry_number = usage_entry_number_;
return file_handle_->UpdateUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
usage_provider_session_token_,
usage_data);
}
// For testing only - takes ownership of pointers
void CdmSession::set_license_parser(CdmLicense* license_parser) {

View File

@@ -19,45 +19,71 @@ const std::string kProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
/*
* Provisioning response is a base64-encoded protobuf, optionally within a
* JSON wrapper. If the JSON wrapper is present, extract the embedded response
* message. Then perform the base64 decode and return the result.
*
* If an error occurs during the parse or the decode, return an empty string.
*/
void ExtractAndDecodeSignedMessage(const std::string& provisioning_response,
std::string* result) {
const std::string json_start_substr("\"signedResponse\": \"");
const std::string json_end_substr("\"");
std::string message_string;
size_t start = provisioning_response.find(json_start_substr);
if (start == provisioning_response.npos) {
// Message is not properly wrapped - reject it.
LOGE("ExtractAndDecodeSignedMessage: cannot locate start substring");
result->clear();
return;
} else {
// Appears to be JSON-wrapped protobuf - find end of protobuf portion.
size_t end = provisioning_response.find(json_end_substr,
start + json_start_substr.length());
if (end == provisioning_response.npos) {
LOGE("ExtractAndDecodeSignedMessage: cannot locate end substring");
result->clear();
return;
}
size_t b64_string_size = end - start - json_start_substr.length();
message_string.assign(provisioning_response,
start + json_start_substr.length(), b64_string_size);
}
if (message_string.empty()) {
LOGE("ExtractAndDecodeSignedMessage: CdmProvisioningResponse is empty");
result->clear();
return;
}
// Decode the base64-encoded message.
const std::vector<uint8_t> decoded_message =
wvcdm::Base64SafeDecode(message_string);
result->assign(decoded_message.begin(), decoded_message.end());
}
}
namespace wvcdm {
// Protobuf generated classes.
using video_widevine::ClientIdentification;
using video_widevine::EncryptedClientIdentification;
using video_widevine::ProvisioningOptions;
using video_widevine::ProvisioningRequest;
using video_widevine::ProvisioningResponse;
using video_widevine::SignedProvisioningMessage;
/*
* 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
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
*
* The JSON formated request takes the following format:
*
* base64 encoded message
*/
void CertificateProvisioning::ComposeJsonRequestAsQueryString(
const std::string& message, CdmProvisioningRequest* request) {
// Performs base64 encoding for message
std::vector<uint8_t> message_vector(message.begin(), message.end());
std::string message_b64 = Base64SafeEncodeNoPad(message_vector);
request->assign(message_b64);
}
/*
* Return the ClientIdentification message token type for provisioning request.
* NOTE: a DRM Cert should never be presented to the provisioning server.
*/
bool CertificateProvisioning::GetProvisioningTokenType(
ClientIdentification::TokenType* token_type) {
CdmClientTokenType token = crypto_session_.GetPreProvisionTokenType();
switch (token) {
switch (crypto_session_.GetPreProvisionTokenType()) {
case kClientTokenKeybox:
*token_type = ClientIdentification::KEYBOX;
return true;
@@ -67,8 +93,6 @@ bool CertificateProvisioning::GetProvisioningTokenType(
case kClientTokenDrmCert:
default:
// shouldn't happen
LOGE("CertificateProvisioning::GetProvisioningTokenType: unexpected "
"provisioning type: %d", token);
return false;
}
}
@@ -83,7 +107,7 @@ bool CertificateProvisioning::SetSpoidParameter(
const std::string& origin, const std::string& spoid,
ProvisioningRequest* request) {
if (!request) {
LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer "
LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer "
"passed to method.");
return false;
}
@@ -102,7 +126,8 @@ bool CertificateProvisioning::SetSpoidParameter(
// Legacy behavior - Concatenate Unique ID with Origin
std::string device_unique_id;
if (!crypto_session_.GetInternalDeviceUniqueId(&device_unique_id)) {
LOGE("CryptoSession::GetStableIdField: Failure to get device unique ID");
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
"device unique ID");
return false;
}
request->set_stable_id(device_unique_id + origin);
@@ -123,9 +148,9 @@ SignedProvisioningMessage::ProtocolVersion
}
/*
* Composes a device provisioning request and output the request in JSON format
* in *request. It also returns the default url for the provisioning server
* in *default_url.
* Compose a device provisioning request and output *request in a
* JSON-compatible format (web-safe base64).
* Also return *default_url of the provisioning server.
*
* Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails.
*/
@@ -164,6 +189,22 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
client_id->set_token(token);
client_id->set_type(token_type);
#if 0 // TODO(gmorgan) Encrypt ClientIdentification. Pending Design.
if (service_certificate_->has_certificate()) {
EncryptedClientIdentification* encrypted_client_id =
provisioning_request.mutable_encrypted_client_id();
CdmResponseType status;
status = service_certificate_->EncryptClientId(&crypto_session_, client_id,
encrypted_client_id);
if (status == NO_ERROR) {
provisioning_request.clear_client_id();
} else {
provisioning_request.clear_encrypted_client_id();
}
return status;
}
#endif
uint32_t nonce;
if (!crypto_session_.GenerateNonce(&nonce)) {
LOGE("GetProvisioningRequest: fails to generate a nonce");
@@ -220,43 +261,13 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
// Converts request into JSON string
ComposeJsonRequestAsQueryString(serialized_request, request);
// Return request as web-safe base64 string
std::vector<uint8_t> request_vector(serialized_request.begin(),
serialized_request.end());
request->assign(Base64SafeEncodeNoPad(request_vector));
return NO_ERROR;
}
/*
* Parses the input json_str and locates substring using start_substr and
* end_stubstr. The found base64 substring is then decoded and returns
* in *result.
*
* Returns true for success and false if fails.
*/
bool CertificateProvisioning::ParseJsonResponse(
const CdmProvisioningResponse& json_str, const std::string& start_substr,
const std::string& end_substr, std::string* result) {
std::string b64_string;
size_t start = json_str.find(start_substr);
if (start == json_str.npos) {
LOGE("ParseJsonResponse: cannot find start substring");
return false;
}
size_t end = json_str.find(end_substr, start + start_substr.length());
if (end == json_str.npos) {
LOGE("ParseJsonResponse cannot locate end substring");
return false;
}
size_t b64_string_size = end - start - start_substr.length();
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
// Decodes base64 substring and returns it in *result
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
result->assign(result_vector.begin(), result_vector.end());
return true;
}
/*
* The response message consists of a device certificate and the device RSA key.
* The device RSA key is stored in the T.E.E. The device certificate is stored
@@ -267,13 +278,15 @@ bool CertificateProvisioning::ParseJsonResponse(
CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
FileSystem* file_system, const CdmProvisioningResponse& response,
std::string* cert, std::string* wrapped_key) {
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string serialized_signed_response;
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
&serialized_signed_response)) {
LOGE("Fails to extract signed serialized response from JSON response");
std::string raw_string;
// The response is base64 encoded in a JSON wrapper.
// Extract it and decode it. If errors, return an empty string.
ExtractAndDecodeSignedMessage(response, &raw_string);
if (raw_string.empty()) {
LOGE("HandleProvisioningResponse: response message is empty or "
"an invalid JSON/base64 string.");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
}
@@ -281,7 +294,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
// the provisioing request's input). Validate provisioning response and
// stores private device RSA key and certificate.
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(serialized_signed_response)) {
if (!signed_response.ParseFromString(raw_string)) {
LOGE("HandleProvisioningResponse: fails to parse signed response");
return CERT_PROVISIONING_RESPONSE_ERROR_2;
}
@@ -300,6 +313,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3;
const std::string& signed_message = signed_response.message();
const std::string& signature = signed_response.signature();
ProvisioningResponse provisioning_response;
if (!provisioning_response.ParseFromString(signed_message)) {
@@ -312,17 +326,28 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
return CERT_PROVISIONING_RESPONSE_ERROR_5;
}
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
// If Provisioning 3.0 (OEM Cert provisioned), verify that the
// message is properly signed.
if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert) {
if (!service_certificate_->VerifySignedMessage(signed_message, signature)) {
LOGE("HandleProvisioningResponse: message not properly signed");
return CERT_PROVISIONING_RESPONSE_ERROR_6;
}
}
const std::string& new_private_key = provisioning_response.device_rsa_key();
const std::string& nonce = provisioning_response.nonce();
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
const std::string& iv = provisioning_response.device_rsa_key_iv();
const std::string& wrapping_key = (provisioning_response.has_wrapping_key()) ?
provisioning_response.wrapping_key() : std::string();
const std::string& signature = signed_response.signature();
std::string wrapped_rsa_key;
provisioning_response.wrapping_key() : std::string();
std::string wrapped_private_key;
if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce,
enc_rsa_key, rsa_key_iv, wrapping_key,
&wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
new_private_key, iv, wrapping_key,
&wrapped_private_key)) {
LOGE("HandleProvisioningResponse: RewrapCertificate fails");
return CERT_PROVISIONING_RESPONSE_ERROR_6;
}
@@ -330,10 +355,13 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (cert_type_ == kCertificateX509) {
*cert = provisioning_response.device_certificate();
*wrapped_key = wrapped_rsa_key;
*wrapped_key = wrapped_private_key;
return NO_ERROR;
}
// This is the entire certificate (SignedDrmDeviceCertificate).
// This will be stored to the device as the final step in the device
// provisioning process.
const std::string& device_certificate =
provisioning_response.device_certificate();
@@ -342,7 +370,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
LOGE("HandleProvisioningResponse: failed to init DeviceFiles");
return CERT_PROVISIONING_RESPONSE_ERROR_7;
}
if (!handle.StoreCertificate(device_certificate, wrapped_rsa_key)) {
if (!handle.StoreCertificate(device_certificate, wrapped_private_key)) {
LOGE("HandleProvisioningResponse: failed to save provisioning certificate");
return CERT_PROVISIONING_RESPONSE_ERROR_8;
}

File diff suppressed because it is too large Load Diff

View File

@@ -40,8 +40,6 @@ using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE;
using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO;
using video_widevine_client::sdk::
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN;
namespace {
@@ -51,7 +49,7 @@ const char kUsageInfoFileNamePrefix[] = "usage";
const char kUsageInfoFileNameExt[] = ".bin";
const char kLicenseFileNameExt[] = ".lic";
const char kEmptyFileName[] = "";
const char kUsageTableFileName[] = "usgtable.bin";
const char kUsageTableFileName[] = "usagetable.bin";
const char kWildcard[] = "*";
bool Hash(const std::string& data, std::string* hash) {
@@ -146,7 +144,6 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
}
DeviceCertificate device_certificate = file.device_certificate();
*certificate = device_certificate.certificate();
*wrapped_private_key = device_certificate.wrapped_private_key();
return true;
@@ -179,8 +176,7 @@ bool DeviceFiles::StoreLicense(
const std::string& release_server_url, int64_t playback_start_time,
int64_t last_playback_time, int64_t grace_period_end_time,
const CdmAppParameterMap& app_parameters,
const CdmUsageEntry& usage_entry,
const uint32_t usage_entry_number) {
const CdmUsageEntry& usage_entry) {
if (!initialized_) {
LOGW("DeviceFiles::StoreLicense: not initialized");
return false;
@@ -222,7 +218,6 @@ bool DeviceFiles::StoreLicense(
app_params->set_value(iter->second);
}
license->set_usage_entry(usage_entry);
license->set_usage_entry_number(usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
@@ -237,8 +232,7 @@ bool DeviceFiles::RetrieveLicense(
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
std::string* release_server_url, int64_t* playback_start_time,
int64_t* last_playback_time, int64_t* grace_period_end_time,
CdmAppParameterMap* app_parameters, CdmUsageEntry* usage_entry,
uint32_t* usage_entry_number) {
CdmAppParameterMap* app_parameters, CdmUsageEntry* usage_entry) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveLicense: not initialized");
return false;
@@ -293,7 +287,6 @@ bool DeviceFiles::RetrieveLicense(
license.app_parameters(i).value();
}
*usage_entry = license.usage_entry();
*usage_entry_number = license.usage_entry_number();
return true;
}
@@ -354,7 +347,6 @@ bool DeviceFiles::DeleteAllFiles() {
// We pass an empty string to RemoveFile to delete the device files base
// directory itself.
// TODO[gmorgan]: verify RemoveFile("") should remove all files.
return RemoveFile(kEmptyFileName);
}
@@ -388,21 +380,21 @@ bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) {
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response,
const std::string& usage_info_file_name,
const std::string& app_id,
const std::string& key_set_id,
const std::string& usage_entry,
uint32_t usage_entry_number) {
const CdmUsageEntry& usage_entry) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
if (!FileExists(usage_info_file_name)) {
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name)) {
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
} else {
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::StoreUsageInfo: Unable to parse file");
return false;
}
@@ -417,21 +409,92 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
provider_session->set_license(key_response.data(), key_response.size());
provider_session->set_key_set_id(key_set_id.data(), key_set_id.size());
provider_session->set_usage_entry(usage_entry);
provider_session->set_usage_entry_number(usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
return StoreFileWithHash(file_name, serialized_file);
}
bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name,
bool DeviceFiles::ListUsageRecords(const std::string& app_id,
std::vector<std::string>* ksids) {
if (!initialized_) {
LOGW("DeviceFiles::ListUsageRecords: not initialized");
return false;
}
if (ksids == NULL) {
LOGW("DeviceFiles::ListUsageRecords: return parameter not provided");
return false;
}
// Empty or non-existent file == no usage records.
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
ksids->clear();
return true;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::ListUsageRecords: Unable to parse file");
return false;
}
ksids->clear();
size_t num_records = file.usage_info().sessions_size();
for (size_t i = 0; i < num_records; ++i) {
if (!file.usage_info().sessions(i).key_set_id().empty()) {
ksids->push_back(file.usage_info().sessions(i).key_set_id());
}
}
return true;
}
bool DeviceFiles::GetProviderSessionToken(const std::string& app_id,
const std::string& key_set_id,
std::string* provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::GetProviderSessionToken: not initialized");
return false;
}
if (provider_session_token == NULL) {
LOGW("DeviceFiles::GetProviderSessionToken: NULL return argument pointer");
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
LOGW("DeviceFiles::GetProviderSessionToken: empty file");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::GetProviderSessionToken: unable to parse file");
return false;
}
size_t num_records = file.usage_info().sessions_size();
for (size_t i = 0; i < num_records; ++i) {
if (file.usage_info().sessions(i).key_set_id() == key_set_id) {
*provider_session_token = file.usage_info().sessions(i).token();
return true;
}
}
return false;
}
bool DeviceFiles::DeleteUsageInfo(const std::string& app_id,
const std::string& provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) return false;
std::string file_name = GetUsageInfoFileName(app_id);
if (!RetrieveHashedFile(file_name, &file)) return false;
UsageInfo* usage_info = file.mutable_usage_info();
int index = 0;
@@ -460,11 +523,11 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name,
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
return StoreFileWithHash(file_name, serialized_file);
}
bool DeviceFiles::DeleteAllUsageInfoForApp(
const std::string& usage_info_file_name,
const std::string& app_id,
std::vector<std::string>* provider_session_tokens) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized");
@@ -476,21 +539,22 @@ bool DeviceFiles::DeleteAllUsageInfoForApp(
}
provider_session_tokens->clear();
if (!FileExists(usage_info_file_name)) return true;
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name)) return true;
video_widevine_client::sdk::File file;
if (RetrieveHashedFile(usage_info_file_name, &file)) {
if (RetrieveHashedFile(file_name, &file)) {
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
provider_session_tokens->push_back(file.usage_info().sessions(i).token());
}
} else {
LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file");
}
return RemoveFile(usage_info_file_name);
return RemoveFile(file_name);
}
bool DeviceFiles::RetrieveUsageInfo(
const std::string& usage_info_file_name,
const std::string& app_id,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
@@ -504,14 +568,14 @@ bool DeviceFiles::RetrieveUsageInfo(
return false;
}
if (!FileExists(usage_info_file_name) ||
GetFileSize(usage_info_file_name) == 0) {
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
usage_info->resize(0);
return true;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file");
return false;
}
@@ -526,19 +590,19 @@ bool DeviceFiles::RetrieveUsageInfo(
return true;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
std::string* usage_entry,
uint32_t* usage_entry_number) {
CdmUsageEntry* usage_entry) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
if (!RetrieveHashedFile(file_name, &file)) {
return false;
}
@@ -548,8 +612,6 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
@@ -558,32 +620,28 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
}
bool DeviceFiles::RetrieveUsageInfoByKeySetId(
const std::string& usage_info_file_name,
const std::string& app_id,
const std::string& key_set_id,
std::string* provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response,
std::string* usage_entry,
uint32_t* usage_entry_number) {
CdmUsageEntry* usage_entry) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: not initialized");
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
if (!RetrieveHashedFile(file_name, &file)) {
return false;
}
int index = 0;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).key_set_id() == key_set_id) {
*provider_session_token = file.usage_info().sessions(index).token();
*license_request = file.usage_info().sessions(index).license_request();
*license_response = file.usage_info().sessions(index).license();
*usage_entry = file.usage_info().sessions(index).usage_entry();
*usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
@@ -591,190 +649,6 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId(
return false;
}
bool DeviceFiles::StoreUsageInfo(const std::string& usage_info_file_name,
const std::vector<CdmUsageData>& usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1);
UsageInfo* usage_info = file.mutable_usage_info();
for (size_t i = 0; i < usage_data.size(); ++i) {
UsageInfo_ProviderSession* provider_session = usage_info->add_sessions();
provider_session->set_token(usage_data[i].provider_session_token.data(),
usage_data[i].provider_session_token.size());
provider_session->set_license_request(usage_data[i].license_request.data(),
usage_data[i].license_request.size());
provider_session->set_license(usage_data[i].license.data(),
usage_data[i].license.size());
provider_session->set_key_set_id(usage_data[i].key_set_id.data(),
usage_data[i].key_set_id.size());
provider_session->set_usage_entry(usage_data[i].usage_entry);
provider_session->set_usage_entry_number(usage_data[i].usage_entry_number);
}
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
bool DeviceFiles::UpdateUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
const CdmUsageData& usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::UpdateUsageInfo: not initialized");
return false;
}
video_widevine_client::sdk::File file;
if (!FileExists(usage_info_file_name)) {
LOGW("DeviceFiles::UpdateUsageInfo: Usage file does not exist");
return false;
}
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
LOGW("DeviceFiles::UpdateUsageInfo: Unable to parse file");
return false;
}
int index = 0;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).token() == provider_session_token) {
UsageInfo* usage_info = file.mutable_usage_info();
UsageInfo_ProviderSession* provider_session =
usage_info->mutable_sessions(index);
provider_session->set_license_request(usage_data.license_request);
provider_session->set_license(usage_data.license);
provider_session->set_key_set_id(usage_data.key_set_id);
provider_session->set_usage_entry(usage_data.usage_entry);
provider_session->set_usage_entry_number(usage_data.usage_entry_number);
std::string serialized_file;
file.SerializeToString(&serialized_file);
return StoreFileWithHash(usage_info_file_name, serialized_file);
}
}
return false;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
std::vector<CdmUsageData>* usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
if (usage_data == NULL) {
LOGW("DeviceFiles::RetrieveUsageInfo: usage_data not provided");
return false;
}
if (!FileExists(usage_info_file_name) ||
GetFileSize(usage_info_file_name) == 0) {
usage_data->resize(0);
return true;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
usage_data->resize(file.usage_info().sessions_size());
for (int i = 0; i < file.usage_info().sessions_size(); ++i) {
(*usage_data)[i].provider_session_token =
file.usage_info().sessions(i).token();
(*usage_data)[i].license_request =
file.usage_info().sessions(i).license_request();
(*usage_data)[i].license = file.usage_info().sessions(i).license();
(*usage_data)[i].key_set_id = file.usage_info().sessions(i).key_set_id();
(*usage_data)[i].usage_entry = file.usage_info().sessions(i).usage_entry();
(*usage_data)[i].usage_entry_number =
file.usage_info().sessions(i).usage_entry_number();
}
return true;
}
bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name,
const std::string& provider_session_token,
CdmUsageData* usage_data) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false;
}
if (usage_data == NULL) {
LOGW("DeviceFiles::RetrieveUsageInfo: usage_data not provided");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(usage_info_file_name, &file)) {
return false;
}
int index = 0;
for (; index < file.usage_info().sessions_size(); ++index) {
if (file.usage_info().sessions(index).token() == provider_session_token) {
usage_data->provider_session_token =
file.usage_info().sessions(index).token();
usage_data->license_request =
file.usage_info().sessions(index).license_request();
usage_data->license = file.usage_info().sessions(index).license();
usage_data->key_set_id = file.usage_info().sessions(index).key_set_id();
usage_data->usage_entry = file.usage_info().sessions(index).usage_entry();
usage_data->usage_entry_number =
file.usage_info().sessions(index).usage_entry_number();
return true;
}
}
return false;
}
bool DeviceFiles::ListUsageInfoFiles(
std::vector<std::string>* usage_info_file_names) {
if (!initialized_) {
LOGW("DeviceFiles::ListUsageInfoFiles: not initialized");
return false;
}
if (usage_info_file_names == NULL) {
LOGW("DeviceFiles::ListUsageInfoFiles: usage_info_file_names not provided");
return false;
}
// Get list of filenames
std::vector<std::string> filenames;
if (!ListFiles(&filenames)) {
return false;
}
// Scan list of all filenames and return only usage info filenames
usage_info_file_names->clear();
for (size_t i = 0; i < filenames.size(); i++) {
std::string* name = &filenames[i];
std::size_t pos_prefix = name->find(kUsageInfoFileNamePrefix);
std::size_t pos_suffix = name->find(kUsageInfoFileNameExt);
if (pos_prefix == std::string::npos ||
pos_suffix == std::string::npos) {
// Skip this file - extension does not match
continue;
}
usage_info_file_names->push_back(*name);
}
return true;
}
bool DeviceFiles::StoreHlsAttributes(
const std::string& key_set_id, const CdmHlsMethod method,
const std::vector<uint8_t>& media_segment_iv) {
@@ -874,7 +748,7 @@ bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) {
bool DeviceFiles::StoreUsageTableInfo(
const CdmUsageTableHeader& usage_table_header,
const std::vector<CdmUsageEntryInfo>& usage_entry_info) {
const std::vector<DeviceFiles::UsageEntryInfo>& usage_entry_info) {
if (!initialized_) {
LOGW("DeviceFiles::StoreUsageTableHeader: not initialized");
return false;
@@ -891,23 +765,23 @@ bool DeviceFiles::StoreUsageTableInfo(
for (size_t i = 0; i < usage_entry_info.size(); ++i) {
UsageTableInfo_UsageEntryInfo* info =
usage_table_info->add_usage_entry_info();
info->set_key_set_id(usage_entry_info[i].key_set_id);
switch (usage_entry_info[i].storage_type) {
case kStorageLicense:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE);
info->set_key_set_id(usage_entry_info[i].key_set_id);
break;
case kStorageUsageInfo:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO);
info->set_usage_info_file_name(
usage_entry_info[i].usage_info_file_name);
info->set_provider_session_token(
usage_entry_info[i].provider_session_token);
info->set_app_id(usage_entry_info[i].app_id);
break;
case kStorageUnknown:
default:
info->set_storage(
UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN);
break;
LOGW("DeviceFiles::StoreUsageTableHeader: unknown storage type: %d",
usage_entry_info[i].storage_type);
return false;
}
}
@@ -919,7 +793,7 @@ bool DeviceFiles::StoreUsageTableInfo(
bool DeviceFiles::RetrieveUsageTableInfo(
CdmUsageTableHeader* usage_table_header,
std::vector<CdmUsageEntryInfo>* usage_entry_info) {
std::vector<DeviceFiles::UsageEntryInfo>* usage_entry_info) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageTableInfo: not initialized");
return false;
@@ -963,20 +837,21 @@ bool DeviceFiles::RetrieveUsageTableInfo(
for (int i = 0; i < usage_table_info.usage_entry_info_size(); ++i) {
const UsageTableInfo_UsageEntryInfo& info =
usage_table_info.usage_entry_info(i);
(*usage_entry_info)[i].key_set_id = info.key_set_id();
switch (info.storage()) {
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE:
(*usage_entry_info)[i].storage_type = kStorageLicense;
(*usage_entry_info)[i].key_set_id = info.key_set_id();
break;
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO:
(*usage_entry_info)[i].storage_type = kStorageUsageInfo;
(*usage_entry_info)[i].usage_info_file_name =
info.usage_info_file_name();
(*usage_entry_info)[i].provider_session_token =
info.provider_session_token();
(*usage_entry_info)[i].app_id = info.app_id();
break;
case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN:
default:
(*usage_entry_info)[i].storage_type = kStorageUnknown;
break;
LOGW("DeviceFiles::RetrieveUsageTableInfo: Unknown storage type: %d",
info.storage());
return false;
}
}

View File

@@ -44,7 +44,6 @@ message License {
// ignored if there is no grace period.
optional int64 grace_period_end_time = 11 [default = 0];
optional bytes usage_entry = 12;
optional int64 usage_entry_number = 13;
}
message UsageInfo {
@@ -54,7 +53,6 @@ message UsageInfo {
optional bytes license = 3;
optional bytes key_set_id = 4;
optional bytes usage_entry = 5;
optional int64 usage_entry_number = 6;
}
repeated ProviderSession sessions = 1;
@@ -74,12 +72,12 @@ message UsageTableInfo {
enum UsageEntryStorage {
LICENSE = 1;
USAGE_INFO = 2;
UNKNOWN = 3;
}
optional UsageEntryStorage storage = 1;
optional bytes key_set_id = 2;
optional bytes usage_info_file_name = 3; // hash of the app_id
optional bytes key_set_id = 2; // used if storage is LICENSE
optional bytes provider_session_token = 3; // used if storage is USAGE_INFO
optional bytes app_id = 4; // used if storage is USAGE_INFO
}
optional bytes usage_table_header = 1;

View File

@@ -7,7 +7,6 @@
#include <vector>
#include "clock.h"
#include "cdm_session.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "device_files.h"
@@ -284,8 +283,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
bool is_renewal, const CdmAppParameterMap& app_parameters,
CdmSession* cdm_session, CdmKeyMessage* signed_request,
std::string* server_url) {
CdmKeyMessage* signed_request, std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_1;
@@ -343,13 +341,6 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
if (NO_ERROR != status) return status;
}
// TODO(rfrias): Refactor to avoid needing to call CdmSession
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return status;
}
std::string usage_report;
CdmResponseType status = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
@@ -624,8 +615,7 @@ bool CdmLicense::RestoreOfflineLicense(
const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response, int64_t playback_start_time,
int64_t last_playback_time, int64_t grace_period_end_time,
CdmSession* cdm_session) {
int64_t last_playback_time, int64_t grace_period_end_time) {
if (license_request.empty() || license_response.empty()) {
LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
@@ -667,16 +657,9 @@ bool CdmLicense::RestoreOfflineLicense(
}
if (!provider_session_token_.empty()) {
if (cdm_session &&
cdm_session->get_usage_support_type() == kUsageEntrySupport) {
CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return false;
}
std::string usage_report;
CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid;
int64_t seconds_since_started, seconds_since_last_played;
sts = crypto_session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
@@ -805,43 +788,6 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
return loaded_keys_.find(key_id) != loaded_keys_.end();
}
bool CdmLicense::ExtractProviderSessionToken(
const CdmKeyResponse& license_response,
std::string* provider_session_token) {
if (license_response.empty()) {
LOGW("CdmLicense::ExtractProviderSessionToken: empty license response");
return false;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) {
LOGW(
"CdmLicense::ExtractProviderSessionToken: unable to parse signed "
"license response");
return false;
}
if (signed_response.type() != SignedMessage::LICENSE) {
LOGW("CdmLicense::ExtractProviderSessionToken: unrecognized signed message "
"type: %d", signed_response.type());
return false;
}
License license;
if (!license.ParseFromString(signed_response.msg())) {
LOGE("CdmLicense::ExtractProviderSessionToken: unable to parse license "
"response");
return false;
}
if (license.id().has_provider_session_token()) {
*provider_session_token = license.id().provider_session_token();
return true;
}
return false;
}
CdmResponseType CdmLicense::HandleKeyErrorResponse(
const SignedMessage& signed_message) {
LicenseError license_error;

View File

@@ -5,6 +5,7 @@
#include <string>
#include "log.h"
#include "wv_cdm_constants.h"
namespace {
// License protocol aliases
@@ -86,17 +87,14 @@ bool LicenseKeys::GetAllowedUsage(const KeyId& key_id,
}
}
bool LicenseKeys::ApplyStatusChange(
const CdmKeyStatus* new_status, const uint32_t* new_resolution,
const CryptoSession::HdcpCapability* new_hdcp_level,
bool* new_usable_keys) {
bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys) {
bool keys_changed = false;
bool newly_usable = false;
*new_usable_keys = false;
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
bool usable;
if (it->second->ApplyStatusChange(new_status, new_resolution,
new_hdcp_level, &usable)) {
if (it->second->ApplyStatusChange(new_status, &usable)) {
newly_usable |= usable;
keys_changed = true;
}
@@ -125,6 +123,13 @@ bool LicenseKeys::MeetsConstraints(const KeyId& key_id) {
}
}
void LicenseKeys::ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) {
i->second->ApplyConstraints(new_resolution, new_hdcp_level);
}
}
void LicenseKeys::SetFromLicense(
const video_widevine::License& license) {
this->Clear();
@@ -141,9 +146,6 @@ void LicenseKeys::SetFromLicense(
LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) :
is_content_key_(false),
key_status_(kKeyStatusInternalError),
resolution_(kNoResolution),
hdcp_level_(HDCP_NONE),
can_check_constraints_(false),
meets_constraints_(true),
default_hdcp_level_(HDCP_NONE) {
@@ -220,55 +222,26 @@ bool LicenseKeyStatus::GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage) {
return true;
}
bool LicenseKeyStatus::ApplyStatusChange(
const CdmKeyStatus* maybe_new_status, const uint32_t* maybe_new_resolution,
const CryptoSession::HdcpCapability* maybe_new_hdcp_level,
bool* newly_usable) {
*newly_usable = false;
bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_key) {
*new_usable_key = false;
if (!is_content_key_) {
return false;
}
// Most of this function is various work to calculate an updated value for
// the status. We start at the same value as the current status.
CdmKeyStatus updated_status = key_status_;
// Account for the new status, if provided.
if (maybe_new_status != NULL) {
const CdmKeyStatus& new_status = *maybe_new_status;
can_check_constraints_ = (new_status == kKeyStatusUsable);
updated_status = new_status;
}
// Account for the new resolution, if provided.
if (maybe_new_resolution != NULL) {
resolution_ = *maybe_new_resolution;
}
// Account for the new HDCP level, if provided.
if (maybe_new_hdcp_level != NULL) {
hdcp_level_ = *maybe_new_hdcp_level;
}
// If we can, check the current state against the constraints and update the
// status if needed.
if (can_check_constraints_) {
ApplyConstraints();
CdmKeyStatus updated_status = new_status;
if (updated_status == kKeyStatusUsable) {
if (!MeetsConstraints()) {
updated_status = kKeyStatusOutputNotAllowed;
}
}
// Check if any of that work changed the key status.
if (key_status_ != updated_status) {
key_status_ = updated_status;
if (updated_status == kKeyStatusUsable) {
*newly_usable = true;
*new_usable_key = true;
}
return true;
} else {
return false;
}
return false;
}
// If the key has constraints, find the constraint that applies.
@@ -278,16 +251,12 @@ bool LicenseKeyStatus::ApplyStatusChange(
// If the key has no constraints, or if the constraint has no HDCP
// requirement, use the key's default HDCP setting to check against the
// device's current HDCP level.
void LicenseKeyStatus::ApplyConstraints() {
if (resolution_ == kNoResolution) {
// Until a resolution has been detected, the constraints cannot be checked.
meets_constraints_ = true;
return;
}
void LicenseKeyStatus::ApplyConstraints(
uint32_t video_pixels, CryptoSession::HdcpCapability new_hdcp_level) {
VideoResolutionConstraint* current_constraint = NULL;
if (HasConstraints()) {
current_constraint = GetConstraintForRes(resolution_, constraints_);
if (HasConstraints() && video_pixels != HDCP_UNSPECIFIED_VIDEO_RESOLUTION) {
current_constraint = GetConstraintForRes(video_pixels, constraints_);
if (NULL == current_constraint) {
meets_constraints_ = false;
return;
@@ -302,7 +271,7 @@ void LicenseKeyStatus::ApplyConstraints() {
desired_hdcp_level = default_hdcp_level_;
}
meets_constraints_ = (hdcp_level_ >= desired_hdcp_level);
meets_constraints_ = (new_hdcp_level >= desired_hdcp_level);
}
void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) {

View File

@@ -29,7 +29,8 @@
#include "level3.h"
#include "lock.h"
#include "log.h"
#include "metrics_collections.h"
#include "metrics_front_end.h"
#include "metrics_group.h"
#include "properties.h"
#include "wv_cdm_constants.h"
@@ -40,8 +41,6 @@ using wvcdm::kLevel3;
namespace {
static const size_t kMaxGenericEncryptChunkSize = 100*1024;
const OEMCryptoResult kOemCryptoResultVendorSpecificError1 =
static_cast<OEMCryptoResult>(10008);
typedef struct {
const uint8_t* key_id;
@@ -376,169 +375,6 @@ void clear_cache_function(void *page, size_t len) {
#endif
}
// The WatchDog looks after a worker thread that is trying to initialize L3.
// Once in a rare while, the L3 init does not finish and eats up CPU cycles.
// If that happens, the watchdog thread will give up and return an error.
class WatchDog {
public:
// Created by main thread.
WatchDog() {
pthread_mutex_init(&mutex_, NULL);
pthread_cond_init(&condition_, NULL);
status_ = OEMCrypto_SUCCESS;
gave_up_ = false;
}
// Deleted by either thread.
~WatchDog() {
pthread_cond_destroy(&condition_);
}
// Starts worker thread.
void StartThread() {
running_ = true;
if(pthread_create(&thread_, NULL, RunWatchDog, this)) {
LOGE("Could not create watch dog thread.");
status_ = OEMCrypto_ERROR_INIT_FAILED;
running_ = false;
return;
}
}
// Function called by new worker thread in pthread_create.
static void *RunWatchDog(void *watcher) {
WatchDog* dog = reinterpret_cast<WatchDog *>(watcher);
dog->DoInit();
dog->SignalDoneAndCleanUp();
return NULL;
}
// Called by worker thread.
void DoInit() {
std::string base_path;
wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&base_path);
status_ = Level3_Initialize(clear_cache_function,
base_path.c_str());
}
std::string FailureFilename() {
std::string path;
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGW("WatchDog::FailureFilename: Unable to get base path");
return "/data/l3_failure_file";
}
path += "l3_failure_file";
return path;
}
// Check to see if the failure file was created before that last abort.
void CheckForPreviousFailure(wvcdm::metrics::OemCryptoDynamicAdapterMetrics* metrics) {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
if (!file_system.Exists(filename)) return;
wvcdm::File* file = file_system.Open(filename, file_system.kReadOnly);
if (file) {
uint32_t flag = 0;
ssize_t size = sizeof(flag);
ssize_t size_read = file->Read(reinterpret_cast<char*>(&flag), size);
file->Close();
file_system.Remove(filename);
if (size == size_read && flag) {
LOGE("Previous L3 Init failed.");
if (metrics == nullptr) return;
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
wvcdm::metrics::OEMCrypto_INITIALIZED_L3_INITIALIZATION_FAILED);
}
}
}
// Save the failure file before we abort.
void SaveFailureInformation() {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
LOGD("failure filename = %s", filename.c_str());
wvcdm::File* file = file_system.Open(
filename, file_system.kCreate | file_system.kTruncate);
if (!file) {
LOGE("Could not create file %s", filename.c_str());
return;
}
uint32_t flag = 0x6261640a; // bad
ssize_t size = sizeof(flag);
ssize_t size_written = file->Write(reinterpret_cast<char*>(&flag), size);
file->Close();
if (size != size_written) {
LOGE("Wrote %d bytes, not %d, to file %s", size_written, size,
filename.c_str());
} else {
LOGE("I wrote %d to %s", size_written, filename.c_str());
}
}
// Called by worker thread after DoInit has finshed.
void SignalDoneAndCleanUp() {
pthread_mutex_lock(&mutex_);
running_ = false;
pthread_cond_signal(&condition_);
// If the main thread gave up, it won't delete this, so we must.
bool should_delete = gave_up_;
pthread_mutex_unlock(&mutex_);
// https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
if (should_delete) delete this;
}
// Called by main thread to wait for worker thread.
OEMCryptoResult WaitForStatusAndCleanUp() {
pthread_mutex_lock(&mutex_);
struct timespec time_to_giveup;
clock_gettime(CLOCK_REALTIME, &time_to_giveup);
time_to_giveup.tv_sec += 120; // wait 2 minutes.
if (running_) {
pthread_cond_timedwait(&condition_, &mutex_, &time_to_giveup);
}
if (running_) {
gave_up_ = true;
status_ = OEMCrypto_ERROR_INIT_FAILED;
LOGE("XXX WATCH DOG ERROR XXX");
// HACK: this normally just returns an error. However, we are using it
// as a signal to dump debugging information.
Level3_GetOEMPublicCertificate(0, nullptr, nullptr);
SaveFailureInformation();
// This tells the worker thread to clean up after itself. It is not
// really needed since we are going to abort. However, if somebody
// removes the "abort()" below, then this is needed.
pthread_detach(thread_);
// This is controversial. The argument for an abort here is that if we
// do not abort, we will suck all the life out of the user's battery. By
// saving information to the file system, above, we can still track
// metrics.
abort();
}
// If we gave up waiting for init thread, we should not delete the mutex
// out from under it.
bool should_delete = !gave_up_;
OEMCryptoResult status = status_;
pthread_mutex_unlock(&mutex_);
if (should_delete) {
pthread_join(thread_, NULL);
delete this;
}
return status;
}
OEMCryptoResult status() { return status_; }
private:
OEMCryptoResult status_;
pthread_t thread_;
pthread_mutex_t mutex_;
pthread_cond_t condition_;
bool running_;
bool gave_up_;
};
struct LevelSession {
FunctionPointers* fcn;
OEMCrypto_SESSION session;
@@ -584,36 +420,44 @@ class Adapter {
OEMCryptoResult Initialize() {
/*
* To avoid changing the function signature and function contract, use a
* reference to a singleton object for the metrics collected from the
* dynamic adapter.
* To avoid changing the function signature and function contract - declare
* a one-off metrics group to collect detailed information about how
* oemcrypto was intialized.
*/
wvcdm::metrics::OemCryptoDynamicAdapterMetrics& metrics =
wvcdm::metrics::GetDynamicAdapterMetricsInstance();
wvcdm::metrics::MetricsGroup metrics;
level1_ = FunctionPointers(); // start with all null pointers.
level3_ = FunctionPointers(); // start with all null pointers.
LoadLevel3();
WatchDog *watcher = new WatchDog();
watcher->CheckForPreviousFailure(&metrics);
watcher->StartThread();
OEMCryptoResult result = watcher->WaitForStatusAndCleanUp();
std::string base_path;
wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&base_path);
OEMCryptoResult result = Level3_Initialize(clear_cache_function,
base_path.c_str());
if (Level3_IsInApp()) {
metrics.SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_IN_APP);
return result;
}
if (force_level3()) {
LOGW("Test code. User requested falling back to L3");
metrics.SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_FORCING_L3);
return result;
}
LOGI("L3 Initialized. Trying L1.");
std::string library_name;
if (!wvcdm::Properties::GetOEMCryptoPath(&library_name)) {
LOGW("L1 library not specified. Falling back to L3");
metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_NO_L1_LIBRARY_PATH);
return result;
}
@@ -621,11 +465,14 @@ class Adapter {
if (level1_library_ == NULL) {
LOGW("Could not load %s. Falling back to L3. %s", library_name.c_str(),
dlerror());
metrics.OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_L1_OPEN_FAILED);
return result;
}
if (LoadLevel1(&metrics)) {
if (LoadLevel1(metrics)) {
LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1.");
} else {
level1_ = FunctionPointers(); // revert to all null pointers.
@@ -636,10 +483,7 @@ class Adapter {
return result;
}
bool LoadLevel1(wvcdm::metrics::OemCryptoDynamicAdapterMetrics* metrics) {
if (metrics == nullptr) {
return false;
}
bool LoadLevel1(wvcdm::metrics::MetricsGroup& metrics) {
level1_valid_ = true;
const uint32_t kMinimumVersion = 8;
const uint32_t kMaximumVersion = 13;
@@ -648,25 +492,37 @@ class Adapter {
LOOKUP_ALL(8, APIVersion, OEMCrypto_APIVersion);
LOOKUP_ALL(8, Terminate, OEMCrypto_Terminate);
if (!level1_valid_) {
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_INVALID_L1);
return false;
}
OEMCryptoResult st = level1_.Initialize();
if (st != OEMCrypto_SUCCESS) {
LOGW("Could not initialize L1. Falling Back to L3.");
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INITIALIZE_L1);
return false;
}
level1_.version = level1_.APIVersion();
metrics->SetL1ApiVersion(level1_.version);
metrics->SetL1MinApiVersion(kMinimumVersion);
M_RECORD(
&metrics,
oemcrypto_l1_api_version_,
NO_TIME,
level1_.version,
kMinimumVersion);
if (level1_.version < kMinimumVersion) {
LOGW("liboemcrypto.so is version %d, not %d. Falling Back to L3.",
level1_.version, kMinimumVersion);
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_WRONG_L1_VERSION);
level1_.Terminate();
return false;
@@ -741,7 +597,10 @@ class Adapter {
// If we have a valid keybox, initialization is done. We're good.
if (OEMCrypto_SUCCESS == level1_.IsKeyboxValid()) {
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX);
return true;
}
@@ -750,7 +609,10 @@ class Adapter {
// will have to be caught in the future when provisioning fails.
if (level1_.version > 11 &&
(level1_.GetProvisioningMethod() == OEMCrypto_OEMCertificate)) {
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0);
return true;
}
@@ -767,10 +629,16 @@ class Adapter {
// because we still want to test OEMCrypto in that configuration.
LOGE("OEMCrypto uses cert as identification, but cdm does not!");
LOGE("This will not work on a production device.");
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_CERTIFICATE_MIX);
} else {
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_WITH_CERTIFICATE);
}
return true;
@@ -780,7 +648,10 @@ class Adapter {
if (!wvcdm::Properties::GetFactoryKeyboxPath(&filename)) {
LOGW("Bad Level 1 Keybox. Falling Back to L3.");
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX);
return false;
}
@@ -789,7 +660,10 @@ class Adapter {
if (size <= 0 || !file) {
LOGW("Could not open %s. Falling Back to L3.", filename.c_str());
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX);
return false;
}
@@ -800,12 +674,18 @@ class Adapter {
LOGE("Could NOT install keybox from %s. Falling Back to L3.",
filename.c_str());
level1_.Terminate();
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX);
return false;
}
LOGI("Installed keybox from %s", filename.c_str());
metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode(
M_RECORD(
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX);
return true;
}
@@ -1276,26 +1156,18 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
} else {
if (pair.fcn->LoadKeys_V9_or_V10 == NULL)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
OEMCryptoResult result = pair.fcn->LoadKeys_V9_or_V10(
pair.session, message, message_length, signature, signature_length,
enc_mac_key_iv, enc_mac_key, num_keys, &key_array_v10[0], pst,
pst_length);
// Convert a vendor specific error, to make it actionable
if (result == kOemCryptoResultVendorSpecificError1)
result = OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE;
return result;
return pair.fcn->LoadKeys_V9_or_V10(pair.session, message, message_length,
signature, signature_length,
enc_mac_key_iv, enc_mac_key, num_keys,
&key_array_v10[0], pst, pst_length);
}
} else {
if (pair.fcn->version < 13) {
if (pair.fcn->LoadKeys_V11_or_V12 == NULL)
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
OEMCryptoResult result = pair.fcn->LoadKeys_V11_or_V12(
return pair.fcn->LoadKeys_V11_or_V12(
pair.session, message, message_length, signature, signature_length,
enc_mac_key_iv, enc_mac_key, num_keys, key_array, pst, pst_length);
// Convert a vendor specific error, to make it actionable
if (result == kOemCryptoResultVendorSpecificError1)
result = OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE;
return result;
} else {
if (pair.fcn->LoadKeys == NULL) return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return pair.fcn->LoadKeys(pair.session, message, message_length,

View File

@@ -14,14 +14,6 @@
using video_widevine::License;
namespace {
const int kCdmPolicyTimerDurationSeconds = 1;
const int kClockSkewDelta = 5; // seconds
const int64_t kHdcpCheckInterval = 10;
} // namespace
namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
@@ -36,7 +28,6 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
last_expiry_time_set_(false),
was_expired_on_load_(false),
next_renewal_time_(0),
last_recorded_current_time_(0),
session_id_(session_id),
event_listener_(event_listener),
license_keys_(new LicenseKeys),
@@ -57,38 +48,36 @@ bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
}
void PolicyEngine::InitDevice(CryptoSession* crypto_session) {
current_resolution_ = kNoResolution;
current_resolution_ = HDCP_UNSPECIFIED_VIDEO_RESOLUTION;
next_device_check_ = 0;
crypto_session_ = crypto_session;
}
void PolicyEngine::CheckDevice(int64_t current_time) {
if (current_time < next_device_check_) {
return;
}
void PolicyEngine::SetDeviceResolution(uint32_t width, uint32_t height) {
current_resolution_ = width * height;
CheckDeviceHdcpStatus();
}
if (!license_keys_->Empty() && current_resolution_ != kNoResolution) {
void PolicyEngine::CheckDeviceHdcpStatusOnTimer(int64_t current_time) {
if (current_time >= next_device_check_) {
CheckDeviceHdcpStatus();
next_device_check_ = current_time + HDCP_DEVICE_CHECK_INTERVAL;
}
}
void PolicyEngine::CheckDeviceHdcpStatus() {
if (!license_keys_->Empty()) {
CryptoSession::HdcpCapability current_hdcp_level;
CryptoSession::HdcpCapability ignored;
if (!crypto_session_->GetHdcpCapabilities(&current_hdcp_level, &ignored)) {
current_hdcp_level = HDCP_NONE;
}
bool new_usable_keys = false;
bool keys_changed =
license_keys_->ApplyStatusChange(NULL, // new_status
&current_resolution_,
&current_hdcp_level,
&new_usable_keys);
NotifyIfKeysChanged(keys_changed, new_usable_keys);
next_device_check_ = current_time + kHdcpCheckInterval;
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
}
}
void PolicyEngine::OnTimerEvent() {
last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds;
int64_t current_time = GetCurrentTime();
int64_t current_time = clock_->GetCurrentTime();
// If we have passed the grace period, the expiration will update.
if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) {
@@ -100,19 +89,21 @@ void PolicyEngine::OnTimerEvent() {
if (HasLicenseOrPlaybackDurationExpired(current_time) &&
license_state_ != kLicenseStateExpired) {
license_state_ = kLicenseStateExpired;
UpdateKeyStatus(kKeyStatusExpired);
NotifyKeysChange(kKeyStatusExpired);
return;
}
// Check device conditions that affect playability (HDCP, resolution)
CheckDevice(current_time);
CheckDeviceHdcpStatusOnTimer(current_time);
// Test to determine if renewal should be attempted.
bool renewal_needed = false;
// Test to determine if renewal should be attempted.
switch (license_state_) {
case kLicenseStateCanPlay: {
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break;
}
@@ -129,7 +120,7 @@ void PolicyEngine::OnTimerEvent() {
case kLicenseStatePending: {
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
UpdateKeyStatus(kKeyStatusUsable);
NotifyKeysChange(kKeyStatusUsable);
}
break;
}
@@ -141,7 +132,7 @@ void PolicyEngine::OnTimerEvent() {
default: {
license_state_ = kLicenseStateExpired;
UpdateKeyStatus(kKeyStatusInternalError);
NotifyKeysChange(kKeyStatusInternalError);
break;
}
}
@@ -166,7 +157,7 @@ void PolicyEngine::SetLicenseForRelease(const License& license) {
policy_.Clear();
// Expire any old keys.
UpdateKeyStatus(kKeyStatusExpired);
NotifyKeysChange(kKeyStatusExpired);
UpdateLicense(license);
}
@@ -195,21 +186,21 @@ void PolicyEngine::UpdateLicense(const License& license) {
license_start_time_ = license.license_start_time();
next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds();
int64_t current_time = GetCurrentTime();
int64_t current_time = clock_->GetCurrentTime();
if (!policy_.can_play() ||
HasLicenseOrPlaybackDurationExpired(current_time)) {
license_state_ = kLicenseStateExpired;
UpdateKeyStatus(kKeyStatusExpired);
NotifyKeysChange(kKeyStatusExpired);
return;
}
// Update state
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
UpdateKeyStatus(kKeyStatusUsable);
NotifyKeysChange(kKeyStatusUsable);
} else {
license_state_ = kLicenseStatePending;
UpdateKeyStatus(kKeyStatusPending);
NotifyKeysChange(kKeyStatusPending);
}
NotifyExpirationUpdate(current_time);
}
@@ -220,7 +211,7 @@ void PolicyEngine::BeginDecryption() {
case kLicenseStateCanPlay:
case kLicenseStateNeedRenewal:
case kLicenseStateWaitingLicenseUpdate:
playback_start_time_ = GetCurrentTime();
playback_start_time_ = clock_->GetCurrentTime();
last_playback_time_ = playback_start_time_;
if (policy_.play_start_grace_period_seconds() == 0)
grace_period_end_time_ = playback_start_time_;
@@ -240,7 +231,7 @@ void PolicyEngine::BeginDecryption() {
}
void PolicyEngine::DecryptionEvent() {
last_playback_time_ = GetCurrentTime();
last_playback_time_ = clock_->GetCurrentTime();
}
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
@@ -249,12 +240,12 @@ void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
void PolicyEngine::NotifySessionExpiration() {
license_state_ = kLicenseStateExpired;
UpdateKeyStatus(kKeyStatusExpired);
NotifyKeysChange(kKeyStatusExpired);
}
CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
std::stringstream ss;
int64_t current_time = GetCurrentTime();
int64_t current_time = clock_->GetCurrentTime();
if (license_state_ == kLicenseStateInitial) {
query_response->clear();
@@ -295,7 +286,7 @@ CdmResponseType PolicyEngine::QueryKeyAllowedUsage(
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
if (playback_start_time_ == 0) return false;
*seconds_since_started = GetCurrentTime() - playback_start_time_;
*seconds_since_started = clock_->GetCurrentTime() - playback_start_time_;
return (*seconds_since_started >= 0) ? true : false;
}
@@ -303,12 +294,12 @@ bool PolicyEngine::GetSecondsSinceLastPlayed(
int64_t* seconds_since_last_played) {
if (last_playback_time_ == 0) return false;
*seconds_since_last_played = GetCurrentTime() - last_playback_time_;
*seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_;
return (*seconds_since_last_played >= 0) ? true : false;
}
int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() {
const int64_t current_time = GetCurrentTime();
const int64_t current_time = clock_->GetCurrentTime();
const int64_t expiry_time =
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ false);
@@ -332,7 +323,7 @@ void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
playback_start_time_ = grace_period_end_time;
}
const int64_t current_time = GetCurrentTime();
const int64_t current_time = clock_->GetCurrentTime();
const int64_t expiry_time =
GetExpiryTime(current_time,
/* ignore_soft_enforce_playback_duration */ true);
@@ -403,8 +394,6 @@ int64_t PolicyEngine::GetLicenseOrRentalDurationRemaining(
if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX;
if (license_expiry_time < current_time) return 0;
const int64_t policy_license_duration = policy_.license_duration_seconds();
if (policy_license_duration == NEVER_EXPIRES)
return license_expiry_time - current_time;
return std::min(license_expiry_time - current_time, policy_license_duration);
}
@@ -440,23 +429,19 @@ bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
next_renewal_time_ <= current_time;
}
void PolicyEngine::UpdateKeyStatus(CdmKeyStatus new_status) {
bool new_usable_keys = false;
bool keys_changed =
license_keys_->ApplyStatusChange(&new_status,
NULL, // new_resolution
NULL, // new_hdcp_level
&new_usable_keys);
NotifyIfKeysChanged(keys_changed, new_usable_keys);
}
void PolicyEngine::NotifyIfKeysChanged(bool keys_changed,
bool new_usable_keys) {
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
bool keys_changed;
bool has_new_usable_key = false;
if (new_status == kKeyStatusUsable) {
CheckDeviceHdcpStatus();
}
keys_changed = license_keys_->ApplyStatusChange(new_status,
&has_new_usable_key);
if (event_listener_ && keys_changed) {
CdmKeyStatusMap content_keys;
license_keys_->ExtractKeyStatuses(&content_keys);
event_listener_->OnSessionKeysChange(session_id_, content_keys,
new_usable_keys);
has_new_usable_key);
}
}
@@ -472,15 +457,6 @@ void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) {
last_expiry_time_set_ = true;
}
int64_t PolicyEngine::GetCurrentTime() {
int64_t current_time = clock_->GetCurrentTime();
if (current_time + kClockSkewDelta < last_recorded_current_time_)
current_time = last_recorded_current_time_;
else
last_recorded_current_time_ = current_time;
return current_time;
}
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
} // namespace wvcdm

View File

@@ -29,15 +29,14 @@ RSA* GetKey(const std::string& serialized_key) {
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));
BIO_free(bio);
return NULL;
}
BIO_free(bio);
return key;
}
@@ -183,7 +182,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message,
// LogOpenSSLError is a callback from OpenSSL which is called with each error
// in the thread's error queue.
static int LogOpenSSLError(const char *msg, size_t /* len */, void */* ctx */) {
static int LogOpenSSLError(const char* msg, size_t /* len */, void* /* ctx */) {
LOGE(" %s", msg);
return 1;
}

View File

@@ -67,6 +67,40 @@ CdmResponseType ServiceCertificate::Init(const std::string& raw_certificate) {
return VerifyAndExtract(raw_certificate);
}
CdmResponseType ServiceCertificate::VerifySignedMessage(
const std::string& message, const std::string& signature) {
if (certificate_.empty()) {
LOGE("ServiceCertificate::VerifySignedMessage: "
"service certificate is not properly initialized");
return UNKNOWN_ERROR;
}
DrmDeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(certificate_)) {
LOGE("ServiceCertificate::EncryptClientId: unable to parse retrieved "
"service certificate");
return PARSE_SERVICE_CERTIFICATE_ERROR;
}
if (service_certificate.type() !=
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
LOGE("ServiceCertificate::EncryptClientId: retrieved certificate not of "
"type service, %d", service_certificate.type());
return SERVICE_CERTIFICATE_TYPE_ERROR;
}
// TODO(gm) verify; rework error codes.
RsaPublicKey rsa;
if (!rsa.Init(service_certificate.public_key()))
return CLIENT_ID_RSA_INIT_ERROR;
if (!rsa.VerifySignature(message, signature))
return CLIENT_ID_RSA_ENCRYPT_ERROR;
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {

View File

@@ -10,14 +10,28 @@
#include <iostream>
#include <vector>
#include <modp_b64.h>
#include <modp_b64w.h>
#include "log.h"
namespace wvcdm {
static bool CharToDigit(char ch, unsigned char* digit) {
static const char kBase64Codes[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// Gets the low |n| bits of |in|.
#define GET_LOW_BITS(in, n) ((in) & ((1 << (n)) - 1))
// Gets the given (zero-indexed) bits [a, b) of |in|.
#define GET_BITS(in, a, b) GET_LOW_BITS((in) >> (a), (b) - (a))
// Calculates a/b using round-up division (only works for positive numbers).
#define CEIL_DIVIDE(a, b) ((((a) - 1) / (b)) + 1)
int DecodeBase64Char(char c) {
const char* it = strchr(kBase64Codes, c);
if (it == NULL)
return -1;
return it - kBase64Codes;
}
bool DecodeHexChar(char ch, unsigned char* digit) {
if (ch >= '0' && ch <= '9') {
*digit = ch - '0';
} else {
@@ -43,8 +57,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
for (unsigned int i = 0; i < count / 2; ++i) {
unsigned char msb = 0; // most significant 4 bits
unsigned char lsb = 0; // least significant 4 bits
if (!CharToDigit(byte[i * 2], &msb) ||
!CharToDigit(byte[i * 2 + 1], &lsb)) {
if (!DecodeHexChar(byte[i * 2], &msb) ||
!DecodeHexChar(byte[i * 2 + 1], &lsb)) {
LOGE("Invalid hex value %c%c at index %d", byte[i * 2], byte[i * 2 + 1],
i);
return array;
@@ -80,23 +94,50 @@ std::string b2a_hex(const std::string& byte) {
}
// Encode for standard base64 encoding (RFC4648).
// https://en.wikipedia.org/wiki/Base64
// Text | M | a | n |
// ASCI | 77 (0x4d) | 97 (0x61) | 110 (0x6e) |
// Bits | 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 1 1 0 |
// Index | 19 | 22 | 5 | 46 |
// Base64 | T | W | F | u |
// | <----------------- 24-bits -----------------> |
std::string Base64Encode(const std::vector<uint8_t>& bin_input) {
if (bin_input.empty()) {
return std::string();
}
int in_size = bin_input.size();
std::string b64_output(modp_b64_encode_len(in_size), 0);
// |temp| stores a 24-bit block that is treated as an array where insertions
// occur from high to low.
uint32_t temp = 0;
size_t out_index = 0;
const size_t out_size = CEIL_DIVIDE(bin_input.size(), 3) * 4;
std::string result(out_size, '\0');
for (size_t i = 0; i < bin_input.size(); i++) {
// "insert" 8-bits of data
temp |= (bin_input[i] << ((2 - (i % 3)) * 8));
int out_size = modp_b64_encode(
&b64_output[0], reinterpret_cast<const char*>(&bin_input[0]), in_size);
if (out_size == -1) {
LOGE("Base64Encode failed");
return std::string();
if (i % 3 == 2) {
result[out_index++] = kBase64Codes[GET_BITS(temp, 18, 24)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 12, 18)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 6, 12)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 0, 6)];
temp = 0;
}
}
b64_output.resize(out_size);
return b64_output;
if (bin_input.size() % 3 == 1) {
result[out_index++] = kBase64Codes[GET_BITS(temp, 18, 24)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 12, 18)];
result[out_index++] = '=';
result[out_index++] = '=';
} else if (bin_input.size() % 3 == 2) {
result[out_index++] = kBase64Codes[GET_BITS(temp, 18, 24)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 12, 18)];
result[out_index++] = kBase64Codes[GET_BITS(temp, 6, 12)];
result[out_index++] = '=';
}
return result;
}
// Filename-friendly base64 encoding (RFC4648), commonly referred to
@@ -111,18 +152,14 @@ std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
return std::string();
}
int in_size = bin_input.size();
std::string b64_output(modp_b64w_encode_len(in_size), 0);
int out_size = modp_b64w_encode(
&b64_output[0], reinterpret_cast<const char*>(&bin_input[0]), in_size);
if (out_size == -1) {
LOGE("Base64SafeEncode failed");
return std::string();
std::string ret = Base64Encode(bin_input);
for (size_t i = 0; i < ret.size(); i++) {
if (ret[i] == '+')
ret[i] = '-';
else if (ret[i] == '/')
ret[i] = '_';
}
b64_output.resize(out_size);
return b64_output;
return ret;
}
std::string Base64SafeEncodeNoPad(const std::vector<uint8_t>& bin_input) {
@@ -138,17 +175,57 @@ std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
return std::vector<uint8_t>();
}
int in_size = b64_input.size();
std::vector<uint8_t> bin_output(modp_b64_decode_len(in_size), 0);
int out_size = modp_b64_decode(reinterpret_cast<char*>(&bin_output[0]),
b64_input.data(), in_size);
if (out_size == -1) {
LOGE("Base64Decode failed");
return std::vector<uint8_t>(0);
const size_t out_size_max = CEIL_DIVIDE(b64_input.size() * 3, 4);
std::vector<uint8_t> result(out_size_max, '\0');
// |temp| stores 24-bits of data that is treated as an array where insertions
// occur from high to low.
uint32_t temp = 0;
size_t out_index = 0;
size_t i;
for (i = 0; i < b64_input.size(); i++) {
if (b64_input[i] == '=') {
// Verify an '=' only appears at the end. We want i to remain at the
// first '=', so we need an inner loop.
for (size_t j = i; j < b64_input.size(); j++) {
if (b64_input[j] != '=') {
LOGE("base64Decode failed");
return std::vector<uint8_t>();
}
}
break;
}
const int decoded = DecodeBase64Char(b64_input[i]);
if (decoded < 0) {
LOGE("base64Decode failed");
return std::vector<uint8_t>();
}
// "insert" 6-bits of data
temp |= (decoded << ((3 - (i % 4)) * 6));
if (i % 4 == 3) {
result[out_index++] = GET_BITS(temp, 16, 24);
result[out_index++] = GET_BITS(temp, 8, 16);
result[out_index++] = GET_BITS(temp, 0, 8);
temp = 0;
}
}
bin_output.resize(out_size);
return bin_output;
switch (i % 4) {
case 1:
LOGE("base64Decode failed");
return std::vector<uint8_t>();
case 2:
result[out_index++] = GET_BITS(temp, 16, 24);
break;
case 3:
result[out_index++] = GET_BITS(temp, 16, 24);
result[out_index++] = GET_BITS(temp, 8, 16);
break;
}
result.resize(out_index);
return result;
}
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
@@ -158,17 +235,16 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
return std::vector<uint8_t>();
}
int in_size = b64_input.size();
std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0);
int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]),
b64_input.data(), in_size);
if (out_size == -1) {
LOGE("Base64SafeDecode failed");
return std::vector<uint8_t>(0);
// Make a copy so we can modify it to replace the web-safe special characters
// with the normal ones.
std::string input_copy = b64_input;
for (size_t i = 0; i < input_copy.size(); i++) {
if (input_copy[i] == '-')
input_copy[i] = '+';
else if (input_copy[i] == '_')
input_copy[i] = '/';
}
bin_output.resize(out_size);
return bin_output;
return Base64Decode(input_copy);
}
std::string HexEncode(const uint8_t* in_buffer, unsigned int size) {

View File

@@ -24,7 +24,6 @@ UsageTableHeader::UsageTableHeader()
: security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_inited_(false) {
file_system_.reset(new FileSystem());
file_handle_.reset(new DeviceFiles(file_system_.get()));
}
@@ -110,7 +109,7 @@ CdmResponseType UsageTableHeader::AddEntry(
size_t number_of_entries = usage_entry_info_.size();
usage_entry_info_.resize(*usage_entry_number + 1);
for (size_t i = number_of_entries; i < usage_entry_info_.size() - 1; ++i) {
usage_entry_info_[i].storage_type = kStorageUnknown;
usage_entry_info_[i].storage_type = kStorageTypeUnknown;
usage_entry_info_[i].key_set_id.clear();
usage_entry_info_[i].usage_info_file_name.clear();
}
@@ -183,7 +182,7 @@ CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number,
if (status == NO_ERROR) swap_usage_entry_valid = true;
break;
}
case kStorageUnknown:
case kStorageTypeUnknown:
default:
break;
}
@@ -199,7 +198,7 @@ CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number,
// If unable to move entry, unset storage type of entry to be deleted and
// resize |usage_entry_info_| so that swap usage entry is the last entry.
if (status != NO_ERROR) {
usage_entry_info_[usage_entry_number].storage_type = kStorageUnknown;
usage_entry_info_[usage_entry_number].storage_type = kStorageTypeUnknown;
usage_entry_info_[usage_entry_number].key_set_id.clear();
if (usage_entry_info_.size() - 1 == swap_entry_number) {
file_handle_->StoreUsageTableInfo(usage_table_header_,
@@ -286,7 +285,7 @@ CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number,
&last_playback_time, &grace_period_end_time, &app_parameters,
usage_entry, &entry_number)) {
LOGE("UsageTableHeader::GetEntry: Failed to retrieve license");
return USAGE_RETRIEVE_LICENSE_FAILED;
return USAGE_GET_ENTRY_RETRIEVE_LICENSE_FAILED;
}
break;
}
@@ -302,17 +301,17 @@ CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number,
usage_entry, &entry_number)) {
LOGE(
"UsageTableHeader::GetEntry: Failed to retrieve usage information");
return USAGE_RETRIEVE_USAGE_INFO_FAILED;
return USAGE_GET_ENTRY_RETRIEVE_USAGE_INFO_FAILED;
}
break;
}
case kStorageUnknown:
case kStorageTypeUnknown:
default:
LOGE(
"UsageTableHeader::GetEntry: Attempting to retrieve usage "
"information from unknown storage type: %d",
usage_entry_info_[usage_entry_number].storage_type);
return USAGE_RETRIEVE_INVALID_STORAGE_TYPE;
return USAGE_GET_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE;
}
if (usage_entry_number != entry_number) {
@@ -343,7 +342,7 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
&last_playback_time, &grace_period_end_time, &app_parameters,
&entry, &entry_number)) {
LOGE("UsageTableHeader::StoreEntry: Failed to retrieve license");
return USAGE_RETRIEVE_LICENSE_FAILED;
return USAGE_STORE_ENTRY_RETRIEVE_LICENSE_FAILED;
}
if (!handle->StoreLicense(
usage_entry_info_[usage_entry_number].key_set_id, license_state,
@@ -368,7 +367,7 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
LOGE(
"UsageTableHeader::StoreEntry: Failed to retrieve usage "
"information");
return USAGE_RETRIEVE_USAGE_INFO_FAILED;
return USAGE_STORE_ENTRY_RETRIEVE_USAGE_INFO_FAILED;
}
handle->DeleteUsageInfo(
usage_entry_info_[usage_entry_number].usage_info_file_name,
@@ -383,13 +382,13 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
}
break;
}
case kStorageUnknown:
case kStorageTypeUnknown:
default:
LOGE(
"UsageTableHeader::GetUsageEntry: Attempting to retrieve usage "
"information from unknown storage type: %d",
usage_entry_info_[usage_entry_number].storage_type);
return USAGE_RETRIEVE_INVALID_STORAGE_TYPE;
return USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE;
}
return NO_ERROR;
}

View File

@@ -53,6 +53,9 @@ const std::pair<const std::string *, const std::string *> kBase64TestVectors[] =
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
make_pair(&kTestData, &kB64TestData)};
const std::string kBase64ErrorVectors[] = {"Foo$sa", "Foo\x99\x23\xfa\02",
"Foo==Foo", "FooBa"};
std::string ConvertToBase64WebSafe(const std::string &std_base64_string) {
std::string str(std_base64_string);
for (size_t i = 0; i < str.size(); ++i) {
@@ -89,9 +92,19 @@ TEST_P(Base64EncodeDecodeTest, WebSafeEncodeDecodeTest) {
EXPECT_STREQ(encoded_string.data(), b64_string.data());
}
class Base64ErrorDecodeTest : public ::testing::TestWithParam<std::string> {};
TEST_P(Base64ErrorDecodeTest, EncoderErrors) {
std::vector<uint8_t> result = Base64Decode(GetParam());
EXPECT_EQ(0u, result.size());
}
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest,
::testing::ValuesIn(kBase64TestVectors));
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64ErrorDecodeTest,
::testing::ValuesIn(kBase64ErrorVectors));
class HtoNLL64Test : public ::testing::Test {};
TEST_F(HtoNLL64Test, PositiveNumber) {

View File

@@ -52,7 +52,7 @@ class WvCdmEngineTest : public testing::Test {
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id());
g_license_server.assign(config.license_server_url());
g_license_server.assign(config.license_server());
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
// Extract the key ID from the PSSH box.

View File

@@ -99,8 +99,7 @@ class MockDeviceFiles : public DeviceFiles {
class MockCryptoSession : public CryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
: CryptoSession(crypto_metrics) { }
MockCryptoSession() : CryptoSession(NULL) { }
MOCK_METHOD1(GetClientToken, bool(std::string*));
MOCK_METHOD1(GetProvisioningToken, bool(std::string*));
MOCK_METHOD0(GetPreProvisionTokenType, CdmClientTokenType());
@@ -142,11 +141,11 @@ class CdmSessionTest : public ::testing::Test {
protected:
virtual void SetUp() {
service_cert_ = new ServiceCertificate;
cdm_session_.reset(new CdmSession(NULL, &metrics_));
cdm_session_.reset(new CdmSession(NULL));
// Inject testing mocks.
license_parser_ = new MockCdmLicense(cdm_session_->session_id());
cdm_session_->set_license_parser(license_parser_);
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
crypto_session_ = new MockCryptoSession();
cdm_session_->set_crypto_session(crypto_session_);
policy_engine_ = new MockPolicyEngine();
cdm_session_->set_policy_engine(policy_engine_);
@@ -154,16 +153,8 @@ class CdmSessionTest : public ::testing::Test {
cdm_session_->set_file_handle(file_handle_);
}
virtual void TearDown() {
// Force the cdm_session_ to be deleted. This enforces a requirement that
// the CDM session metrics exist at least as long as the CDM session.
cdm_session_.reset();
}
metrics::SessionMetrics metrics_;
scoped_ptr<CdmSession> cdm_session_;
MockCdmLicense* license_parser_;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_;
MockPolicyEngine* policy_engine_;
MockDeviceFiles* file_handle_;

View File

@@ -2,21 +2,23 @@
#include "config_test_env.h"
#include "string_conversions.h"
namespace wvcdm {
namespace {
const std::string kWidevineKeySystem = "com.widevine.alpha";
// QA/Test server
const std::string kQALicenseServerUrl =
"http://0.widevine-qa-wls.licenseserver.widevine-license-qa.lf.borg.google.com/";
const std::string kQAServiceCertificate = "";
const std::string kQAProvisioningServerUrl = "http://www-googleapis-test.sandbox.google.com/certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// Content Protection license server (UAT) data
const std::string kCpUatLicenseServer =
"http://widevine-proxy.appspot.com/proxy";
const std::string kUatProvisioningServerUrl =
"https://staging-www.sandbox.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const std::string kCpClientAuth = "";
const std::string kCpKeyId =
"00000042" // blob size
@@ -26,7 +28,7 @@ const std::string kCpKeyId =
"00000022" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573"
"74220f73747265616d696e675f636c69" // "streaming_clip1"
"74220f73747265616d696e675f636c69"
"7031";
const std::string kCpOfflineKeyId =
"00000040" // blob size
@@ -36,26 +38,7 @@ const std::string kCpOfflineKeyId =
"00000020" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573"
"74220d6f66666c696e655f636c697032"; // "offline_clip2"
const CdmInitData kCpStagingSrmOuputProtectionRequired =
"0000003d" // blob size
"70737368" // "pssh"
"00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"0000001d" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573"
"74220a74656172735f73726d32"; // "tears_srm2"
const CdmInitData kCpStagingSrmOuputProtectionRequested =
"0000003d" // blob size
"70737368" // "pssh"
"00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"0000001d" // pssh data size
// pssh data:
"08011a0d7769646576696e655f746573"
"74220a74656172735f73726d32"; // "tears_srm1"
const CdmInitData kEmptyData;
"74220d6f66666c696e655f636c697032";
const std::string kCpUatServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F99005228E023082010A02"
"82010100B52112B8D05D023FCC5D95E2C251C1C649B4177CD8D2BEEF355BB06743DE661E3D"
@@ -78,46 +61,9 @@ const std::string kCpUatServiceCertificate =
"7C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A250A4EB9C84AB3E6539F6B6FDF"
"56899EA29914";
const std::string kCpProductionLicenseServer =
"https://widevine-proxy.appspot.com/proxy";
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// NOTE: Provider ID = staging.google.com
const std::string kCpProductionServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522"
"8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417"
"7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781"
"5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D"
"0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6"
"AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0"
"40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A"
"0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6"
"283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0"
"D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301"
"00013A1273746167696E672E676F6F676C652E636F6D128003983E303526"
"75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA"
"EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA"
"97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91"
"5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0"
"32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0"
"EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28"
"8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932"
"1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784"
"C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742"
"BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940"
"383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8"
"38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A"
"250A4EB9C84AB3E6539F6B6FDF56899EA29914";
// Content Protection license server (staging) data
const std::string kCpStagingLicenseServer =
"https://proxy.staging.widevine.com/proxy";
const std::string kStagingProvisioningServerUrl =
"https://staging-www.sandbox.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
"http://wv-staging-proxy.appspot.com/proxy";
const std::string kCpStagingServiceCertificate =
"0AC102080312101705B917CC1204868B06333A2F772A8C1882B4829205228E023082010A02"
"8201010099ED5B3B327DAB5E24EFC3B62A95B598520AD5BCCB37503E0645B814D876B8DF40"
@@ -178,17 +124,21 @@ const std::string kWrongKeyId =
"0901121094889920e8d6520098577df8"
"f2dd5546";
// URL of provisioning server (returned by GetProvisioningRequest())
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, kProductionProvisioningServerUrl},
{kContentProtectionProductionServer, kCpProductionLicenseServer,
kCpProductionServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kProductionProvisioningServerUrl},
{kContentProtectionUatServer, kCpUatLicenseServer, kCpUatServiceCertificate,
kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kUatProvisioningServerUrl},
{kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, ""},
{kContentProtectionUatServer, kCpUatLicenseServer, kCpClientAuth,
kCpKeyId, kCpOfflineKeyId, kCpUatServiceCertificate},
{kContentProtectionStagingServer, kCpStagingLicenseServer,
kCpStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl},
kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kCpStagingServiceCertificate},
{kContentProtectionTestQAServer, kQALicenseServerUrl,
kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kQAServiceCertificate},
};
} // namespace
@@ -219,45 +169,13 @@ ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
}
void ConfigTestEnv::Init(LicenseServerId server_id) {
server_id_ = server_id;
client_auth_ = license_servers[server_id].client_tag;
key_id_ = license_servers[server_id].key_id;
key_system_ = kWidevineKeySystem;
license_server_url_ = license_servers[server_id].license_server_url;
provisioning_server_url_ = license_servers[server_id].provisioning_server_url;
service_certificate_ = license_servers[server_id].license_service_certificate;
license_server_ = license_servers[server_id].url;
provisioning_server_url_ = kProductionProvisioningServerUrl;
service_certificate_ = license_servers[server_id].service_certificate;
wrong_key_id_ = kWrongKeyId;
}
const CdmInitData ConfigTestEnv::GetInitData(ContentId content_id) {
switch (content_id) {
case kContentIdStreaming:
return wvcdm::a2bs_hex(kCpKeyId);
case kContentIdOffline:
return wvcdm::a2bs_hex(kCpOfflineKeyId);
case kContentIdStagingSrmOuputProtectionRequested:
return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequested);
case kContentIdStagingSrmOuputProtectionRequired:
return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequired);
default:
return kEmptyData;
}
}
const std::string& ConfigTestEnv::GetLicenseServerUrl(
LicenseServerId license_server_id) {
switch (license_server_id) {
case kGooglePlayServer:
return kGpLicenseServer;
case kContentProtectionUatServer:
return kCpUatLicenseServer;
case kContentProtectionStagingServer:
return kCpStagingLicenseServer;
case kContentProtectionProductionServer:
return kCpProductionLicenseServer;
default:
return kEmptyData;
}
}
} // namespace wvcdm

View File

@@ -11,30 +11,19 @@ typedef enum {
kGooglePlayServer,
kContentProtectionUatServer,
kContentProtectionStagingServer,
kContentProtectionProductionServer,
kContentProtectionTestQAServer,
} LicenseServerId;
// Identifies content used in tests. Specify Prod/Uat/Staging if content
// has been registered across license services.
enum ContentId {
kContentIdStreaming,
kContentIdOffline,
kContentIdStagingSrmOuputProtectionRequested,
kContentIdStagingSrmOuputProtectionRequired,
};
// Configures default test environment.
class ConfigTestEnv {
public:
typedef struct {
LicenseServerId id;
std::string license_server_url;
std::string license_service_certificate;
std::string url;
std::string client_tag;
std::string key_id;
std::string offline_key_id;
std::string provisioning_server_url;
std::string service_certificate;
} LicenseServerConfiguration;
explicit ConfigTestEnv(LicenseServerId server_id);
@@ -46,7 +35,7 @@ class ConfigTestEnv {
const std::string& client_auth() const { return client_auth_; }
const KeyId& key_id() const { return key_id_; }
const CdmKeySystem& key_system() const { return key_system_; }
const std::string& license_server_url() const { return license_server_url_; }
const std::string& license_server() const { return license_server_; }
const std::string& provisioning_server_url() const {
return provisioning_server_url_;
}
@@ -55,19 +44,12 @@ class ConfigTestEnv {
}
const KeyId& wrong_key_id() const { return wrong_key_id_; }
static const CdmInitData GetInitData(ContentId content_id);
static const std::string& GetLicenseServerUrl(
LicenseServerId license_server_id);
void set_key_id(KeyId& key_id) { key_id_.assign(key_id); }
void set_key_system(CdmKeySystem& key_system) {
key_system_.assign(key_system);
}
void set_license_server(std::string& license_server) {
license_server_url_.assign(license_server);
}
void set_provisioning_server(std::string& provisioning_server) {
provisioning_server_url_.assign(provisioning_server);
license_server_.assign(license_server);
}
private:
@@ -76,11 +58,10 @@ class ConfigTestEnv {
std::string client_auth_;
KeyId key_id_;
CdmKeySystem key_system_;
std::string license_server_url_;
std::string license_server_;
std::string provisioning_server_url_;
std::string service_certificate_;
KeyId wrong_key_id_;
LicenseServerId server_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);
};

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,8 @@ class WvGenericOperationsTest : public testing::Test {
virtual void SetUp() {
::testing::Test::SetUp();
cdm_engine_ = NULL;
// TODO(fredgc or gmorgan): This should be updated for provisioning 3.0
// Load test keybox. This keybox will be used by any CryptoSession
// created by the CDM under test.
@@ -68,7 +70,9 @@ class WvGenericOperationsTest : public testing::Test {
virtual void TearDown() {
oec_util_session_.close();
cdm_engine_->CloseSession(session_id_);
if (cdm_engine_ != NULL) {
cdm_engine_->CloseSession(session_id_);
}
// OEMCrypto_Terminate() will be performed during the test class's
// destruction (specifically by the CryptoSession destructor)
}

View File

@@ -392,16 +392,14 @@ TEST_F(LicenseKeysTest, CanDecrypt) {
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
bool new_usable_keys = false;
bool any_change = false;
CdmKeyStatus status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
status = kKeyStatusExpired;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -507,7 +505,6 @@ TEST_F(LicenseKeysTest, ExtractKeyStatuses) {
TEST_F(LicenseKeysTest, KeyStatusChanges) {
bool new_usable_keys = false;
bool any_change = false;
CdmKeyStatus status;
CdmKeyStatusMap key_status_map;
StageOperatorSessionKeys();
StageContentKeys();
@@ -517,8 +514,7 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
// change to pending
status = kKeyStatusPending;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -530,8 +526,7 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to pending (again)
status = kKeyStatusPending;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending,
&new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -543,8 +538,7 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to usable
status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -556,8 +550,7 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to usable (again)
status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -569,8 +562,7 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to expired
status = kKeyStatusExpired;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -585,14 +577,10 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
TEST_F(LicenseKeysTest, HdcpChanges) {
bool new_usable_keys = false;
bool any_change = false;
CdmKeyStatus status;
uint32_t resolution;
CryptoSession::HdcpCapability hdcp_level;
CdmKeyStatusMap key_status_map;
StageHdcpKeys();
status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -610,11 +598,9 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
status = kKeyStatusUsable;
resolution = 100;
hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(100, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -637,11 +623,9 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
status = kKeyStatusUsable;
resolution = 100;
hdcp_level = HDCP_V1;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(100, HDCP_V1);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -664,11 +648,9 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusOutputNotAllowed);
status = kKeyStatusUsable;
resolution = 100;
hdcp_level = HDCP_V2_2;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(100, HDCP_V2_2);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -692,11 +674,9 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed);
status = kKeyStatusUsable;
resolution = 100;
hdcp_level = HDCP_NO_DIGITAL_OUTPUT;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(100, HDCP_NO_DIGITAL_OUTPUT);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -720,11 +700,9 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusUsable);
status = kKeyStatusUsable;
resolution = 100;
hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(100, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -752,15 +730,11 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
TEST_F(LicenseKeysTest, ConstraintChanges) {
bool new_usable_keys = false;
bool any_change = false;
CdmKeyStatus status;
uint32_t resolution;
CryptoSession::HdcpCapability hdcp_level;
CdmKeyStatusMap key_status_map;
StageConstraintKeys();
// No constraints set by device
status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -775,11 +749,9 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
// Low-res device, no HDCP support
status = kKeyStatusUsable;
resolution = dev_lo_res;
hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);
@@ -799,11 +771,9 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed);
// Hi-res device, HDCP_V1 support
status = kKeyStatusUsable;
resolution = dev_hi_res;
hdcp_level = HDCP_V1;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(dev_hi_res, HDCP_V1);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -823,11 +793,9 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed);
// Lo-res device, HDCP V2.2 support
status = kKeyStatusUsable;
resolution = dev_lo_res;
hdcp_level = HDCP_V2_2;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(dev_lo_res, HDCP_V2_2);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -848,11 +816,9 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed);
// Hi-res device, Maximal HDCP support
status = kKeyStatusUsable;
resolution = dev_hi_res;
hdcp_level = HDCP_NO_DIGITAL_OUTPUT;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(dev_hi_res, HDCP_NO_DIGITAL_OUTPUT);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -873,11 +839,9 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusUsable);
// Lo-res device, no HDCP support
status = kKeyStatusUsable;
resolution = dev_lo_res;
hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys);
@@ -898,11 +862,9 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed);
// Too-high-res -- all keys rejected
status = kKeyStatusUsable;
resolution = dev_top_res;
hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
license_keys_.ApplyConstraints(dev_top_res, HDCP_NONE);
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable,
&new_usable_keys);
EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys);

View File

@@ -81,8 +81,7 @@ const std::string kLicenseRequestSignature = a2bs_hex(
class MockCryptoSession : public CryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
: CryptoSession(crypto_metrics) { }
MockCryptoSession() : CryptoSession(NULL) { }
MOCK_METHOD0(IsOpen, bool());
MOCK_METHOD1(GenerateRequestId, bool(std::string*));
MOCK_METHOD1(UsageInformationSupport, bool(bool*));
@@ -132,7 +131,7 @@ class CdmLicenseTest : public ::testing::Test {
protected:
virtual void SetUp() {
clock_ = new MockClock();
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
crypto_session_ = new MockCryptoSession();
init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT,
kCencInitDataHdr + kCencPssh);
policy_engine_ = new MockPolicyEngine(crypto_session_);
@@ -153,7 +152,6 @@ class CdmLicenseTest : public ::testing::Test {
CdmLicense* cdm_license_;
MockClock* clock_;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_;
MockInitializationData* init_data_;
MockPolicyEngine* policy_engine_;
@@ -193,7 +191,7 @@ TEST_F(CdmLicenseTest, InitWithNullServiceCert) {
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(NULL, kToken, kClientTokenDrmCert,
crypto_session_, policy_engine_));
crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {

View File

@@ -1,10 +1,8 @@
// Copyright 2016 Google Inc. All Rights Reserved.
#include <iostream>
#include <tuple>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "crypto_session.h"
#include "license.h"
#include "policy_engine.h"
@@ -16,8 +14,6 @@
// protobuf generated classes.
using video_widevine::License;
using video_widevine::License_Policy;
using video_widevine::LicenseType;
using video_widevine::OFFLINE;
using video_widevine::STREAMING;
namespace wvcdm {
@@ -80,47 +76,14 @@ class MockCdmEventListener : public WvCdmEventListener {
int64_t new_expiry_time_seconds));
};
struct LicenseTypeParam {
std::string name;
LicenseType license_type;
bool can_persist;
};
const LicenseTypeParam kStreamingParam{"Streaming", STREAMING, false};
const LicenseTypeParam kOfflineParam{"Offline", OFFLINE, true};
void PrintTo(const LicenseTypeParam& param, std::ostream* os) {
*os << param.name;
}
struct LicenseRenewalParam {
std::string name;
bool can_renew;
int64_t renewal_delay_seconds;
};
const LicenseRenewalParam kNoRenewalParam{"Cannot Renew", false, 0};
const LicenseRenewalParam kShortRenewalParam{"Short Renewal Delay", true, 1};
const LicenseRenewalParam kLongRenewalParam{"Long Renewal Delay", true, 60};
void PrintTo(const LicenseRenewalParam& param, std::ostream* os) {
*os << param.name;
}
} // namespace
class PolicyEngineConstraintsTest
: public TestWithParam<std::tuple<LicenseTypeParam, LicenseRenewalParam>> {
class PolicyEngineConstraintsTest : public Test {
protected:
virtual void SetUp() {
mock_clock_ = new NiceMock<MockClock>();
current_time_ = 0;
// mock_event_listener_ is a StrictMock, but we don't care about renewal
// calls for these tests and want to ignore them.
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_))
.Times(AtLeast(0));
policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_,
&crypto_session_));
InjectMockClock();
@@ -146,25 +109,19 @@ class PolicyEngineConstraintsTest
}
void SetupLicense() {
LicenseTypeParam typeParam;
LicenseRenewalParam renewalParam;
std::tie(typeParam, renewalParam) = GetParam();
license_.set_license_start_time(current_time_);
LicenseIdentification* id = license_.mutable_id();
id->set_version(1);
id->set_type(typeParam.license_type);
id->set_type(STREAMING);
License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy();
policy->set_can_play(true);
policy->set_can_persist(typeParam.can_persist);
policy->set_can_renew(renewalParam.can_renew);
policy->set_can_persist(false);
policy->set_rental_duration_seconds(kRentalDuration);
policy->set_playback_duration_seconds(kPlaybackDuration);
policy->set_license_duration_seconds(kStreamingLicenseDuration);
policy->set_renewal_delay_seconds(renewalParam.renewal_delay_seconds);
KeyList* keys = license_.mutable_key();
@@ -255,23 +212,35 @@ class PolicyEngineConstraintsTest
License license_;
};
TEST_P(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()).Times(2);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
ExpectSessionKeysChange(kKeyStatusUsable, true);
{
Sequence key_change;
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusOutputNotAllowed,
kKeyId3, kKeyStatusUsable,
kKeyId4, kKeyStatusOutputNotAllowed, true);
}
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
}
TEST_P(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
{
Sequence time;
for (int i=0; i<4; ++i) {
@@ -293,7 +262,6 @@ TEST_P(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(true)));
policy_engine_->SetLicense(license_);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
@@ -323,7 +291,7 @@ TEST_P(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
}
TEST_P(PolicyEngineConstraintsTest,
TEST_F(PolicyEngineConstraintsTest,
RequestsHdcpImmediatelyAndOnlyAfterInterval) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(0))
@@ -331,17 +299,34 @@ TEST_P(PolicyEngineConstraintsTest,
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
ExpectSessionKeysChange(kKeyStatusUsable, true);
{
Sequence key_change;
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusOutputNotAllowed,
kKeyId3, kKeyStatusUsable,
kKeyId4, kKeyStatusOutputNotAllowed, true);
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillOnce(
DoAll(SetArgPointee<0>(HDCP_V2_2),
Return(false)))
.WillOnce(
DoAll(SetArgPointee<0>(HDCP_V2_2),
Return(false)));
}
int64_t start_time = current_time_ + 5;
{
InSequence calls;
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillOnce(
DoAll(SetArgPointee<0>(HDCP_V2_2),
Return(true)));
Return(false)));
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(start_time + kHdcpInterval / 2))
.WillOnce(Return(start_time + kHdcpInterval));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillOnce(
DoAll(SetArgPointee<0>(HDCP_V2_2),
Return(false)))
.WillOnce(
DoAll(SetArgPointee<0>(HDCP_V2_2),
Return(true)));
@@ -354,7 +339,7 @@ TEST_P(PolicyEngineConstraintsTest,
policy_engine_->OnTimerEvent();
}
TEST_P(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) {
TEST_F(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(0));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
@@ -362,7 +347,7 @@ TEST_P(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) {
policy_engine_->OnTimerEvent();
}
TEST_P(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
{
Sequence time;
for (int i=0; i<3; ++i) {
@@ -404,7 +389,7 @@ TEST_P(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
}
TEST_P(PolicyEngineConstraintsTest, HandlesNoHdcp) {
TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
{
Sequence time;
for (int i=0; i<3; ++i) {
@@ -417,14 +402,13 @@ TEST_P(PolicyEngineConstraintsTest, HandlesNoHdcp) {
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusOutputNotAllowed,
kKeyId3, kKeyStatusUsable,
kKeyId4, kKeyStatusOutputNotAllowed, false);
kKeyId4, kKeyStatusOutputNotAllowed, true);
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusOutputNotAllowed,
kKeyId3, kKeyStatusOutputNotAllowed,
kKeyId4, kKeyStatusOutputNotAllowed, false);
}
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NONE),
@@ -451,7 +435,7 @@ TEST_P(PolicyEngineConstraintsTest, HandlesNoHdcp) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
}
TEST_P(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
TEST_F(PolicyEngineConstraintsTest, UsesDefaultHdcpWhenResolutionNotSet) {
{
Sequence time;
for (int i=0; i<2; ++i) {
@@ -459,23 +443,30 @@ TEST_P(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
.WillOnce(Return(i * 10));
}
}
ExpectSessionKeysChange(kKeyStatusUsable, true);
{
Sequence key_change;
ExpectSessionKeysChanges(kKeyId1, kKeyStatusUsable,
kKeyId2, kKeyStatusOutputNotAllowed,
kKeyId3, kKeyStatusUsable,
kKeyId4, kKeyStatusOutputNotAllowed, true);
}
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId2));
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId4));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
}
INSTANTIATE_TEST_CASE_P(Default, PolicyEngineConstraintsTest,
Combine(
Values(kStreamingParam, kOfflineParam),
Values(kNoRenewalParam, kShortRenewalParam, kLongRenewalParam)));
} // namespace wvcdm

File diff suppressed because it is too large Load Diff

View File

@@ -44,16 +44,13 @@ const std::string kTestSignedCertificate = a2bs_hex(
class MockCryptoSession : public CryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* crypto_metrics)
: CryptoSession(crypto_metrics) { }
MockCryptoSession() : CryptoSession(NULL) { }
MOCK_METHOD2(GetRandom, bool(size_t, uint8_t*));
};
class ServiceCertificateTest : public ::testing::Test {
protected:
virtual void SetUp() {
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
}
virtual void SetUp() { crypto_session_ = new MockCryptoSession(); }
virtual void TearDown() {
if (crypto_session_) delete crypto_session_;
@@ -64,7 +61,6 @@ class ServiceCertificateTest : public ::testing::Test {
}
ServiceCertificate* service_certificate_;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_;
};
@@ -111,7 +107,7 @@ class StubCdmClientPropertySet : public CdmClientPropertySet {
};
TEST_F(ServiceCertificateTest, InitSuccess) {
MockCryptoSession crypto_session(&crypto_metrics_);
MockCryptoSession crypto_session;
CreateServiceCertificate();
service_certificate_->Init(kTestSessionId1);
@@ -119,7 +115,6 @@ TEST_F(ServiceCertificateTest, InitSuccess) {
}
TEST_F(ServiceCertificateTest, InitPrivacyModeRequired) {
MockCryptoSession crypto_session(&crypto_metrics_);
StubCdmClientPropertySet property_set;
property_set.enable_privacy_mode();
@@ -133,7 +128,6 @@ TEST_F(ServiceCertificateTest, InitPrivacyModeRequired) {
}
TEST_F(ServiceCertificateTest, InitServiceCertificatePresent) {
MockCryptoSession crypto_session(&crypto_metrics_);
StubCdmClientPropertySet property_set;
property_set.enable_privacy_mode();
@@ -143,16 +137,15 @@ TEST_F(ServiceCertificateTest, InitServiceCertificatePresent) {
Properties::AddSessionPropertySet(kTestSessionId1, &property_set);
CreateServiceCertificate();
std::string raw_service_certificate;
std::string service_certificate;
EXPECT_TRUE(Properties::GetServiceCertificate(kTestSessionId1,
&raw_service_certificate));
&service_certificate));
EXPECT_EQ(NO_ERROR,
service_certificate_->Init(raw_service_certificate));
service_certificate_->Init(service_certificate));
EXPECT_TRUE(service_certificate_->HasCertificate());
}
TEST_F(ServiceCertificateTest, SetServiceCertificate) {
MockCryptoSession crypto_session(&crypto_metrics_);
StubCdmClientPropertySet property_set;
property_set.enable_privacy_mode();

View File

@@ -249,6 +249,12 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case RELEASE_ALL_USAGE_INFO_ERROR_2:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_2";
break;
case RELEASE_ALL_USAGE_INFO_ERROR_3:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_3";
break;
case RELEASE_ALL_USAGE_INFO_ERROR_4:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_4";
break;
case RELEASE_KEY_ERROR: *os << "RELEASE_KEY_ERROR";
break;
case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR";
@@ -302,8 +308,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case STORE_LICENSE_ERROR_2: *os << "STORE_LICENSE_ERROR_2";
break;
case STORE_LICENSE_ERROR_4: *os << "STORE_LICENSE_ERROR_4";
break;
case STORE_USAGE_INFO_ERROR: *os << "STORE_USAGE_INFO_ERROR";
break;
case UNPROVISION_ERROR_1: *os << "UNPROVISION_ERROR_1";
@@ -450,6 +454,8 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case INVALID_PARAMETERS_ENG_17: *os << "INVALID_PARAMETERS_ENG_17";
break;
case INVALID_PARAMETERS_ENG_18: *os << "INVALID_PARAMETERS_ENG_18";
break;
case CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1:
*os << "CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1";
break;
@@ -478,8 +484,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case LOAD_USAGE_HEADER_UNKNOWN_ERROR:
*os << "LOAD_USAGE_HEADER_UNKNOWN_ERROR";
break;
case INVALID_PARAMETERS_ENG_18: *os << "INVALID_PARAMETERS_ENG_18";
break;
case INSUFFICIENT_CRYPTO_RESOURCES_3:
*os << "INSUFFICIENT_CRYPTO_RESOURCES_3";
break;
@@ -515,8 +519,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case COPY_OLD_USAGE_ENTRY_UNKNOWN_ERROR:
*os << "COPY_OLD_USAGE_ENTRY_UNKNOWN_ERROR";
break;
case LIST_LICENSES_ERROR: *os << "LIST_LICENSES_ERROR";
break;
case INVALID_PARAMETERS_ENG_23:
*os << "INVALID_PARAMETERS_ENG_23";
break;
@@ -552,15 +554,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case USAGE_INVALID_LOAD_ENTRY: *os << "USAGE_INVALID_LOAD_ENTRY";
break;
case RELEASE_ALL_USAGE_INFO_ERROR_4:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_4";
break;
case RELEASE_ALL_USAGE_INFO_ERROR_5:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_5";
break;
case RELEASE_ALL_USAGE_INFO_ERROR_6:
*os << "RELEASE_ALL_USAGE_INFO_ERROR_6";
break;
case RELEASE_USAGE_INFO_FAILED: *os << "RELEASE_USAGE_INFO_FAILED";
break;
case INCORRECT_USAGE_SUPPORT_TYPE_1:
@@ -575,8 +568,11 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case KEY_NOT_FOUND_IN_SESSION:
*os << "KEY_NOT_FOUND_IN_SESSION";
break;
case NO_USAGE_ENTRIES:
*os << "NO_USAGE_ENTRIES";
case NO_USAGE_ENTRIES: *os << "NO_USAGE_ENTRIES";
break;
case LIST_LICENSE_ERROR_1: *os << "LIST_LICENSE_ERROR_1";
break;
case LIST_LICENSE_ERROR_2: *os << "LIST_LICENSE_ERROR_2";
break;
case LIST_USAGE_ERROR_1: *os << "LIST_USAGE_ERROR_1";
break;
@@ -588,24 +584,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case DELETE_USAGE_ERROR_3: *os << "DELETE_USAGE_ERROR_3";
break;
case PRIVACY_MODE_ERROR_1: *os << "PRIVACY_MODE_ERROR_1";
break;
case PRIVACY_MODE_ERROR_2: *os << "PRIVACY_MODE_ERROR_2";
break;
case PRIVACY_MODE_ERROR_3: *os << "PRIVACY_MODE_ERROR_3";
break;
case EMPTY_RESPONSE_ERROR_1: *os << "EMPTY_RESPONSE_ERROR_1";
break;
case INVALID_PARAMETERS_ENG_24: *os << "INVALID_PARAMETERS_ENG_24";
break;
case PARSE_RESPONSE_ERROR_1: *os << "PARSE_RESPONSE_ERROR_1";
break;
case PARSE_RESPONSE_ERROR_2: *os << "PARSE_RESPONSE_ERROR_2";
break;
case PARSE_RESPONSE_ERROR_3: *os << "PARSE_RESPONSE_ERROR_3";
break;
case PARSE_RESPONSE_ERROR_4: *os << "PARSE_RESPONSE_ERROR_4";
break;
default:
*os << "Unknown CdmResponseType";

View File

@@ -167,9 +167,9 @@ bool UrlRequest::PostRequest(const std::string& data) {
bool UrlRequest::PostCertRequestInQueryString(const std::string& data) {
std::string path = socket_.resource_path();
path.append("&signedRequest=");
path.append((path.find('?') == std::string::npos) ? "?" : "&");
path.append("signedRequest=");
path.append(data);
return PostRequestWithPath(path, "");
}

View File

@@ -47,7 +47,7 @@ const CdmUsageEntryInfo kUsageEntryInfoSecureStop3 = {
.key_set_id = "secure_stop_key_set_3",
.usage_info_file_name = "usage_info_file_3"};
const CdmUsageEntryInfo kUsageEntryInfoStorageTypeUnknown = {
.storage_type = kStorageUnknown,
.storage_type = kStorageTypeUnknown,
.key_set_id = "",
.usage_info_file_name = ""};
const std::vector<std::string> kEmptyLicenseList;
@@ -140,8 +140,7 @@ class MockDeviceFiles : public DeviceFiles {
class MockCryptoSession : public CryptoSession {
public:
MockCryptoSession(metrics::CryptoMetrics* metrics)
: CryptoSession(metrics) {}
MockCryptoSession() : CryptoSession(NULL) {}
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
MOCK_METHOD1(LoadUsageTableHeader,
CdmResponseType(const CdmUsageTableHeader&));
@@ -171,7 +170,7 @@ class UsageTableHeaderTest : public ::testing::Test {
virtual void SetUp() {
// UsageTableHeader will take ownership of the pointer
device_files_ = new MockDeviceFiles();
crypto_session_ = new MockCryptoSession(&crypto_metrics_);
crypto_session_ = new MockCryptoSession();
usage_table_header_ = new UsageTableHeader();
// usage_table_header_ object takes ownership of these objects
@@ -197,7 +196,6 @@ class UsageTableHeaderTest : public ::testing::Test {
}
MockDeviceFiles* device_files_;
metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_;
UsageTableHeader* usage_table_header_;
};
@@ -252,6 +250,8 @@ TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveLicenses) {
EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR)));
// TODO: Why not being called?
//EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader,
kEmptyUsageEntryInfoVector))
.Times(2)