Source release v3.3.0

This commit is contained in:
Gene Morgan
2017-05-04 14:01:27 -07:00
parent baa7b133d3
commit 8082775924
678 changed files with 51264 additions and 14200 deletions

View File

@@ -3,9 +3,11 @@
#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"
@@ -26,11 +28,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:
@@ -44,6 +44,20 @@ class CdmEngine {
virtual CdmResponseType SetServiceCertificate(
const std::string& certificate);
// Report whether the service certificate has been set.
virtual bool HasServiceCertificate();
// Generate and return a Service Certificate Request message.
// This message can be sent to the License Server to get a service
// certificate.
virtual bool GetServiceCertificateRequest(CdmKeyMessage* request);
// Parse the message returned by the License Server in response to a
// Service Certificate Request message. Return the service certificate
// from the parsed response.
virtual CdmResponseType ParseServiceCertificateResponse(
const std::string& response, std::string* certificate);
// Session related methods
virtual CdmResponseType OpenSession(
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
@@ -158,24 +172,45 @@ class CdmEngine {
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
// Delete OEMCrypto usage tables. Used by Unprovision().
CdmResponseType DeleteUsageTable(CdmSecurityLevel security_level);
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);
// 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,
@@ -215,7 +250,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);
@@ -255,7 +290,7 @@ class CdmEngine {
void CloseExpiredReleaseSessions();
// instance variables
CdmSessionMap sessions_;
CdmSessionMap session_map_;
CdmReleaseKeySetMap release_key_sets_;
scoped_ptr<CertificateProvisioning> cert_provisioning_;
SecurityLevel cert_provisioning_requested_security_level_;
@@ -276,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

@@ -28,6 +28,9 @@ class CdmSession {
CdmSession(FileSystem* file_system);
virtual ~CdmSession();
void Close() { closed_ = true; }
bool IsClosed() { return closed_; }
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set);
virtual CdmResponseType Init(ServiceCertificate* service_certificate,
CdmClientPropertySet* cdm_client_property_set,
@@ -171,6 +174,7 @@ class CdmSession {
// instance variables
bool initialized_;
bool closed_; // Session closed, but final shared_ptr has not been released.
CdmSessionId session_id_;
scoped_ptr<CdmLicense> license_parser_;
scoped_ptr<CryptoSession> crypto_session_;

View File

@@ -0,0 +1,52 @@
// Copyright 2017 Google Inc. All Rights Reserved.
#ifndef WVCDM_CORE_CDM_SESSION_MAP_H_
#define WVCDM_CORE_CDM_SESSION_MAP_H_
#include <list>
#include <memory>
#include <string>
#include "lock.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class CdmSession;
typedef std::list<std::shared_ptr<CdmSession> > CdmSessionList;
class CdmSessionMap {
public:
CdmSessionMap() {}
virtual ~CdmSessionMap();
void Add(const std::string& id, CdmSession* session);
bool CloseSession(const std::string& id);
bool Exists(const std::string& id);
size_t Size() const { return sessions_.size(); }
bool FindSession(const CdmSessionId& id,
std::shared_ptr<CdmSession>& session);
void GetSessionList(CdmSessionList& sessions);
private:
typedef std::map<CdmSessionId, std::shared_ptr<CdmSession> >
CdmIdToSessionMap;
bool FindSessionNoLock(const CdmSessionId& session_id,
std::shared_ptr<CdmSession>& session);
Lock lock_;
CdmIdToSessionMap sessions_;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSessionMap);
};
} // namespace wvcdm
#endif // WVCDM_CORE_CDM_SESSION_MAP_H_

View File

@@ -49,12 +49,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

@@ -68,12 +68,15 @@ class CryptoSession {
virtual bool GenerateDerivedKeys(const std::string& message);
virtual bool GenerateDerivedKeys(const std::string& message,
const std::string& session_key);
virtual bool RewrapDeviceRSAKey(const std::string& message,
const std::string& signature,
const std::string& nonce,
const std::string& enc_rsa_key,
const std::string& rsa_key_iv,
std::string* wrapped_rsa_key);
virtual bool RewrapCertificate(const std::string& signed_message,
const std::string& signature,
const std::string& nonce,
const std::string& private_key,
const std::string& iv,
const std::string& wrapping_key,
std::string* wrapped_private_key);
// Media data path
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
@@ -127,7 +130,7 @@ class CryptoSession {
const std::string& signature);
private:
bool GetProvisioningMethod(CdmClientTokenType* token_type);
bool GetProvisioningMethod(CdmClientTokenType& token_type);
void Init();
void Terminate();
bool GetTokenFromKeybox(std::string* token);
@@ -140,6 +143,17 @@ class CryptoSession {
bool GenerateRsaSignature(const std::string& message, std::string* signature);
size_t GetOffset(std::string message, std::string field);
bool SetDestinationBufferType();
bool RewrapDeviceRSAKey(
const std::string& message, const std::string& signature,
const std::string& nonce, const std::string& enc_rsa_key,
const std::string& rsa_key_iv, std::string* wrapped_rsa_key);
bool RewrapDeviceRSAKey30(
const std::string& message, const std::string& nonce,
const std::string& private_key, const std::string& iv,
const std::string& wrapping_key, std::string* wrapped_private_key);
bool SelectKey(const std::string& key_id);
static const OEMCrypto_Algorithm kInvalidAlgorithm =
@@ -170,6 +184,7 @@ class CryptoSession {
bool open_;
CdmClientTokenType pre_provision_token_type_;
std::string oem_token_; // Cached OEMCrypto Public Key
bool update_usage_table_after_close_session_;
CryptoSessionId oec_session_id_;

View File

@@ -73,29 +73,45 @@ class DeviceFiles {
virtual bool ReserveLicenseId(const std::string& key_set_id);
virtual bool UnreserveLicenseId(const std::string& key_set_id);
// 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& app_id,
const std::string& key_set_id);
// 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 token for the given key_set_id.
virtual bool GetProviderToken(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& app_id,
std::vector<std::string>* provider_session_tokens);
// Retrieve one usage info from the file. Subsequent calls will retrieve
// subsequent entries in the table for this app_id.
virtual bool RetrieveUsageInfo(
const std::string& app_id,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
// Retrieve the usage info entry specified by |provider_session_token|.
// Returns false if the entry could not be found.
virtual bool RetrieveUsageInfo(const std::string& app_id,
const std::string& provider_session_token,
CdmKeyMessage* license_request,
CdmKeyResponse* license_response);
// 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& app_id,
@@ -110,6 +126,7 @@ class DeviceFiles {
CdmHlsMethod* method,
std::vector<uint8_t>* media_segment_iv);
virtual bool DeleteHlsAttributes(const std::string& key_set_id);
private:
// Extract serial number and system ID from DRM Device certificate
bool ExtractDeviceInfo(const std::string& device_certificate,
@@ -138,7 +155,7 @@ class DeviceFiles {
#if defined(UNIT_TEST)
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
FRIEND_TEST(DeviceCertificateStoreTest, StoreCertificate);
FRIEND_TEST(DeviceCertificateTest, ReadCertificate);
FRIEND_TEST(DeviceCertificateTest, DISABLED_ReadCertificate);
FRIEND_TEST(DeviceCertificateTest, HasCertificate);
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
FRIEND_TEST(DeviceFilesHlsAttributesTest, Delete);

View File

@@ -29,7 +29,7 @@ class CdmLicense {
virtual bool Init(
ServiceCertificate* service_certificate, const std::string& client_token,
CdmClientTokenType client_token_type, const std::string& serial_number,
CdmClientTokenType client_token_type, const std::string& device_id,
CryptoSession* session, PolicyEngine* policy_engine);
virtual CdmResponseType PrepareKeyRequest(
@@ -52,7 +52,6 @@ class CdmLicense {
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(); }
virtual bool IsKeyLoaded(const KeyId& key_id);
virtual std::string provider_session_token() {
@@ -89,9 +88,8 @@ class CdmLicense {
std::string server_url_;
std::string client_token_;
CdmClientTokenType client_token_type_;
std::string serial_number_;
std::string device_id_;
const CdmSessionId session_id_;
scoped_ptr<InitializationData> stored_init_data_;
bool initialized_;
std::set<KeyId> loaded_keys_;
std::string provider_session_token_;

View File

@@ -37,7 +37,11 @@ class LicenseKeys {
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Populates the CdmKeyStatusMap with the current content keys.
// Returns current CdmKeyStatus for the given key.
// Returns kKeyStatusKeyUnknown if key_id not found.
virtual CdmKeyStatus GetKeyStatus(const KeyId& key_id);
// Populates a CdmKeyStatusMap with the current content keys.
virtual void ExtractKeyStatuses(CdmKeyStatusMap* content_keys);
// Determines whether the specified key can be used under the current

View File

@@ -34,6 +34,11 @@ class PolicyEngine {
// status is not calculated to avoid overhead in the decryption path.
virtual bool CanDecryptContent(const KeyId& key_id);
// Returns the current CdmKeyStatus for the given key, or
// kKeyStatusKeyUnknown if the key is not found. This is useful for finding
// out why a key is not usable.
virtual CdmKeyStatus GetKeyStatus(const KeyId& key_id);
// OnTimerEvent is called when a timer fires. It notifies the Policy Engine
// that the timer has fired and dispatches the relevant events through
// |event_listener_|.
@@ -104,11 +109,9 @@ class PolicyEngine {
friend class PolicyEngineConstraintsTest;
void InitDevice(CryptoSession* crypto_session);
void CheckDevice(int64_t current_time);
void SetDeviceResolution(uint32_t width, uint32_t height) {
current_resolution_ = width * height;
}
void SetDeviceResolution(uint32_t width, uint32_t height);
void CheckDeviceHdcpStatusOnTimer(int64_t current_time);
void CheckDeviceHdcpStatus();
typedef enum {
kLicenseStateInitial,

View File

@@ -40,6 +40,9 @@ class Properties {
static inline bool use_certificates_as_identification() {
return use_certificates_as_identification_;
}
static inline bool provisioning_messages_are_binary() {
return provisioning_messages_are_binary_;
}
static inline bool security_level_path_backward_compatibility_support() {
return security_level_path_backward_compatibility_support_;
}
@@ -90,6 +93,9 @@ class Properties {
static void set_use_certificates_as_identification(bool flag) {
use_certificates_as_identification_ = flag;
}
static void set_provisioning_messages_are_binary(bool flag) {
provisioning_messages_are_binary_ = flag;
}
static void set_security_level_path_backward_compatibility_support(
bool flag) {
security_level_path_backward_compatibility_support_ = flag;
@@ -111,6 +117,7 @@ class Properties {
static bool oem_crypto_use_userspace_buffers_;
static bool use_certificates_as_identification_;
static bool security_level_path_backward_compatibility_support_;
static bool provisioning_messages_are_binary_;
static scoped_ptr<CdmClientPropertySetMap> session_property_set_;
CORE_DISALLOW_COPY_AND_ASSIGN(Properties);

View File

@@ -11,13 +11,11 @@
// Certificate Request to the target server to get one. Once the Service
// Certificate is established for the session, it should not change.
#include "license_protocol.pb.h"
#include "wv_cdm_types.h"
#include <memory>
namespace video_widevine {
class SignedMessage;
class LicenseRequest;
} // namespace video_widevine
#include "license_protocol.pb.h"
#include "privacy_crypto.h"
#include "wv_cdm_types.h"
namespace wvcdm {
@@ -25,23 +23,20 @@ class CryptoSession;
class ServiceCertificate {
public:
ServiceCertificate() {}
ServiceCertificate() : has_certificate_(false) {}
virtual ~ServiceCertificate() {}
// Set up a new service certificate.
// Accept a serialized video_widevine::SignedDrmDeviceCertificate message.
virtual CdmResponseType Init(const std::string& signed_certificate);
// Initialize the service certificate.
// Set the certificate with no certificate and provider ID.
virtual void Clear();
bool has_certificate() const { return has_certificate_; }
const std::string certificate() const { return certificate_; }
const std::string& provider_id() const { return provider_id_; }
// Current state of certificate.
// If !HasCertificate() and privacy mode is enabled, then should call
// PrepareRequest() and pass the request to the license server.
virtual bool HasCertificate() { return !certificate_.empty(); }
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
@@ -54,29 +49,23 @@ class ServiceCertificate {
const video_widevine::ClientIdentification* clear_client_id,
video_widevine::EncryptedClientIdentification* encrypted_client_id);
// Construct service certificate request.
virtual bool PrepareRequest(CdmKeyMessage* signed_request);
// Parse service certificate response and make it usable.
virtual CdmResponseType HandleResponse(
const std::string& signed_respnse);
private:
// Verify the signature on the signed service certificate.
// Extract and save the certificate and provider_id.
// Expected format: serialized video_widevine::SignedDrmDeviceCertificate.
virtual CdmResponseType VerifyAndExtract(
const std::string& raw_certificate);
// True while waiting for response to service certificate request.
bool fetch_in_progress_;
// Track whether object holds valid certificate
bool has_certificate_;
// Certificate, verified and extracted from signed message.
std::string certificate_;
// Certificate serial number.
std::string serial_number_;
// Provider ID, extracted from certificate message.
std::string provider_id_;
// Public key.
std::unique_ptr<RsaPublicKey> public_key_;
CORE_DISALLOW_COPY_AND_ASSIGN(ServiceCertificate);
};

View File

@@ -99,6 +99,10 @@ static const std::string HLS_URI_ATTRIBUTE = "URI";
static const char EMPTY_ORIGIN[] = "";
static const char EMPTY_SPOID[] = "";
//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

@@ -177,9 +177,10 @@ enum CdmResponseType {
UNPROVISION_ERROR_4,
UNSUPPORTED_INIT_DATA,
USAGE_INFO_NOT_FOUND,
LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR, /* 140 */
UNUSED_8, /* 140 */
/* UNUSED_8 previously LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR */
PARSE_SERVICE_CERTIFICATE_ERROR,
SERVICE_CERTIFICATE_TYPE_ERROR,
UNUSED_10, /* previously SERVICE_CERTIFICATE_TYPE_ERROR */
CLIENT_ID_GENERATE_RANDOM_ERROR,
CLIENT_ID_AES_INIT_ERROR,
CLIENT_ID_AES_ENCRYPT_ERROR, /* 145 */
@@ -194,7 +195,8 @@ enum CdmResponseType {
UNUSED_2, /* previously INVALID_PARAMETERS_LIC_5 */
INVALID_PARAMETERS_LIC_6,
INVALID_PARAMETERS_LIC_7, /* 155 */
LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR,
UNUSED_9,
/* UNUSED_9 previously LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR */
CENC_INIT_DATA_UNAVAILABLE,
PREPARE_CENC_CONTENT_ID_FAILED,
WEBM_INIT_DATA_UNAVAILABLE,
@@ -250,10 +252,29 @@ enum CdmResponseType {
CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2, /* 210 */
LICENSING_CLIENT_TOKEN_ERROR_1,
INVALID_PARAMETERS_ENG_17,
STORE_LICENSE_ERROR_3,
INVALID_PARAMETERS_ENG_18,
LIST_LICENSE_ERROR_1,
LIST_LICENSE_ERROR_2, /* 215 */
LIST_USAGE_ERROR_1,
LIST_USAGE_ERROR_2,
DELETE_USAGE_ERROR_1,
DELETE_USAGE_ERROR_2,
DELETE_USAGE_ERROR_3, /* 220 */
RELEASE_ALL_USAGE_INFO_ERROR_3,
RELEASE_ALL_USAGE_INFO_ERROR_4,
PRIVACY_MODE_ERROR_1,
PRIVACY_MODE_ERROR_2,
PRIVACY_MODE_ERROR_3, /* 225 */
EMPTY_RESPONSE_ERROR_1,
INVALID_PARAMETERS_ENG_19,
PARSE_RESPONSE_ERROR_1,
PARSE_RESPONSE_ERROR_2,
PARSE_RESPONSE_ERROR_3, /* 230 */
PARSE_RESPONSE_ERROR_4,
};
enum CdmKeyStatus {
kKeyStatusKeyUnknown,
kKeyStatusUsable,
kKeyStatusExpired,
kKeyStatusOutputNotAllowed,
@@ -270,9 +291,6 @@ enum CdmLicenseType {
kLicenseTypeOffline,
kLicenseTypeStreaming,
kLicenseTypeRelease,
// If the original request was saved to make a service certificate request,
// use Deferred for the license type in the subsequent request.
kLicenseTypeDeferred,
// Like Streaming, but stricter. Does not permit storage of any kind.
// Named after the 'temporary' session type in EME, which has this behavior.
kLicenseTypeTemporary,

View File

@@ -3,12 +3,14 @@
#include "cdm_engine.h"
#include <assert.h>
#include <list>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include "cdm_session.h"
#include "cdm_session_map.h"
#include "clock.h"
#include "device_files.h"
#include "file_store.h"
@@ -27,6 +29,9 @@ const size_t kUsageReportsPerRequest = 1;
namespace wvcdm {
using video_widevine::SignedMessage;
using video_widevine::LicenseError;
class UsagePropertySet : public CdmClientPropertySet {
public:
UsagePropertySet() {}
@@ -70,20 +75,77 @@ CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
}
}
CdmEngine::~CdmEngine() {
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator i(sessions_.begin());
for (; i != sessions_.end(); ++i) {
delete i->second;
}
sessions_.clear();
}
CdmEngine::~CdmEngine() {}
CdmResponseType CdmEngine::SetServiceCertificate(
const std::string& certificate) {
return service_certificate_.Init(certificate);
}
bool CdmEngine::HasServiceCertificate() {
return service_certificate_.has_certificate();
}
bool CdmEngine::GetServiceCertificateRequest(CdmKeyMessage* request) {
if (!request) {
LOGE("ServiceCertificate::PrepareRequest: no request parameter provided");
return false;
}
SignedMessage message;
message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
message.SerializeToString(request);
return true;
}
CdmResponseType CdmEngine::ParseServiceCertificateResponse(
const std::string& response, std::string* certificate) {
if (response.empty()) {
LOGE("CdmEngine::ParseServiceCertificateResponse: empty response");
return EMPTY_RESPONSE_ERROR_1;
}
if (!certificate) {
LOGE("CdmEngine::ParseServiceCertificateResponse: null return parameter");
return INVALID_PARAMETERS_ENG_19;
}
SignedMessage signed_response;
if (!signed_response.ParseFromString(response)) {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: cannot parse response");
return PARSE_RESPONSE_ERROR_1;
}
if (signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) {
CdmResponseType status;
status = service_certificate_.Init(signed_response.msg());
if (status != NO_ERROR) {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: certificate handling "
"failure, status=%d", status);
return PARSE_SERVICE_CERTIFICATE_ERROR;
}
certificate->assign(signed_response.msg());
} else if (signed_response.type() == SignedMessage::ERROR_RESPONSE) {
LicenseError license_error;
if (!license_error.ParseFromString(signed_response.msg())) {
LOGE("CdmEngine::ParseServiceCertificateResponse: cannot parse "
"license error");
return PARSE_RESPONSE_ERROR_2;
}
LOGE("CdmEngine::ParseServiceCertificateResponse: server returned error:"
"error code = %d", license_error.error_code());
return PARSE_RESPONSE_ERROR_3;
} else {
LOGE(
"CdmEngine::ParseServiceCertificateResponse: response (%d) is "
"wrong type", signed_response.type());
return PARSE_RESPONSE_ERROR_4;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::OpenSession(
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) {
@@ -114,7 +176,7 @@ CdmResponseType CdmEngine::OpenSession(
}
if (forced_session_id) {
if (sessions_.find(*forced_session_id) != sessions_.end()) {
if (session_map_.Exists(*forced_session_id)) {
return DUPLICATE_SESSION_ID_SPECIFIED;
}
}
@@ -138,8 +200,7 @@ CdmResponseType CdmEngine::OpenSession(
}
CdmSessionId id = new_session->session_id();
AutoLock lock(session_list_lock_);
sessions_[id] = new_session.release();
session_map_.Add(id, new_session.release());
if (session_id) *session_id = id;
return NO_ERROR;
}
@@ -180,15 +241,10 @@ CdmResponseType CdmEngine::OpenKeySetSession(
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::CloseSession");
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
if (!session_map_.CloseSession(session_id)) {
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
return SESSION_NOT_FOUND_1;
}
CdmSession* session = iter->second;
sessions_.erase(session_id);
delete session;
return NO_ERROR;
}
@@ -218,9 +274,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
}
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator iter = sessions_.find(session_id);
return iter != sessions_.end();
return session_map_.Exists(session_id);
}
CdmResponseType CdmEngine::GenerateKeyRequest(
@@ -258,8 +312,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
id = iter->second.first;
}
CdmSessionMap::iterator iter = sessions_.find(id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(id, session)) {
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
id.c_str());
return SESSION_NOT_FOUND_2;
@@ -273,8 +327,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
key_request->message.clear();
if (license_type == kLicenseTypeRelease &&
!iter->second->license_received()) {
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
!session->license_received()) {
sts = session->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
if (sts != KEY_ADDED) {
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed,"
"sts = %d", static_cast<int>(sts));
@@ -282,13 +336,13 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
}
}
sts = iter->second->GenerateKeyRequest(
init_data, license_type, app_parameters, key_request);
sts = session->GenerateKeyRequest(init_data, license_type, app_parameters,
key_request);
if (KEY_MESSAGE != sts) {
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
session->GetRequestedSecurityLevel();
}
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, "
"sts = %d", static_cast<int>(sts));
@@ -331,9 +385,8 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
id = iter->second.first;
}
CdmSessionMap::iterator iter = sessions_.find(id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(id, session)) {
LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str());
return SESSION_NOT_FOUND_3;
}
@@ -343,9 +396,9 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
return EMPTY_KEY_DATA_1;
}
CdmResponseType sts = iter->second->AddKey(key_data);
CdmResponseType sts = session->AddKey(key_data);
if (key_set_id) {
*key_set_id = iter->second->key_set_id();
*key_set_id = session->key_set_id();
}
switch (sts) {
@@ -371,18 +424,18 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
return EMPTY_KEYSET_ID_ENG_4;
}
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_4;
}
CdmResponseType sts =
iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
session->RestoreOfflineSession(key_set_id, kLicenseTypeOffline);
if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel();
session->GetRequestedSecurityLevel();
}
if (sts != KEY_ADDED && sts != GET_RELEASED_LICENSE_ERROR) {
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
@@ -393,14 +446,14 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
LOGI("CdmEngine::RemoveKeys");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::RemoveKeys: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_5;
}
iter->second->ReleaseCrypto();
session->ReleaseCrypto();
return NO_ERROR;
}
@@ -408,8 +461,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
const CdmSessionId& session_id, CdmKeyRequest* key_request) {
LOGI("CdmEngine::GenerateRenewalRequest");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_6;
@@ -422,7 +475,7 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
key_request->message.clear();
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request);
CdmResponseType sts = session->GenerateRenewalRequest(key_request);
if (KEY_MESSAGE != sts) {
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
@@ -437,8 +490,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data) {
LOGI("CdmEngine::RenewKey");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
return SESSION_NOT_FOUND_7;
}
@@ -448,7 +501,7 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
return EMPTY_KEY_DATA_2;
}
CdmResponseType sts = iter->second->RenewKey(key_data);
CdmResponseType sts = session->RenewKey(key_data);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast<int>(sts));
return sts;
@@ -473,8 +526,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
}
if (query_token == QUERY_KEY_SECURITY_LEVEL) {
CdmSecurityLevel security_level = crypto_session.GetSecurityLevel();
switch (security_level) {
CdmSecurityLevel level = crypto_session.GetSecurityLevel();
switch (level) {
case kSecurityLevelL1:
*query_response = QUERY_VALUE_SECURITY_LEVEL_L1;
break;
@@ -489,8 +542,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
*query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
LOGW("CdmEngine::QueryStatus: Unknown security level: %d",
security_level);
LOGW("CdmEngine::QueryStatus: Unknown security level: %d", level);
return UNKNOWN_ERROR;
}
} else if (query_token == QUERY_KEY_DEVICE_ID) {
@@ -581,47 +633,47 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
CdmQueryMap* query_response) {
LOGI("CdmEngine::QuerySessionStatus");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QuerySessionStatus: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_8;
}
return iter->second->QueryStatus(query_response);
return session->QueryStatus(query_response);
}
bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsReleaseSession");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::IsReleaseSession: session_id not found = %s",
session_id.c_str());
return false;
}
return iter->second->is_release();
return session->is_release();
}
bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::IsOfflineSession");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::IsOfflineSession: session_id not found = %s",
session_id.c_str());
return false;
}
return iter->second->is_offline();
return session->is_offline();
}
CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryKeyStatus");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_9;
}
return iter->second->QueryKeyStatus(query_response);
return session->QueryKeyStatus(query_response);
}
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
@@ -632,13 +684,13 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id,
LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination");
return INVALID_PARAMETERS_ENG_12;
}
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QueryKeyAllowedUsage: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_12;
}
return iter->second->QueryKeyAllowedUsage(key_id, key_usage);
return session->QueryKeyAllowedUsage(key_id, key_usage);
}
CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
@@ -652,10 +704,16 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
return INVALID_PARAMETERS_ENG_7;
}
key_usage->Clear();
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
session_sts = iter->second->QueryKeyAllowedUsage(key_id,
&found_in_this_session);
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
session_sts = (*iter)->QueryKeyAllowedUsage(key_id, &found_in_this_session);
if (session_sts == NO_ERROR) {
if (found) {
// Found another key. If usage settings do not match, fail.
@@ -680,13 +738,13 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id,
CdmResponseType CdmEngine::QueryOemCryptoSessionId(
const CdmSessionId& session_id, CdmQueryMap* query_response) {
LOGI("CdmEngine::QueryOemCryptoSessionId");
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::QueryOemCryptoSessionId: session_id not found = %s",
session_id.c_str());
return SESSION_NOT_FOUND_10;
}
return iter->second->QueryOemCryptoSessionId(query_response);
return session->QueryOemCryptoSessionId(query_response);
}
/*
@@ -835,19 +893,73 @@ CdmResponseType CdmEngine::ListStoredLicenses(
CdmSecurityLevel security_level, std::vector<std::string>* key_set_ids) {
DeviceFiles handle(file_system_);
if (!key_set_ids) {
LOGE("CdmEngine::QueryStoredLicenses: no response destination");
LOGE("CdmEngine::ListStoredLicenses: no response destination");
return INVALID_PARAMETERS_ENG_17;
}
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ListStoredLicenses: unable to initialize device files");
return STORE_LICENSE_ERROR_3;
return LIST_LICENSE_ERROR_1;
}
if (!handle.ListLicenses(key_set_ids)) {
return UNKNOWN_ERROR;
LOGE("CdmEngine::ListStoredLicenses: ListLicenses call failed");
return LIST_LICENSE_ERROR_2;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::ListUsageRecords(const std::string& app_id,
CdmSecurityLevel security_level,
std::vector<std::string>* ksids) {
DeviceFiles handle(file_system_);
if (!ksids) {
LOGE("CdmEngine::ListUsageRecords: no response destination");
return INVALID_PARAMETERS_ENG_18;
}
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ListUsageRecords: unable to initialize device files");
return LIST_USAGE_ERROR_1;
}
if (!handle.ListUsageRecords(app_id, ksids)) {
LOGE("CdmEngine::ListUsageRecords: ListUsageRecords call failed");
return LIST_USAGE_ERROR_2;
}
return NO_ERROR;
}
CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
CdmSecurityLevel security_level,
const std::string& key_set_id) {
std::string provider_session_token;
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::DeleteUsageRecord: unable to initialize device files");
return DELETE_USAGE_ERROR_1;
}
if (!handle.GetProviderToken(app_id, key_set_id, &provider_session_token)) {
LOGE("CdmEngine::DeleteUsageRecord: GetProviderToken failed");
return DELETE_USAGE_ERROR_2;
}
// Got provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
status = crypto_session->DeleteUsageInformation(provider_session_token);
}
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageRecord: OEMCrypto failure");
}
// Remove from file system.
if (!handle.DeleteUsageInfo(app_id, provider_session_token)) {
LOGE("CdmEngine::DeleteUsageRecord: file system failure");
return DELETE_USAGE_ERROR_3;
}
return status;
}
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info) {
@@ -915,6 +1027,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
usage_info->clear();
return status;
}
return KEY_MESSAGE;
}
@@ -930,7 +1043,9 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
do {
status = GetUsageInfo(app_id, security_level, usage_info);
if (KEY_MESSAGE == status && !usage_info->empty()) return status;
if (KEY_MESSAGE == status && !usage_info->empty()) {
return status;
}
} while (KEY_CANCELED == status);
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
@@ -1015,6 +1130,37 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
return status;
}
CdmResponseType CdmEngine::ReleaseAllUsageInfo(
const std::string& app_id, CdmSecurityLevel security_level) {
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: unable to initialize device files");
return RELEASE_ALL_USAGE_INFO_ERROR_3;
}
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete usage records");
return RELEASE_ALL_USAGE_INFO_ERROR_4;
}
if (provider_session_tokens.size() == 0UL) {
return NO_ERROR;
}
// Got at least one provider token. Remove from OEMCrypto.
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
status = crypto_session->
DeleteMultipleUsageInformation(provider_session_tokens);
}
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageRecord: CryptoSession failure");
}
return status;
}
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {
if (NULL == usage_property_set_.get()) {
usage_property_set_.reset(new UsagePropertySet());
@@ -1081,8 +1227,8 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
return EMPTY_KEYSET_ID_ENG_5;
}
CdmSessionMap::iterator iter = sessions_.find(key_set_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(key_set_id, session)) {
LOGE("CdmEngine::LoadUsageSession: session_id not found = %s ",
key_set_id.c_str());
return SESSION_NOT_FOUND_11;
@@ -1094,13 +1240,13 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
}
DeviceFiles handle(file_system_);
if (!handle.Init(iter->second->GetSecurityLevel())) {
if (!handle.Init(session->GetSecurityLevel())) {
LOGE("CdmEngine::LoadUsageSession: unable to initialize device files");
return LOAD_USAGE_INFO_FILE_ERROR;
}
std::string app_id;
iter->second->GetApplicationId(&app_id);
session->GetApplicationId(&app_id);
CdmKeyMessage key_message;
CdmKeyResponse key_response;
@@ -1110,22 +1256,22 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
return LOAD_USAGE_INFO_MISSING;
}
CdmResponseType status =
iter->second->RestoreUsageSession(key_message, key_response);
CdmResponseType status = session->RestoreUsageSession(key_message,
key_response);
if (KEY_ADDED != status) {
LOGE("CdmEngine::LoadUsageSession: usage session error %ld", status);
return status;
}
CdmKeyRequest request;
status = iter->second->GenerateReleaseRequest(&request);
status = session->GenerateReleaseRequest(&request);
*release_message = request.message;
switch (status) {
case KEY_MESSAGE:
break;
case KEY_CANCELED: // usage information not present in
iter->second->DeleteLicense(); // OEMCrypto, delete and try again
session->DeleteLicense(); // OEMCrypto, delete and try again
break;
default:
LOGE("CdmEngine::LoadUsageSession: generate release request error: %d",
@@ -1161,45 +1307,55 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
// else we must be level 1 direct and we don't need to return a buffer.
}
CdmSessionMap::iterator session_iter = sessions_.end();
std::shared_ptr<CdmSession> session;
if (session_id.empty()) {
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
// Loop through the sessions to find the session containing the key_id
// with the longest remaining license validity.
int64_t seconds_remaining = 0;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyLoaded(*parameters.key_id)) {
int64_t duration = iter->second->GetDurationRemaining();
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
if ((*iter)->IsKeyLoaded(*parameters.key_id)) {
int64_t duration = (*iter)->GetDurationRemaining();
if (duration > seconds_remaining) {
session_iter = iter;
session = *iter;
seconds_remaining = duration;
}
}
}
} else {
session_iter = sessions_.find(session_id);
session_map_.FindSession(session_id, session);
}
if (session_iter == sessions_.end()) {
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.c_str(), session_id.size());
if (session.get() == NULL) {
if (session_id.empty()) {
LOGE("CdmEngine::Decrypt: session not found: Empty session ID");
} else {
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.c_str(), session_id.size());
}
return SESSION_NOT_FOUND_FOR_DECRYPT;
}
return session_iter->second->Decrypt(parameters);
return session->Decrypt(parameters);
}
CdmResponseType CdmEngine::GenericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm, std::string* out_buffer) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericEncrypt: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_13;
}
return iter->second->GenericEncrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
return session->GenericEncrypt(in_buffer, key_id, iv, algorithm, out_buffer);
}
CdmResponseType CdmEngine::GenericDecrypt(
@@ -1207,46 +1363,51 @@ CdmResponseType CdmEngine::GenericDecrypt(
const std::string& key_id, const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericDecrypt: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_14;
}
return iter->second->GenericDecrypt(in_buffer, key_id, iv, algorithm,
out_buffer);
return session->GenericDecrypt(in_buffer, key_id, iv, algorithm, out_buffer);
}
CdmResponseType CdmEngine::GenericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
std::string* signature) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericSign: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_15;
}
return iter->second->GenericSign(message, key_id, algorithm, signature);
return session->GenericSign(message, key_id, algorithm, signature);
}
CdmResponseType CdmEngine::GenericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, CdmSigningAlgorithm algorithm,
const std::string& signature) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
std::shared_ptr<CdmSession> session;
if (!session_map_.FindSession(session_id, session)) {
LOGE("CdmEngine::GenericVerify: session_id not found = %s ",
session_id.c_str());
return SESSION_NOT_FOUND_16;
}
return iter->second->GenericVerify(message, key_id, algorithm, signature);
return session->GenericVerify(message, key_id, algorithm, signature);
}
// TODO(gmorgan) Used? Delete if unused.
bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyLoaded(key_id)) {
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
if ((*iter)->IsKeyLoaded(key_id)) {
return true;
}
}
@@ -1260,18 +1421,23 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
return false;
}
uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id);
CdmSessionMap::iterator session_iter = sessions_.end();
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
CdmSessionList::iterator session_iter = sessions.end();
int64_t seconds_remaining = 0;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
CdmSessionId local_session_id = iter->second->session_id();
if (Properties::GetSessionSharingId(local_session_id) ==
session_sharing_id) {
if (iter->second->IsKeyLoaded(key_id)) {
int64_t duration = iter->second->GetDurationRemaining();
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
CdmSessionId id = (*iter)->session_id();
if (Properties::GetSessionSharingId(id) == session_sharing_id) {
if ((*iter)->IsKeyLoaded(key_id)) {
int64_t duration = (*iter)->GetDurationRemaining();
if (duration > seconds_remaining) {
session_iter = iter;
seconds_remaining = duration;
@@ -1280,8 +1446,8 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
}
}
if (session_iter != sessions_.end()) {
*session_id = session_iter->second->session_id();
if (session_iter != sessions.end()) {
*session_id = (*session_iter)->session_id();
return true;
}
return false;
@@ -1289,9 +1455,9 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
uint32_t height) {
CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter != sessions_.end()) {
iter->second->NotifyResolution(width, height);
std::shared_ptr<CdmSession> session;
if (session_map_.FindSession(session_id, session)) {
session->NotifyResolution(width, height);
return true;
}
return false;
@@ -1304,8 +1470,11 @@ bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
void CdmEngine::OnTimerEvent() {
Clock clock;
uint64_t current_time = clock.GetCurrentTime();
bool usage_update_period_expired = false;
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
bool usage_update_period_expired = false;
if (current_time - last_usage_information_update_time_ >
kUpdateUsageInformationPeriod) {
usage_update_period_expired = true;
@@ -1315,27 +1484,35 @@ void CdmEngine::OnTimerEvent() {
bool is_initial_usage_update = false;
bool is_usage_update_needed = false;
AutoLock lock(session_list_lock_);
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
while (!sessions.empty()) {
is_initial_usage_update =
is_initial_usage_update || iter->second->is_initial_usage_update();
is_initial_usage_update || sessions.front()->is_initial_usage_update();
is_usage_update_needed =
is_usage_update_needed || iter->second->is_usage_update_needed();
is_usage_update_needed || sessions.front()->is_usage_update_needed();
iter->second->OnTimerEvent(usage_update_period_expired);
if (!sessions.front()->IsClosed()) {
sessions.front()->OnTimerEvent(usage_update_period_expired);
}
sessions.pop_front();
}
if (is_usage_update_needed &&
(usage_update_period_expired || is_initial_usage_update)) {
bool has_usage_been_updated = false;
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->reset_usage_flags();
// Session list may have changed. Rebuild.
session_map_.GetSessionList(sessions);
for (CdmSessionList::iterator iter = sessions.begin();
iter != sessions.end(); ++iter) {
if ((*iter)->IsClosed()) {
continue;
}
(*iter)->reset_usage_flags();
if (!has_usage_been_updated) {
// usage is updated for all sessions so this needs to be
// called only once per update usage information period
CdmResponseType status = iter->second->UpdateUsageInformation();
CdmResponseType status = (*iter)->UpdateUsageInformation();
if (NO_ERROR != status) {
LOGW("Update usage information failed: %d", status);
} else {
@@ -1344,15 +1521,18 @@ void CdmEngine::OnTimerEvent() {
}
}
}
CloseExpiredReleaseSessions();
}
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
AutoLock lock(session_list_lock_);
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnKeyReleaseEvent(key_set_id);
CdmSessionList sessions;
session_map_.GetSessionList(sessions);
while (!sessions.empty()) {
if (!sessions.front()->IsClosed()) {
sessions.front()->OnKeyReleaseEvent(key_set_id);
}
sessions.pop_front();
}
}
@@ -1415,12 +1595,12 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
status = crypto_session->DeleteAllUsageReports();
if (NO_ERROR != status) {
LOGW(
"CdmEngine::GetProvisioningRequest: "
"CdmEngine::DeleteAllUsageReportsUponFactoryReset: "
"Fails to delete usage reports: %d", status);
}
} else {
LOGW(
"CdmEngine::GetProvisioningRequest: "
"CdmEngine::DeleteAllUsageReportsUponFactoryReset: "
"Fails to open crypto session: error=%d.\n"
"Usage reports are not removed after factory reset.", status);
}

View File

@@ -25,6 +25,7 @@ namespace wvcdm {
CdmSession::CdmSession(FileSystem* file_system) :
initialized_(false),
closed_(false),
crypto_session_(new CryptoSession),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
@@ -101,9 +102,8 @@ CdmResponseType CdmSession::Init(
// License server client ID token is a stored certificate. Stage it or
// indicate that provisioning is needed. Get token from stored certificate
std::string wrapped_key;
uint32_t system_id;
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
&serial_number, &system_id) ||
&serial_number, nullptr) ||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
return NEED_PROVISIONING;
}
@@ -243,26 +243,6 @@ CdmResponseType CdmSession::GenerateKeyRequest(
case kLicenseTypeRelease:
is_release_ = true;
break;
case kLicenseTypeDeferred:
// If you're going to pass Deferred, you must have empty init data in
// this call and stored init data from the previous call.
if (!init_data.IsEmpty() || !license_parser_->HasInitData()) {
return INVALID_LICENSE_TYPE;
}
// The arguments check out.
// The is_release_ and is_offline_ flags were already set last time based
// on the original license type. Do not change them, and use them to
// re-derive the original license type.
if (is_release_) {
license_type = kLicenseTypeRelease;
} else if (is_offline_) {
license_type = kLicenseTypeOffline;
} else if (is_temporary_) {
license_type = kLicenseTypeTemporary;
} else {
license_type = kLicenseTypeStreaming;
}
break;
default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type);
@@ -276,16 +256,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
} else {
key_request->type = kKeyRequestTypeInitial;
if (!license_parser_->HasInitData()) {
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA;
}
if (init_data.IsEmpty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return INIT_DATA_NOT_FOUND;
}
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA;
}
if (init_data.IsEmpty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return INIT_DATA_NOT_FOUND;
}
if (is_offline_ && key_set_id_.empty()) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
@@ -297,16 +275,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
init_data, license_type,
app_parameters, &key_request->message,
&key_request->url);
if (KEY_MESSAGE != status) return status;
if (status != KEY_MESSAGE)
return status;
key_request_ = key_request->message;
if (is_offline_) {
offline_init_data_ = init_data.data();
offline_release_server_url_ = key_request->url;
}
return KEY_MESSAGE;
}
}
@@ -336,6 +312,10 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
license_received_ = true;
key_response_ = key_response;
LOGV("AddKey: provider_session_token (size=%d) =%s",
license_parser_->provider_session_token().size(),
license_parser_->provider_session_token().c_str());
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
sts = StoreLicense();
if (sts != NO_ERROR) return sts;
@@ -423,6 +403,10 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// is updated, so we treat this Decrypt call as invalid.
if (params.is_encrypted &&
!policy_engine_->CanDecryptContent(*params.key_id)) {
if (policy_engine_->GetKeyStatus(*params.key_id) ==
kKeyStatusOutputNotAllowed) {
return INSUFFICIENT_OUTPUT_PROTECTION;
}
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
}

View File

@@ -0,0 +1,71 @@
// Copyright 2017 Google Inc. All Rights Reserved.
#include "cdm_session_map.h"
#include <assert.h>
#include "cdm_session.h"
#include "log.h"
namespace wvcdm {
CdmSessionMap::~CdmSessionMap() {
AutoLock lock(lock_);
for (CdmIdToSessionMap::iterator i = sessions_.begin();
i != sessions_.end(); ++i) {
i->second->Close();
i->second.reset();
}
sessions_.clear();
}
void CdmSessionMap::Add(const std::string& id, CdmSession* session) {
AutoLock lock(lock_);
sessions_[id].reset(session);
}
bool CdmSessionMap::CloseSession(const std::string& id) {
AutoLock lock(lock_);
std::shared_ptr<CdmSession> session;
if (!FindSessionNoLock(id, session)) {
return false;
}
session->Close();
sessions_.erase(id);
return true;
}
bool CdmSessionMap::Exists(const std::string& id) {
AutoLock lock(lock_);
return sessions_.find(id) != sessions_.end();
}
bool CdmSessionMap::FindSession(const CdmSessionId& id,
std::shared_ptr<CdmSession>& session) {
AutoLock lock(lock_);
return FindSessionNoLock(id, session);
}
bool CdmSessionMap::FindSessionNoLock(const CdmSessionId& session_id,
std::shared_ptr<CdmSession>& session) {
CdmIdToSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
session = iter->second;
assert(session.get() != NULL);
return true;
}
typedef std::list<std::shared_ptr<CdmSession> > CdmSessionList;
void CdmSessionMap::GetSessionList(CdmSessionList& sessions) {
sessions.clear();
AutoLock lock(lock_);
for (CdmIdToSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
sessions.push_back(iter->second);
}
}
} // namespace wvcdm

View File

@@ -19,37 +19,64 @@ 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.
@@ -88,7 +115,7 @@ bool CertificateProvisioning::SetSpoidParameter(
// Use the SPOID that has been pre-provided
request->set_spoid(spoid);
} else if (Properties::UseProviderIdInProvisioningRequest()) {
if (service_certificate_->HasProviderId()) {
if (!service_certificate_->provider_id().empty()) {
request->set_provider_id(service_certificate_->provider_id());
} else {
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
@@ -121,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.
*/
@@ -162,17 +189,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
client_id->set_token(token);
client_id->set_type(token_type);
#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification.
if (service_certificate_->HasCertificate()) {
#if 0 // TODO(gmorgan) Encrypt ClientIdentification. Pending Design.
if (service_certificate_->has_certificate()) {
EncryptedClientIdentification* encrypted_client_id =
provisioning_request->mutable_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();
provisioning_request.clear_client_id();
} else {
provisioning_request->clear_encrypted_client_id();
provisioning_request.clear_encrypted_client_id();
}
return status;
}
@@ -234,43 +261,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
// Converts request into JSON string
ComposeJsonRequestAsQueryString(serialized_request, request);
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
// Return request as web-safe base64 string
std::vector<uint8_t> request_vector(serialized_request.begin(),
serialized_request.end());
request->assign(Base64SafeEncodeNoPad(request_vector));
} else {
request->swap(serialized_request);
}
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
@@ -281,13 +282,19 @@ 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;
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
// The response is base64 encoded in a JSON wrapper.
// Extract it and decode it. If errors, return an empty string.
ExtractAndDecodeSignedMessage(response, &raw_string);
} else {
raw_string.assign(response);
}
if (raw_string.empty()) {
LOGE("HandleProvisioningResponse: response message is empty or "
"an invalid JSON/base64 string.");
return CERT_PROVISIONING_RESPONSE_ERROR_1;
}
@@ -295,7 +302,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;
}
@@ -314,6 +321,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)) {
@@ -326,15 +334,29 @@ 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)
!= NO_ERROR) {
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& signature = signed_response.signature();
std::string wrapped_rsa_key;
if (!crypto_session_.RewrapDeviceRSAKey(signed_message, signature, nonce,
enc_rsa_key, rsa_key_iv,
&wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
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();
std::string wrapped_private_key;
if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce,
new_private_key, iv, wrapping_key,
&wrapped_private_key)) {
LOGE("HandleProvisioningResponse: RewrapCertificate fails");
return CERT_PROVISIONING_RESPONSE_ERROR_6;
}
@@ -342,7 +364,7 @@ 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;
}
@@ -357,7 +379,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;
}

View File

@@ -54,17 +54,17 @@ CryptoSession::~CryptoSession() {
Terminate();
}
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType& token_type) {
OEMCrypto_ProvisioningMethod method;
switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) {
case OEMCrypto_OEMCertificate:
*token_type = kClientTokenOemCert;
token_type = kClientTokenOemCert;
break;
case OEMCrypto_Keybox:
*token_type = kClientTokenKeybox;
token_type = kClientTokenKeybox;
break;
case OEMCrypto_DrmCertificate:
*token_type = kClientTokenDrmCert;
token_type = kClientTokenDrmCert;
break;
case OEMCrypto_ProvisioningError:
default:
@@ -86,7 +86,7 @@ void CryptoSession::Init() {
}
initialized_ = true;
}
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
if (!GetProvisioningMethod(pre_provision_token_type_)) {
initialized_ = false;
}
}
@@ -125,6 +125,10 @@ bool CryptoSession::GetTokenFromKeybox(std::string* token) {
bool CryptoSession::GetTokenFromOemCert(std::string* token) {
OEMCryptoResult status;
if (!oem_token_.empty()) {
token->assign(oem_token_);
return true;
}
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
// lock is held by caller
bool retrying = false;
@@ -133,6 +137,8 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) {
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
if (OEMCrypto_SUCCESS == status) {
temp_buffer.resize(buf_size);
oem_token_.assign(temp_buffer);
token->swap(temp_buffer);
return true;
}
@@ -387,7 +393,8 @@ bool CryptoSession::PrepareRequest(const std::string& message,
return false;
}
if (!Properties::use_certificates_as_identification() || is_provisioning) {
if (!Properties::use_certificates_as_identification() ||
(is_provisioning && (pre_provision_token_type_ == kClientTokenKeybox))) {
if (!GenerateDerivedKeys(message)) return false;
if (!GenerateSignature(message, signature)) return false;
@@ -535,8 +542,16 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) {
LOGV("CryptoSession::LoadCertificatePrivateKey: Lock");
AutoLock auto_lock(crypto_lock_);
// Call OEMCrypto_GetOEMPublicCertificate before OEMCrypto_LoadDeviceRSAKey
// so it caches the OEMCrypto Public Key and then throw away result
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
size_t buf_size = temp_buffer.size();
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
OEMCryptoResult sts =
OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_);
OEMCryptoResult sts = OEMCrypto_LoadDeviceRSAKey(
sts = OEMCrypto_LoadDeviceRSAKey(
oec_session_id_, reinterpret_cast<const uint8_t*>(wrapped_key.data()),
wrapped_key.size());
@@ -1089,6 +1104,34 @@ bool CryptoSession::SetDestinationBufferType() {
return true;
}
bool CryptoSession::RewrapCertificate(const std::string& signed_message,
const std::string& signature,
const std::string& nonce,
const std::string& private_key,
const std::string& iv,
const std::string& wrapping_key,
std::string* wrapped_private_key) {
LOGV("CryptoSession::RewrapCertificate, session id=%ld",
static_cast<uint32_t>(oec_session_id_));
if (pre_provision_token_type_ == kClientTokenKeybox) {
return RewrapDeviceRSAKey(signed_message, signature, nonce, private_key,
iv, wrapped_private_key);
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
return RewrapDeviceRSAKey30(signed_message, nonce, private_key, iv,
wrapping_key, wrapped_private_key);
} else {
LOGE("CryptoSession::RewrapCertificate, Bad pre-provision type=%d: "
"session id=%ld", pre_provision_token_type_,
static_cast<uint32_t>(oec_session_id_));
return false;
}
}
bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
const std::string& signature,
const std::string& nonce,
@@ -1140,6 +1183,57 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
return true;
}
bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message,
const std::string& nonce,
const std::string& private_key,
const std::string& iv,
const std::string& wrapping_key,
std::string* wrapped_private_key) {
LOGV("CryptoSession::RewrapDeviceRSAKey30, session id=%ld",
static_cast<uint32_t>(oec_session_id_));
const uint8_t* signed_msg = reinterpret_cast<const uint8_t*>(message.data());
const uint8_t* msg_private_key = NULL;
const uint8_t* msg_iv = NULL;
const uint32_t* msg_nonce = NULL;
const uint8_t* msg_wrapping_key = NULL;
if (private_key.size() >= MAC_KEY_SIZE && iv.size() >= KEY_IV_SIZE) {
msg_private_key = signed_msg + GetOffset(message, private_key);
msg_iv = signed_msg + GetOffset(message, iv);
msg_nonce = reinterpret_cast<const uint32_t*>(signed_msg +
GetOffset(message, nonce));
msg_wrapping_key = signed_msg + GetOffset(message, wrapping_key);
}
// Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key
// and 0 as wrapped_rsa_key_length.
size_t wrapped_private_key_length = 0;
OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv, NULL,
&wrapped_private_key_length);
if (status != OEMCrypto_ERROR_SHORT_BUFFER) {
LOGE("OEMCrypto_RewrapDeviceRSAKey30 failed getting wrapped key length");
return false;
}
wrapped_private_key->resize(wrapped_private_key_length);
status = OEMCrypto_RewrapDeviceRSAKey30(
oec_session_id_, msg_nonce, msg_wrapping_key, wrapping_key.size(),
msg_private_key, private_key.size(), msg_iv,
reinterpret_cast<uint8_t*>(&(*wrapped_private_key)[0]),
&wrapped_private_key_length);
wrapped_private_key->resize(wrapped_private_key_length);
if (OEMCrypto_SUCCESS != status) {
LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status);
return false;
}
return true;
}
bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current,
HdcpCapability* max) {
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);

View File

@@ -143,52 +143,43 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
}
DeviceCertificate device_certificate = file.device_certificate();
ExtractDeviceInfo(device_certificate.certificate(), serial_number, system_id);
*certificate = device_certificate.certificate();
*wrapped_private_key = device_certificate.wrapped_private_key();
return true;
return ExtractDeviceInfo(device_certificate.certificate(), serial_number,
system_id);
}
bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
std::string* serial_number,
uint32_t* system_id) {
LOGI("[WEM] ExtractDeviceInfo");
LOGI("ExtractDeviceInfo Entry");
if (!serial_number && !system_id) {
LOGE("Invalid paramters to DeviceFiles::ExtractDeviceInfo");
return false;
}
// Get serial number and system ID from certificate
if ((serial_number != NULL) || (system_id != NULL)) {
SignedDrmDeviceCertificate signed_drm_device_certificate;
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
!signed_drm_device_certificate.has_drm_certificate()) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing signed drm device "
"certificate.");
return false;
}
DrmDeviceCertificate drm_device_certificate;
if (!drm_device_certificate.ParseFromString(
signed_drm_device_certificate.drm_certificate()) ||
(drm_device_certificate.type() !=
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing drm device "
"certificate message.");
return false;
}
if (serial_number != NULL) {
if (drm_device_certificate.has_serial_number()) {
LOGI("DeviceFiles::ExtractDeviceInfo: serial number: [%s]",
(b2a_hex(drm_device_certificate.serial_number())).c_str());
*serial_number = drm_device_certificate.serial_number();
}
}
if (system_id != NULL) {
if (drm_device_certificate.has_system_id()) {
LOGI("DeviceFiles::ExtractDeviceInfo: system id: [%d]",
drm_device_certificate.system_id());
*system_id = drm_device_certificate.system_id();
} else {
*system_id = 0;
}
}
SignedDrmDeviceCertificate signed_drm_device_certificate;
if (!signed_drm_device_certificate.ParseFromString(device_certificate) ||
!signed_drm_device_certificate.has_drm_certificate()) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing signed drm device "
"certificate.");
return false;
}
DrmDeviceCertificate drm_device_certificate;
if (!drm_device_certificate.ParseFromString(
signed_drm_device_certificate.drm_certificate()) ||
(drm_device_certificate.type() !=
video_widevine::DrmDeviceCertificate::DRM_USER_DEVICE)) {
LOGE("DeviceFiles::ExtractDeviceInfo: fails parsing drm device "
"certificate message.");
return false;
}
if (serial_number != NULL) {
*serial_number = drm_device_certificate.serial_number();
}
if (system_id != NULL) {
*system_id = drm_device_certificate.system_id();
}
return true;
}
@@ -341,7 +332,7 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
LOGW("DeviceFiles::ListLicenses: not initialized");
return false;
}
@@ -354,7 +345,7 @@ bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
// Scan list of returned filenames, remove extension, and return
// as a list of key_set_ids.
key_set_ids->clear();
for (int i = 0; i < filenames.size(); i++) {
for (size_t i = 0; i < filenames.size(); i++) {
std::string* name = &filenames[i];
std::size_t pos = name->find(kLicenseFileNameExt);
if (pos == std::string::npos) {
@@ -449,6 +440,77 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
return StoreFileWithHash(file_name, serialized_file);
}
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::GetProviderToken(const std::string& app_id,
const std::string& key_set_id,
std::string* provider_session_token) {
if (!initialized_) {
LOGW("DeviceFiles::GetProviderToken: not initialized");
return false;
}
if (provider_session_token == NULL) {
LOGW("DeviceFiles::GetProviderToken: NULL return argument pointer");
return false;
}
std::string file_name = GetUsageInfoFileName(app_id);
if (!FileExists(file_name) || GetFileSize(file_name) == 0) {
LOGW("DeviceFiles::GetProviderToken: empty file");
return false;
}
video_widevine_client::sdk::File file;
if (!RetrieveHashedFile(file_name, &file)) {
LOGW("DeviceFiles::GetProviderToken: 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_) {

View File

@@ -19,16 +19,16 @@
#include "wv_cdm_constants.h"
namespace {
std::string kCompanyNameKey = "company_name";
std::string kModelNameKey = "model_name";
std::string kArchitectureNameKey = "architecture_name";
std::string kDeviceNameKey = "device_name";
std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
std::string kWVCdmVersionKey = "widevine_cdm_version";
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
} // namespace
const std::string kCompanyNameKey = "company_name";
const std::string kModelNameKey = "model_name";
const std::string kArchitectureNameKey = "architecture_name";
const std::string kDeviceNameKey = "device_name";
const std::string kProductNameKey = "product_name";
const std::string kBuildInfoKey = "build_info";
const std::string kDeviceIdKey = "device_id";
const std::string kWVCdmVersionKey = "widevine_cdm_version";
const std::string kOemCryptoSecurityPatchLevelKey =
"oem_crypto_security_patch_level";
const uint32_t kFourCcCbc1 = 0x63626331;
const uint32_t kFourCcCbcs = 0x63626373;
@@ -37,6 +37,8 @@ const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
const uint32_t kFourCcCenc = 0x63656e63;
const uint32_t kFourCcCens = 0x63656e73;
} // namespace
namespace wvcdm {
// Protobuf generated classes.
@@ -144,7 +146,7 @@ CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(
ServiceCertificate* service_certificate, const std::string& client_token,
CdmClientTokenType client_token_type, const std::string& serial_number,
CdmClientTokenType client_token_type, const std::string& device_id,
CryptoSession* session, PolicyEngine* policy_engine) {
if (clock_.get() == NULL) {
LOGE("CdmLicense::Init: clock parameter not provided");
@@ -170,7 +172,7 @@ bool CdmLicense::Init(
service_certificate_ = service_certificate;
client_token_ = client_token;
client_token_type_ = client_token_type;
serial_number_ = serial_number;
device_id_ = device_id;
crypto_session_ = session;
policy_engine_ = policy_engine;
initialized_ = true;
@@ -185,12 +187,6 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
LOGE("CdmLicense::PrepareKeyRequest: not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_4;
}
if (init_data.IsEmpty() && stored_init_data_.get()) {
InitializationData restored_init_data = *stored_init_data_;
stored_init_data_.reset();
return PrepareKeyRequest(restored_init_data, license_type, app_parameters,
signed_request, server_url);
}
if (!init_data.is_supported()) {
LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
@@ -209,16 +205,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
return INVALID_PARAMETERS_LIC_7;
}
// If privacy mode and no service certificate, initiate a
// service certificate request.
// If privacy mode, must have service certificate
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->HasCertificate()) {
stored_init_data_.reset(new InitializationData(init_data));
*server_url = server_url_;
if (service_certificate_->PrepareRequest(signed_request)) {
return KEY_MESSAGE;
}
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
!service_certificate_->has_certificate()) {
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_1;
}
std::string request_id;
@@ -303,12 +295,10 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
if (renew_with_client_id_) {
if (Properties::UsePrivacyMode(session_id_) &&
!service_certificate_->HasCertificate()) {
*server_url = server_url_;
if (service_certificate_->PrepareRequest(signed_request)) {
return KEY_MESSAGE;
}
return LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
!service_certificate_->has_certificate()) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - "
"no service certificate.");
return PRIVACY_MODE_ERROR_2;
}
}
@@ -423,14 +413,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
switch (signed_response.type()) {
case SignedMessage::LICENSE:
break;
case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status;
status = service_certificate_->HandleResponse(signed_response.msg());
if (status != NO_ERROR) {
return status;
}
return NEED_KEY;
}
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
@@ -494,8 +476,13 @@ CdmResponseType CdmLicense::HandleKeyResponse(
license.policy().can_persist())
is_offline_ = true;
if (license.id().has_provider_session_token())
LOGV("Get Provider_session_token:");
if (license.id().has_provider_session_token()) {
provider_session_token_ = license.id().provider_session_token();
LOGV("Provider_session_token=%s", provider_session_token_.c_str());
} else {
LOGV("NO Provider_session_token");
}
if (license.policy().has_renewal_server_url()) {
server_url_ = license.policy().renewal_server_url();
@@ -540,14 +527,6 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
switch (signed_response.type()) {
case SignedMessage::LICENSE:
break;
case SignedMessage::SERVICE_CERTIFICATE: {
CdmResponseType status;
status = service_certificate_->HandleResponse(signed_response.msg());
if (status != NO_ERROR) {
return status;
}
return NEED_KEY;
}
case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response);
default:
@@ -820,6 +799,7 @@ bool CdmLicense::GetClientTokenType(
case kClientTokenOemCert:
default:
// shouldn't happen
LOGE("GetClientTokenType: BAD TOKEN TYPE");
return false;
}
}
@@ -873,10 +853,10 @@ CdmResponseType CdmLicense::PrepareClientId(
client_info->set_name(kBuildInfoKey);
client_info->set_value(value);
}
if (!serial_number_.empty()) {
if (!device_id_.empty()) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
client_info->set_value(b2a_hex(serial_number_));
client_info->set_value(b2a_hex(device_id_));
} else if (crypto_session_->GetDeviceUniqueId(&value)) {
client_info = client_id->add_client_info();
client_info->set_name(kDeviceIdKey);
@@ -950,9 +930,9 @@ CdmResponseType CdmLicense::PrepareClientId(
}
if (Properties::UsePrivacyMode(session_id_)) {
if (!service_certificate_->HasCertificate()) {
if (service_certificate_->certificate().empty()) {
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
return PRIVACY_MODE_ERROR_3;
}
EncryptedClientIdentification* encrypted_client_id =
license_request->mutable_encrypted_client_id();
@@ -983,7 +963,7 @@ CdmResponseType CdmLicense::PrepareContentId(
if (!init_data.IsEmpty()) {
cenc_content_id->add_pssh(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
LOGE("CdmLicense::PrepareContentId: ISO-CENC init data not available");
return CENC_INIT_DATA_UNAVAILABLE;
}
@@ -997,7 +977,7 @@ CdmResponseType CdmLicense::PrepareContentId(
if (!init_data.IsEmpty()) {
webm_content_id->set_header(init_data.data());
} else {
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
LOGE("CdmLicense::PrepareContentId: WebM init data not available");
return WEBM_INIT_DATA_UNAVAILABLE;
}
@@ -1005,7 +985,7 @@ CdmResponseType CdmLicense::PrepareContentId(
return PREPARE_WEBM_CONTENT_ID_FAILED;
}
} else {
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
LOGE("CdmLicense::PrepareContentId: no support for init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA_FORMAT;
}

View File

@@ -5,6 +5,7 @@
#include <string>
#include "log.h"
#include "wv_cdm_constants.h"
namespace {
// License protocol aliases
@@ -102,7 +103,15 @@ bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status,
return keys_changed;
}
CdmKeyStatus LicenseKeys::GetKeyStatus(const KeyId& key_id) {
if (keys_.count(key_id) == 0) {
return kKeyStatusKeyUnknown;
}
return keys_[key_id]->GetKeyStatus();
}
void LicenseKeys::ExtractKeyStatuses(CdmKeyStatusMap* content_keys) {
content_keys->clear();
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
if (it->second->IsContentKey()) {
const KeyId key_id = it->first;
@@ -251,11 +260,11 @@ bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
// requirement, use the key's default HDCP setting to check against the
// device's current HDCP level.
void LicenseKeyStatus::ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
uint32_t video_pixels, CryptoSession::HdcpCapability new_hdcp_level) {
VideoResolutionConstraint* current_constraint = NULL;
if (HasConstraints()) {
current_constraint = GetConstraintForRes(new_resolution, constraints_);
if (HasConstraints() && video_pixels != HDCP_UNSPECIFIED_VIDEO_RESOLUTION) {
current_constraint = GetConstraintForRes(video_pixels, constraints_);
if (NULL == current_constraint) {
meets_constraints_ = false;
return;

View File

@@ -10,75 +10,73 @@
namespace wvcdm {
OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session,
SecurityLevel level) {
SecurityLevel) {
return ::OEMCrypto_OpenSession(session);
}
OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox,
size_t keyBoxLength,
SecurityLevel level) {
size_t keyBoxLength, SecurityLevel) {
return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength);
}
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel level) {
OEMCryptoResult OEMCrypto_IsKeyboxValid(SecurityLevel) {
return ::OEMCrypto_IsKeyboxValid();
}
OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID, size_t* idLength,
SecurityLevel level) {
SecurityLevel) {
return ::OEMCrypto_GetDeviceID(deviceID, idLength);
}
OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData, size_t* keyDataLength,
SecurityLevel level) {
SecurityLevel) {
return ::OEMCrypto_GetKeyData(keyData, keyDataLength);
}
uint32_t OEMCrypto_APIVersion(SecurityLevel level) {
uint32_t OEMCrypto_APIVersion(SecurityLevel) {
return ::OEMCrypto_APIVersion();
}
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level) {
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel) {
return ::OEMCrypto_Security_Patch_Level();
}
const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
const char* OEMCrypto_SecurityLevel(SecurityLevel) {
return ::OEMCrypto_SecurityLevel();
}
OEMCryptoResult OEMCrypto_GetHDCPCapability(
SecurityLevel level, OEMCrypto_HDCP_Capability* current,
SecurityLevel, OEMCrypto_HDCP_Capability* current,
OEMCrypto_HDCP_Capability* maximum) {
return ::OEMCrypto_GetHDCPCapability(current, maximum);
}
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
bool OEMCrypto_SupportsUsageTable(SecurityLevel) {
return ::OEMCrypto_SupportsUsageTable();
}
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel level) {
bool OEMCrypto_IsAntiRollbackHwPresent(SecurityLevel) {
return ::OEMCrypto_IsAntiRollbackHwPresent();
}
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel,
size_t* count) {
return ::OEMCrypto_GetNumberOfOpenSessions(count);
}
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel,
size_t* maximum) {
return ::OEMCrypto_GetMaxNumberOfSessions(maximum);
}
OEMCryptoResult OEMCrypto_CopyBuffer(
SecurityLevel level, const uint8_t* data_addr, size_t data_length,
SecurityLevel, const uint8_t* data_addr, size_t data_length,
OEMCrypto_DestBufferDesc* out_buffer, uint8_t subsample_flags) {
return ::OEMCrypto_CopyBuffer(data_addr, data_length, out_buffer,
subsample_flags);
}
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
SecurityLevel level) {
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(SecurityLevel) {
return ::OEMCrypto_GetProvisioningMethod();
}

View File

@@ -14,13 +14,6 @@
using video_widevine::License;
namespace {
const int64_t kHdcpCheckInterval = 10;
const uint32_t kNoResolution = 0;
} // namespace
namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
@@ -54,25 +47,36 @@ bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
}
}
CdmKeyStatus PolicyEngine::GetKeyStatus(const KeyId& key_id) {
return license_keys_->GetKeyStatus(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;
}
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
next_device_check_ = current_time + kHdcpCheckInterval;
}
}
@@ -94,14 +98,16 @@ void PolicyEngine::OnTimerEvent() {
}
// Check device conditions that affect playability (HDCP, resolution)
CheckDevice(current_time);
CheckDeviceHdcpStatusOnTimer(current_time);
bool renewal_needed = false;
// Test to determine if renewal should be attempted.
switch (license_state_) {
case kLicenseStateCanPlay: {
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
if (HasRenewalDelayExpired(current_time)) {
renewal_needed = true;
}
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break;
@@ -113,7 +119,9 @@ void PolicyEngine::OnTimerEvent() {
}
case kLicenseStateWaitingLicenseUpdate: {
if (HasRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
if (HasRenewalRetryIntervalExpired(current_time)) {
renewal_needed = true;
}
break;
}
@@ -420,9 +428,16 @@ bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
next_renewal_time_ <= current_time;
}
// Apply a key status to the current keys.
// If this represents a new key status, perform a notification callback.
// NOTE: if the new status is kKeyStatusUsable, the HDCP check may result in an
// override to kKeyStatusOutputNotAllowed.
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) {

View File

@@ -182,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;
}
@@ -199,7 +199,8 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message,
goto err;
}
if (EVP_PKEY_CTX_set_signature_md(pctx, EVP_sha1()) != 1) {
if (EVP_PKEY_CTX_set_signature_md(pctx,
const_cast<EVP_MD *>(EVP_sha1())) != 1) {
LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature");
goto err;
}

View File

@@ -13,6 +13,7 @@ bool Properties::oem_crypto_use_secure_buffers_;
bool Properties::oem_crypto_use_fifo_;
bool Properties::oem_crypto_use_userspace_buffers_;
bool Properties::use_certificates_as_identification_;
bool Properties::provisioning_messages_are_binary_;
bool Properties::security_level_path_backward_compatibility_support_;
scoped_ptr<CdmClientPropertySetMap> Properties::session_property_set_;

View File

@@ -11,41 +11,110 @@
namespace {
// Service certificate for Google/Widevine Provisioning and License servers.
const unsigned char kServiceCertificateCAPublicKey[] = {
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39,
0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd,
0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87,
0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6,
0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40,
0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98,
0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84,
0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab,
0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0x53, 0x3e, 0x47, 0x5f,
0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf,
0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, 0x45, 0xd6,
0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7,
0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50,
0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd,
0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0,
0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7,
0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b,
0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11,
0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8,
0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33,
0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0x60, 0x1a, 0x11, 0x3d,
0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b,
0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, 0xbe, 0x7f, 0xb0,
0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e, 0x69,
0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, 0x01,
0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24,
0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31,
0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5,
0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8,
0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d,
0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03,
0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f,
0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01,
0x00, 0x01};
static const unsigned char kRootCertForProd[] = {
0x0a, 0x9c, 0x03, 0x08, 0x00, 0x12, 0x01, 0x00,
0x18, 0xdd, 0x94, 0x88, 0x8b, 0x05, 0x22, 0x8e,
0x03, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01,
0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90,
0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68,
0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d,
0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc,
0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c,
0xf6, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3,
0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26,
0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21,
0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46,
0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb,
0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b,
0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48,
0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd,
0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c,
0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10,
0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19,
0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84,
0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26,
0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25,
0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1,
0xbd, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4,
0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17,
0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b,
0xb7, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba,
0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84,
0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c,
0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca,
0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6,
0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45,
0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba,
0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2,
0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77,
0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19,
0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc,
0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e,
0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed,
0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56,
0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3,
0x24, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c,
0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c,
0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff,
0xa5, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4,
0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58,
0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41,
0x3d, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4,
0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12,
0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55,
0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33,
0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, 0x12,
0x80, 0x03, 0x58, 0xf1, 0xd6, 0x4d, 0x04, 0x09,
0x7b, 0xdf, 0xd7, 0xef, 0x5d, 0x3b, 0x02, 0x39,
0x17, 0xfa, 0x14, 0x36, 0x75, 0x4a, 0x38, 0x67,
0x85, 0x57, 0x12, 0xa7, 0x14, 0xee, 0x35, 0x16,
0xd5, 0x3d, 0xbf, 0x42, 0x86, 0xf6, 0x69, 0x00,
0x76, 0xcd, 0x93, 0xf4, 0x7c, 0xb2, 0xdf, 0x9e,
0x44, 0xcd, 0x4c, 0xd4, 0xae, 0x09, 0x18, 0x53,
0x44, 0x32, 0xec, 0xe0, 0x61, 0x1b, 0xe5, 0xda,
0x13, 0xd3, 0x55, 0xc5, 0xdd, 0x1a, 0xcb, 0x90,
0x1e, 0x7e, 0x5b, 0xc6, 0xe9, 0x0f, 0x22, 0x9f,
0xbe, 0x85, 0x02, 0xfe, 0x90, 0x31, 0xcc, 0x6b,
0x03, 0x84, 0xbd, 0x22, 0xc4, 0x55, 0xfa, 0xf5,
0xf2, 0x08, 0xcd, 0x65, 0x41, 0x58, 0xe8, 0x7d,
0x29, 0xda, 0x04, 0x58, 0x82, 0xf5, 0x37, 0x69,
0xbc, 0xf3, 0x5a, 0x57, 0x84, 0x17, 0x7b, 0x32,
0x87, 0x70, 0xb2, 0xb0, 0x76, 0x9c, 0xb2, 0xc3,
0x15, 0xd1, 0x11, 0x26, 0x2a, 0x23, 0x75, 0x99,
0x3e, 0xb9, 0x77, 0x22, 0x32, 0x0d, 0xbc, 0x1a,
0x19, 0xc1, 0xd5, 0x65, 0x90, 0x76, 0x55, 0x74,
0x0f, 0x0e, 0x69, 0x4d, 0x5f, 0x4d, 0x8f, 0x19,
0xaf, 0xdf, 0xd6, 0x16, 0x31, 0x94, 0xa8, 0x92,
0x5f, 0x4f, 0xbc, 0x7a, 0x31, 0xf8, 0xae, 0x8e,
0xad, 0x33, 0xb7, 0xe9, 0x30, 0xd0, 0x8c, 0x0a,
0x8a, 0x6c, 0x83, 0x35, 0xf8, 0x8a, 0x81, 0xb2,
0xfe, 0x1c, 0x88, 0xac, 0x2a, 0x66, 0xc5, 0xff,
0xbd, 0xe6, 0x17, 0xd0, 0x62, 0x0b, 0xdc, 0x8a,
0x45, 0xf7, 0xb0, 0x3e, 0x5a, 0xc8, 0x1e, 0x4a,
0x24, 0x2f, 0x6c, 0xa5, 0xe3, 0x1c, 0x88, 0x14,
0x83, 0xd5, 0xc5, 0xef, 0x5e, 0x9f, 0x3d, 0x85,
0x45, 0x73, 0xe2, 0x6b, 0x50, 0x52, 0x57, 0x4c,
0xfb, 0x92, 0x6c, 0x66, 0x75, 0x8a, 0xd6, 0x0d,
0x1b, 0xae, 0xf3, 0xec, 0xaf, 0x51, 0x22, 0x03,
0x5d, 0x0a, 0x2e, 0x63, 0x93, 0x9c, 0x0b, 0x01,
0x20, 0xa8, 0xa9, 0x84, 0x2e, 0x17, 0xca, 0xae,
0x73, 0xec, 0x22, 0x1b, 0x79, 0xae, 0xf6, 0xa0,
0x72, 0x2c, 0xdf, 0x07, 0x47, 0xdb, 0x88, 0x86,
0x30, 0x14, 0x78, 0x21, 0x11, 0x22, 0x88, 0xac,
0xd7, 0x54, 0x74, 0xf9, 0xf3, 0x26, 0xc2, 0xa5,
0x56, 0xc8, 0x56, 0x4f, 0x00, 0x29, 0x1d, 0x08,
0x7b, 0x7a, 0xfb, 0x95, 0x89, 0xc3, 0xee, 0x98,
0x54, 0x9e, 0x3c, 0x6b, 0x94, 0x05, 0x13, 0x12,
0xf6, 0x71, 0xb9, 0xab, 0x13, 0xc3, 0x0c, 0x9b,
0x46, 0x08, 0x7b, 0x3d, 0x32, 0x6a, 0x68, 0xca,
0x1e, 0x9c, 0x90, 0x62, 0xc5, 0xed, 0x10, 0xb9,
0x1f, 0x17, 0x25, 0xce, 0x90, 0xb9, 0x6d, 0xcd,
0xc4, 0x46, 0xf5, 0xa3, 0x62, 0x13, 0x74, 0x02,
0xa7, 0x62, 0xa4, 0xfa, 0x55, 0xd9, 0xde, 0xcf,
0xa2, 0xe6, 0x80, 0x74, 0x55, 0x06, 0x49, 0xd5,
0x02, 0x0c
};
} // namespace
namespace wvcdm {
@@ -57,41 +126,90 @@ using video_widevine::EncryptedClientIdentification;
using video_widevine::SignedDrmDeviceCertificate;
using video_widevine::SignedMessage;
void ServiceCertificate::Clear() {
fetch_in_progress_ = false;
certificate_.clear();
provider_id_.clear();
CdmResponseType ServiceCertificate::Init(const std::string& certificate) {
std::string root_cert_str(reinterpret_cast<const char*>(&kRootCertForProd[0]),
sizeof(kRootCertForProd));
// Load root cert public key. Don't bother verifying it.
SignedDrmDeviceCertificate signed_root_cert;
if (!signed_root_cert.ParseFromString(root_cert_str)) {
LOGE("Failed to deserialize signed root certificate.");
return DEVICE_CERTIFICATE_ERROR_1;
}
DrmDeviceCertificate root_cert;
if (!root_cert.ParseFromString(signed_root_cert.drm_certificate())) {
LOGE("Failed to deserialize signed root certificate.");
return DEVICE_CERTIFICATE_ERROR_1;
}
RsaPublicKey root_key;
if (!root_key.Init(root_cert.public_key())) {
LOGE("Failed to load root certificate public key.");
return DEVICE_CERTIFICATE_ERROR_1;
}
// Load the provided service certificate.
// First, parse it and verify its signature.
SignedDrmDeviceCertificate signed_service_cert;
if (!signed_service_cert.ParseFromString(certificate)) {
LOGE("Failed to parse signed service certificate.");
return DEVICE_CERTIFICATE_ERROR_2;
}
if (!root_key.VerifySignature(signed_service_cert.drm_certificate(),
signed_service_cert.signature())) {
LOGE("Service certificate signature verification failed.");
return DEVICE_CERTIFICATE_ERROR_3;
}
DrmDeviceCertificate service_cert;
if (!service_cert.ParseFromString(signed_service_cert.drm_certificate())) {
LOGE("Failed to parse service certificate.");
return DEVICE_CERTIFICATE_ERROR_2;
}
if (service_cert.type() !=
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
LOGE("Not a service certificate.");
return DEVICE_CERTIFICATE_ERROR_3;
}
// Service certificate passes all checks - set up its RSA public key.
public_key_.reset(new RsaPublicKey);
if (!public_key_->Init(service_cert.public_key())) {
public_key_.reset();
LOGE("Failed to load service certificate public key.");
return DEVICE_CERTIFICATE_ERROR_2;
}
// Have service certificate and its public key - keep relevant fields.
certificate_ = certificate;
serial_number_ = service_cert.serial_number();
provider_id_ = service_cert.provider_id();
has_certificate_ = true;
return NO_ERROR;
}
CdmResponseType ServiceCertificate::Init(const std::string& raw_certificate) {
return VerifyAndExtract(raw_certificate);
CdmResponseType ServiceCertificate::VerifySignedMessage(
const std::string& message, const std::string& signature) {
if (!public_key_) {
LOGE("Service certificate not set.");
return DEVICE_CERTIFICATE_ERROR_4;
}
if (!public_key_->VerifySignature(message, signature))
return CLIENT_ID_RSA_ENCRYPT_ERROR; // TODO(tinskip): Need new error code.
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
DrmDeviceCertificate service_certificate;
if (certificate_.empty()) {
LOGE("ServiceCertificate::EncryptClientId: "
"service certificate is not properly initialized");
return UNKNOWN_ERROR;
}
if (!service_certificate.ParseFromString(certificate_)) {
LOGE("ServiceCertificate::EncryptClientId: unable to parse retrieved "
"service certificate");
return PARSE_SERVICE_CERTIFICATE_ERROR;
if (!public_key_) {
LOGE("Service certificate not set.");
return DEVICE_CERTIFICATE_ERROR_4;
}
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;
}
encrypted_client_id->set_provider_id(service_certificate.provider_id());
encrypted_client_id->set_service_certificate_serial_number(
service_certificate.serial_number());
encrypted_client_id->set_provider_id(provider_id_);
encrypted_client_id->set_service_certificate_serial_number(serial_number_);
std::string iv(KEY_IV_SIZE, 0);
std::string key(KEY_SIZE, 0);
@@ -109,10 +227,8 @@ CdmResponseType ServiceCertificate::EncryptClientId(
if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR;
if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR;
RsaPublicKey rsa;
if (!rsa.Init(service_certificate.public_key()))
return CLIENT_ID_RSA_INIT_ERROR;
if (!rsa.Encrypt(key, &enc_key)) return CLIENT_ID_RSA_ENCRYPT_ERROR;
if (!public_key_->Encrypt(key, &enc_key))
return CLIENT_ID_RSA_ENCRYPT_ERROR;
encrypted_client_id->set_encrypted_client_id_iv(iv);
encrypted_client_id->set_encrypted_privacy_key(enc_key);
@@ -120,97 +236,4 @@ CdmResponseType ServiceCertificate::EncryptClientId(
return NO_ERROR;
}
bool ServiceCertificate::PrepareRequest(CdmKeyMessage* signed_request) {
if (!signed_request) {
LOGE("ServiceCertificate::PrepareRequest: no signed request provided");
return false;
}
SignedMessage signed_message;
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
signed_message.SerializeToString(signed_request);
fetch_in_progress_ = true;
return true;
}
CdmResponseType ServiceCertificate::HandleResponse(
const std::string& signed_response) {
if (!fetch_in_progress_) {
LOGE("ServiceCertificate::HandleResponse: unexpected service "
"certificate response.");
return UNKNOWN_ERROR;
}
fetch_in_progress_ = false;
CdmResponseType status = VerifyAndExtract(signed_response);
if (status != NO_ERROR) {
return status;
}
return NO_ERROR;
}
CdmResponseType ServiceCertificate::VerifyAndExtract(
const std::string& raw_certificate) {
if (raw_certificate.empty()) {
Clear();
return NO_ERROR;
}
// Deserialize and parse raw certificate.
SignedDrmDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(raw_certificate)) {
LOGE(
"ServiceCertificate::VerifyAndExtract: unable to parse signed "
"service certificate");
return DEVICE_CERTIFICATE_ERROR_1;
}
// Set up root key (for verifying signature).
RsaPublicKey root_ca_key;
std::string ca_public_key(
reinterpret_cast<const char*>(&kServiceCertificateCAPublicKey[0]),
sizeof(kServiceCertificateCAPublicKey));
if (!root_ca_key.Init(ca_public_key)) {
LOGE(
"ServiceCertificate::VerifyAndExtract: public key initialization "
"failed");
return DEVICE_CERTIFICATE_ERROR_2;
}
// Verify the signature.
if (!root_ca_key.VerifySignature(
signed_service_certificate.drm_certificate(),
signed_service_certificate.signature())) {
LOGE(
"ServiceCertificate::VerifyAndExtract: service certificate "
"verification failed");
return DEVICE_CERTIFICATE_ERROR_3;
}
// Deserialize and parse actual certificate.
DrmDeviceCertificate service_certificate;
if (!service_certificate.ParseFromString(
signed_service_certificate.drm_certificate())) {
LOGE(
"ServiceCertificate::VerifyAndExtract: unable to parse retrieved "
"service certificate");
return DEVICE_CERTIFICATE_ERROR_4;
}
// Verify, extract needed fields.
if (service_certificate.type() !=
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
LOGE(
"ServiceCertificate::VerifyAndExtract: certificate not of type "
"service, %d", service_certificate.type());
return INVALID_DEVICE_CERTIFICATE_TYPE;
}
if (service_certificate.has_provider_id()) {
provider_id_.assign(service_certificate.provider_id());
} else {
provider_id_.clear();
}
certificate_.assign(signed_service_certificate.drm_certificate());
return NO_ERROR;
}
} // namespace wvcdm

View File

@@ -152,16 +152,21 @@ std::vector<uint8_t> Base64Decode(const std::string& b64_input) {
}
// Decode for Filename-friendly base64 encoding (RFC4648), commonly referred
// as Base64WebSafeDecode.
// as Base64WebSafeDecode. Add padding if needed.
std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
if (b64_input.empty()) {
return std::vector<uint8_t>();
}
int in_size = b64_input.size();
std::string b64_padded(b64_input);
while (b64_padded.size() % 4 != 0) {
b64_padded = b64_padded + "=";
}
int in_size = b64_padded.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);
b64_padded.data(), in_size);
if (out_size == -1) {
LOGE("Base64SafeDecode failed");
return std::vector<uint8_t>(0);

View File

@@ -11,12 +11,12 @@
#include "cdm_engine.h"
#include "config_test_env.h"
#include "default_service_certificate.h"
#include "initialization_data.h"
#include "license_request.h"
#include "log.h"
#include "OEMCryptoCENC.h"
#include "properties.h"
#include "properties_ce.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "test_printers.h"
@@ -27,6 +27,7 @@
namespace wvcdm {
namespace {
// Http OK response code.
const int kHttpOk = 200;
@@ -37,27 +38,313 @@ KeyId g_key_id_pssh;
KeyId g_key_id_unwrapped;
CdmKeySystem g_key_system;
std::string g_license_server;
std::string g_provisioning_server;
std::string g_provisioning_service_certificate;
std::string g_license_service_certificate;
KeyId g_wrong_key_id;
const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm";
static void CommonSetup(ServerConfigurationId which,
bool bin_prov = false) {
widevine::PropertiesCE::SetProvisioningMessagesAreBinary(bin_prov);
Properties::Init();
// NOTE: Select configuration
ConfigTestEnv config(which);
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id());
g_license_server.assign(config.license_server());
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
g_provisioning_service_certificate.assign(
config.provisioning_service_certificate());
g_license_service_certificate.assign(config.license_service_certificate());
g_provisioning_server.assign(config.provisioning_server());
// Extract the key ID from the PSSH box.
InitializationData extractor(CENC_INIT_DATA_FORMAT, g_key_id_pssh);
g_key_id_unwrapped = extractor.data();
}
/*
* Locate the portion of the server's response message that is between
* the strings jason_start_substr and json_end_substr. Returns the string
* through *result. If the start substring match fails, assume the entire
* string represents a serialized protobuf mesaage and return true with
* the entire string. If the end_substring match fails, return false with
* an empty *result.
*/
bool ExtractSignedMessage(const std::string& response,
const std::string& json_start_substr,
const std::string& json_end_substr,
std::string* result) {
std::string response_string;
size_t start = response.find(json_start_substr);
if (start == response.npos) {
// Assume serialized protobuf message.
result->assign(response);
} else {
// Assume JSON-wrapped protobuf.
size_t end = response.find(json_end_substr,
start + json_start_substr.length());
if (end == response.npos) {
LOGE("ExtractSignedMessage cannot locate end substring");
result->clear();
return false;
}
size_t result_string_size = end - start - json_start_substr.length();
result->assign(response, start + json_start_substr.length(),
result_string_size);
}
if (result->empty()) {
LOGE("ExtractSignedMessage: Response message is empty");
return false;
}
return true;
}
} // namespace
class WvCdmEngineTest : public testing::Test {
class WvCdmEnginePreProvTest : public testing::Test {
public:
WvCdmEngineTest() : cdm_engine_(&file_system_) {}
WvCdmEnginePreProvTest() : cdm_engine_(&file_system_),
session_opened_(false) {}
virtual ~WvCdmEnginePreProvTest() {}
virtual void SetUp() {
CdmResponseType status =
cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_);
ASSERT_EQ(status, NO_ERROR);
session_opened_ = true;
}
virtual void TearDown() {
if (session_opened_) {
cdm_engine_.CloseSession(session_id_);
session_opened_ = false;
}
}
protected:
// Trade request for response via the license server.
virtual bool LicenseServerRequestResponse(const std::string& request,
std::string* response) {
LOGV("LicenseServerRequestResponse: server url: %s",
g_license_server.c_str());
UrlRequest url_request(g_license_server + g_client_auth);
url_request.PostRequest(request);
std::string http_response;
if (!url_request.GetResponse(&http_response)) {
return false;
}
LOGV("http_response:\n%s\n", http_response.c_str());
// Separate message from HTTP headers.
LicenseRequest license_request;
license_request.GetDrmMessage(http_response, *response);
LOGV("response: size=%u, string:\n%s\n", response->size(),
Base64SafeEncode(std::vector<uint8_t>(response->begin(),
response->end())).c_str());
return true;
}
virtual void Provision() {
LOGV("WvCdmEnginePreProvTest::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.SetServiceCertificate(
g_provisioning_service_certificate));
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok);
LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n",
http_message.c_str());
ASSERT_EQ(NO_ERROR,
cdm_engine_.HandleProvisioningResponse(http_message,
&cert, &wrapped_key));
ASSERT_EQ(NO_ERROR,
cdm_engine_.SetServiceCertificate(g_license_service_certificate));
}
FileSystem file_system_;
CdmEngine cdm_engine_;
bool session_opened_;
std::string key_msg_;
std::string session_id_;
};
class WvCdmEnginePreProvTestStaging : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStaging() {}
virtual ~WvCdmEnginePreProvTestStaging() {}
static void SetUpTestCase() {
ConfigTestEnv config(kContentProtectionUatServer);
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id());
g_license_server.assign(config.license_server());
g_key_id_pssh.assign(a2bs_hex(config.key_id()));
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingLicense);
}
};
// Extract the key ID from the PSSH box.
InitializationData extractor(CENC_INIT_DATA_FORMAT, g_key_id_pssh);
g_key_id_unwrapped = extractor.data();
class WvCdmEnginePreProvTestProd : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestProd() {}
virtual ~WvCdmEnginePreProvTestProd() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionProdLicense);
}
};
class WvCdmEnginePreProvTestUat : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestUat() {}
virtual ~WvCdmEnginePreProvTestUat() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionUatLicense);
}
};
class WvCdmEnginePreProvTestStagingProv30 : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStagingProv30() {}
virtual ~WvCdmEnginePreProvTestStagingProv30() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingPlusProv30);
}
};
class WvCdmEnginePreProvTestStagingProv30Binary : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestStagingProv30Binary() {}
virtual ~WvCdmEnginePreProvTestStagingProv30Binary() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
// Override default setting of provisioning_messages_are_binary property
CommonSetup(kContentProtectionUatPlusProv30, true);
}
protected:
virtual void Provision() {
LOGV("WvCdmEnginePreProvTestProv30Binary::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest binary_prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.SetServiceCertificate(
g_provisioning_service_certificate));
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &binary_prov_request,
&provisioning_server_url));
// prov_request is binary - base64 encode it
std::string prov_request(Base64SafeEncodeNoPad(
std::vector<uint8_t>(binary_prov_request.begin(),
binary_prov_request.end())));
LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok);
LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n",
http_message.c_str());
// extract provisioning response from received message
// Extracts signed response from JSON string, result is serialized protobuf.
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string protobuf_response;
EXPECT_TRUE (ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
&protobuf_response)) <<
"Failed to extract signed serialized response from JSON response";
LOGV("WvCdmEnginePreProvTest::Provision: extracted response "
"message: \n%s\n", protobuf_response.c_str());
// base64 decode response to yield binary protobuf
std::vector<uint8_t> response_vec(Base64SafeDecode(
std::string(protobuf_response.begin(), protobuf_response.end())));
std::string binary_protobuf_response(response_vec.begin(),
response_vec.end());
ASSERT_EQ(NO_ERROR,
cdm_engine_.HandleProvisioningResponse(binary_protobuf_response,
&cert, &wrapped_key));
ASSERT_EQ(NO_ERROR,
cdm_engine_.SetServiceCertificate(g_license_service_certificate));
}
};
class WvCdmEnginePreProvTestUatProv30 : public WvCdmEnginePreProvTest {
public:
WvCdmEnginePreProvTestUatProv30() {}
virtual ~WvCdmEnginePreProvTestUatProv30() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingPlusProv30);
}
};
class WvCdmEngineTest : public WvCdmEnginePreProvTest {
public:
WvCdmEngineTest() {}
static void SetUpTestCase() {
// NOTE: Select server configuration
CommonSetup(kContentProtectionStagingLicense);
}
virtual void SetUp() {
@@ -72,29 +359,7 @@ class WvCdmEngineTest : public testing::Test {
ASSERT_TRUE(cdm_engine_.IsOpenSession(session_id_));
}
virtual void TearDown() { cdm_engine_.CloseSession(session_id_); }
protected:
void Provision() {
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR, cdm_engine_.SetServiceCertificate(
kDefaultServiceCertificate));
ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
EXPECT_TRUE(ok);
ASSERT_EQ(NO_ERROR, cdm_engine_.HandleProvisioningResponse(message, &cert,
&wrapped_key));
}
void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string) {
@@ -138,6 +403,9 @@ class WvCdmEngineTest : public testing::Test {
const std::string& client_auth,
bool expect_success) {
// Use secure connection and chunk transfer coding.
LOGV("GetKeyRequestResponse: server_url: %s", server_url.c_str());
UrlRequest url_request(server_url + client_auth);
if (!url_request.is_connected()) {
return "";
@@ -146,7 +414,7 @@ class WvCdmEngineTest : public testing::Test {
url_request.PostRequest(key_msg_);
std::string response;
bool ok = url_request.GetResponse(&response);
LOGD("response: %s\n", response.c_str());
LOGV("response: %s\n", response.c_str());
EXPECT_TRUE(ok);
int status_code = url_request.GetStatusCode(response);
@@ -180,37 +448,147 @@ class WvCdmEngineTest : public testing::Test {
EXPECT_EQ(KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
}
FileSystem file_system_;
CdmEngine cdm_engine_;
std::string key_msg_;
std::string session_id_;
std::string server_url_;
};
// Test that provisioning works, even if device is already provisioned.
TEST_F(WvCdmEngineTest, ProvisioningTest) {
// Test that service certificate is initially absent.
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateInitialNoneTest) {
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
};
// Test that service certificate can be properly installed.
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateGoodTest) {
ASSERT_EQ(cdm_engine_.SetServiceCertificate(g_license_service_certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
};
// Test that service certificate can be retrieved from the license server.
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateRequestResponse) {
CdmKeyMessage request;
std::string certificate;
// Initial condition - no service certificate.
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
// Generate request.
// The request will be a serialized protobuf message.
ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request));
std::string response;
ASSERT_TRUE(LicenseServerRequestResponse(request, &response));
// Extract the service certificate
ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str());
};
// Test that service certificate can be retrieved from the license server.
TEST_F(WvCdmEnginePreProvTestUat, ServiceCertificateRequestResponse) {
CdmKeyMessage request;
std::string certificate;
// Initial condition - no service certificate.
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
// Generate request.
// The request will be a serialized protobuf message.
ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request));
std::string response;
ASSERT_TRUE(LicenseServerRequestResponse(request, &response));
// Extract the service certificate
ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str());
};
// Test that service certificate can be retrieved from the license server.
TEST_F(WvCdmEnginePreProvTestProd, ServiceCertificateRequestResponse) {
CdmKeyMessage request;
std::string certificate;
// Initial condition - no service certificate.
ASSERT_FALSE(cdm_engine_.HasServiceCertificate());
// Generate request.
// The request will be a serialized protobuf message.
ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request));
std::string response;
ASSERT_TRUE(LicenseServerRequestResponse(request, &response));
// Extract the service certificate
ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str());
};
// Test that empty service certificate fails.
TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateEmptyFailTest) {
std::string empty_cert;
ASSERT_EQ(cdm_engine_.SetServiceCertificate(g_license_service_certificate),
NO_ERROR);
ASSERT_TRUE(cdm_engine_.HasServiceCertificate());
};
TEST_F(WvCdmEnginePreProvTestStaging, ProvisioningTest) {
uint32_t nonce = 0;
uint8_t buffer[1];
size_t size = 0;
int result = OEMCrypto_RewrapDeviceRSAKey(
0, buffer, 0, buffer, 0, &nonce, buffer, 0, buffer, buffer, &size);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
int result30 = OEMCrypto_RewrapDeviceRSAKey30(
0, &nonce, buffer, 0, buffer, 0, buffer, buffer, &size);
int method = OEMCrypto_GetProvisioningMethod(kLevelDefault);
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED &&
result30 == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
LOGW("WARNING: Skipping ProvisioningTest because the device does not "
"support provisioning. If you are using a baked-in certificate, this "
"is expected. Otherwise, something is wrong.");
return;
"support provisioning. If you are using a baked-in certificate, "
"this is expected. Otherwise, something is wrong.");
ASSERT_EQ(method, OEMCrypto_DrmCertificate);
} else {
if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
ASSERT_EQ(method, OEMCrypto_OEMCertificate);
} else {
ASSERT_EQ(method, OEMCrypto_Keybox);
}
}
Provision();
}
TEST_F(WvCdmEnginePreProvTestStagingProv30, ProvisioningTest) {
Provision();
}
TEST_F(WvCdmEnginePreProvTestStagingProv30Binary, ProvisioningTest) {
Provision();
}
// Test that provisioning works, even if device is already provisioned.
TEST_F(WvCdmEngineTest, ProvisioningTest) {
Provision();
}
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
GenerateKeyRequest(g_key_id_pssh, kCencMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, BaseWebmMessageTest) {
TEST_F(WvCdmEngineTest, DISABLED_BaseWebmMessageTest) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
@@ -230,7 +608,7 @@ TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
}
// TODO(juce): Set up with correct test data.
TEST_F(WvCdmEngineTest, NormalDecryptionWebm) {
TEST_F(WvCdmEngineTest, DISABLED_NormalDecryptionWebm) {
GenerateKeyRequest(g_key_id_unwrapped, kWebmMimeType);
VerifyNewKeyResponse(g_license_server, g_client_auth);
}

View File

@@ -1,3 +1,4 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include <gmock/gmock.h>
@@ -176,7 +177,7 @@ TEST_F(CdmSessionTest, InitWithBuiltInCertificate) {
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
.WillOnce(Return(kClientTokenDrmCert));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
NotNull(), NotNull()))
NotNull(), _))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
@@ -206,7 +207,7 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
NotNull(), NotNull()))
NotNull(), _))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
@@ -260,7 +261,7 @@ TEST_F(CdmSessionTest, ReInitFail) {
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
NotNull(), NotNull()))
NotNull(), _))
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
Return(true)));
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
@@ -299,7 +300,7 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
.WillOnce(Return(kClientTokenKeybox));
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
NotNull(), NotNull()))
NotNull(), _))
.WillOnce(Return(false));
Properties::set_use_certificates_as_identification(true);

View File

@@ -1,6 +1,13 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "config_test_env.h"
#include "string_conversions.h"
// Holds the data needed to talk to the various provisioning and
// license servers.
//
// Define a series of configurations, and specify the specific
// data items needed for that configuration.
namespace wvcdm {
@@ -8,9 +15,100 @@ namespace {
const std::string kWidevineKeySystem = "com.widevine.alpha";
// For staging servers
// NOTE: This matches the service cert returned by the staging
// server. This is the one that the staging provisioning server uses.
// NOTE: Provider ID = license.widevine.com
const std::string kStagingServiceCertificate =
"0ac102080312101705b917cc1204868b06333a2f772a8c1882b482920522"
"8e023082010a028201010099ed5b3b327dab5e24efc3b62a95b598520ad5"
"bccb37503e0645b814d876b8df40510441ad8ce3adb11bb88c4e725a5e4a"
"9e0795291d58584023a7e1af0e38a91279393008610b6f158c878c7e21bf"
"fbfeea77e1019e1e5781e8a45f46263d14e60e8058a8607adce04fac8457"
"b137a8d67ccdeb33705d983a21fb4eecbd4a10ca47490ca47eaa5d438218"
"ddbaf1cade3392f13d6ffb6442fd31e1bf40b0c604d1c4ba4c9520a4bf97"
"eebd60929afceef55bbaf564e2d0e76cd7c55c73a082b996120b8359edce"
"24707082680d6f67c6d82c4ac5f3134490a74eec37af4b2f010c59e82843"
"e2582f0b6b9f5db0fc5e6edf64fbd308b4711bcf1250019c9f5a09020301"
"00013a146c6963656e73652e7769646576696e652e636f6d128003ae3473"
"14b5a835297f271388fb7bb8cb5277d249823cddd1da30b93339511eb3cc"
"bdea04b944b927c121346efdbdeac9d413917e6ec176a10438460a503bc1"
"952b9ba4e4ce0fc4bfc20a9808aaaf4bfcd19c1dcfcdf574ccac28d1b410"
"416cf9de8804301cbdb334cafcd0d40978423a642e54613df0afcf96ca4a"
"9249d855e42b3a703ef1767f6a9bd36d6bf82be76bbf0cba4fde59d2abcc"
"76feb64247b85c431fbca52266b619fc36979543fca9cbbdbbfafa0e1a55"
"e755a3c7bce655f9646f582ab9cf70aa08b979f867f63a0b2b7fdb362c5b"
"c4ecd555d85bcaa9c593c383c857d49daab77e40b7851ddfd24998808e35"
"b258e75d78eac0ca16f7047304c20d93ede4e8ff1c6f17e6243e3f3da8fc"
"1709870ec45fba823a263f0cefa1f7093b1909928326333705043a29bda6"
"f9b4342cc8df543cb1a1182f7c5fff33f10490faca5b25360b76015e9c5a"
"06ab8ee02f00d2e8d5986104aacc4dd475fd96ee9ce4e326f21b83c70585"
"77b38732cddabc6a6bed13fb0d49d38a45eb87a5f4";
// NOTE: Provider ID = staging.google.com
const std::string kProdServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522"
"8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417"
"7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781"
"5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D"
"0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6"
"AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0"
"40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A"
"0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6"
"283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0"
"D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301"
"00013A1273746167696E672E676F6F676C652E636F6D128003983E303526"
"75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA"
"EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA"
"97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91"
"5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0"
"32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0"
"EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28"
"8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932"
"1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784"
"C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742"
"BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940"
"383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8"
"38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A"
"250A4EB9C84AB3E6539F6B6FDF56899EA29914";
// For UAT License servers
// NOTE: This matches the service cert returned by the UAT server.
// NOTE: Provider ID = staging.google.com
const std::string kUatServiceCertificate =
"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 (Production) data
const std::string kCpProdLicenseServer =
"https://widevine-proxy.appspot.com/proxy";
// Content Protection license server (UAT) data
const std::string kCpUatLicenseServer =
"http://widevine-proxy.appspot.com/proxy";
"https://proxy.uat.widevine.com/proxy";
const std::string kCpClientAuth = "";
const std::string kCpKeyId =
"00000042" // blob size
@@ -31,52 +129,10 @@ const std::string kCpOfflineKeyId =
// pssh data:
"08011a0d7769646576696e655f746573"
"74220d6f66666c696e655f636c697032";
const std::string kCpUatServiceCertificate =
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F99005228E023082010A02"
"82010100B52112B8D05D023FCC5D95E2C251C1C649B4177CD8D2BEEF355BB06743DE661E3D"
"2ABC3182B79946D55FDC08DFE95407815E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94"
"B2516F075B66EF811D0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1E"
"F9B6AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A040C50B09BB"
"C740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A0E498CC01F00532BAC217850"
"BD905E90923656B7DFEFEF42486767F33EF6283D4F4254AB72589390BEE55808F1D668080D"
"45D893C2BCA2F74D60A0C0D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD9"
"4302030100013A1273746167696E672E676F6F676C652E636F6D128003983E30352675F40B"
"A715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AAEFC5E27BC980DAEADABF"
"3FC386D084A02C82537848CC753FF497B011A7DA97788A00E2AA6B84CD7D71C07A48EBF616"
"02CCA5A3F32030A7295C30DA915B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE"
"18FA82E81BB032630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0EF"
"D45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F288F0D9D45960E259E"
"85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F869321F6ADE18905F4D92F9A6DA6536DB84"
"75871D168E870BB2303CF70C6E9784C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D25"
"92C72429F8C01742BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F39"
"40383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D838540F8A0C22"
"7C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A250A4EB9C84AB3E6539F6B6FDF"
"56899EA29914";
// Content Protection license server (staging) data
const std::string kCpStagingLicenseServer =
"http://wv-staging-proxy.appspot.com/proxy";
const std::string kCpStagingServiceCertificate =
"0AC102080312101705B917CC1204868B06333A2F772A8C1882B4829205228E023082010A02"
"8201010099ED5B3B327DAB5E24EFC3B62A95B598520AD5BCCB37503E0645B814D876B8DF40"
"510441AD8CE3ADB11BB88C4E725A5E4A9E0795291D58584023A7E1AF0E38A9127939300861"
"0B6F158C878C7E21BFFBFEEA77E1019E1E5781E8A45F46263D14E60E8058A8607ADCE04FAC"
"8457B137A8D67CCDEB33705D983A21FB4EECBD4A10CA47490CA47EAA5D438218DDBAF1CADE"
"3392F13D6FFB6442FD31E1BF40B0C604D1C4BA4C9520A4BF97EEBD60929AFCEEF55BBAF564"
"E2D0E76CD7C55C73A082B996120B8359EDCE24707082680D6F67C6D82C4AC5F3134490A74E"
"EC37AF4B2F010C59E82843E2582F0B6B9F5DB0FC5E6EDF64FBD308B4711BCF1250019C9F5A"
"0902030100013A146C6963656E73652E7769646576696E652E636F6D128003AE347314B5A8"
"35297F271388FB7BB8CB5277D249823CDDD1DA30B93339511EB3CCBDEA04B944B927C12134"
"6EFDBDEAC9D413917E6EC176A10438460A503BC1952B9BA4E4CE0FC4BFC20A9808AAAF4BFC"
"D19C1DCFCDF574CCAC28D1B410416CF9DE8804301CBDB334CAFCD0D40978423A642E54613D"
"F0AFCF96CA4A9249D855E42B3A703EF1767F6A9BD36D6BF82BE76BBF0CBA4FDE59D2ABCC76"
"FEB64247B85C431FBCA52266B619FC36979543FCA9CBBDBBFAFA0E1A55E755A3C7BCE655F9"
"646F582AB9CF70AA08B979F867F63A0B2B7FDB362C5BC4ECD555D85BCAA9C593C383C857D4"
"9DAAB77E40B7851DDFD24998808E35B258E75D78EAC0CA16F7047304C20D93EDE4E8FF1C6F"
"17E6243E3F3DA8FC1709870EC45FBA823A263F0CEFA1F7093B1909928326333705043A29BD"
"A6F9B4342CC8DF543CB1A1182F7C5FFF33F10490FACA5B25360B76015E9C5A06AB8EE02F00"
"D2E8D5986104AACC4DD475FD96EE9CE4E326F21B83C7058577B38732CDDABC6A6BED13FB0D"
"49D38A45EB87A5F4";
"https://proxy.staging.widevine.com/proxy";
// Google Play license server data
const std::string kGpLicenseServer =
@@ -116,31 +172,58 @@ const std::string kWrongKeyId =
"0901121094889920e8d6520098577df8"
"f2dd5546";
// URL of provisioning server (returned by GetProvisioningRequest())
// URL of provisioning server (overrides value from GetProvisioningRequest())
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const std::string kStagingProvisioningServerUrl =
"https://staging-www.sandbox.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, ""},
{kContentProtectionUatServer, kCpUatLicenseServer, kCpClientAuth,
kCpKeyId, kCpOfflineKeyId, kCpUatServiceCertificate},
{kContentProtectionStagingServer, kCpStagingLicenseServer,
kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kCpStagingServiceCertificate},
{kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId,
kGpOfflineKeyId, kStagingProvisioningServerUrl, ""},
{kContentProtectionProdLicense, kCpProdLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kProductionProvisioningServerUrl, kProdServiceCertificate},
{kContentProtectionUatLicense, kCpUatLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kProductionProvisioningServerUrl, kProdServiceCertificate},
{kContentProtectionStagingLicense, kCpStagingLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionProdPlusProv30, kCpProdLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionUatPlusProv30, kCpUatLicenseServer,
kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
{kContentProtectionStagingPlusProv30, kCpStagingLicenseServer,
kStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId,
kStagingProvisioningServerUrl, kStagingServiceCertificate},
};
} // namespace
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) { Init(server_id); }
ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id) {
Init(server_id);
}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) {
ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id, bool streaming) {
Init(server_id);
if (!streaming) key_id_ = license_servers[server_id].offline_key_id;
}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
ConfigTestEnv::ConfigTestEnv(ServerConfigurationId server_id, bool streaming,
bool renew, bool release) {
Init(server_id);
if (!streaming) {
@@ -158,13 +241,16 @@ ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
}
}
void ConfigTestEnv::Init(LicenseServerId server_id) {
void ConfigTestEnv::Init(ServerConfigurationId server_id) {
client_auth_ = license_servers[server_id].client_tag;
key_id_ = license_servers[server_id].key_id;
key_system_ = kWidevineKeySystem;
license_server_ = license_servers[server_id].url;
provisioning_server_url_ = kProductionProvisioningServerUrl;
service_certificate_ = license_servers[server_id].service_certificate;
license_server_ = license_servers[server_id].license_server_url;
provisioning_server_ = license_servers[server_id].provisioning_server_url;
license_service_certificate_ =
a2bs_hex(license_servers[server_id].license_service_certificate);
provisioning_service_certificate_ =
a2bs_hex(license_servers[server_id].provisioning_service_certificate);
wrong_key_id_ = kWrongKeyId;
}

View File

@@ -6,28 +6,50 @@
#include <string>
#include "wv_cdm_types.h"
// Declare class ConfigTestEnv - holds the configuration settings needed
// to talk to the various provisioning and license servers.
//
// License Servers
// QA - early test server (corporate access only, not generally usable).
// UAT - test server with non-production data.
// Staging - test server with access to production data.
// Production - live, production server.
// Google Play - Allows testing on Google Play servers (very stale).
//
// Provisioning Servers
// UAT - early access provisioning server.
// Staging - early access to features.
// Production - live production server.
// Useful configurations
namespace wvcdm {
typedef enum {
kGooglePlayServer,
kContentProtectionUatServer,
kContentProtectionStagingServer,
} LicenseServerId;
kGooglePlayServer, // not tested recently
kContentProtectionProdLicense,
kContentProtectionUatLicense,
kContentProtectionStagingLicense,
kContentProtectionProdPlusProv30,
kContentProtectionUatPlusProv30,
kContentProtectionStagingPlusProv30,
} ServerConfigurationId;
// Configures default test environment.
class ConfigTestEnv {
public:
typedef struct {
LicenseServerId id;
std::string url;
ServerConfigurationId id;
std::string license_server_url;
std::string license_service_certificate;
std::string client_tag;
std::string key_id;
std::string offline_key_id;
std::string service_certificate;
std::string provisioning_server_url;
std::string provisioning_service_certificate;
} LicenseServerConfiguration;
explicit ConfigTestEnv(LicenseServerId server_id);
ConfigTestEnv(LicenseServerId server_id, bool streaming);
ConfigTestEnv(LicenseServerId server_id, bool streaming, bool renew,
explicit ConfigTestEnv(ServerConfigurationId server_id);
ConfigTestEnv(ServerConfigurationId server_id, bool streaming);
ConfigTestEnv(ServerConfigurationId server_id, bool streaming, bool renew,
bool release);
~ConfigTestEnv() {};
@@ -35,11 +57,14 @@ class ConfigTestEnv {
const KeyId& key_id() const { return key_id_; }
const CdmKeySystem& key_system() const { return key_system_; }
const std::string& license_server() const { return license_server_; }
const std::string& provisioning_server_url() const {
return provisioning_server_url_;
const std::string& provisioning_server() const {
return provisioning_server_;
}
const std::string& service_certificate() const {
return service_certificate_;
const std::string& license_service_certificate() const {
return license_service_certificate_;
}
const std::string& provisioning_service_certificate() const {
return provisioning_service_certificate_;
}
const KeyId& wrong_key_id() const { return wrong_key_id_; }
@@ -52,14 +77,15 @@ class ConfigTestEnv {
}
private:
void Init(LicenseServerId server_id);
void Init(ServerConfigurationId server_id);
std::string client_auth_;
KeyId key_id_;
CdmKeySystem key_system_;
std::string license_server_;
std::string provisioning_server_url_;
std::string service_certificate_;
std::string provisioning_server_;
std::string license_service_certificate_;
std::string provisioning_service_certificate_;
KeyId wrong_key_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);

View File

@@ -1,36 +0,0 @@
// Copyright 2017 Google Inc. All Rights Reserved.
#ifndef CDM_TEST_DEFAULT_SERVICE_CERTIFICATE_H_
#define CDM_TEST_DEFAULT_SERVICE_CERTIFICATE_H_
#include <string>
#include "string_conversions.h"
namespace {
const std::string kDefaultServiceCertificate = wvcdm::a2bs_hex(
"0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F99005228E023082010A02"
"82010100B52112B8D05D023FCC5D95E2C251C1C649B4177CD8D2BEEF355BB06743DE661E3D"
"2ABC3182B79946D55FDC08DFE95407815E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94"
"B2516F075B66EF811D0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1E"
"F9B6AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A040C50B09BB"
"C740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A0E498CC01F00532BAC217850"
"BD905E90923656B7DFEFEF42486767F33EF6283D4F4254AB72589390BEE55808F1D668080D"
"45D893C2BCA2F74D60A0C0D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD9"
"4302030100013A1273746167696E672E676F6F676C652E636F6D128003983E30352675F40B"
"A715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AAEFC5E27BC980DAEADABF"
"3FC386D084A02C82537848CC753FF497B011A7DA97788A00E2AA6B84CD7D71C07A48EBF616"
"02CCA5A3F32030A7295C30DA915B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE"
"18FA82E81BB032630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0EF"
"D45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F288F0D9D45960E259E"
"85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F869321F6ADE18905F4D92F9A6DA6536DB84"
"75871D168E870BB2303CF70C6E9784C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D25"
"92C72429F8C01742BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F39"
"40383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D838540F8A0C22"
"7C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A250A4EB9C84AB3E6539F6B6FDF"
"56899EA29914");
} // namespace
#endif // CDM_TEST_DEFAULT_SERVICE_CERTIFICATE_H_

View File

@@ -1634,7 +1634,8 @@ TEST_F(DeviceCertificateStoreTest, StoreCertificate) {
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
TEST_F(DeviceCertificateTest, ReadCertificate) {
// TODO(tinskip): Fix. kTestCertificateFileData appears to be incorect.
TEST_F(DeviceCertificateTest, DISABLED_ReadCertificate) {
MockFileSystem file_system;
std::string device_certificate_path =
device_base_path_ + DeviceFiles::GetCertificateFileName();
@@ -1664,7 +1665,7 @@ TEST_F(DeviceCertificateTest, ReadCertificate) {
&serial_number, &system_id));
EXPECT_EQ(kTestCertificate, b2a_hex(certificate));
EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key));
EXPECT_EQ(0, system_id);
EXPECT_EQ(0u, system_id);
EXPECT_EQ("", b2a_hex(serial_number));
}
@@ -2328,5 +2329,4 @@ TEST_P(DeviceFilesHlsAttributesTest, Delete) {
INSTANTIATE_TEST_CASE_P(HlsAttributes, DeviceFilesHlsAttributesTest,
::testing::Range(&kHlsAttributesTestData[0],
&kHlsAttributesTestData[2]));
} // namespace wvcdm

View File

@@ -171,7 +171,7 @@ TEST_F(FileTest, ListFiles) {
EXPECT_TRUE(file_system.List(path_dir, &names));
// Should find three files. Order not important.
EXPECT_EQ(3, names.size());
EXPECT_EQ(3u, names.size());
EXPECT_THAT(names, ::testing::UnorderedElementsAre(kTestFileName,
kTestFileName2,
kTestFileName3));
@@ -180,14 +180,14 @@ TEST_F(FileTest, ListFiles) {
EXPECT_TRUE(file_system.Remove(wild_card_path));
EXPECT_TRUE(file_system.List(path_dir, &names));
EXPECT_EQ(1, names.size());
EXPECT_EQ(1u, names.size());
EXPECT_TRUE(names[0].compare(kTestFileName3) == 0);
std::string wild_card_path2 = path_dir + kWildcard + kTestFileNameExt3;
EXPECT_TRUE(file_system.Remove(wild_card_path2));
EXPECT_TRUE(file_system.List(path_dir, &names));
EXPECT_EQ(0, names.size());
EXPECT_EQ(0u, names.size());
}
} // namespace wvcdm

View File

@@ -10,8 +10,7 @@
#include <string>
#include "cdm_engine.h"
#include "default_service_certificate.h"
#include "config_test_env.h"
#include "license_request.h"
#include "log.h"
#include "oec_session_util.h"
@@ -25,6 +24,52 @@ namespace {
const std::string kKeySystem = "com.widevine.alpha";
std::string g_provisioning_server;
std::string g_license_service_certificate;
std::string g_provisioning_service_certificate;
/*
* Locate the portion of the server's response message that is between
* the strings jason_start_substr and json_end_substr. Returns the string
* through *result. If the start substring match fails, assume the entire
* string represents a serialized protobuf mesaage and return true with
* the entire string. If the end_substring match fails, return false with
* an empty *result.
*/
bool ExtractSignedMessage(const std::string& response,
const std::string& json_start_substr,
const std::string& json_end_substr,
std::string* result) {
std::string b64_string;
size_t start = response.find(json_start_substr);
if (start == response.npos) {
// Assume web safe protobuf
b64_string.assign(response);
} else {
// Assume JSON-wrapped protobuf
size_t end = response.find(json_end_substr,
start + json_start_substr.length());
if (end == response.npos) {
LOGE("ExtractSignedMessage cannot locate end substring");
result->clear();
return false;
}
size_t b64_string_size = end - start - json_start_substr.length();
b64_string.assign(response, start + json_start_substr.length(),
b64_string_size);
}
if (b64_string.empty()) {
LOGE("Response message is empty");
result->clear();
return false;
}
result->swap(b64_string);
return true;
}
} // namespace
namespace wvcdm {
@@ -34,6 +79,15 @@ class WvGenericOperationsTest : public testing::Test {
virtual void SetUp() {
::testing::Test::SetUp();
ConfigTestEnv config(kContentProtectionStagingPlusProv30);
g_provisioning_service_certificate.assign(
config.provisioning_service_certificate());
g_license_service_certificate.assign(config.license_service_certificate());
g_provisioning_server.assign(config.provisioning_server());
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.
@@ -42,6 +96,8 @@ class WvGenericOperationsTest : public testing::Test {
// Perform CdmEngine setup
cdm_engine_ = new CdmEngine(&file_system_);
Provision();
CdmResponseType status =
cdm_engine_->OpenSession(kKeySystem, NULL, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
@@ -66,7 +122,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)
}
@@ -156,26 +214,55 @@ class WvGenericOperationsTest : public testing::Test {
}
protected:
void Provision() {
virtual void Provision() {
LOGE("WvCdmEnginePreProvTest::Provision: url=%s",
g_provisioning_server.c_str());
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
cdm_engine_->SetServiceCertificate(kDefaultServiceCertificate);
ASSERT_EQ(NO_ERROR,
cdm_engine_->GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
ASSERT_EQ(NO_ERROR, cdm_engine_->SetServiceCertificate(
g_provisioning_service_certificate));
ASSERT_EQ(NO_ERROR, cdm_engine_->GetProvisioningRequest(
cert_type, cert_authority, &prov_request,
&provisioning_server_url));
LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str());
// Ignore URL provided by CdmEngine. Use ours, as configured
// for test vs. production server.
provisioning_server_url.assign(g_provisioning_server);
UrlRequest url_request(provisioning_server_url);
EXPECT_TRUE(url_request.is_connected());
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
std::string http_message;
bool ok = url_request.GetResponse(&http_message);
EXPECT_TRUE(ok);
LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n",
http_message.c_str());
// extract provisioning response from received message
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string base64_response;
EXPECT_TRUE (ExtractSignedMessage(http_message, kMessageStart, kMessageEnd,
&base64_response)) <<
"Failed to extract signed serialized response from JSON response";
LOGV("WvCdmEnginePreProvTest::Provision: extracted response "
"message: \n%s\n", base64_response.c_str());
ASSERT_EQ(NO_ERROR,
cdm_engine_->HandleProvisioningResponse(message, &cert,
&wrapped_key));
cdm_engine_->HandleProvisioningResponse(base64_response,
&cert, &wrapped_key));
ASSERT_EQ(NO_ERROR,
cdm_engine_->SetServiceCertificate(
g_license_service_certificate));
}
// This CryptoSession object handles Initialization and Termination

View File

@@ -317,7 +317,7 @@ class LicenseKeysTest : public ::testing::Test {
}
int content_key_count_;
size_t content_key_count_;
LicenseKeys license_keys_;
License license_;
};
@@ -495,7 +495,7 @@ TEST_F(LicenseKeysTest, ExtractKeyStatuses) {
CdmKeyStatusMap key_status_map;
StageOperatorSessionKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(0, key_status_map.size());
EXPECT_EQ(0u, key_status_map.size());
StageContentKeys();
license_keys_.ExtractKeyStatuses(&key_status_map);
EXPECT_EQ(content_key_count_, key_status_map.size());

View File

@@ -5,7 +5,6 @@
#include "clock.h"
#include "crypto_session.h"
#include "default_service_certificate.h"
#include "initialization_data.h"
#include "license.h"
#include "policy_engine.h"
@@ -220,7 +219,8 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true)));
CreateCdmLicense();
service_cert_.Init(kDefaultServiceCertificate);
// TODO(gmorgan) fix below - no default service certificate
//service_cert_.Init(kDefaultServiceCertificate);
EXPECT_TRUE(cdm_license_->Init(
&service_cert_, kToken, kClientTokenDrmCert, kEmptyString,
crypto_session_, policy_engine_));

View File

@@ -2,6 +2,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "crypto_session.h"
#include "license.h"
#include "policy_engine.h"
@@ -212,15 +213,27 @@ class PolicyEngineConstraintsTest : public Test {
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));
}
@@ -247,7 +260,6 @@ TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(true)));
policy_engine_->SetLicense(license_);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
@@ -285,17 +297,34 @@ TEST_F(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)));
@@ -371,14 +400,13 @@ TEST_F(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),
@@ -405,7 +433,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
}
TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
TEST_F(PolicyEngineConstraintsTest, UsesDefaultHdcpWhenResolutionNotSet) {
{
Sequence time;
for (int i=0; i<2; ++i) {
@@ -413,16 +441,28 @@ TEST_F(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));
}

View File

@@ -18,6 +18,8 @@
namespace wvcdm {
using namespace testing;
namespace {
const int64_t kDurationUnlimited = 0;
const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013
@@ -52,6 +54,11 @@ int64_t ParseInt(const std::string& str) {
return ret;
}
class HdcpOnlyMockCryptoSession : public CryptoSession {
public:
MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*));
};
class MockCdmEventListener : public WvCdmEventListener {
public:
MOCK_METHOD1(OnSessionRenewalNeeded, void(const CdmSessionId& session_id));
@@ -85,7 +92,8 @@ class PolicyEngineTest : public ::testing::Test {
protected:
virtual void SetUp() {
policy_engine_.reset(
new PolicyEngine(kSessionId, &mock_event_listener_, NULL));
new PolicyEngine(kSessionId, &mock_event_listener_,
&crypto_session_));
InjectMockClock();
license_.set_license_start_time(kLicenseStartTime);
@@ -141,6 +149,7 @@ class PolicyEngineTest : public ::testing::Test {
expected_has_new_usable_key));
}
StrictMock<HdcpOnlyMockCryptoSession> crypto_session_;
StrictMock<MockCdmEventListener> mock_event_listener_;
MockClock* mock_clock_;
scoped_ptr<PolicyEngine> policy_engine_;
@@ -164,6 +173,11 @@ TEST_F(PolicyEngineTest, PlaybackSuccess_OfflineLicense) {
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(true)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
policy_engine_->OnTimerEvent();
@@ -185,6 +199,11 @@ TEST_F(PolicyEngineTest, PlaybackSuccess_StreamingLicense) {
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
policy_engine_->OnTimerEvent();
@@ -203,6 +222,11 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
@@ -223,6 +247,11 @@ TEST_F(PolicyEngineTest, LicenseExpired_RentalDurationExpiredWithoutPlayback) {
.WillOnce(Return(kLicenseStartTime + kLowDuration - 1))
.WillOnce(Return(kLicenseStartTime + kLowDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -253,6 +282,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDurationPassedWithPlayback) {
.WillOnce(Return(kLicenseStartTime + kLowDuration - 1))
.WillOnce(Return(kLicenseStartTime + kLowDuration + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -283,6 +317,11 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) {
.WillOnce(Return(playback_start_time + kPlaybackDuration - 2))
.WillOnce(Return(playback_start_time + kPlaybackDuration + 2));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -315,6 +354,11 @@ TEST_F(PolicyEngineTest, LicenseExpired_LicenseDurationExpiredWithoutPlayback) {
.WillOnce(Return(kLicenseStartTime + kLowDuration - 1))
.WillOnce(Return(kLicenseStartTime + kLowDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -346,6 +390,11 @@ TEST_F(PolicyEngineTest, PlaybackFails_ExpiryBeforeRenewalDelay_Offline) {
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 1))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -381,6 +430,11 @@ TEST_F(PolicyEngineTest, PlaybackFails_ExpiryBeforeRenewalDelay_Streaming) {
.WillOnce(Return(kLicenseStartTime + kLicenseDuration - 1))
.WillOnce(Return(kLicenseStartTime + kLicenseDuration + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -411,6 +465,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
.WillOnce(Return(kLicenseStartTime + kRentalDuration + 10))
.WillOnce(Return(kLicenseStartTime + kLicenseDuration + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -443,6 +502,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
.WillOnce(Return(kLicenseStartTime + kLicenseDuration - 2))
.WillOnce(Return(kLicenseStartTime + kLicenseDuration + 2));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -477,6 +541,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 1))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -514,6 +583,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackAndRental0) {
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLicenseDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
policy_engine_->OnTimerEvent();
@@ -537,6 +611,16 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, NEVER_EXPIRES));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -555,6 +639,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseWithFutureStartTime) {
.WillOnce(Return(kLicenseStartTime))
.WillOnce(Return(kPlaybackStartTime));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusPending, false);
EXPECT_CALL(mock_event_listener_,
@@ -589,6 +678,11 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 10))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -630,6 +724,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 10));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -682,6 +781,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess_WithFutureStartTime) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 30))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 60));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -734,6 +838,11 @@ TEST_F(PolicyEngineTest, LicenseExpired_RenewFailedVersionNotUpdated) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 40))
.WillOnce(Return(kLicenseStartTime + kLowDuration + 10));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -792,6 +901,11 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
.WillOnce(Return(kLicenseStartTime + kLicenseDuration - 15))
.WillOnce(Return(kLicenseStartTime + kLicenseDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -848,6 +962,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
.WillOnce(Return(new_license_start_time))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 20));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -918,6 +1037,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 67))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 200));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -972,6 +1096,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
.WillOnce(Return(kLicenseStartTime + 20))
.WillOnce(Return(kLicenseStartTime + 40));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1030,6 +1159,11 @@ TEST_F(PolicyEngineTest, MultipleKeysInLicense) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
@@ -1052,6 +1186,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_SoftEnforcePlaybackDuration) {
.WillOnce(Return(kLicenseStartTime + kLicenseDuration - 5))
.WillOnce(Return(kLicenseStartTime + kLicenseDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence seq;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1085,6 +1224,11 @@ TEST_F(PolicyEngineTest, LicenseExpired_SoftEnforceLoadBeforeExpire) {
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 5))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence seq;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1109,6 +1253,11 @@ TEST_F(PolicyEngineTest, LicenseExpired_SoftEnforceLoadAfterExpire) {
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 5))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence seq;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1138,6 +1287,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_GracePeriod) {
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 5))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence seq;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1177,6 +1331,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_GracePeriodWithLoad) {
.WillOnce(Return(kNewPlaybackStartTime + kPlaybackDuration - 5))
.WillOnce(Return(kNewPlaybackStartTime + kPlaybackDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence seq;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1215,6 +1374,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_GracePeriodWithExpiredLoad) {
.WillOnce(Return(kNewPlaybackStartTime))
.WillOnce(Return(kNewPlaybackStartTime));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence seq;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1242,6 +1406,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_CanStoreGracePeriod) {
.WillOnce(Return(kPlaybackStartTime + 50))
.WillOnce(Return(kPlaybackStartTime + kGracePeriod + 2));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
InSequence seq;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
@@ -1383,6 +1552,11 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageBasic) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
@@ -1438,6 +1612,11 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
@@ -1463,7 +1642,7 @@ class PolicyEngineQueryTest : public PolicyEngineTest {
protected:
virtual void SetUp() {
PolicyEngineTest::SetUp();
policy_engine_.reset(new PolicyEngine(kSessionId, NULL, NULL));
policy_engine_.reset(new PolicyEngine(kSessionId, NULL, &crypto_session_));
InjectMockClock();
// Use a STREAMING license policy.
@@ -1501,6 +1680,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess) {
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 100));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
CdmQueryMap query_info;
@@ -1523,6 +1707,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_PlaybackNotBegun) {
.WillOnce(Return(kLicenseStartTime + 100))
.WillOnce(Return(kLicenseStartTime + 200));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
CdmQueryMap query_info;
@@ -1559,6 +1748,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_PlaybackBegun) {
.WillOnce(Return(kLicenseStartTime + 150))
.WillOnce(Return(kLicenseStartTime + 200));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(true)));
policy_engine_->SetLicense(license_);
CdmQueryMap query_info;
@@ -1608,6 +1802,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_Offline) {
.WillOnce(Return(kLicenseStartTime + 200))
.WillOnce(Return(kLicenseStartTime + 300));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->OnTimerEvent();
@@ -1690,6 +1889,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_CanPlayFalse) {
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + 100));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
@@ -1725,6 +1929,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_RentalDurationExpired) {
.WillOnce(Return(kLicenseStartTime + kLowDuration))
.WillOnce(Return(kLicenseStartTime + kLowDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -1763,6 +1972,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_PlaybackDurationExpired) {
.WillOnce(Return(playback_start_time + 2 + kLowDuration))
.WillOnce(Return(playback_start_time + 5 + kLowDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -1797,6 +2011,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_LicenseDurationExpired) {
.WillOnce(Return(kLicenseStartTime + kLowDuration))
.WillOnce(Return(kLicenseStartTime + kLowDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -1835,6 +2054,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_RentalDuration0) {
.WillOnce(Return(kLicenseStartTime + kLowDuration))
.WillOnce(Return(kLicenseStartTime + kLowDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -1876,6 +2100,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_PlaybackDuration0) {
.WillOnce(Return(kLicenseStartTime + kHighDuration + 2))
.WillOnce(Return(kLicenseStartTime + kHighDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -1928,6 +2157,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_LicenseDuration0) {
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 5));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -1965,6 +2199,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_Durations0) {
.WillOnce(Return(kLicenseStartTime + kHighDuration + 9))
.WillOnce(Return(kLicenseStartTime + kHighDuration + 15));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -1998,6 +2237,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_LicenseWithFutureStartTime) {
.WillOnce(Return(kLicenseStartTime + 10))
.WillOnce(Return(kLicenseStartTime + 25));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->OnTimerEvent();
@@ -2049,6 +2293,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_Renew) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 15));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -2102,6 +2351,11 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_RenewWithFutureStartTime) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 40));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
@@ -2177,6 +2431,11 @@ TEST_F(PolicyEngineTest, SetLicenseForReleaseAfterSetLicense) {
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(false)));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
policy_engine_->OnTimerEvent();

View File

@@ -136,9 +136,11 @@ TEST_F(ServiceCertificateTest, InitServiceCertificatePresent) {
Properties::AddSessionPropertySet(kTestSessionId1, &property_set);
CreateServiceCertificate();
std::string service_certificate;
EXPECT_TRUE(Properties::GetServiceCertificate(kTestSessionId1,
&service_certificate));
EXPECT_EQ(NO_ERROR,
service_certificate_->Init(
Properties::service_certificate(kTestSessionId1));
service_certificate_->Init(service_certificate));
EXPECT_TRUE(service_certificate_->HasCertificate());
}
@@ -151,7 +153,7 @@ TEST_F(ServiceCertificateTest, SetServiceCertificate) {
Properties::AddSessionPropertySet(kTestSessionId1, &property_set);
CreateServiceCertificate();
EXPECT_EQ(NO_ERROR, service_certificate_->Init(kTestSignedCertificate);
EXPECT_EQ(NO_ERROR, service_certificate_->Init(kTestSignedCertificate));
EXPECT_TRUE(service_certificate_->HasCertificate());
}

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_3: *os << "STORE_LICENSE_ERROR_3";
break;
case STORE_USAGE_INFO_ERROR: *os << "STORE_USAGE_INFO_ERROR";
break;
case UNPROVISION_ERROR_1: *os << "UNPROVISION_ERROR_1";
@@ -318,18 +322,12 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case USAGE_INFO_NOT_FOUND: *os << "USAGE_INFO_NOT_FOUND";
break;
case LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR:
*os << "LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR";
break;
case EMPTY_PROVISIONING_CERTIFICATE_2:
*os << "EMPTY_PROVISIONING_CERTIFICATE_2";
break;
case PARSE_SERVICE_CERTIFICATE_ERROR:
*os << "PARSE_SERVICE_CERTIFICATE_ERROR";
break;
case SERVICE_CERTIFICATE_TYPE_ERROR:
*os << "SERVICE_CERTIFICATE_TYPE_ERROR";
break;
case CLIENT_ID_GENERATE_RANDOM_ERROR:
*os << "CLIENT_ID_GENERATE_RANDOM_ERROR";
break;
@@ -354,9 +352,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
break;
case INVALID_PARAMETERS_LIC_7: *os << "INVALID_PARAMETERS_LIC_7";
break;
case LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR:
*os << "LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR";
break;
case CENC_INIT_DATA_UNAVAILABLE: *os << "CENC_INIT_DATA_UNAVAILABLE";
break;
case PREPARE_CENC_CONTENT_ID_FAILED:
@@ -449,6 +444,10 @@ 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 INVALID_PARAMETERS_ENG_19: *os << "INVALID_PARAMETERS_ENG_19";
break;
case CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1:
*os << "CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1";
break;
@@ -458,6 +457,36 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case LICENSING_CLIENT_TOKEN_ERROR_1:
*os << "LICENSING_CLIENT_TOKEN_ERROR_1";
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;
case LIST_USAGE_ERROR_2: *os << "LIST_USAGE_ERROR_2";
break;
case DELETE_USAGE_ERROR_1: *os << "DELETE_USAGE_ERROR_1";
break;
case DELETE_USAGE_ERROR_2: *os << "DELETE_USAGE_ERROR_2";
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 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";
break;
@@ -472,8 +501,6 @@ void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) {
break;
case kLicenseTypeRelease: *os << "kLicenseTypeRelease";
break;
case kLicenseTypeDeferred: *os << "kLicenseTypeDeferred";
break;
default:
*os << "Unknown CdmLicenseType";
break;

View File

@@ -167,10 +167,11 @@ 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, "");
return true;
}
} // namespace wvcdm