Source release v3.2.0
This commit is contained in:
@@ -4,14 +4,18 @@
|
||||
#define WVCDM_CORE_CDM_ENGINE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "certificate_provisioning.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "file_store.h"
|
||||
#include "initialization_data.h"
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "service_certificate.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -23,28 +27,41 @@ class UsagePropertySet;
|
||||
class WvCdmEventListener;
|
||||
|
||||
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
|
||||
typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap;
|
||||
typedef std::map<
|
||||
CdmKeySetId,
|
||||
std::pair<CdmSessionId, int64_t /* expiration time in seconds */> >
|
||||
CdmReleaseKeySetMap;
|
||||
|
||||
class CdmEngine {
|
||||
public:
|
||||
CdmEngine(FileSystem* file_system);
|
||||
CdmEngine(FileSystem* file_system, const std::string& spoid = EMPTY_SPOID);
|
||||
virtual ~CdmEngine();
|
||||
|
||||
// Set service certificate for all sessions under this CDM/CdmEngine.
|
||||
// Setting to the empty string is OK. If the License Service certificate is
|
||||
// empty and privacy mode is true, the certificate will be fetched from
|
||||
// the server before the first license request.
|
||||
virtual CdmResponseType SetServiceCertificate(
|
||||
const std::string& certificate);
|
||||
|
||||
// Session related methods
|
||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
const CdmSessionId& forced_session_id,
|
||||
WvCdmEventListener* event_listener);
|
||||
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
CdmSessionId* session_id);
|
||||
virtual CdmResponseType OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
const CdmSessionId& forced_session_id,
|
||||
WvCdmEventListener* event_listener);
|
||||
|
||||
virtual CdmResponseType OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener, CdmSessionId* session_id);
|
||||
|
||||
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
|
||||
|
||||
virtual bool IsOpenSession(const CdmSessionId& session_id);
|
||||
|
||||
virtual CdmResponseType OpenKeySetSession(const CdmKeySetId& key_set_id,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener);
|
||||
virtual CdmResponseType OpenKeySetSession(
|
||||
const CdmKeySetId& key_set_id, CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener);
|
||||
|
||||
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
|
||||
|
||||
// License related methods
|
||||
@@ -122,19 +139,32 @@ class CdmEngine {
|
||||
virtual CdmResponseType QueryOemCryptoSessionId(
|
||||
const CdmSessionId& session_id, CdmQueryMap* query_response);
|
||||
|
||||
// Provisioning related methods
|
||||
// Generate and return a valid provisioning request.
|
||||
virtual CdmResponseType GetProvisioningRequest(
|
||||
CdmCertificateType cert_type, const std::string& cert_authority,
|
||||
CdmProvisioningRequest* request, std::string* default_url);
|
||||
|
||||
// Verify and process a provisioning response.
|
||||
virtual CdmResponseType HandleProvisioningResponse(
|
||||
const CdmProvisioningResponse& response, std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
|
||||
// Return true if there is a device certificate on the current
|
||||
// (origin-specific) file system.
|
||||
virtual bool IsProvisioned(CdmSecurityLevel security_level);
|
||||
|
||||
// Remove provisioning-related from the current (origin-specific) file system.
|
||||
// This will force the device to reprovision itself.
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel security_level);
|
||||
|
||||
// Delete OEMCrypto usage tables. Used by Unprovision().
|
||||
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);
|
||||
|
||||
// Usage related methods for streaming licenses
|
||||
// Retrieve a random usage info from the list of all usage infos for this app
|
||||
// id.
|
||||
@@ -191,8 +221,9 @@ class CdmEngine {
|
||||
virtual bool IsKeyLoaded(const KeyId& key_id);
|
||||
virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId);
|
||||
|
||||
// Used for notifying the Max-Res Engine of resolution changes
|
||||
virtual void NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
// Used for notifying the Max-Res Engine of resolution changes.
|
||||
// Return false if no match is found for session_id.
|
||||
virtual bool NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
uint32_t height);
|
||||
|
||||
// Timer expiration method. This method is not re-entrant -- there can be
|
||||
@@ -206,11 +237,10 @@ class CdmEngine {
|
||||
|
||||
private:
|
||||
// private methods
|
||||
CdmResponseType OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id);
|
||||
CdmResponseType OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener, const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id);
|
||||
|
||||
void DeleteAllUsageReportsUponFactoryReset();
|
||||
bool ValidateKeySystem(const CdmKeySystem& key_system);
|
||||
@@ -222,15 +252,25 @@ class CdmEngine {
|
||||
|
||||
std::string MapHdcpVersion(CryptoSession::HdcpCapability version);
|
||||
|
||||
void CloseExpiredReleaseSessions();
|
||||
|
||||
// instance variables
|
||||
CdmSessionMap sessions_;
|
||||
CdmReleaseKeySetMap release_key_sets_;
|
||||
scoped_ptr<CertificateProvisioning> cert_provisioning_;
|
||||
SecurityLevel cert_provisioning_requested_security_level_;
|
||||
FileSystem* file_system_;
|
||||
Clock clock_;
|
||||
std::string spoid_;
|
||||
|
||||
static bool seeded_;
|
||||
|
||||
// Service certificate for license server and provisioning server.
|
||||
// It is initially empty. If left empty, the operations that
|
||||
// require them (getting provider_id, encrypting ClientIdentification)
|
||||
// are not performed.
|
||||
ServiceCertificate service_certificate_;
|
||||
|
||||
// usage related variables
|
||||
scoped_ptr<CdmSession> usage_session_;
|
||||
scoped_ptr<UsagePropertySet> usage_property_set_;
|
||||
@@ -244,6 +284,8 @@ class CdmEngine {
|
||||
// occur simultaneously with OpenSession or CloseSession.
|
||||
Lock session_list_lock_;
|
||||
|
||||
Lock release_key_sets_lock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
namespace wvcdm {
|
||||
|
||||
class CdmClientPropertySet;
|
||||
class ServiceCertificate;
|
||||
class WvCdmEventListener;
|
||||
|
||||
class CdmSession {
|
||||
@@ -27,7 +29,8 @@ class CdmSession {
|
||||
virtual ~CdmSession();
|
||||
|
||||
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set);
|
||||
virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
virtual CdmResponseType Init(ServiceCertificate* service_certificate,
|
||||
CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id,
|
||||
WvCdmEventListener* event_listener);
|
||||
|
||||
@@ -117,6 +120,7 @@ class CdmSession {
|
||||
// release the underlying crypto session) rather than call this method.
|
||||
virtual CdmResponseType ReleaseCrypto();
|
||||
|
||||
// Delete current license and matching usage record
|
||||
bool DeleteLicense();
|
||||
|
||||
// Generate unique ID for each new session.
|
||||
|
||||
@@ -6,43 +6,62 @@
|
||||
#include <string>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class CdmClientPropertySet;
|
||||
class CdmSession;
|
||||
class FileSystem;
|
||||
class ServiceCertificate;
|
||||
|
||||
class CertificateProvisioning {
|
||||
public:
|
||||
CertificateProvisioning() : cert_type_(kCertificateWidevine) {};
|
||||
explicit CertificateProvisioning(ServiceCertificate* service_certificate) :
|
||||
cert_type_(kCertificateWidevine),
|
||||
service_certificate_(service_certificate) {}
|
||||
|
||||
~CertificateProvisioning() {};
|
||||
|
||||
// Provisioning related methods
|
||||
CdmResponseType GetProvisioningRequest(SecurityLevel requested_security_level,
|
||||
CdmCertificateType cert_type,
|
||||
const std::string& cert_authority,
|
||||
const std::string& origin,
|
||||
CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
// Construct a valid provisioning request.
|
||||
// The request will be sent to the provisioning server.
|
||||
CdmResponseType GetProvisioningRequest(
|
||||
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
||||
const std::string& cert_authority, const std::string& origin,
|
||||
const std::string& spoid, CdmProvisioningRequest* request,
|
||||
std::string* default_url);
|
||||
|
||||
// Process the provisioning response.
|
||||
CdmResponseType HandleProvisioningResponse(
|
||||
FileSystem* file_system,
|
||||
const CdmProvisioningResponse& response,
|
||||
std::string* cert,
|
||||
std::string* wrapped_key);
|
||||
FileSystem* file_system, const CdmProvisioningResponse& response,
|
||||
std::string* cert, std::string* wrapped_key);
|
||||
|
||||
private:
|
||||
bool GetProvisioningTokenType(
|
||||
video_widevine::ClientIdentification::TokenType* token_type);
|
||||
|
||||
bool SetSpoidParameter(const std::string& origin, const std::string& spoid,
|
||||
video_widevine::ProvisioningRequest* request);
|
||||
|
||||
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_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_CERTIFICATE_PROVISIONING_H_
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "lock.h"
|
||||
#include "oemcrypto_adapter.h"
|
||||
@@ -28,8 +29,11 @@ class CryptoSession {
|
||||
CryptoSession();
|
||||
virtual ~CryptoSession();
|
||||
|
||||
virtual bool ValidateKeybox();
|
||||
virtual bool GetToken(std::string* token);
|
||||
virtual bool GetClientToken(std::string* client_token);
|
||||
virtual bool GetProvisioningToken(std::string* client_token);
|
||||
virtual CdmClientTokenType GetPreProvisionTokenType() {
|
||||
return pre_provision_token_type_;
|
||||
}
|
||||
virtual CdmSecurityLevel GetSecurityLevel();
|
||||
virtual bool GetDeviceUniqueId(std::string* device_id);
|
||||
virtual bool GetApiVersion(uint32_t* version);
|
||||
@@ -123,8 +127,11 @@ class CryptoSession {
|
||||
const std::string& signature);
|
||||
|
||||
private:
|
||||
bool GetProvisioningMethod(CdmClientTokenType* token_type);
|
||||
void Init();
|
||||
void Terminate();
|
||||
bool GetTokenFromKeybox(std::string* token);
|
||||
bool GetTokenFromOemCert(std::string* token);
|
||||
void GenerateMacContext(const std::string& input_context,
|
||||
std::string* deriv_context);
|
||||
void GenerateEncryptContext(const std::string& input_context,
|
||||
@@ -143,6 +150,18 @@ class CryptoSession {
|
||||
CdmEncryptionAlgorithm algorithm);
|
||||
size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm);
|
||||
|
||||
// These methods are used when a subsample exceeds the maximum buffer size
|
||||
// that the device can handle.
|
||||
OEMCryptoResult CopyBufferInChunks(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor);
|
||||
OEMCryptoResult DecryptInChunks(
|
||||
const CdmDecryptionParameters& params,
|
||||
const OEMCrypto_DestBufferDesc& full_buffer_descriptor,
|
||||
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor,
|
||||
size_t max_chunk_size);
|
||||
static void IncrementIV(uint64_t increase_by, std::vector<uint8_t>* iv_out);
|
||||
|
||||
static const size_t kAes128BlockSize = 16; // Block size for AES_CBC_128
|
||||
static const size_t kSignatureSize = 32; // size for HMAC-SHA256 signature
|
||||
static Lock crypto_lock_;
|
||||
@@ -150,6 +169,7 @@ class CryptoSession {
|
||||
static int session_count_;
|
||||
|
||||
bool open_;
|
||||
CdmClientTokenType pre_provision_token_type_;
|
||||
bool update_usage_table_after_close_session_;
|
||||
CryptoSessionId oec_session_id_;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "device_files.pb.h"
|
||||
#include "scoped_ptr.h"
|
||||
@@ -39,7 +40,9 @@ class DeviceFiles {
|
||||
virtual bool StoreCertificate(const std::string& certificate,
|
||||
const std::string& wrapped_private_key);
|
||||
virtual bool RetrieveCertificate(std::string* certificate,
|
||||
std::string* wrapped_private_key);
|
||||
std::string* wrapped_private_key,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id);
|
||||
virtual bool HasCertificate();
|
||||
virtual bool RemoveCertificate();
|
||||
|
||||
@@ -53,6 +56,7 @@ class DeviceFiles {
|
||||
const std::string& release_server_url,
|
||||
int64_t playback_start_time,
|
||||
int64_t last_playback_time,
|
||||
int64_t grace_period_end_time,
|
||||
const CdmAppParameterMap& app_parameters);
|
||||
virtual bool RetrieveLicense(
|
||||
const std::string& key_set_id, LicenseState* state,
|
||||
@@ -60,8 +64,9 @@ class DeviceFiles {
|
||||
CdmKeyResponse* key_response, CdmKeyMessage* key_renewal_request,
|
||||
CdmKeyResponse* key_renewal_response, std::string* release_server_url,
|
||||
int64_t* playback_start_time, int64_t* last_playback_time,
|
||||
CdmAppParameterMap* app_parameters);
|
||||
int64_t* grace_period_end_time, CdmAppParameterMap* app_parameters);
|
||||
virtual bool DeleteLicense(const std::string& key_set_id);
|
||||
virtual bool ListLicenses(std::vector<std::string>* key_set_ids);
|
||||
virtual bool DeleteAllFiles();
|
||||
virtual bool DeleteAllLicenses();
|
||||
virtual bool LicenseExists(const std::string& key_set_id);
|
||||
@@ -106,6 +111,11 @@ class DeviceFiles {
|
||||
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,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id);
|
||||
|
||||
// Helpers that wrap the File interface and automatically handle hashing, as
|
||||
// well as adding the device files base path to to the file name.
|
||||
bool StoreFileWithHash(const std::string& name,
|
||||
@@ -115,6 +125,7 @@ class DeviceFiles {
|
||||
bool RetrieveHashedFile(const std::string& name,
|
||||
video_widevine_client::sdk::File* file);
|
||||
bool FileExists(const std::string& name);
|
||||
bool ListFiles(std::vector<std::string>* names);
|
||||
bool RemoveFile(const std::string& name);
|
||||
ssize_t GetFileSize(const std::string& name);
|
||||
|
||||
|
||||
@@ -55,12 +55,22 @@ class FileSystem {
|
||||
virtual bool Remove(const std::string& file_path);
|
||||
virtual ssize_t FileSize(const std::string& file_path);
|
||||
|
||||
// Return the filenames stored at dir_path.
|
||||
// dir_path will be stripped from the returned names.
|
||||
virtual bool List(const std::string& dir_path,
|
||||
std::vector<std::string>* names);
|
||||
|
||||
const std::string& origin() const { return origin_; }
|
||||
void SetOrigin(const std::string& origin);
|
||||
|
||||
const std::string& identifier() const { return identifier_; }
|
||||
void SetIdentifier(const std::string& identifier);
|
||||
bool IsGlobal() const { return identifier_.empty(); }
|
||||
|
||||
private:
|
||||
Impl* impl_;
|
||||
std::string origin_;
|
||||
std::string identifier_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(FileSystem);
|
||||
};
|
||||
|
||||
@@ -6,32 +6,34 @@
|
||||
#include <set>
|
||||
|
||||
#include "initialization_data.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace video_widevine_server {
|
||||
namespace sdk {
|
||||
namespace video_widevine {
|
||||
class SignedMessage;
|
||||
class LicenseRequest;
|
||||
}
|
||||
} // namespace video_widevine_server
|
||||
} // namespace video_widevine
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class Clock;
|
||||
class CryptoSession;
|
||||
class PolicyEngine;
|
||||
class ServiceCertificate;
|
||||
|
||||
class CdmLicense {
|
||||
public:
|
||||
CdmLicense(const CdmSessionId& session_id);
|
||||
virtual ~CdmLicense();
|
||||
|
||||
virtual bool Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine);
|
||||
virtual bool Init(
|
||||
ServiceCertificate* service_certificate, const std::string& client_token,
|
||||
CdmClientTokenType client_token_type, const std::string& serial_number,
|
||||
CryptoSession* session, PolicyEngine* policy_engine);
|
||||
|
||||
virtual CdmResponseType PrepareKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
virtual CdmResponseType PrepareKeyUpdateRequest(
|
||||
@@ -46,7 +48,8 @@ class CdmLicense {
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response,
|
||||
int64_t playback_start_time, int64_t last_playback_time);
|
||||
int64_t playback_start_time, int64_t last_playback_time,
|
||||
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(); }
|
||||
@@ -60,33 +63,33 @@ class CdmLicense {
|
||||
return is_offline_;
|
||||
}
|
||||
|
||||
static CdmResponseType VerifySignedServiceCertificate(
|
||||
const std::string& signed_service_certificate);
|
||||
|
||||
private:
|
||||
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
|
||||
CdmResponseType HandleKeyErrorResponse(
|
||||
const video_widevine_server::sdk::SignedMessage& signed_message);
|
||||
const video_widevine::SignedMessage& signed_message);
|
||||
|
||||
bool GetClientTokenType(
|
||||
video_widevine::ClientIdentification::TokenType* token_type);
|
||||
|
||||
CdmResponseType PrepareClientId(
|
||||
bool encrypt, const std::string& certificate,
|
||||
const CdmAppParameterMap& app_parameters,
|
||||
video_widevine_server::sdk::LicenseRequest* license_request);
|
||||
video_widevine::LicenseRequest* license_request);
|
||||
|
||||
CdmResponseType PrepareContentId(
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const std::string& request_id,
|
||||
video_widevine::LicenseRequest* license_request);
|
||||
|
||||
template <typename T>
|
||||
bool PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id);
|
||||
bool SetTypeAndId(CdmLicenseType license_type,
|
||||
const std::string& request_id, T* content_id);
|
||||
|
||||
static CdmResponseType VerifyAndExtractSignedServiceCertificate(
|
||||
const std::string& signed_service_certificate,
|
||||
std::string* service_certificate);
|
||||
bool GetServiceCertificate(std::string* service_certificate);
|
||||
|
||||
CryptoSession* session_;
|
||||
CryptoSession* crypto_session_;
|
||||
PolicyEngine* policy_engine_;
|
||||
std::string server_url_;
|
||||
std::string token_;
|
||||
std::string client_token_;
|
||||
CdmClientTokenType client_token_type_;
|
||||
std::string serial_number_;
|
||||
const CdmSessionId session_id_;
|
||||
scoped_ptr<InitializationData> stored_init_data_;
|
||||
bool initialized_;
|
||||
@@ -95,6 +98,9 @@ class CdmLicense {
|
||||
bool renew_with_client_id_;
|
||||
bool is_offline_;
|
||||
|
||||
// Used to encrypt ClientIdentification message
|
||||
ServiceCertificate* service_certificate_;
|
||||
|
||||
// Used for certificate based licensing
|
||||
CdmKeyMessage key_request_;
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@ class LicenseKeys {
|
||||
// Extracts the keys from a license and makes them available for
|
||||
// querying usage and constraint settings.
|
||||
virtual void SetFromLicense(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
const video_widevine::License& license);
|
||||
|
||||
private:
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
typedef std::map<wvcdm::KeyId, LicenseKeyStatus*>::const_iterator
|
||||
LicenseKeyStatusIterator;
|
||||
|
||||
@@ -101,7 +101,7 @@ class LicenseKeyStatus {
|
||||
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level);
|
||||
|
||||
protected:
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
typedef KeyContainer::OperatorSessionKeyPermissions
|
||||
OperatorSessionKeyPermissions;
|
||||
typedef KeyContainer::OutputProtection OutputProtection;
|
||||
|
||||
@@ -35,6 +35,8 @@ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(SecurityLevel level,
|
||||
OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level,
|
||||
size_t* maximum);
|
||||
uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level);
|
||||
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
|
||||
SecurityLevel level);
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
using video_widevine::LicenseIdentification;
|
||||
|
||||
class Clock;
|
||||
class CryptoSession;
|
||||
@@ -32,7 +32,7 @@ class PolicyEngine {
|
||||
// UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync
|
||||
// depending on the amount of time elapsed. The current decryption
|
||||
// status is not calculated to avoid overhead in the decryption path.
|
||||
virtual bool CanDecrypt(const KeyId& key_id);
|
||||
virtual bool CanDecryptContent(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
|
||||
@@ -43,12 +43,12 @@ class PolicyEngine {
|
||||
// an exact copy of the policy information stored in the license.
|
||||
// The license state transitions to kLicenseStateCanPlay if the license
|
||||
// permits playback.
|
||||
virtual void SetLicense(const video_widevine_server::sdk::License& license);
|
||||
virtual void SetLicense(const video_widevine::License& license);
|
||||
|
||||
// SetLicenseForRelease is used when releasing a license. The keys in this
|
||||
// license will be ignored, and any old keys will be expired.
|
||||
virtual void SetLicenseForRelease(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
const video_widevine::License& license);
|
||||
|
||||
// Call this on first decrypt to set the start of playback.
|
||||
virtual void BeginDecryption(void);
|
||||
@@ -60,7 +60,7 @@ class PolicyEngine {
|
||||
// updated license_start_time from the server. The license will transition to
|
||||
// kLicenseStateCanPlay if the license permits playback.
|
||||
virtual void UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license);
|
||||
const video_widevine::License& license);
|
||||
|
||||
// Used for notifying the Policy Engine of resolution changes
|
||||
virtual void NotifyResolution(uint32_t width, uint32_t height);
|
||||
@@ -80,13 +80,21 @@ class PolicyEngine {
|
||||
// for offline save and restore
|
||||
int64_t GetPlaybackStartTime() { return playback_start_time_; }
|
||||
int64_t GetLastPlaybackTime() { return last_playback_time_; }
|
||||
int64_t GetGracePeriodEndTime() { return grace_period_end_time_; }
|
||||
void RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time);
|
||||
int64_t last_playback_time,
|
||||
int64_t grace_period_end_time);
|
||||
|
||||
bool IsLicenseForFuture() { return license_state_ == kLicenseStatePending; }
|
||||
bool IsPlaybackStarted() { return playback_start_time_ > 0; }
|
||||
bool HasPlaybackStarted(int64_t current_time) {
|
||||
if (playback_start_time_ == 0)
|
||||
return false;
|
||||
|
||||
bool IsLicenseOrPlaybackDurationExpired(int64_t current_time);
|
||||
const int64_t playback_time = current_time - playback_start_time_;
|
||||
return playback_time >= policy_.play_start_grace_period_seconds();
|
||||
}
|
||||
|
||||
bool HasLicenseOrPlaybackDurationExpired(int64_t current_time);
|
||||
int64_t GetLicenseOrPlaybackDurationRemaining();
|
||||
|
||||
bool CanRenew() { return policy_.can_renew(); }
|
||||
@@ -111,15 +119,22 @@ class PolicyEngine {
|
||||
kLicenseStateExpired
|
||||
} LicenseState;
|
||||
|
||||
int64_t GetLicenseExpiryTime();
|
||||
int64_t GetPlaybackExpiryTime();
|
||||
// Gets the clock time that the license expires. This is the hard limit that
|
||||
// all license types must obey at all times.
|
||||
int64_t GetHardLicenseExpiryTime();
|
||||
// Gets the clock time that the rental duration will expire, using the license
|
||||
// duration if one is not present.
|
||||
int64_t GetRentalExpiryTime();
|
||||
// Gets the clock time that the license expires based on whether we have
|
||||
// started playing. This takes into account GetHardLicenseExpiryTime.
|
||||
int64_t GetExpiryTime(int64_t current_time, bool is_load);
|
||||
|
||||
int64_t GetLicenseDurationRemaining(int64_t current_time);
|
||||
int64_t GetLicenseOrRentalDurationRemaining(int64_t current_time);
|
||||
int64_t GetPlaybackDurationRemaining(int64_t current_time);
|
||||
|
||||
bool IsRenewalDelayExpired(int64_t current_time);
|
||||
bool IsRenewalRecoveryDurationExpired(int64_t current_time);
|
||||
bool IsRenewalRetryIntervalExpired(int64_t current_time);
|
||||
bool HasRenewalDelayExpired(int64_t current_time);
|
||||
bool HasRenewalRecoveryDurationExpired(int64_t current_time);
|
||||
bool HasRenewalRetryIntervalExpired(int64_t current_time);
|
||||
|
||||
void UpdateRenewalRequest(int64_t current_time);
|
||||
|
||||
@@ -129,7 +144,7 @@ class PolicyEngine {
|
||||
|
||||
// Notifies updates in expiry time and fire OnExpirationUpdate event if
|
||||
// expiry time changes.
|
||||
void NotifyExpirationUpdate();
|
||||
void NotifyExpirationUpdate(int64_t current_time);
|
||||
|
||||
// set_clock() is for testing only. It alters ownership of the
|
||||
// passed-in pointer.
|
||||
@@ -139,12 +154,12 @@ class PolicyEngine {
|
||||
|
||||
// This is the current policy information for this license. This gets updated
|
||||
// as license renewals occur.
|
||||
video_widevine_server::sdk::License::Policy policy_;
|
||||
video_widevine::License::Policy policy_;
|
||||
|
||||
// This is the license id field from server response. This data gets passed
|
||||
// back to the server in each renewal request. When we get a renewal response
|
||||
// from the license server we will get an updated id field.
|
||||
video_widevine_server::sdk::LicenseIdentification license_id_;
|
||||
video_widevine::LicenseIdentification license_id_;
|
||||
|
||||
// The server returns the license start time in the license/license renewal
|
||||
// response based off the request time sent by the client in the
|
||||
@@ -153,13 +168,14 @@ class PolicyEngine {
|
||||
int64_t playback_start_time_;
|
||||
int64_t last_playback_time_;
|
||||
int64_t last_expiry_time_;
|
||||
int64_t grace_period_end_time_;
|
||||
bool last_expiry_time_set_;
|
||||
bool was_expired_on_load_;
|
||||
|
||||
// This is used as a reference point for policy management. This value
|
||||
// represents an offset from license_start_time_. This is used to
|
||||
// calculate the time where renewal retries should occur.
|
||||
int64_t next_renewal_time_;
|
||||
int64_t policy_max_duration_seconds_;
|
||||
|
||||
// Used to dispatch CDM events.
|
||||
CdmSessionId session_id_;
|
||||
|
||||
@@ -55,6 +55,7 @@ class Properties {
|
||||
static bool GetFactoryKeyboxPath(std::string* keybox);
|
||||
static bool GetOEMCryptoPath(std::string* library_name);
|
||||
static bool AlwaysUseKeySetIds();
|
||||
static bool UseProviderIdInProvisioningRequest();
|
||||
|
||||
static bool GetSecurityLevelDirectories(std::vector<std::string>* dirs);
|
||||
static bool GetApplicationId(const CdmSessionId& session_id,
|
||||
@@ -63,6 +64,10 @@ class Properties {
|
||||
std::string* service_certificate);
|
||||
static bool SetServiceCertificate(const CdmSessionId& session_id,
|
||||
const std::string& service_certificate);
|
||||
static bool GetDeviceProvisioningServiceCertificate(
|
||||
const CdmSessionId& session_id, std::string* service_certificate);
|
||||
static bool SetDeviceProvisioningServiceCertificate(
|
||||
const CdmSessionId& session_id, const std::string& service_certificate);
|
||||
static bool UsePrivacyMode(const CdmSessionId& session_id);
|
||||
static uint32_t GetSessionSharingId(const CdmSessionId& session_id);
|
||||
|
||||
@@ -91,6 +96,7 @@ class Properties {
|
||||
}
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
FRIEND_TEST(CdmSessionTest, InitWithBuiltInCertificate);
|
||||
FRIEND_TEST(CdmSessionTest, InitWithCertificate);
|
||||
FRIEND_TEST(CdmSessionTest, InitWithKeybox);
|
||||
FRIEND_TEST(CdmSessionTest, ReInitFail);
|
||||
|
||||
85
core/include/service_certificate.h
Normal file
85
core/include/service_certificate.h
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
#ifndef WVCDM_CORE_SERVICE_CERTIFICATE_H_
|
||||
#define WVCDM_CORE_SERVICE_CERTIFICATE_H_
|
||||
|
||||
// Service Certificates are used to encrypt the ClientIdentification message
|
||||
// that is part of Device Provisioning, License, Renewal, and Release requests.
|
||||
// It also supplies a provider_id setting used in device provisioning.
|
||||
// Service Certificates are typically supplied by the application. If one
|
||||
// is not supplied and privacy mode is enabled, the CDM will send a Service
|
||||
// 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"
|
||||
|
||||
namespace video_widevine {
|
||||
class SignedMessage;
|
||||
class LicenseRequest;
|
||||
} // namespace video_widevine
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
class CryptoSession;
|
||||
|
||||
class ServiceCertificate {
|
||||
public:
|
||||
ServiceCertificate() {}
|
||||
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();
|
||||
|
||||
// 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_; }
|
||||
|
||||
// Encrypt the ClientIdentification message for a provisioning or
|
||||
// licensing request. Encryption is performed using the current
|
||||
// service certificate. Return a failure if the service certificate is
|
||||
// not present, not valid, or if some other error occurs.
|
||||
// The routine should not be called if privacy mode is off or if the
|
||||
// certificate is empty.
|
||||
virtual CdmResponseType EncryptClientId(
|
||||
CryptoSession* crypto_session,
|
||||
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_;
|
||||
|
||||
// Certificate, verified and extracted from signed message.
|
||||
std::string certificate_;
|
||||
|
||||
// Provider ID, extracted from certificate message.
|
||||
std::string provider_id_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(ServiceCertificate);
|
||||
};
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_SERVICE_CERTIFICATE_H_
|
||||
@@ -14,6 +14,10 @@ static const size_t KEY_SIZE = 16;
|
||||
static const size_t MAC_KEY_SIZE = 32;
|
||||
static const size_t KEYBOX_KEY_DATA_SIZE = 72;
|
||||
|
||||
// Initial estimate of certificate size. Code that
|
||||
// uses this estimate should be able to adapt to a larger or smaller size.
|
||||
static const size_t CERTIFICATE_DATA_SIZE = 4 * 1024;
|
||||
|
||||
// Use 0 to represent never expired license as specified in EME spec
|
||||
// (NaN in JS translates to 0 in unix timestamp).
|
||||
static const int64_t NEVER_EXPIRES = 0;
|
||||
@@ -32,9 +36,9 @@ static const std::string QUERY_KEY_PERSIST_ALLOWED = "PersistAllowed";
|
||||
static const std::string QUERY_KEY_RENEW_ALLOWED = "RenewAllowed";
|
||||
// "True", "False"
|
||||
static const std::string QUERY_KEY_LICENSE_DURATION_REMAINING =
|
||||
"LicenseDurationRemaining"; // non-negative integer
|
||||
"LicenseDurationRemaining"; // non-negative integer denoting seconds
|
||||
static const std::string QUERY_KEY_PLAYBACK_DURATION_REMAINING =
|
||||
"PlaybackDurationRemaining"; // non-negative integer
|
||||
"PlaybackDurationRemaining"; // non-negative integer denoting seconds
|
||||
static const std::string QUERY_KEY_RENEWAL_SERVER_URL = "RenewalServerUrl";
|
||||
// url
|
||||
static const std::string QUERY_KEY_OEMCRYPTO_SESSION_ID = "OemCryptoSessionId";
|
||||
@@ -94,6 +98,7 @@ static const std::string HLS_IV_ATTRIBUTE = "IV";
|
||||
static const std::string HLS_URI_ATTRIBUTE = "URI";
|
||||
|
||||
static const char EMPTY_ORIGIN[] = "";
|
||||
static const char EMPTY_SPOID[] = "";
|
||||
} // namespace wvcdm
|
||||
|
||||
#endif // WVCDM_CORE_WV_CDM_CONSTANTS_H_
|
||||
|
||||
@@ -37,214 +37,220 @@ enum CdmKeyRequestType {
|
||||
};
|
||||
|
||||
enum CdmResponseType {
|
||||
NO_ERROR,
|
||||
NO_ERROR, /* 0 */
|
||||
UNKNOWN_ERROR,
|
||||
KEY_ADDED,
|
||||
KEY_ERROR,
|
||||
KEY_MESSAGE,
|
||||
NEED_KEY,
|
||||
NEED_KEY, /* 5 */
|
||||
KEY_CANCELED,
|
||||
NEED_PROVISIONING,
|
||||
DEVICE_REVOKED,
|
||||
INSUFFICIENT_CRYPTO_RESOURCES,
|
||||
ADD_KEY_ERROR,
|
||||
ADD_KEY_ERROR, /* 10 */
|
||||
CERT_PROVISIONING_GET_KEYBOX_ERROR_1,
|
||||
CERT_PROVISIONING_GET_KEYBOX_ERROR_2,
|
||||
CERT_PROVISIONING_INVALID_CERT_TYPE,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_1,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_2,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_2, /* 15 */
|
||||
CERT_PROVISIONING_REQUEST_ERROR_3,
|
||||
CERT_PROVISIONING_REQUEST_ERROR_4,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_1,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_2,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_3,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_3, /* 20 */
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_4,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_5,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_6,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_7,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_8,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_8, /* 25 */
|
||||
CRYPTO_SESSION_OPEN_ERROR_1,
|
||||
CRYPTO_SESSION_OPEN_ERROR_2,
|
||||
CRYPTO_SESSION_OPEN_ERROR_3,
|
||||
CRYPTO_SESSION_OPEN_ERROR_4,
|
||||
CRYPTO_SESSION_OPEN_ERROR_5,
|
||||
CRYPTO_SESSION_OPEN_ERROR_5, /* 30 */
|
||||
DECRYPT_NOT_READY,
|
||||
DEVICE_CERTIFICATE_ERROR_1,
|
||||
DEVICE_CERTIFICATE_ERROR_2,
|
||||
DEVICE_CERTIFICATE_ERROR_3,
|
||||
DEVICE_CERTIFICATE_ERROR_4,
|
||||
DEVICE_CERTIFICATE_ERROR_4, /* 35 */
|
||||
EMPTY_KEY_DATA_1,
|
||||
EMPTY_KEY_DATA_2,
|
||||
EMPTY_KEYSET_ID,
|
||||
EMPTY_KEYSET_ID_ENG_1,
|
||||
EMPTY_KEYSET_ID_ENG_2,
|
||||
EMPTY_KEYSET_ID_ENG_2, /* 40 */
|
||||
EMPTY_KEYSET_ID_ENG_3,
|
||||
EMPTY_KEYSET_ID_ENG_4,
|
||||
EMPTY_LICENSE_RENEWAL,
|
||||
EMPTY_LICENSE_RESPONSE_1,
|
||||
EMPTY_LICENSE_RESPONSE_2,
|
||||
EMPTY_LICENSE_RESPONSE_2, /* 45 */
|
||||
EMPTY_PROVISIONING_CERTIFICATE_1,
|
||||
EMPTY_PROVISIONING_RESPONSE,
|
||||
EMPTY_SESSION_ID,
|
||||
GENERATE_DERIVED_KEYS_ERROR,
|
||||
LICENSE_RENEWAL_NONCE_GENERATION_ERROR,
|
||||
LICENSE_RENEWAL_NONCE_GENERATION_ERROR, /* 50 */
|
||||
GENERATE_USAGE_REPORT_ERROR,
|
||||
GET_LICENSE_ERROR,
|
||||
GET_RELEASED_LICENSE_ERROR,
|
||||
GET_USAGE_INFO_ERROR_1,
|
||||
GET_USAGE_INFO_ERROR_2,
|
||||
GET_USAGE_INFO_ERROR_2, /* 55 */
|
||||
GET_USAGE_INFO_ERROR_3,
|
||||
GET_USAGE_INFO_ERROR_4,
|
||||
INIT_DATA_NOT_FOUND,
|
||||
INVALID_CRYPTO_SESSION_1,
|
||||
INVALID_CRYPTO_SESSION_2,
|
||||
INVALID_CRYPTO_SESSION_2, /* 60 */
|
||||
INVALID_CRYPTO_SESSION_3,
|
||||
INVALID_CRYPTO_SESSION_4,
|
||||
INVALID_CRYPTO_SESSION_5,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_1,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_2,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_2, /* 65 */
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_3,
|
||||
INVALID_DECRYPT_PARAMETERS_ENG_4,
|
||||
INVALID_DEVICE_CERTIFICATE_TYPE,
|
||||
INVALID_KEY_SYSTEM,
|
||||
INVALID_LICENSE_RESPONSE,
|
||||
INVALID_LICENSE_RESPONSE, /* 70 */
|
||||
INVALID_LICENSE_TYPE,
|
||||
INVALID_PARAMETERS_ENG_1,
|
||||
INVALID_PARAMETERS_ENG_2,
|
||||
INVALID_PARAMETERS_ENG_3,
|
||||
INVALID_PARAMETERS_ENG_4,
|
||||
INVALID_PARAMETERS_ENG_4, /* 75 */
|
||||
INVALID_PARAMETERS_LIC_1,
|
||||
INVALID_PARAMETERS_LIC_2,
|
||||
INVALID_PROVISIONING_PARAMETERS_1,
|
||||
INVALID_PROVISIONING_PARAMETERS_2,
|
||||
INVALID_PROVISIONING_REQUEST_PARAM_1,
|
||||
INVALID_PROVISIONING_REQUEST_PARAM_1, /* 80 */
|
||||
INVALID_PROVISIONING_REQUEST_PARAM_2,
|
||||
INVALID_QUERY_KEY,
|
||||
INVALID_SESSION_ID,
|
||||
KEY_REQUEST_ERROR_1,
|
||||
UNUSED_1, /* previously KEY_REQUEST_ERROR_2 */
|
||||
UNUSED_1, /* previously KEY_REQUEST_ERROR_2 */ /* 85 */
|
||||
KEY_SIZE_ERROR,
|
||||
KEYSET_ID_NOT_FOUND_1,
|
||||
KEYSET_ID_NOT_FOUND_2,
|
||||
KEYSET_ID_NOT_FOUND_3,
|
||||
LICENSE_ID_NOT_FOUND,
|
||||
LICENSE_ID_NOT_FOUND, /* 90 */
|
||||
LICENSE_PARSER_INIT_ERROR,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_1,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_2,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_3,
|
||||
LICENSE_RESPONSE_NOT_SIGNED,
|
||||
LICENSE_RESPONSE_NOT_SIGNED, /* 95 */
|
||||
LICENSE_RESPONSE_PARSE_ERROR_1,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_2,
|
||||
LICENSE_RESPONSE_PARSE_ERROR_3,
|
||||
LOAD_KEY_ERROR,
|
||||
NO_CONTENT_KEY,
|
||||
NO_CONTENT_KEY, /* 100 */
|
||||
REFRESH_KEYS_ERROR,
|
||||
RELEASE_ALL_USAGE_INFO_ERROR_1,
|
||||
RELEASE_ALL_USAGE_INFO_ERROR_2,
|
||||
RELEASE_KEY_ERROR,
|
||||
RELEASE_KEY_REQUEST_ERROR,
|
||||
RELEASE_KEY_REQUEST_ERROR, /* 105 */
|
||||
RELEASE_LICENSE_ERROR_1,
|
||||
RELEASE_LICENSE_ERROR_2,
|
||||
RELEASE_USAGE_INFO_ERROR,
|
||||
RENEW_KEY_ERROR_1,
|
||||
RENEW_KEY_ERROR_2,
|
||||
RENEW_KEY_ERROR_2, /* 110 */
|
||||
LICENSE_RENEWAL_SIGNING_ERROR,
|
||||
UNUSED_4, /* previously RESTORE_OFFLINE_LICENSE_ERROR_1 */
|
||||
RESTORE_OFFLINE_LICENSE_ERROR_2,
|
||||
UNUSED_5, /* SESSION_INIT_ERROR_1 */
|
||||
SESSION_INIT_ERROR_2,
|
||||
SESSION_INIT_GET_KEYBOX_ERROR,
|
||||
SESSION_INIT_ERROR_1,
|
||||
SESSION_INIT_ERROR_2, /* 115 */
|
||||
UNUSED_5, /* previously SESSION_INIT_GET_KEYBOX_ERROR */
|
||||
SESSION_NOT_FOUND_1,
|
||||
SESSION_NOT_FOUND_2,
|
||||
SESSION_NOT_FOUND_3,
|
||||
SESSION_NOT_FOUND_4,
|
||||
SESSION_NOT_FOUND_4, /* 120 */
|
||||
SESSION_NOT_FOUND_5,
|
||||
SESSION_NOT_FOUND_6,
|
||||
SESSION_NOT_FOUND_7,
|
||||
SESSION_NOT_FOUND_8,
|
||||
SESSION_NOT_FOUND_9,
|
||||
SESSION_NOT_FOUND_9, /* 125 */
|
||||
SESSION_NOT_FOUND_10,
|
||||
SESSION_NOT_FOUND_FOR_DECRYPT,
|
||||
SESSION_KEYS_NOT_FOUND,
|
||||
SIGNATURE_NOT_FOUND,
|
||||
STORE_LICENSE_ERROR_1,
|
||||
STORE_LICENSE_ERROR_1, /* 130 */
|
||||
STORE_LICENSE_ERROR_2,
|
||||
UNUSED_6, /* previously STORE_LICENSE_ERROR_3 */
|
||||
STORE_USAGE_INFO_ERROR,
|
||||
UNPROVISION_ERROR_1,
|
||||
UNPROVISION_ERROR_2,
|
||||
UNPROVISION_ERROR_2, /* 135 */
|
||||
UNPROVISION_ERROR_3,
|
||||
UNPROVISION_ERROR_4,
|
||||
UNSUPPORTED_INIT_DATA,
|
||||
USAGE_INFO_NOT_FOUND,
|
||||
LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR,
|
||||
LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR, /* 140 */
|
||||
PARSE_SERVICE_CERTIFICATE_ERROR,
|
||||
SERVICE_CERTIFICATE_TYPE_ERROR,
|
||||
CLIENT_ID_GENERATE_RANDOM_ERROR,
|
||||
CLIENT_ID_AES_INIT_ERROR,
|
||||
CLIENT_ID_AES_ENCRYPT_ERROR,
|
||||
CLIENT_ID_AES_ENCRYPT_ERROR, /* 145 */
|
||||
CLIENT_ID_RSA_INIT_ERROR,
|
||||
CLIENT_ID_RSA_ENCRYPT_ERROR,
|
||||
INVALID_QUERY_STATUS,
|
||||
UNUSED_3, /* previously EMPTY_PROVISIONING_CERTIFICATE_2 on mnc-dev, */
|
||||
/* DUPLICATE_SESSION_ID_SPECIFIED on master */
|
||||
LICENSE_PARSER_NOT_INITIALIZED_4,
|
||||
LICENSE_PARSER_NOT_INITIALIZED_4, /* 150 */
|
||||
INVALID_PARAMETERS_LIC_3,
|
||||
INVALID_PARAMETERS_LIC_4,
|
||||
UNUSED_2, /* previously INVALID_PARAMETERS_LIC_5 */
|
||||
INVALID_PARAMETERS_LIC_6,
|
||||
INVALID_PARAMETERS_LIC_7,
|
||||
INVALID_PARAMETERS_LIC_7, /* 155 */
|
||||
LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR,
|
||||
CENC_INIT_DATA_UNAVAILABLE,
|
||||
PREPARE_CENC_CONTENT_ID_FAILED,
|
||||
WEBM_INIT_DATA_UNAVAILABLE,
|
||||
PREPARE_WEBM_CONTENT_ID_FAILED,
|
||||
PREPARE_WEBM_CONTENT_ID_FAILED, /* 160 */
|
||||
UNSUPPORTED_INIT_DATA_FORMAT,
|
||||
LICENSE_REQUEST_NONCE_GENERATION_ERROR,
|
||||
LICENSE_REQUEST_SIGNING_ERROR,
|
||||
EMPTY_LICENSE_REQUEST,
|
||||
SECURE_BUFFER_REQUIRED,
|
||||
SECURE_BUFFER_REQUIRED, /* 165 */
|
||||
DUPLICATE_SESSION_ID_SPECIFIED,
|
||||
LICENSE_RENEWAL_PROHIBITED,
|
||||
EMPTY_PROVISIONING_CERTIFICATE_2,
|
||||
OFFLINE_LICENSE_PROHIBITED,
|
||||
STORAGE_PROHIBITED,
|
||||
STORAGE_PROHIBITED, /* 170 */
|
||||
EMPTY_KEYSET_ID_ENG_5,
|
||||
SESSION_NOT_FOUND_11,
|
||||
LOAD_USAGE_INFO_FILE_ERROR,
|
||||
LOAD_USAGE_INFO_MISSING,
|
||||
SESSION_FILE_HANDLE_INIT_ERROR,
|
||||
SESSION_FILE_HANDLE_INIT_ERROR, /* 175 */
|
||||
INCORRECT_CRYPTO_MODE,
|
||||
INVALID_PARAMETERS_ENG_5,
|
||||
DECRYPT_ERROR,
|
||||
INSUFFICIENT_OUTPUT_PROTECTION,
|
||||
SESSION_NOT_FOUND_12,
|
||||
SESSION_NOT_FOUND_12, /* 180 */
|
||||
KEY_NOT_FOUND_1,
|
||||
KEY_NOT_FOUND_2,
|
||||
KEY_CONFLICT_1,
|
||||
INVALID_PARAMETERS_ENG_6,
|
||||
INVALID_PARAMETERS_ENG_7,
|
||||
INVALID_PARAMETERS_ENG_7, /* 185 */
|
||||
INVALID_PARAMETERS_ENG_8,
|
||||
INVALID_PARAMETERS_ENG_9,
|
||||
INVALID_PARAMETERS_ENG_10,
|
||||
INVALID_PARAMETERS_ENG_11,
|
||||
INVALID_PARAMETERS_ENG_11, /* 190 */
|
||||
INVALID_PARAMETERS_ENG_12,
|
||||
SESSION_NOT_FOUND_13,
|
||||
SESSION_NOT_FOUND_14,
|
||||
SESSION_NOT_FOUND_15,
|
||||
SESSION_NOT_FOUND_16,
|
||||
SESSION_NOT_FOUND_16, /* 195 */
|
||||
KEY_NOT_FOUND_3,
|
||||
KEY_NOT_FOUND_4,
|
||||
KEY_NOT_FOUND_5,
|
||||
KEY_NOT_FOUND_6,
|
||||
KEY_ERROR_1,
|
||||
KEY_ERROR_1, /* 200 */
|
||||
KEY_ERROR_2,
|
||||
KEY_ERROR_3,
|
||||
KEY_ERROR_4,
|
||||
INVALID_PARAMETERS_ENG_13,
|
||||
INVALID_PARAMETERS_ENG_14,
|
||||
INVALID_PARAMETERS_ENG_14, /* 205 */
|
||||
INVALID_PARAMETERS_ENG_15,
|
||||
INVALID_PARAMETERS_ENG_16,
|
||||
UNUSED_7, /* previously DEVICE_CERTIFICATE_ERROR_5 */
|
||||
CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1,
|
||||
CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2, /* 210 */
|
||||
LICENSING_CLIENT_TOKEN_ERROR_1,
|
||||
INVALID_PARAMETERS_ENG_17,
|
||||
STORE_LICENSE_ERROR_3,
|
||||
};
|
||||
|
||||
enum CdmKeyStatus {
|
||||
@@ -311,6 +317,12 @@ enum CdmSigningAlgorithm {
|
||||
kSigningAlgorithmHmacSha256
|
||||
};
|
||||
|
||||
enum CdmClientTokenType {
|
||||
kClientTokenKeybox,
|
||||
kClientTokenDrmCert,
|
||||
kClientTokenOemCert
|
||||
};
|
||||
|
||||
class CdmKeyAllowedUsage {
|
||||
public:
|
||||
CdmKeyAllowedUsage() {
|
||||
@@ -362,11 +374,9 @@ class CdmKeyAllowedUsage {
|
||||
struct CdmCencPatternEncryptionDescriptor {
|
||||
size_t encrypt_blocks; // number of 16 byte blocks to decrypt
|
||||
size_t skip_blocks; // number of 16 byte blocks to leave in clear
|
||||
size_t offset_blocks; // offset into the pattern for this call, in blocks
|
||||
CdmCencPatternEncryptionDescriptor()
|
||||
: encrypt_blocks(0),
|
||||
skip_blocks(0),
|
||||
offset_blocks(0) {}
|
||||
skip_blocks(0) {}
|
||||
};
|
||||
|
||||
struct CdmDecryptionParameters {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
namespace {
|
||||
const uint64_t kReleaseSessionTimeToLive = 60; // seconds
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
} // namespace
|
||||
@@ -54,17 +55,17 @@ class UsagePropertySet : public CdmClientPropertySet {
|
||||
|
||||
bool CdmEngine::seeded_ = false;
|
||||
|
||||
CdmEngine::CdmEngine(FileSystem* file_system)
|
||||
CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid)
|
||||
: cert_provisioning_(NULL),
|
||||
cert_provisioning_requested_security_level_(kLevelDefault),
|
||||
file_system_(file_system),
|
||||
spoid_(spoid),
|
||||
usage_session_(NULL),
|
||||
last_usage_information_update_time_(0) {
|
||||
assert(file_system);
|
||||
Properties::Init();
|
||||
if (!seeded_) {
|
||||
Clock clock;
|
||||
srand(clock.GetCurrentTime());
|
||||
srand(clock_.GetCurrentTime());
|
||||
seeded_ = true;
|
||||
}
|
||||
}
|
||||
@@ -78,27 +79,28 @@ CdmEngine::~CdmEngine() {
|
||||
sessions_.clear();
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
const CdmSessionId& forced_session_id,
|
||||
WvCdmEventListener* event_listener) {
|
||||
CdmResponseType CdmEngine::SetServiceCertificate(
|
||||
const std::string& certificate) {
|
||||
return service_certificate_.Init(certificate);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) {
|
||||
return OpenSession(key_system, property_set, event_listener,
|
||||
&forced_session_id, NULL);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
CdmSessionId* session_id) {
|
||||
return OpenSession(key_system, property_set, event_listener, NULL,
|
||||
session_id);
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener, CdmSessionId* session_id) {
|
||||
return OpenSession(key_system, property_set, event_listener, NULL, session_id);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id) {
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener, const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id) {
|
||||
LOGI("CdmEngine::OpenSession");
|
||||
|
||||
if (!ValidateKeySystem(key_system)) {
|
||||
@@ -117,10 +119,11 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
}
|
||||
}
|
||||
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
|
||||
CloseExpiredReleaseSessions();
|
||||
|
||||
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
|
||||
event_listener);
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_));
|
||||
CdmResponseType sts = new_session->Init(&service_certificate_, property_set,
|
||||
forced_session_id, event_listener);
|
||||
if (sts != NO_ERROR) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
@@ -151,13 +154,27 @@ CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
return EMPTY_KEYSET_ID_ENG_1;
|
||||
}
|
||||
|
||||
// If in-use, release key set before re-opening, to avoid leaking
|
||||
// resources (CryptoSession etc).
|
||||
bool key_set_in_use = false;
|
||||
{
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
key_set_in_use =
|
||||
release_key_sets_.find(key_set_id) != release_key_sets_.end();
|
||||
}
|
||||
if (key_set_in_use)
|
||||
CloseKeySetSession(key_set_id);
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmResponseType sts = OpenSession(KEY_SYSTEM, property_set, event_listener,
|
||||
NULL /* forced_session_id */, &session_id);
|
||||
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
release_key_sets_[key_set_id] = session_id;
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
release_key_sets_[key_set_id] = std::make_pair(session_id,
|
||||
clock_.GetCurrentTime() + kReleaseSessionTimeToLive);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -178,19 +195,30 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::CloseKeySetSession");
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
return KEYSET_ID_NOT_FOUND_1;
|
||||
CdmSessionId session_id;
|
||||
{
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
|
||||
key_set_id.c_str());
|
||||
return KEYSET_ID_NOT_FOUND_1;
|
||||
}
|
||||
session_id = iter->second.first;
|
||||
}
|
||||
|
||||
CdmResponseType sts = CloseSession(iter->second);
|
||||
release_key_sets_.erase(iter);
|
||||
CdmResponseType sts = CloseSession(session_id);
|
||||
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter != release_key_sets_.end()) {
|
||||
release_key_sets_.erase(iter);
|
||||
}
|
||||
return sts;
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
return iter != sessions_.end();
|
||||
}
|
||||
@@ -219,6 +247,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
return INVALID_SESSION_ID;
|
||||
}
|
||||
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
@@ -226,7 +255,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
return KEYSET_ID_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
id = iter->second.first;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
@@ -292,13 +321,14 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
return EMPTY_KEYSET_ID_ENG_3;
|
||||
}
|
||||
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
|
||||
return KEYSET_ID_NOT_FOUND_3;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
id = iter->second.first;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
@@ -466,7 +496,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
} else if (query_token == QUERY_KEY_DEVICE_ID) {
|
||||
std::string deviceId;
|
||||
if (!crypto_session.GetDeviceUniqueId(&deviceId)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetDeviceUniqueId failed");
|
||||
LOGW("CdmEngine::QueryStatus: QUERY_KEY_DEVICE_ID unknown failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -474,7 +504,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
} else if (query_token == QUERY_KEY_SYSTEM_ID) {
|
||||
uint32_t system_id;
|
||||
if (!crypto_session.GetSystemId(&system_id)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetSystemId failed");
|
||||
LOGW("CdmEngine::QueryStatus: QUERY_KEY_SYSTEM_ID unknown failure");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -681,11 +711,12 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
DeleteAllUsageReportsUponFactoryReset();
|
||||
|
||||
if (NULL == cert_provisioning_.get()) {
|
||||
cert_provisioning_.reset(new CertificateProvisioning());
|
||||
cert_provisioning_.reset(
|
||||
new CertificateProvisioning(&service_certificate_));
|
||||
}
|
||||
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
|
||||
cert_provisioning_requested_security_level_, cert_type, cert_authority,
|
||||
file_system_->origin(), request, default_url);
|
||||
file_system_->origin(), spoid_, request, default_url);
|
||||
if (ret != NO_ERROR) {
|
||||
cert_provisioning_.reset(NULL); // Release resources.
|
||||
}
|
||||
@@ -707,14 +738,14 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
cert_provisioning_.reset(NULL);
|
||||
return EMPTY_PROVISIONING_RESPONSE;
|
||||
}
|
||||
if (NULL == cert) {
|
||||
if (cert == NULL) {
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid certificate "
|
||||
"destination");
|
||||
cert_provisioning_.reset(NULL);
|
||||
return INVALID_PROVISIONING_PARAMETERS_1;
|
||||
}
|
||||
if (NULL == wrapped_key) {
|
||||
if (wrapped_key == NULL) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
cert_provisioning_.reset(NULL);
|
||||
@@ -768,7 +799,7 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
return UNPROVISION_ERROR_1;
|
||||
}
|
||||
|
||||
if (!file_system_->origin().empty()) {
|
||||
if (!file_system_->IsGlobal()) {
|
||||
if (!handle.RemoveCertificate()) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete certificate");
|
||||
return UNPROVISION_ERROR_2;
|
||||
@@ -779,22 +810,44 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) {
|
||||
LOGE("CdmEngine::Unprovision: unable to delete files");
|
||||
return UNPROVISION_ERROR_3;
|
||||
}
|
||||
|
||||
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
|
||||
CdmResponseType status = crypto_session->Open(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::Unprovision: error opening crypto session: %d", status);
|
||||
return UNPROVISION_ERROR_4;
|
||||
}
|
||||
status = crypto_session->DeleteAllUsageReports();
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("CdmEngine::Unprovision: error deleteing usage reports: %d", status);
|
||||
}
|
||||
return status;
|
||||
return DeleteUsageTable(security_level);
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::DeleteUsageTable(CdmSecurityLevel security_level) {
|
||||
scoped_ptr<CryptoSession> crypto_session(new CryptoSession());
|
||||
CdmResponseType status = crypto_session->Open(
|
||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("CdmEngine::DeleteUsageTable: error opening crypto session: %d",
|
||||
status);
|
||||
return UNPROVISION_ERROR_4;
|
||||
}
|
||||
status = crypto_session->DeleteAllUsageReports();
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("CdmEngine::DeleteUsageTable: error deleting usage reports: %d",
|
||||
status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
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");
|
||||
return INVALID_PARAMETERS_ENG_17;
|
||||
}
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("CdmEngine::ListStoredLicenses: unable to initialize device files");
|
||||
return STORE_LICENSE_ERROR_3;
|
||||
}
|
||||
if (!handle.ListLicenses(key_set_ids)) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
CdmUsageInfo* usage_info) {
|
||||
@@ -1234,12 +1287,14 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
return false;
|
||||
}
|
||||
|
||||
void CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
|
||||
@@ -1289,6 +1344,8 @@ void CdmEngine::OnTimerEvent() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseExpiredReleaseSessions();
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
@@ -1318,6 +1375,29 @@ std::string CdmEngine::MapHdcpVersion(
|
||||
return "";
|
||||
}
|
||||
|
||||
void CdmEngine::CloseExpiredReleaseSessions() {
|
||||
int64_t current_time = clock_.GetCurrentTime();
|
||||
|
||||
std::set<CdmSessionId> close_session_set;
|
||||
{
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
for (CdmReleaseKeySetMap::iterator iter = release_key_sets_.begin();
|
||||
iter != release_key_sets_.end();) {
|
||||
if (iter->second.second < current_time) {
|
||||
close_session_set.insert(iter->second.first);
|
||||
release_key_sets_.erase(iter++);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::set<CdmSessionId>::iterator iter = close_session_set.begin();
|
||||
iter != close_session_set.end(); ++iter) {
|
||||
CloseSession(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
|
||||
std::string device_base_path_level1 = "";
|
||||
std::string device_base_path_level3 = "";
|
||||
|
||||
@@ -50,12 +50,13 @@ CdmSession::~CdmSession() {
|
||||
|
||||
CdmResponseType CdmSession::Init(
|
||||
CdmClientPropertySet* cdm_client_property_set) {
|
||||
return Init(cdm_client_property_set, NULL, NULL);
|
||||
return Init(NULL, cdm_client_property_set, NULL, NULL);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id,
|
||||
WvCdmEventListener* event_listener) {
|
||||
CdmResponseType CdmSession::Init(
|
||||
ServiceCertificate* service_certificate,
|
||||
CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener) {
|
||||
if (initialized_) {
|
||||
LOGE("CdmSession::Init: Failed due to previous initialization");
|
||||
return SESSION_INIT_ERROR_2;
|
||||
@@ -76,18 +77,42 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
return SESSION_FILE_HANDLE_INIT_ERROR;
|
||||
}
|
||||
|
||||
std::string token;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
// Device Provisioning state is not yet known.
|
||||
// If not using certificates, then Keybox is client token for license
|
||||
// requests.
|
||||
// Otherwise, try to fetch device certificate. If not successful and
|
||||
// provisioning is supported, return NEED_PROVISIONING. Otherwise, return
|
||||
// an error.
|
||||
// client_token and client_token_type are determined here; they are needed
|
||||
// to initialize the license parser.
|
||||
std::string client_token;
|
||||
std::string serial_number;
|
||||
CdmClientTokenType client_token_type =
|
||||
crypto_session_->GetPreProvisionTokenType();
|
||||
if ((client_token_type == kClientTokenKeybox) &&
|
||||
!Properties::use_certificates_as_identification()) {
|
||||
// Keybox is client token.
|
||||
LOGW("CdmSession::Init: Properties::use_certificates_as_identification() "
|
||||
"is not set - using Keybox for license requests (not recommended).");
|
||||
if (!crypto_session_->GetClientToken(&client_token)) {
|
||||
return SESSION_INIT_ERROR_1;
|
||||
}
|
||||
} else {
|
||||
// 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;
|
||||
if (!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
|
||||
uint32_t system_id;
|
||||
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key,
|
||||
&serial_number, &system_id) ||
|
||||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
} else {
|
||||
if (!crypto_session_->GetToken(&token))
|
||||
return SESSION_INIT_GET_KEYBOX_ERROR;
|
||||
client_token_type = kClientTokenDrmCert;
|
||||
}
|
||||
|
||||
// Session is provisioned with certificate needed to construct
|
||||
// license request (or with keybox).
|
||||
|
||||
if (forced_session_id) {
|
||||
key_set_id_ = *forced_session_id;
|
||||
} else {
|
||||
@@ -112,8 +137,9 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
policy_engine_.reset(new PolicyEngine(
|
||||
session_id_, event_listener, crypto_session_.get()));
|
||||
|
||||
if (!license_parser_->Init(token, crypto_session_.get(),
|
||||
policy_engine_.get()))
|
||||
if (!license_parser_->Init(
|
||||
service_certificate, client_token, client_token_type,
|
||||
serial_number, crypto_session_.get(), policy_engine_.get()))
|
||||
return LICENSE_PARSER_INIT_ERROR;
|
||||
|
||||
license_received_ = false;
|
||||
@@ -129,12 +155,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
DeviceFiles::LicenseState license_state;
|
||||
int64_t playback_start_time;
|
||||
int64_t last_playback_time;
|
||||
int64_t grace_period_end_time;
|
||||
|
||||
if (!file_handle_->RetrieveLicense(
|
||||
key_set_id, &license_state, &offline_init_data_, &key_request_,
|
||||
&key_response_, &offline_key_renewal_request_,
|
||||
&offline_key_renewal_response_, &offline_release_server_url_,
|
||||
&playback_start_time, &last_playback_time, &app_parameters_)) {
|
||||
&playback_start_time, &last_playback_time, &grace_period_end_time,
|
||||
&app_parameters_)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return GET_LICENSE_ERROR;
|
||||
@@ -156,7 +184,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
} else {
|
||||
if (!license_parser_->RestoreOfflineLicense(
|
||||
key_request_, key_response_, offline_key_renewal_response_,
|
||||
playback_start_time, last_playback_time)) {
|
||||
playback_start_time, last_playback_time, grace_period_end_time)) {
|
||||
return RESTORE_OFFLINE_LICENSE_ERROR_2;
|
||||
}
|
||||
}
|
||||
@@ -297,13 +325,13 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
|
||||
|
||||
if (is_release_) {
|
||||
CdmResponseType sts = ReleaseKey(key_response);
|
||||
return (NO_ERROR == sts) ? KEY_ADDED : sts;
|
||||
return (sts == NO_ERROR) ? KEY_ADDED : sts;
|
||||
} else if (license_received_) { // renewal
|
||||
return RenewKey(key_response);
|
||||
} else {
|
||||
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
|
||||
|
||||
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? ADD_KEY_ERROR : sts;
|
||||
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? ADD_KEY_ERROR : sts;
|
||||
|
||||
license_received_ = true;
|
||||
key_response_ = key_response;
|
||||
@@ -393,7 +421,8 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
|
||||
// Playback may not begin until either the start time passes or the license
|
||||
// is updated, so we treat this Decrypt call as invalid.
|
||||
if (params.is_encrypted && !policy_engine_->CanDecrypt(*params.key_id)) {
|
||||
if (params.is_encrypted &&
|
||||
!policy_engine_->CanDecryptContent(*params.key_id)) {
|
||||
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
|
||||
}
|
||||
|
||||
@@ -412,7 +441,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
} else {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
if (policy_engine_->IsLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
if (policy_engine_->HasLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
return NEED_KEY;
|
||||
}
|
||||
}
|
||||
@@ -442,7 +471,7 @@ CdmResponseType CdmSession::GenerateRenewalRequest(
|
||||
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(true, key_response);
|
||||
if (sts != KEY_ADDED) return (KEY_ERROR == sts) ? RENEW_KEY_ERROR_1 : sts;
|
||||
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RENEW_KEY_ERROR_1 : sts;
|
||||
|
||||
if (is_offline_) {
|
||||
offline_key_renewal_response_ = key_response;
|
||||
@@ -474,7 +503,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts =
|
||||
license_parser_->HandleKeyUpdateResponse(false, key_response);
|
||||
if (KEY_ADDED != sts) return (KEY_ERROR == sts) ? RELEASE_KEY_ERROR : sts;
|
||||
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RELEASE_KEY_ERROR : sts;
|
||||
|
||||
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
|
||||
DeleteLicense();
|
||||
@@ -567,7 +596,8 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
key_set_id_, state, offline_init_data_, key_request_, key_response_,
|
||||
offline_key_renewal_request_, offline_key_renewal_response_,
|
||||
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
|
||||
policy_engine_->GetLastPlaybackTime(), app_parameters_);
|
||||
policy_engine_->GetLastPlaybackTime(),
|
||||
policy_engine_->GetGracePeriodEndTime(), app_parameters_);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::ReleaseCrypto() {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "file_store.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "service_certificate.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
@@ -21,11 +23,11 @@ const std::string kProvisioningServerUrl =
|
||||
|
||||
namespace wvcdm {
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::ProvisioningOptions;
|
||||
using video_widevine_server::sdk::ProvisioningRequest;
|
||||
using video_widevine_server::sdk::ProvisioningResponse;
|
||||
using video_widevine_server::sdk::SignedProvisioningMessage;
|
||||
using video_widevine::ClientIdentification;
|
||||
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
|
||||
@@ -48,6 +50,76 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
request->assign(message_b64);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the ClientIdentification message token type for provisioning request.
|
||||
* NOTE: a DRM Cert should never be presented to the provisioning server.
|
||||
*/
|
||||
bool CertificateProvisioning::GetProvisioningTokenType(
|
||||
ClientIdentification::TokenType* token_type) {
|
||||
switch (crypto_session_.GetPreProvisionTokenType()) {
|
||||
case kClientTokenKeybox:
|
||||
*token_type = ClientIdentification::KEYBOX;
|
||||
return true;
|
||||
case kClientTokenOemCert:
|
||||
*token_type = ClientIdentification::OEM_DEVICE_CERTIFICATE;
|
||||
return true;
|
||||
case kClientTokenDrmCert:
|
||||
default:
|
||||
// shouldn't happen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in the appropriate SPOID (Stable Per-Origin IDentifier) option.
|
||||
* One of spoid, provider_id or stable_id will be passed to the provisioning
|
||||
* server for determining a unique per origin ID for the device.
|
||||
* It is also valid (though deprecated) to leave the settings unset.
|
||||
*/
|
||||
bool CertificateProvisioning::SetSpoidParameter(
|
||||
const std::string& origin, const std::string& spoid,
|
||||
ProvisioningRequest* request) {
|
||||
if (!request) {
|
||||
LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer "
|
||||
"passed to method.");
|
||||
return false;
|
||||
}
|
||||
if (!spoid.empty()) {
|
||||
// Use the SPOID that has been pre-provided
|
||||
request->set_spoid(spoid);
|
||||
} else if (Properties::UseProviderIdInProvisioningRequest()) {
|
||||
if (service_certificate_->HasProviderId()) {
|
||||
request->set_provider_id(service_certificate_->provider_id());
|
||||
} else {
|
||||
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
|
||||
"provider ID");
|
||||
return false;
|
||||
}
|
||||
} else if (origin != EMPTY_ORIGIN) {
|
||||
// Legacy behavior - Concatenate Unique ID with Origin
|
||||
std::string device_unique_id;
|
||||
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
|
||||
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
|
||||
"device unique ID");
|
||||
return false;
|
||||
}
|
||||
request->set_stable_id(device_unique_id + origin);
|
||||
} // No else clause, by design. It is valid to do nothing.
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the provisioning protocol version - dictated by OEMCrypto
|
||||
* support for OEM certificates.
|
||||
*/
|
||||
SignedProvisioningMessage::ProtocolVersion
|
||||
CertificateProvisioning::GetProtocolVersion() {
|
||||
if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert)
|
||||
return SignedProvisioningMessage::VERSION_3;
|
||||
else
|
||||
return SignedProvisioningMessage::VERSION_2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Composes a device provisioning request and output the request in JSON format
|
||||
* in *request. It also returns the default url for the provisioning server
|
||||
@@ -58,7 +130,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
|
||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
||||
const std::string& cert_authority, const std::string& origin,
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
const std::string& spoid, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!default_url) {
|
||||
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_1;
|
||||
@@ -66,22 +139,44 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
|
||||
default_url->assign(kProvisioningServerUrl);
|
||||
|
||||
CdmResponseType sts = crypto_session_.Open(requested_security_level);
|
||||
if (NO_ERROR != sts) {
|
||||
CdmResponseType status = crypto_session_.Open(requested_security_level);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
||||
return sts;
|
||||
return status;
|
||||
}
|
||||
|
||||
// Prepares device provisioning request.
|
||||
// Prepare device provisioning request.
|
||||
ProvisioningRequest provisioning_request;
|
||||
ClientIdentification* client_id = provisioning_request.mutable_client_id();
|
||||
client_id->set_type(ClientIdentification::KEYBOX);
|
||||
std::string token;
|
||||
if (!crypto_session_.GetToken(&token)) {
|
||||
LOGE("GetProvisioningRequest: fails to get token");
|
||||
return CERT_PROVISIONING_GET_KEYBOX_ERROR_1;
|
||||
ClientIdentification* client_id = provisioning_request.mutable_client_id();
|
||||
ClientIdentification::TokenType token_type;
|
||||
|
||||
if (!GetProvisioningTokenType(&token_type)) {
|
||||
LOGE("GetProvisioningRequest: bad token type");
|
||||
return CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1;
|
||||
}
|
||||
if (!crypto_session_.GetProvisioningToken(&token)) {
|
||||
LOGE("GetProvisioningRequest: failure getting provisioning token");
|
||||
return CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2;
|
||||
}
|
||||
client_id->set_token(token);
|
||||
client_id->set_type(token_type);
|
||||
|
||||
#if 0 // TODO(gmorgan) in progress - encrypt ClientIdentification.
|
||||
if (service_certificate_->HasCertificate()) {
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
provisioning_request->mutable_encrypted_client_id();
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->EncryptClientId(&crypto_session_, client_id,
|
||||
encrypted_client_id);
|
||||
if (status == NO_ERROR) {
|
||||
provisioning_request->clear_client_id();
|
||||
} else {
|
||||
provisioning_request->clear_encrypted_client_id();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t nonce;
|
||||
if (!crypto_session_.GenerateNonce(&nonce)) {
|
||||
@@ -98,12 +193,11 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
switch (cert_type) {
|
||||
case kCertificateWidevine:
|
||||
options->set_certificate_type(
|
||||
video_widevine_server::sdk::
|
||||
ProvisioningOptions_CertificateType_WIDEVINE_DRM);
|
||||
video_widevine::ProvisioningOptions_CertificateType_WIDEVINE_DRM);
|
||||
break;
|
||||
case kCertificateX509:
|
||||
options->set_certificate_type(
|
||||
video_widevine_server::sdk::ProvisioningOptions_CertificateType_X509);
|
||||
video_widevine::ProvisioningOptions_CertificateType_X509);
|
||||
break;
|
||||
default:
|
||||
LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type);
|
||||
@@ -113,13 +207,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
cert_type_ = cert_type;
|
||||
options->set_certificate_authority(cert_authority);
|
||||
|
||||
if (origin != EMPTY_ORIGIN) {
|
||||
std::string device_unique_id;
|
||||
if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) {
|
||||
LOGE("GetProvisioningRequest: fails to get device unique ID");
|
||||
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
|
||||
}
|
||||
provisioning_request.set_stable_id(device_unique_id + origin);
|
||||
if (!SetSpoidParameter(origin, spoid, &provisioning_request)) {
|
||||
return CERT_PROVISIONING_GET_KEYBOX_ERROR_2;
|
||||
}
|
||||
|
||||
std::string serialized_message;
|
||||
@@ -140,6 +229,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
SignedProvisioningMessage signed_provisioning_msg;
|
||||
signed_provisioning_msg.set_message(serialized_message);
|
||||
signed_provisioning_msg.set_signature(request_signature);
|
||||
signed_provisioning_msg.set_protocol_version(GetProtocolVersion());
|
||||
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
@@ -256,6 +346,9 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// This is the entire certificate (SignedDrmDeviceCertificate).
|
||||
// This will be stored to the device as the final step in the device
|
||||
// provisioning process.
|
||||
const std::string& device_certificate =
|
||||
provisioning_response.device_certificate();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <arpa/inet.h> // needed for ntoh()
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "crypto_key.h"
|
||||
#include "log.h"
|
||||
@@ -26,6 +27,7 @@ std::string EncodeUint32(unsigned int u) {
|
||||
return s;
|
||||
}
|
||||
const uint32_t kRsaSignatureLength = 256;
|
||||
const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB
|
||||
}
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -52,6 +54,26 @@ CryptoSession::~CryptoSession() {
|
||||
Terminate();
|
||||
}
|
||||
|
||||
bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) {
|
||||
OEMCrypto_ProvisioningMethod method;
|
||||
switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) {
|
||||
case OEMCrypto_OEMCertificate:
|
||||
*token_type = kClientTokenOemCert;
|
||||
break;
|
||||
case OEMCrypto_Keybox:
|
||||
*token_type = kClientTokenKeybox;
|
||||
break;
|
||||
case OEMCrypto_DrmCertificate:
|
||||
*token_type = kClientTokenDrmCert;
|
||||
break;
|
||||
case OEMCrypto_ProvisioningError:
|
||||
default:
|
||||
LOGE("OEMCrypto_GetProvisioningMethod failed", method);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CryptoSession::Init() {
|
||||
LOGV("CryptoSession::Init");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
@@ -64,6 +86,9 @@ void CryptoSession::Init() {
|
||||
}
|
||||
initialized_ = true;
|
||||
}
|
||||
if (!GetProvisioningMethod(&pre_provision_token_type_)) {
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CryptoSession::Terminate() {
|
||||
@@ -83,35 +108,81 @@ void CryptoSession::Terminate() {
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
bool CryptoSession::ValidateKeybox() {
|
||||
LOGV("CryptoSession::ValidateKeybox: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
bool CryptoSession::GetTokenFromKeybox(std::string* token) {
|
||||
OEMCryptoResult status;
|
||||
std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0');
|
||||
// lock is held by caller
|
||||
size_t buf_size = temp_buffer.size();
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
status = OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_);
|
||||
if (status == OEMCrypto_SUCCESS) {
|
||||
token->swap(temp_buffer);
|
||||
return true;
|
||||
}
|
||||
OEMCryptoResult result = OEMCrypto_IsKeyboxValid(requested_security_level_);
|
||||
return (OEMCrypto_SUCCESS == result);
|
||||
LOGE("CryptoSession::GetTokenFromKeybox : error %d.", status);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CryptoSession::GetToken(std::string* token) {
|
||||
if (!token) {
|
||||
LOGE("CryptoSession::GetToken : No token passed to method.");
|
||||
bool CryptoSession::GetTokenFromOemCert(std::string* token) {
|
||||
OEMCryptoResult status;
|
||||
std::string temp_buffer(CERTIFICATE_DATA_SIZE, '\0');
|
||||
// lock is held by caller
|
||||
bool retrying = false;
|
||||
while (true) {
|
||||
size_t buf_size = temp_buffer.size();
|
||||
uint8_t* buf = reinterpret_cast<uint8_t*>(&temp_buffer[0]);
|
||||
status = OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size);
|
||||
if (OEMCrypto_SUCCESS == status) {
|
||||
token->swap(temp_buffer);
|
||||
return true;
|
||||
}
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER && !retrying) {
|
||||
temp_buffer.resize(buf_size);
|
||||
retrying = true;
|
||||
continue;
|
||||
}
|
||||
LOGE("CryptoSession::GetTokenFromOemCert : error %d.", status);
|
||||
return false;
|
||||
}
|
||||
uint8_t buf[KEYBOX_KEY_DATA_SIZE];
|
||||
size_t bufSize = sizeof(buf);
|
||||
LOGV("CryptoSession::GetToken: Lock");
|
||||
}
|
||||
|
||||
bool CryptoSession::GetClientToken(std::string* token) {
|
||||
if (!token) {
|
||||
LOGE("CryptoSession::GetClientToken : No token passed to method.");
|
||||
return false;
|
||||
}
|
||||
LOGV("CryptoSession::GetClientToken: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_GetKeyData(buf, &bufSize, requested_security_level_);
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
|
||||
// Only keybox is used for client token. All other cases use DRM Cert.
|
||||
if (pre_provision_token_type_ != kClientTokenKeybox) {
|
||||
return false;
|
||||
}
|
||||
return GetTokenFromKeybox(token);
|
||||
}
|
||||
|
||||
bool CryptoSession::GetProvisioningToken(std::string* token) {
|
||||
if (!token) {
|
||||
LOGE("CryptoSession::GetProvisioningToken : No token passed to method.");
|
||||
return false;
|
||||
}
|
||||
LOGV("CryptoSession::GetProvisioningToken: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pre_provision_token_type_ == kClientTokenKeybox) {
|
||||
return GetTokenFromKeybox(token);
|
||||
} else if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
return GetTokenFromOemCert(token);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
token->assign((const char*)buf, (size_t)bufSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmSecurityLevel CryptoSession::GetSecurityLevel() {
|
||||
@@ -148,25 +219,31 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> id;
|
||||
size_t id_length = 32;
|
||||
|
||||
id.resize(id_length);
|
||||
|
||||
LOGV("CryptoSession::GetDeviceUniqueId: Lock");
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
OEMCryptoResult sts =
|
||||
|
||||
if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
return GetTokenFromOemCert(device_id);
|
||||
} else {
|
||||
// Device's authentication root is a keybox.
|
||||
// Or not. If no keybox, let the OEMCrypto call fail.
|
||||
std::vector<uint8_t> id;
|
||||
size_t id_length = 32;
|
||||
id.resize(id_length);
|
||||
|
||||
OEMCryptoResult sts =
|
||||
OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_);
|
||||
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
if (OEMCrypto_SUCCESS != sts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
|
||||
return true;
|
||||
device_id->assign(reinterpret_cast<char*>(&id[0]), id_length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CryptoSession::GetApiVersion(uint32_t* version) {
|
||||
@@ -690,6 +767,13 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
sts = OEMCrypto_CopyBuffer(requested_security_level_,
|
||||
params.encrypt_buffer, params.encrypt_length,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
|
||||
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE &&
|
||||
params.encrypt_length > kMaximumChunkSize) {
|
||||
// OEMCrypto_CopyBuffer rejected the buffer as too large, so chunk it up
|
||||
// into 100 KiB sections.
|
||||
sts = CopyBufferInChunks(params, buffer_descriptor);
|
||||
}
|
||||
}
|
||||
if (params.is_encrypted && params.cipher_mode != cipher_mode_) {
|
||||
return INCORRECT_CRYPTO_MODE;
|
||||
@@ -698,7 +782,7 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
OEMCrypto_CENCEncryptPatternDesc pattern_descriptor;
|
||||
pattern_descriptor.encrypt = params.pattern_descriptor.encrypt_blocks;
|
||||
pattern_descriptor.skip = params.pattern_descriptor.skip_blocks;
|
||||
pattern_descriptor.offset = params.pattern_descriptor.offset_blocks;
|
||||
pattern_descriptor.offset = 0; // Deprecated field
|
||||
AutoLock auto_lock(crypto_lock_);
|
||||
// Check if key needs to be selected
|
||||
if (params.is_encrypted) {
|
||||
@@ -710,6 +794,26 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
oec_session_id_, params.encrypt_buffer, params.encrypt_length,
|
||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, params.subsample_flags);
|
||||
|
||||
|
||||
if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) {
|
||||
// OEMCrypto_DecryptCENC rejected the buffer as too large, so chunk it up
|
||||
// into sections no more than 100 KiB. The exact chunk size needs to be
|
||||
// an even number of pattern repetitions long or else the pattern will get
|
||||
// out of sync.
|
||||
const size_t pattern_length =
|
||||
(pattern_descriptor.encrypt + pattern_descriptor.skip) *
|
||||
kAes128BlockSize;
|
||||
const size_t chunk_size =
|
||||
pattern_length > 0 ?
|
||||
kMaximumChunkSize - (kMaximumChunkSize % pattern_length) :
|
||||
kMaximumChunkSize;
|
||||
|
||||
if (params.encrypt_length > chunk_size) {
|
||||
sts = DecryptInChunks(params, buffer_descriptor, pattern_descriptor,
|
||||
chunk_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (sts) {
|
||||
@@ -1302,4 +1406,182 @@ size_t CryptoSession::GenericEncryptionBlockSize(
|
||||
}
|
||||
}
|
||||
|
||||
OEMCryptoResult CryptoSession::CopyBufferInChunks(
|
||||
const CdmDecryptionParameters& params,
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor) {
|
||||
size_t remaining_encrypt_length = params.encrypt_length;
|
||||
uint8_t subsample_flags = OEMCrypto_FirstSubsample;
|
||||
|
||||
while (remaining_encrypt_length > 0) {
|
||||
// Calculate the size of the next chunk and its offset into the original
|
||||
// buffer.
|
||||
const size_t chunk_size = std::min(remaining_encrypt_length,
|
||||
kMaximumChunkSize);
|
||||
const size_t additional_offset =
|
||||
params.encrypt_length - remaining_encrypt_length;
|
||||
|
||||
// Update the remaining length of the original buffer only after calculating
|
||||
// the new values.
|
||||
remaining_encrypt_length -= chunk_size;
|
||||
|
||||
// Update the destination buffer with the new offset.
|
||||
switch (buffer_descriptor.type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
buffer_descriptor.buffer.clear.address =
|
||||
static_cast<uint8_t*>(params.decrypt_buffer) +
|
||||
params.decrypt_buffer_offset + additional_offset;
|
||||
buffer_descriptor.buffer.clear.max_length =
|
||||
params.decrypt_buffer_length -
|
||||
(params.decrypt_buffer_offset + additional_offset);
|
||||
break;
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
buffer_descriptor.buffer.secure.offset =
|
||||
params.decrypt_buffer_offset + additional_offset;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
// OEMCrypto_BufferType_Direct does not need modification.
|
||||
break;
|
||||
}
|
||||
|
||||
// Re-add "last subsample" flag if this is the last subsample.
|
||||
if (remaining_encrypt_length == 0) {
|
||||
subsample_flags |= OEMCrypto_LastSubsample;
|
||||
}
|
||||
|
||||
OEMCryptoResult sts = OEMCrypto_CopyBuffer(
|
||||
requested_security_level_, params.encrypt_buffer + additional_offset,
|
||||
chunk_size, &buffer_descriptor, subsample_flags);
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
return sts;
|
||||
}
|
||||
|
||||
// Clear any subsample flags before the next loop iteration.
|
||||
subsample_flags = 0;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
OEMCryptoResult CryptoSession::DecryptInChunks(
|
||||
const CdmDecryptionParameters& params,
|
||||
const OEMCrypto_DestBufferDesc& full_buffer_descriptor,
|
||||
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor,
|
||||
size_t max_chunk_size) {
|
||||
size_t remaining_encrypt_length = params.encrypt_length;
|
||||
uint8_t subsample_flags =
|
||||
(params.subsample_flags & OEMCrypto_FirstSubsample) ?
|
||||
OEMCrypto_FirstSubsample : 0;
|
||||
std::vector<uint8_t> iv = *params.iv;
|
||||
|
||||
const size_t pattern_length_in_bytes =
|
||||
(pattern_descriptor.encrypt + pattern_descriptor.skip) *
|
||||
kAes128BlockSize;
|
||||
|
||||
while (remaining_encrypt_length > 0) {
|
||||
// Calculate the size of the next chunk and its offset into the
|
||||
// original buffer.
|
||||
const size_t chunk_size = std::min(remaining_encrypt_length,
|
||||
max_chunk_size);
|
||||
const size_t additional_offset =
|
||||
params.encrypt_length - remaining_encrypt_length;
|
||||
|
||||
// Update the remaining length of the original buffer only after
|
||||
// calculating the new values.
|
||||
remaining_encrypt_length -= chunk_size;
|
||||
|
||||
// Update the destination buffer with the new offset. Because OEMCrypto can
|
||||
// modify the OEMCrypto_DestBufferDesc during the call to
|
||||
// OEMCrypto_DecryptCENC, (and is known to do so on some platforms) a new
|
||||
// OEMCrypto_DestBufferDesc must be allocated for each call.
|
||||
OEMCrypto_DestBufferDesc buffer_descriptor = full_buffer_descriptor;
|
||||
switch (buffer_descriptor.type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
buffer_descriptor.buffer.clear.address += additional_offset;
|
||||
buffer_descriptor.buffer.clear.max_length -= additional_offset;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
buffer_descriptor.buffer.secure.offset += additional_offset;
|
||||
break;
|
||||
case OEMCrypto_BufferType_Direct:
|
||||
// OEMCrypto_BufferType_Direct does not need modification.
|
||||
break;
|
||||
}
|
||||
|
||||
// Re-add "last subsample" flag if this is the last subsample.
|
||||
if (remaining_encrypt_length == 0 &&
|
||||
params.subsample_flags & OEMCrypto_LastSubsample) {
|
||||
subsample_flags |= OEMCrypto_LastSubsample;
|
||||
}
|
||||
|
||||
// block_offset and pattern_descriptor do not need to change because
|
||||
// max_chunk_size is guaranteed to be an even multiple of the
|
||||
// pattern length long, which is also guaranteed to be an exact number
|
||||
// of AES blocks long.
|
||||
OEMCryptoResult sts = OEMCrypto_DecryptCENC(
|
||||
oec_session_id_, params.encrypt_buffer + additional_offset,
|
||||
chunk_size, params.is_encrypted, &iv.front(), params.block_offset,
|
||||
&buffer_descriptor, &pattern_descriptor, subsample_flags);
|
||||
if (sts != OEMCrypto_SUCCESS) {
|
||||
return sts;
|
||||
}
|
||||
|
||||
// If we are not yet done, update the IV so that it is valid for the next
|
||||
// iteration.
|
||||
if (remaining_encrypt_length != 0) {
|
||||
if (cipher_mode_ == kCipherModeCtr) {
|
||||
// For CTR modes, update the IV depending on how many encrypted blocks
|
||||
// we passed. Since we calculated the chunk size to be an even number
|
||||
// of crypto blocks and pattern repetitions in size, we can do a
|
||||
// simplified calculation for this.
|
||||
uint64_t encrypted_blocks_passed = 0;
|
||||
if (pattern_length_in_bytes == 0) {
|
||||
encrypted_blocks_passed = chunk_size / kAes128BlockSize;
|
||||
} else {
|
||||
const size_t pattern_repetitions_passed =
|
||||
chunk_size / pattern_length_in_bytes;
|
||||
encrypted_blocks_passed =
|
||||
pattern_repetitions_passed * pattern_descriptor.encrypt;
|
||||
}
|
||||
IncrementIV(encrypted_blocks_passed, &iv);
|
||||
} else if (cipher_mode_ == kCipherModeCbc) {
|
||||
// For CBC modes, use the previous ciphertext block.
|
||||
|
||||
// Stash the last crypto block in the IV. We don't have to handle
|
||||
// partial crypto blocks here because we know we broke the buffer into
|
||||
// chunks along even crypto block boundaries.
|
||||
const uint8_t* const buffer_end =
|
||||
params.encrypt_buffer + additional_offset + chunk_size;
|
||||
|
||||
const uint8_t* block_end = NULL;
|
||||
if (pattern_length_in_bytes == 0) {
|
||||
// For cbc1, the last encrypted block is the last block of the
|
||||
// subsample.
|
||||
block_end = buffer_end;
|
||||
} else {
|
||||
// For cbcs, we must look for the last encrypted block, which is
|
||||
// probably not the last block of the subsample. Luckily, since the
|
||||
// buffer size is guaranteed to be an even number of pattern
|
||||
// repetitions long, we can use the pattern to know how many blocks to
|
||||
// look back.
|
||||
block_end = buffer_end - kAes128BlockSize * pattern_descriptor.skip;
|
||||
}
|
||||
|
||||
iv.assign(block_end - kAes128BlockSize, block_end);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any subsample flags before the next loop iteration.
|
||||
subsample_flags = 0;
|
||||
}
|
||||
|
||||
return OEMCrypto_SUCCESS;
|
||||
}
|
||||
|
||||
void CryptoSession::IncrementIV(uint64_t increase_by,
|
||||
std::vector<uint8_t>* iv_out) {
|
||||
std::vector<uint8_t>& iv = *iv_out;
|
||||
uint64_t* counter_buffer = reinterpret_cast<uint64_t*>(&iv[8]);
|
||||
(*counter_buffer) = htonll64(ntohll64(*counter_buffer) + increase_by);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "file_store.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
@@ -35,6 +36,9 @@ using video_widevine_client::sdk::NameValue;
|
||||
using video_widevine_client::sdk::UsageInfo;
|
||||
using video_widevine_client::sdk::UsageInfo_ProviderSession;
|
||||
|
||||
using video_widevine::SignedDrmDeviceCertificate;
|
||||
using video_widevine::DrmDeviceCertificate;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kCertificateFileName[] = "cert.bin";
|
||||
@@ -110,7 +114,9 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
|
||||
}
|
||||
|
||||
bool DeviceFiles::RetrieveCertificate(std::string* certificate,
|
||||
std::string* wrapped_private_key) {
|
||||
std::string* wrapped_private_key,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveCertificate: not initialized");
|
||||
return false;
|
||||
@@ -138,11 +144,55 @@ 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;
|
||||
}
|
||||
|
||||
bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate,
|
||||
std::string* serial_number,
|
||||
uint32_t* system_id) {
|
||||
LOGI("[WEM] ExtractDeviceInfo");
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::HasCertificate() {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::HasCertificate: not initialized");
|
||||
@@ -168,7 +218,8 @@ bool DeviceFiles::StoreLicense(
|
||||
const CdmKeyMessage& license_renewal_request,
|
||||
const CdmKeyResponse& license_renewal,
|
||||
const std::string& release_server_url, int64_t playback_start_time,
|
||||
int64_t last_playback_time, const CdmAppParameterMap& app_parameters) {
|
||||
int64_t last_playback_time, int64_t grace_period_end_time,
|
||||
const CdmAppParameterMap& app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::StoreLicense: not initialized");
|
||||
return false;
|
||||
@@ -201,6 +252,7 @@ bool DeviceFiles::StoreLicense(
|
||||
license->set_release_server_url(release_server_url);
|
||||
license->set_playback_start_time(playback_start_time);
|
||||
license->set_last_playback_time(last_playback_time);
|
||||
license->set_grace_period_end_time(grace_period_end_time);
|
||||
NameValue* app_params;
|
||||
for (CdmAppParameterMap::const_iterator iter = app_parameters.begin();
|
||||
iter != app_parameters.end(); ++iter) {
|
||||
@@ -221,7 +273,8 @@ bool DeviceFiles::RetrieveLicense(
|
||||
CdmKeyMessage* license_request, CdmKeyResponse* license_message,
|
||||
CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal,
|
||||
std::string* release_server_url, int64_t* playback_start_time,
|
||||
int64_t* last_playback_time, CdmAppParameterMap* app_parameters) {
|
||||
int64_t* last_playback_time, int64_t* grace_period_end_time,
|
||||
CdmAppParameterMap* app_parameters) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::RetrieveLicense: not initialized");
|
||||
return false;
|
||||
@@ -270,6 +323,7 @@ bool DeviceFiles::RetrieveLicense(
|
||||
*release_server_url = license.release_server_url();
|
||||
*playback_start_time = license.playback_start_time();
|
||||
*last_playback_time = license.last_playback_time();
|
||||
*grace_period_end_time = license.grace_period_end_time();
|
||||
for (int i = 0; i < license.app_parameters_size(); ++i) {
|
||||
(*app_parameters)[license.app_parameters(i).name()] =
|
||||
license.app_parameters(i).value();
|
||||
@@ -285,6 +339,34 @@ bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||
return RemoveFile(key_set_id + kLicenseFileNameExt);
|
||||
}
|
||||
|
||||
bool DeviceFiles::ListLicenses(std::vector<std::string>* key_set_ids) {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get list of filenames
|
||||
std::vector<std::string> filenames;
|
||||
if (!ListFiles(&filenames)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
std::string* name = &filenames[i];
|
||||
std::size_t pos = name->find(kLicenseFileNameExt);
|
||||
if (pos == std::string::npos) {
|
||||
// Skip this file - extension does not match
|
||||
continue;
|
||||
}
|
||||
// Store filename (minus extension). This should be a key set ID.
|
||||
key_set_ids->push_back(name->substr(0, pos));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceFiles::DeleteAllLicenses() {
|
||||
if (!initialized_) {
|
||||
LOGW("DeviceFiles::DeleteAllLicenses: not initialized");
|
||||
@@ -688,7 +770,7 @@ bool DeviceFiles::RetrieveHashedFile(
|
||||
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::StoreFileWithHash: Unable to get base path");
|
||||
LOGW("DeviceFiles::RetrieveHashedFile: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -765,6 +847,15 @@ bool DeviceFiles::FileExists(const std::string& name) {
|
||||
return file_system_->Exists(path);
|
||||
}
|
||||
|
||||
bool DeviceFiles::ListFiles(std::vector<std::string>* names) {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
LOGW("DeviceFiles::RemoveFile: Unable to get base path");
|
||||
return false;
|
||||
}
|
||||
return file_system_->List(path, names);
|
||||
}
|
||||
|
||||
bool DeviceFiles::RemoveFile(const std::string& name) {
|
||||
std::string path;
|
||||
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
|
||||
|
||||
@@ -39,6 +39,10 @@ message License {
|
||||
optional int64 playback_start_time = 8 [default = 0];
|
||||
optional int64 last_playback_time = 9 [default = 0];
|
||||
repeated NameValue app_parameters = 10;
|
||||
// This will be 0/missing if the grace period has not expired; otherwise it
|
||||
// contains the playback_start_time we should use as an override. This is
|
||||
// ignored if there is no grace period.
|
||||
optional int64 grace_period_end_time = 11 [default = 0];
|
||||
}
|
||||
|
||||
message UsageInfo {
|
||||
|
||||
@@ -35,9 +35,9 @@ const int kDefaultNumJsonTokens = 128;
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::WidevineCencHeader;
|
||||
using video_widevine_server::sdk::WidevineCencHeader_Algorithm;
|
||||
using video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR;
|
||||
using video_widevine::WidevineCencHeader;
|
||||
using video_widevine::WidevineCencHeader_Algorithm;
|
||||
using video_widevine::WidevineCencHeader_Algorithm_AESCTR;
|
||||
|
||||
InitializationData::InitializationData(const std::string& type,
|
||||
const CdmInitData& data)
|
||||
@@ -476,9 +476,9 @@ bool InitializationData::ConstructWidevineInitData(
|
||||
cenc_header.set_provider(provider);
|
||||
cenc_header.set_content_id(content_id);
|
||||
if (method == kHlsMethodAes128)
|
||||
cenc_header.set_protection_scheme(htonl(kFourCcCbc1));
|
||||
cenc_header.set_protection_scheme(kFourCcCbc1);
|
||||
else
|
||||
cenc_header.set_protection_scheme(htonl(kFourCcCbcs));
|
||||
cenc_header.set_protection_scheme(kFourCcCbcs);
|
||||
cenc_header.SerializeToString(init_data_proto);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "policy_engine.h"
|
||||
#include "privacy_crypto.h"
|
||||
#include "properties.h"
|
||||
#include "service_certificate.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
@@ -27,67 +28,34 @@ std::string kBuildInfoKey = "build_info";
|
||||
std::string kDeviceIdKey = "device_id";
|
||||
std::string kWVCdmVersionKey = "widevine_cdm_version";
|
||||
std::string kOemCryptoSecurityPatchLevelKey = "oem_crypto_security_patch_level";
|
||||
const unsigned char kServiceCertificateCAPublicKey[] = {
|
||||
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39,
|
||||
0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd,
|
||||
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};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const uint32_t kFourCcCbc1 = 0x63626331;
|
||||
const uint32_t kFourCcCbcs = 0x63626373;
|
||||
const uint32_t kFourCcLittleEndianCbc1 = 0x31636263;
|
||||
const uint32_t kFourCcLittleEndianCbcs = 0x73636263;
|
||||
const uint32_t kFourCcCenc = 0x63656e63;
|
||||
const uint32_t kFourCcCens = 0x63656e73;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::ClientIdentification_ClientCapabilities;
|
||||
using video_widevine_server::sdk::ClientIdentification_NameValue;
|
||||
using video_widevine_server::sdk::DeviceCertificate;
|
||||
using video_widevine_server::sdk::EncryptedClientIdentification;
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::License_KeyContainer;
|
||||
using video_widevine_server::sdk::LicenseError;
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
using video_widevine_server::sdk::LicenseRequest;
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC;
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification_WebM;
|
||||
using video_widevine_server::sdk::
|
||||
LicenseRequest_ContentIdentification_ExistingLicense;
|
||||
using video_widevine_server::sdk::SignedDeviceCertificate;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
using video_widevine::ClientIdentification;
|
||||
using video_widevine::ClientIdentification_ClientCapabilities;
|
||||
using video_widevine::ClientIdentification_NameValue;
|
||||
using video_widevine::DrmDeviceCertificate;
|
||||
using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::License;
|
||||
using video_widevine::License_KeyContainer;
|
||||
using video_widevine::LicenseError;
|
||||
using video_widevine::LicenseIdentification;
|
||||
using video_widevine::LicenseRequest;
|
||||
using video_widevine::LicenseRequest_ContentIdentification;
|
||||
using video_widevine::LicenseRequest_ContentIdentification_CencDeprecated;
|
||||
using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated;
|
||||
using video_widevine::LicenseRequest_ContentIdentification_ExistingLicense;
|
||||
using video_widevine::SignedDrmDeviceCertificate;
|
||||
using video_widevine::SignedMessage;
|
||||
|
||||
static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
std::vector<CryptoKey> key_array;
|
||||
@@ -115,12 +83,23 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
}
|
||||
uint32_t four_cc = kFourCcCenc;
|
||||
if (license.has_protection_scheme()) {
|
||||
four_cc = ntohl(license.protection_scheme());
|
||||
four_cc = license.protection_scheme();
|
||||
}
|
||||
switch (four_cc) {
|
||||
// b/30713238: Android N assumed that the "protection scheme" Four
|
||||
// CC code, after being extracted from the protobuf, was host byte
|
||||
// order dependent. Later versions do not assume this, and thus,
|
||||
// for backwards compatibility, must support both byte orders.
|
||||
case kFourCcCbc1:
|
||||
case kFourCcCbcs:
|
||||
case kFourCcLittleEndianCbc1:
|
||||
case kFourCcLittleEndianCbcs:
|
||||
key.set_cipher_mode(kCipherModeCbc);
|
||||
break;
|
||||
default:
|
||||
key.set_cipher_mode(kCipherModeCtr);
|
||||
break;
|
||||
}
|
||||
if (four_cc == kFourCcCbc1 || four_cc == kFourCcCbcs)
|
||||
key.set_cipher_mode(kCipherModeCbc);
|
||||
else
|
||||
key.set_cipher_mode(kCipherModeCtr);
|
||||
key_array.push_back(key);
|
||||
break;
|
||||
}
|
||||
@@ -143,7 +122,7 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
|
||||
}
|
||||
|
||||
CdmLicense::CdmLicense(const CdmSessionId& session_id)
|
||||
: session_(NULL),
|
||||
: crypto_session_(NULL),
|
||||
policy_engine_(NULL),
|
||||
session_id_(session_id),
|
||||
initialized_(false),
|
||||
@@ -152,7 +131,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
|
||||
clock_(new Clock()) {}
|
||||
|
||||
CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
|
||||
: session_(NULL),
|
||||
: crypto_session_(NULL),
|
||||
policy_engine_(NULL),
|
||||
session_id_(session_id),
|
||||
initialized_(false),
|
||||
@@ -163,8 +142,10 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
|
||||
|
||||
CdmLicense::~CdmLicense() {}
|
||||
|
||||
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
||||
PolicyEngine* policy_engine) {
|
||||
bool CdmLicense::Init(
|
||||
ServiceCertificate* service_certificate, const std::string& client_token,
|
||||
CdmClientTokenType client_token_type, const std::string& serial_number,
|
||||
CryptoSession* session, PolicyEngine* policy_engine) {
|
||||
if (clock_.get() == NULL) {
|
||||
LOGE("CdmLicense::Init: clock parameter not provided");
|
||||
return false;
|
||||
@@ -173,8 +154,8 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
||||
LOGE("CdmLicense::Init: empty session id provided");
|
||||
return false;
|
||||
}
|
||||
if (token.size() == 0) {
|
||||
LOGE("CdmLicense::Init: empty token provided");
|
||||
if (client_token.size() == 0) {
|
||||
LOGE("CdmLicense::Init: empty client token provided");
|
||||
return false;
|
||||
}
|
||||
if (session == NULL || !session->IsOpen()) {
|
||||
@@ -185,15 +166,19 @@ bool CdmLicense::Init(const std::string& token, CryptoSession* session,
|
||||
LOGE("CdmLicense::Init: no policy engine provided");
|
||||
return false;
|
||||
}
|
||||
token_ = token;
|
||||
session_ = session;
|
||||
|
||||
service_certificate_ = service_certificate;
|
||||
client_token_ = client_token;
|
||||
client_token_type_ = client_token_type;
|
||||
serial_number_ = serial_number;
|
||||
crypto_session_ = session;
|
||||
policy_engine_ = policy_engine;
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
const InitializationData& init_data, const CdmLicenseType license_type,
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!initialized_) {
|
||||
@@ -224,63 +209,29 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
return INVALID_PARAMETERS_LIC_7;
|
||||
}
|
||||
|
||||
std::string service_certificate;
|
||||
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id_);
|
||||
if (privacy_mode_enabled) {
|
||||
if (!GetServiceCertificate(&service_certificate)) {
|
||||
stored_init_data_.reset(new InitializationData(init_data));
|
||||
return PrepareServiceCertificateRequest(signed_request, server_url)
|
||||
? KEY_MESSAGE
|
||||
: LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
// If privacy mode and no service certificate, initiate a
|
||||
// service certificate request.
|
||||
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;
|
||||
}
|
||||
|
||||
std::string request_id;
|
||||
session_->GenerateRequestId(&request_id);
|
||||
crypto_session_->GenerateRequestId(&request_id);
|
||||
|
||||
LicenseRequest license_request;
|
||||
CdmResponseType status =
|
||||
PrepareClientId(privacy_mode_enabled, service_certificate, app_parameters,
|
||||
&license_request);
|
||||
CdmResponseType status;
|
||||
status = PrepareClientId(app_parameters, &license_request);
|
||||
if (NO_ERROR != status) return status;
|
||||
|
||||
// Content Identification may be a cenc_id, a webm_id or a license_id
|
||||
LicenseRequest_ContentIdentification* content_id =
|
||||
license_request.mutable_content_id();
|
||||
|
||||
if (init_data.is_cenc() || init_data.is_hls()) {
|
||||
LicenseRequest_ContentIdentification_CENC* cenc_content_id =
|
||||
content_id->mutable_cenc_id();
|
||||
|
||||
if (!init_data.IsEmpty()) {
|
||||
cenc_content_id->add_pssh(init_data.data());
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
|
||||
return CENC_INIT_DATA_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (!PrepareContentId(license_type, request_id, cenc_content_id)) {
|
||||
return PREPARE_CENC_CONTENT_ID_FAILED;
|
||||
}
|
||||
} else if (init_data.is_webm()) {
|
||||
LicenseRequest_ContentIdentification_WebM* webm_content_id =
|
||||
content_id->mutable_webm_id();
|
||||
|
||||
if (!init_data.IsEmpty()) {
|
||||
webm_content_id->set_header(init_data.data());
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
|
||||
return WEBM_INIT_DATA_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (!PrepareContentId(license_type, request_id, webm_content_id)) {
|
||||
return PREPARE_WEBM_CONTENT_ID_FAILED;
|
||||
}
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
return UNSUPPORTED_INIT_DATA_FORMAT;
|
||||
}
|
||||
status = PrepareContentId(init_data, license_type, request_id,
|
||||
&license_request);
|
||||
if (NO_ERROR != status) return status;
|
||||
|
||||
license_request.set_type(LicenseRequest::NEW);
|
||||
|
||||
@@ -289,12 +240,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
// Get/set the nonce. This value will be reflected in the Key Control Block
|
||||
// of the license response.
|
||||
uint32_t nonce;
|
||||
if (!session_->GenerateNonce(&nonce)) {
|
||||
if (!crypto_session_->GenerateNonce(&nonce)) {
|
||||
return LICENSE_REQUEST_NONCE_GENERATION_ERROR;
|
||||
}
|
||||
license_request.set_key_control_nonce(nonce);
|
||||
LOGD("PrepareKeyRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
||||
license_request.set_protocol_version(video_widevine::VERSION_2_1);
|
||||
|
||||
// License request is complete. Serialize it.
|
||||
std::string serialized_license_req;
|
||||
@@ -305,8 +256,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
|
||||
// Derive signing and encryption keys and construct signature.
|
||||
std::string license_request_signature;
|
||||
if (!session_->PrepareRequest(serialized_license_req, false,
|
||||
&license_request_signature)) {
|
||||
if (!crypto_session_->PrepareRequest(serialized_license_req, false,
|
||||
&license_request_signature)) {
|
||||
signed_request->clear();
|
||||
return LICENSE_REQUEST_SIGNING_ERROR;
|
||||
}
|
||||
@@ -317,7 +268,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
return EMPTY_LICENSE_REQUEST;
|
||||
}
|
||||
|
||||
// Put serialize license request and signature together
|
||||
// Put serialized license request and signature together
|
||||
SignedMessage signed_message;
|
||||
signed_message.set_type(SignedMessage::LICENSE_REQUEST);
|
||||
signed_message.set_signature(license_request_signature);
|
||||
@@ -350,6 +301,17 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
return LICENSE_RENEWAL_PROHIBITED;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
LicenseRequest license_request;
|
||||
if (is_renewal)
|
||||
license_request.set_type(LicenseRequest::RENEWAL);
|
||||
@@ -359,23 +321,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
license_request.set_request_time(clock_->GetCurrentTime());
|
||||
|
||||
if (renew_with_client_id_) {
|
||||
std::string service_certificate;
|
||||
bool privacy_mode_enabled = Properties::UsePrivacyMode(session_id_);
|
||||
if (privacy_mode_enabled) {
|
||||
if (!GetServiceCertificate(&service_certificate)) {
|
||||
return PrepareServiceCertificateRequest(signed_request, server_url)
|
||||
? KEY_MESSAGE
|
||||
: LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
}
|
||||
CdmResponseType status =
|
||||
PrepareClientId(privacy_mode_enabled, service_certificate,
|
||||
app_parameters, &license_request);
|
||||
CdmResponseType status = PrepareClientId(app_parameters, &license_request);
|
||||
if (NO_ERROR != status) return status;
|
||||
}
|
||||
|
||||
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
|
||||
license_request.mutable_content_id()->mutable_license();
|
||||
license_request.mutable_content_id()->mutable_existing_license();
|
||||
LicenseIdentification license_id = policy_engine_->license_id();
|
||||
current_license->mutable_license_id()->CopyFrom(license_id);
|
||||
|
||||
@@ -385,12 +336,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
if (!provider_session_token_.empty()) {
|
||||
if (!is_renewal) {
|
||||
CdmResponseType status =
|
||||
session_->DeactivateUsageInformation(provider_session_token_);
|
||||
crypto_session_->DeactivateUsageInformation(provider_session_token_);
|
||||
if (NO_ERROR != status) return status;
|
||||
}
|
||||
|
||||
std::string usage_report;
|
||||
CdmResponseType status = session_->GenerateUsageReport(
|
||||
CdmResponseType status = crypto_session_->GenerateUsageReport(
|
||||
provider_session_token_, &usage_report, &usage_duration_status,
|
||||
&seconds_since_started, &seconds_since_last_played);
|
||||
if (!is_renewal) {
|
||||
@@ -415,12 +366,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
// Get/set the nonce. This value will be reflected in the Key Control Block
|
||||
// of the license response.
|
||||
uint32_t nonce;
|
||||
if (!session_->GenerateNonce(&nonce)) {
|
||||
if (!crypto_session_->GenerateNonce(&nonce)) {
|
||||
return LICENSE_RENEWAL_NONCE_GENERATION_ERROR;
|
||||
}
|
||||
license_request.set_key_control_nonce(nonce);
|
||||
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
|
||||
license_request.set_protocol_version(video_widevine_server::sdk::VERSION_2_1);
|
||||
license_request.set_protocol_version(video_widevine::VERSION_2_1);
|
||||
|
||||
// License request is complete. Serialize it.
|
||||
std::string serialized_license_req;
|
||||
@@ -428,8 +379,8 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
|
||||
// Construct signature.
|
||||
std::string license_request_signature;
|
||||
if (!session_->PrepareRenewalRequest(serialized_license_req,
|
||||
&license_request_signature))
|
||||
if (!crypto_session_->PrepareRenewalRequest(serialized_license_req,
|
||||
&license_request_signature))
|
||||
return LICENSE_RENEWAL_SIGNING_ERROR;
|
||||
|
||||
if (license_request_signature.empty()) {
|
||||
@@ -473,12 +424,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status =
|
||||
VerifySignedServiceCertificate(signed_response.msg());
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->HandleResponse(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
Properties::SetServiceCertificate(session_id_, signed_response.msg());
|
||||
return NEED_KEY;
|
||||
}
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
@@ -507,8 +457,8 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
return SESSION_KEYS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!session_->GenerateDerivedKeys(key_request_,
|
||||
signed_response.session_key()))
|
||||
if (!crypto_session_->GenerateDerivedKeys(key_request_,
|
||||
signed_response.session_key()))
|
||||
return GENERATE_DERIVED_KEYS_ERROR;
|
||||
}
|
||||
|
||||
@@ -540,7 +490,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
return NO_CONTENT_KEY;
|
||||
}
|
||||
|
||||
if (license.id().type() == video_widevine_server::sdk::OFFLINE &&
|
||||
if (license.id().type() == video_widevine::OFFLINE &&
|
||||
license.policy().can_persist())
|
||||
is_offline_ = true;
|
||||
|
||||
@@ -551,11 +501,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
|
||||
if (license.policy().has_renew_with_client_id()) {
|
||||
renew_with_client_id_ = license.policy().renew_with_client_id();
|
||||
if (license.policy().has_always_include_client_id()) {
|
||||
renew_with_client_id_ = license.policy().always_include_client_id();
|
||||
}
|
||||
|
||||
CdmResponseType resp = session_->LoadKeys(
|
||||
CdmResponseType resp = crypto_session_->LoadKeys(
|
||||
signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
|
||||
key_array, provider_session_token_);
|
||||
|
||||
@@ -591,12 +541,11 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status =
|
||||
VerifySignedServiceCertificate(signed_response.msg());
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->HandleResponse(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
Properties::SetServiceCertificate(session_id_, signed_response.msg());
|
||||
return NEED_KEY;
|
||||
}
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
@@ -627,15 +576,15 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
return LICENSE_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (license.policy().has_renew_with_client_id()) {
|
||||
renew_with_client_id_ = license.policy().renew_with_client_id();
|
||||
if (license.policy().has_always_include_client_id()) {
|
||||
renew_with_client_id_ = license.policy().always_include_client_id();
|
||||
}
|
||||
|
||||
if (!is_renewal) {
|
||||
if (!license.id().has_provider_session_token()) return KEY_ADDED;
|
||||
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
CdmResponseType status = session_->ReleaseUsageInformation(
|
||||
CdmResponseType status = crypto_session_->ReleaseUsageInformation(
|
||||
signed_response.msg(), signed_response.signature(),
|
||||
provider_session_token_);
|
||||
return (NO_ERROR == status) ? KEY_ADDED : status;
|
||||
@@ -648,8 +597,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
|
||||
std::vector<CryptoKey> key_array = ExtractContentKeys(license);
|
||||
|
||||
if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(),
|
||||
key_array.size(), &key_array[0])) {
|
||||
if (crypto_session_->RefreshKeys(signed_response.msg(),
|
||||
signed_response.signature(),
|
||||
key_array.size(), &key_array[0])) {
|
||||
policy_engine_->UpdateLicense(license);
|
||||
|
||||
return KEY_ADDED;
|
||||
@@ -662,7 +612,7 @@ bool CdmLicense::RestoreOfflineLicense(
|
||||
const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response, int64_t playback_start_time,
|
||||
int64_t last_playback_time) {
|
||||
int64_t last_playback_time, int64_t grace_period_end_time) {
|
||||
if (license_request.empty() || license_response.empty()) {
|
||||
LOGE(
|
||||
"CdmLicense::RestoreOfflineLicense: key_request or response empty: "
|
||||
@@ -688,7 +638,9 @@ bool CdmLicense::RestoreOfflineLicense(
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
key_request_ = signed_request.msg();
|
||||
} else {
|
||||
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false;
|
||||
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType sts = HandleKeyResponse(license_response);
|
||||
@@ -706,7 +658,7 @@ bool CdmLicense::RestoreOfflineLicense(
|
||||
CryptoSession::UsageDurationStatus usage_duration_status =
|
||||
CryptoSession::kUsageDurationsInvalid;
|
||||
int64_t seconds_since_started, seconds_since_last_played;
|
||||
sts = session_->GenerateUsageReport(
|
||||
sts = crypto_session_->GenerateUsageReport(
|
||||
provider_session_token_, &usage_report, &usage_duration_status,
|
||||
&seconds_since_started, &seconds_since_last_played);
|
||||
|
||||
@@ -730,7 +682,8 @@ bool CdmLicense::RestoreOfflineLicense(
|
||||
}
|
||||
}
|
||||
|
||||
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time);
|
||||
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time,
|
||||
grace_period_end_time);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -762,7 +715,9 @@ bool CdmLicense::RestoreLicenseForRelease(
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
key_request_ = signed_request.msg();
|
||||
} else {
|
||||
if (!session_->GenerateDerivedKeys(signed_request.msg())) return false;
|
||||
if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SignedMessage signed_response;
|
||||
@@ -799,8 +754,8 @@ bool CdmLicense::RestoreLicenseForRelease(
|
||||
if (license.id().has_provider_session_token())
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
|
||||
if (license.policy().has_renew_with_client_id())
|
||||
renew_with_client_id_ = license.policy().renew_with_client_id();
|
||||
if (license.policy().has_always_include_client_id())
|
||||
renew_with_client_id_ = license.policy().always_include_client_id();
|
||||
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
if (!signed_response.has_session_key()) {
|
||||
@@ -809,8 +764,8 @@ bool CdmLicense::RestoreLicenseForRelease(
|
||||
}
|
||||
|
||||
if (license.id().has_provider_session_token()) {
|
||||
if (!session_->GenerateDerivedKeys(key_request_,
|
||||
signed_response.session_key()))
|
||||
if (!crypto_session_->GenerateDerivedKeys(key_request_,
|
||||
signed_response.session_key()))
|
||||
return false;
|
||||
} else {
|
||||
return KEY_ADDED == HandleKeyResponse(license_response);
|
||||
@@ -830,92 +785,6 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
|
||||
return loaded_keys_.find(key_id) != loaded_keys_.end();
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::VerifySignedServiceCertificate(
|
||||
const std::string& signed_service_certificate) {
|
||||
return VerifyAndExtractSignedServiceCertificate(signed_service_certificate,
|
||||
NULL);
|
||||
}
|
||||
|
||||
bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
|
||||
std::string* server_url) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmLicense::PrepareServiceCertificateRequest: not initialized");
|
||||
return false;
|
||||
}
|
||||
if (!signed_request) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareServiceCertificateRequest: no signed request"
|
||||
" provided");
|
||||
return false;
|
||||
}
|
||||
if (!server_url) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareServiceCertificateRequest: no server url"
|
||||
" provided");
|
||||
return false;
|
||||
}
|
||||
SignedMessage signed_message;
|
||||
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||
signed_message.SerializeToString(signed_request);
|
||||
*server_url = server_url_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::VerifyAndExtractSignedServiceCertificate(
|
||||
const std::string& signed_certificate, std::string* certificate) {
|
||||
SignedDeviceCertificate signed_service_certificate;
|
||||
if (!signed_service_certificate.ParseFromString(signed_certificate)) {
|
||||
LOGE(
|
||||
"CdmLicense::VerifyAndExtractSignedServiceCertificate: unable to parse "
|
||||
"signed device certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_1;
|
||||
}
|
||||
|
||||
RsaPublicKey root_ca_key;
|
||||
std::string ca_public_key(
|
||||
&kServiceCertificateCAPublicKey[0],
|
||||
&kServiceCertificateCAPublicKey[sizeof(kServiceCertificateCAPublicKey)]);
|
||||
if (!root_ca_key.Init(ca_public_key)) {
|
||||
LOGE(
|
||||
"CdmLicense::VerifyAndExtractSignedServiceCertificate: public key "
|
||||
"initialization failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
|
||||
if (!root_ca_key.VerifySignature(
|
||||
signed_service_certificate.device_certificate(),
|
||||
signed_service_certificate.signature())) {
|
||||
LOGE(
|
||||
"CdmLicense::VerifyAndExtractSignedServiceCertificate: service "
|
||||
"certificate verification failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_3;
|
||||
}
|
||||
|
||||
DeviceCertificate service_certificate;
|
||||
if (!service_certificate.ParseFromString(
|
||||
signed_service_certificate.device_certificate())) {
|
||||
LOGE(
|
||||
"CdmLicense::VerifyAndExtractSignedServiceCertificate: unable to parse "
|
||||
"retrieved service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_4;
|
||||
}
|
||||
|
||||
if (service_certificate.type() !=
|
||||
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE(
|
||||
"CdmLicense::VerifyAndExtractSignedServiceCertificate: certificate not "
|
||||
"of type service, %d",
|
||||
service_certificate.type());
|
||||
return INVALID_DEVICE_CERTIFICATE_TYPE;
|
||||
}
|
||||
|
||||
if (certificate != NULL) {
|
||||
*certificate = signed_service_certificate.device_certificate();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
const SignedMessage& signed_message) {
|
||||
LicenseError license_error;
|
||||
@@ -925,28 +794,46 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
}
|
||||
|
||||
switch (license_error.error_code()) {
|
||||
case LicenseError::INVALID_DEVICE_CERTIFICATE:
|
||||
case LicenseError::INVALID_DRM_DEVICE_CERTIFICATE:
|
||||
return NEED_PROVISIONING;
|
||||
case LicenseError::REVOKED_DEVICE_CERTIFICATE:
|
||||
case LicenseError::REVOKED_DRM_DEVICE_CERTIFICATE:
|
||||
return DEVICE_REVOKED;
|
||||
case LicenseError::SERVICE_UNAVAILABLE:
|
||||
default:
|
||||
LOGW("CdmLicense::HandleKeyErrorResponse: Unknwon error type = %d",
|
||||
LOGW("CdmLicense::HandleKeyErrorResponse: Unknown error type = %d",
|
||||
license_error.error_code());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the ClientIdentification message token type for license request.
|
||||
// NOTE: an OEM Cert should never be presented to the provisioning server.
|
||||
bool CdmLicense::GetClientTokenType(
|
||||
ClientIdentification::TokenType* token_type) {
|
||||
switch (client_token_type_) {
|
||||
case kClientTokenKeybox:
|
||||
*token_type = ClientIdentification::KEYBOX;
|
||||
return true;
|
||||
case kClientTokenDrmCert:
|
||||
*token_type = ClientIdentification::DRM_DEVICE_CERTIFICATE;
|
||||
return true;
|
||||
case kClientTokenOemCert:
|
||||
default:
|
||||
// shouldn't happen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::PrepareClientId(
|
||||
bool encrypt, const std::string& certificate,
|
||||
const CdmAppParameterMap& app_parameters, LicenseRequest* license_request) {
|
||||
ClientIdentification* client_id = license_request->mutable_client_id();
|
||||
|
||||
if (Properties::use_certificates_as_identification())
|
||||
client_id->set_type(ClientIdentification::DEVICE_CERTIFICATE);
|
||||
else
|
||||
client_id->set_type(ClientIdentification::KEYBOX);
|
||||
client_id->set_token(token_);
|
||||
ClientIdentification::TokenType token_type;
|
||||
if (!GetClientTokenType(&token_type)) {
|
||||
return LICENSING_CLIENT_TOKEN_ERROR_1;
|
||||
}
|
||||
client_id->set_type(token_type);
|
||||
client_id->set_token(client_token_);
|
||||
|
||||
ClientIdentification_NameValue* client_info;
|
||||
CdmAppParameterMap::const_iterator iter;
|
||||
@@ -986,7 +873,11 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
client_info->set_name(kBuildInfoKey);
|
||||
client_info->set_value(value);
|
||||
}
|
||||
if (session_->GetDeviceUniqueId(&value)) {
|
||||
if (!serial_number_.empty()) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(b2a_hex(serial_number_));
|
||||
} else if (crypto_session_->GetDeviceUniqueId(&value)) {
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kDeviceIdKey);
|
||||
client_info->set_value(value);
|
||||
@@ -999,55 +890,55 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
client_info = client_id->add_client_info();
|
||||
client_info->set_name(kOemCryptoSecurityPatchLevelKey);
|
||||
std::stringstream ss;
|
||||
ss << (uint32_t)session_->GetSecurityPatchLevel();
|
||||
ss << (uint32_t)crypto_session_->GetSecurityPatchLevel();
|
||||
client_info->set_value(ss.str());
|
||||
|
||||
ClientIdentification_ClientCapabilities* client_capabilities =
|
||||
client_id->mutable_client_capabilities();
|
||||
bool supports_usage_information;
|
||||
if (session_->UsageInformationSupport(&supports_usage_information)) {
|
||||
if (crypto_session_->UsageInformationSupport(&supports_usage_information)) {
|
||||
client_capabilities->set_session_token(supports_usage_information);
|
||||
}
|
||||
|
||||
client_capabilities->set_anti_rollback_usage_table(
|
||||
session_->IsAntiRollbackHwPresent());
|
||||
crypto_session_->IsAntiRollbackHwPresent());
|
||||
|
||||
uint32_t api_version = 0;
|
||||
if (session_->GetApiVersion(&api_version)) {
|
||||
if (crypto_session_->GetApiVersion(&api_version)) {
|
||||
client_capabilities->set_oem_crypto_api_version(api_version);
|
||||
}
|
||||
|
||||
CryptoSession::HdcpCapability current_version, max_version;
|
||||
if (session_->GetHdcpCapabilities(¤t_version, &max_version)) {
|
||||
if (crypto_session_->GetHdcpCapabilities(¤t_version, &max_version)) {
|
||||
switch (max_version) {
|
||||
case HDCP_NONE:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
video_widevine::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
|
||||
break;
|
||||
case HDCP_V1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
video_widevine::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
|
||||
break;
|
||||
case HDCP_V2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
video_widevine::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
|
||||
break;
|
||||
case HDCP_V2_1:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
video_widevine::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
|
||||
break;
|
||||
case HDCP_V2_2:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
video_widevine::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
|
||||
break;
|
||||
case HDCP_NO_DIGITAL_OUTPUT:
|
||||
client_capabilities->set_max_hdcp_version(
|
||||
video_widevine_server::sdk::
|
||||
video_widevine::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NO_DIGITAL_OUTPUT);
|
||||
break;
|
||||
default:
|
||||
@@ -1058,78 +949,80 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
}
|
||||
}
|
||||
|
||||
if (encrypt) {
|
||||
if (Properties::UsePrivacyMode(session_id_)) {
|
||||
if (!service_certificate_->HasCertificate()) {
|
||||
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
|
||||
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
license_request->mutable_encrypted_client_id();
|
||||
DeviceCertificate service_certificate;
|
||||
|
||||
if (!service_certificate.ParseFromString(certificate)) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareClientId: unable to parse retrieved "
|
||||
"service certificate");
|
||||
return PARSE_SERVICE_CERTIFICATE_ERROR;
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->EncryptClientId(crypto_session_, client_id,
|
||||
encrypted_client_id);
|
||||
if (NO_ERROR == status) {
|
||||
license_request->clear_client_id();
|
||||
} else {
|
||||
license_request->clear_encrypted_client_id();
|
||||
}
|
||||
|
||||
if (service_certificate.type() !=
|
||||
video_widevine_server::sdk::DeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE(
|
||||
"CdmLicense::PrepareClientId: retrieved certificate not of type"
|
||||
" service, %d",
|
||||
service_certificate.type());
|
||||
return SERVICE_CERTIFICATE_TYPE_ERROR;
|
||||
}
|
||||
encrypted_client_id->set_service_id(service_certificate.service_id());
|
||||
encrypted_client_id->set_service_certificate_serial_number(
|
||||
service_certificate.serial_number());
|
||||
|
||||
std::string iv(KEY_IV_SIZE, 0);
|
||||
std::string key(KEY_SIZE, 0);
|
||||
|
||||
if (!session_->GetRandom(key.size(), reinterpret_cast<uint8_t*>(&key[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
if (!session_->GetRandom(iv.size(), reinterpret_cast<uint8_t*>(&iv[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
std::string id, enc_id, enc_key;
|
||||
client_id->SerializeToString(&id);
|
||||
|
||||
AesCbcKey aes;
|
||||
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;
|
||||
|
||||
encrypted_client_id->set_encrypted_client_id_iv(iv);
|
||||
encrypted_client_id->set_encrypted_privacy_key(enc_key);
|
||||
encrypted_client_id->set_encrypted_client_id(enc_id);
|
||||
license_request->clear_client_id();
|
||||
return status;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool CdmLicense::GetServiceCertificate(std::string* service_certificate) {
|
||||
std::string signed_service_certificate;
|
||||
return Properties::GetServiceCertificate(session_id_,
|
||||
&signed_service_certificate) &&
|
||||
!signed_service_certificate.empty() &&
|
||||
NO_ERROR == VerifyAndExtractSignedServiceCertificate(
|
||||
signed_service_certificate, service_certificate) &&
|
||||
!service_certificate->empty();
|
||||
CdmResponseType CdmLicense::PrepareContentId(
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const std::string& request_id, LicenseRequest* license_request) {
|
||||
// Content Identification may be a cenc_id, a webm_id or a license_id
|
||||
LicenseRequest_ContentIdentification* content_id =
|
||||
license_request->mutable_content_id();
|
||||
|
||||
if (init_data.is_cenc() || init_data.is_hls()) {
|
||||
LicenseRequest_ContentIdentification_CencDeprecated* cenc_content_id =
|
||||
content_id->mutable_cenc_id_deprecated();
|
||||
|
||||
if (!init_data.IsEmpty()) {
|
||||
cenc_content_id->add_pssh(init_data.data());
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: ISO-CENC init data not available");
|
||||
return CENC_INIT_DATA_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (!SetTypeAndId(license_type, request_id, cenc_content_id)) {
|
||||
return PREPARE_CENC_CONTENT_ID_FAILED;
|
||||
}
|
||||
} else if (init_data.is_webm()) {
|
||||
LicenseRequest_ContentIdentification_WebmDeprecated* webm_content_id =
|
||||
content_id->mutable_webm_id_deprecated();
|
||||
|
||||
if (!init_data.IsEmpty()) {
|
||||
webm_content_id->set_header(init_data.data());
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: WebM init data not available");
|
||||
return WEBM_INIT_DATA_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (!SetTypeAndId(license_type, request_id, webm_content_id)) {
|
||||
return PREPARE_WEBM_CONTENT_ID_FAILED;
|
||||
}
|
||||
} else {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: no support for init data type (%s)",
|
||||
init_data.type().c_str());
|
||||
return UNSUPPORTED_INIT_DATA_FORMAT;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
|
||||
const std::string& request_id,
|
||||
T* content_id) {
|
||||
bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
|
||||
const std::string& request_id,
|
||||
T* content_id) {
|
||||
switch (license_type) {
|
||||
case kLicenseTypeOffline:
|
||||
content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
|
||||
content_id->set_license_type(video_widevine::OFFLINE);
|
||||
break;
|
||||
case kLicenseTypeStreaming:
|
||||
case kLicenseTypeTemporary:
|
||||
content_id->set_license_type(video_widevine_server::sdk::STREAMING);
|
||||
content_id->set_license_type(video_widevine::STREAMING);
|
||||
break;
|
||||
default:
|
||||
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace {
|
||||
// License protocol aliases
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
typedef KeyContainer::OutputProtection OutputProtection;
|
||||
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
@@ -130,7 +130,7 @@ void LicenseKeys::ApplyConstraints(
|
||||
}
|
||||
|
||||
void LicenseKeys::SetFromLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
const video_widevine::License& license) {
|
||||
this->Clear();
|
||||
for (int32_t key_index = 0; key_index < license.key_size(); ++key_index) {
|
||||
const KeyContainer& key = license.key(key_index);
|
||||
|
||||
@@ -9,11 +9,13 @@
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package video_widevine_server.sdk;
|
||||
package video_widevine;
|
||||
|
||||
// need this if we are using libprotobuf-cpp-2.3.0-lite
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
option java_package = "com.google.video.widevine.protos";
|
||||
|
||||
enum LicenseType {
|
||||
STREAMING = 1;
|
||||
OFFLINE = 2;
|
||||
@@ -81,7 +83,15 @@ message License {
|
||||
|
||||
// Indicates to client that license renewal and release requests ought to
|
||||
// include ClientIdentification (client_id).
|
||||
optional bool renew_with_client_id = 12 [default = false];
|
||||
optional bool always_include_client_id = 12 [default = false];
|
||||
|
||||
// Duration of grace period before playback_duration_seconds (short window)
|
||||
// goes into effect. Optional.
|
||||
optional int64 play_start_grace_period_seconds = 13 [default = 0];
|
||||
|
||||
// Enables "soft enforcement" of playback_duration_seconds, letting the user
|
||||
// finish playback even if short window expires. Optional.
|
||||
optional bool soft_enforce_playback_duration = 14 [default = false];
|
||||
}
|
||||
|
||||
message KeyContainer {
|
||||
@@ -117,6 +127,8 @@ message License {
|
||||
}
|
||||
|
||||
message KeyControl {
|
||||
// |key_control| is documented in:
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
// If present, the key control must be communicated to the secure
|
||||
// environment prior to any usage. This message is automatically generated
|
||||
// by the Widevine License Server SDK.
|
||||
@@ -192,14 +204,19 @@ message License {
|
||||
optional LicenseIdentification id = 1;
|
||||
optional Policy policy = 2;
|
||||
repeated KeyContainer key = 3;
|
||||
// Time of the request in seconds (UTC) as set in
|
||||
// LicenseRequest.request_time. If this time is not set in the request,
|
||||
// the local time at the license service is used in this field.
|
||||
optional int64 license_start_time = 4;
|
||||
optional bool remote_attestation_verified = 5 [default = false];
|
||||
// Client token generated by the content provider. Optional.
|
||||
optional bytes provider_client_token = 6;
|
||||
// Protection scheme identifying the encryption algorithm. Represented as one
|
||||
// of the following 4CC values: 'cenc' (AES-CTR), 'cbc1' (AES-CBC),
|
||||
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
|
||||
// 4cc code specifying the CENC protection scheme as defined in the CENC 3.0
|
||||
// specification. Propagated from Widevine PSSH box. Optional.
|
||||
optional uint32 protection_scheme = 7;
|
||||
// Minimum HDCP SRM version needed for using this key on content sent to
|
||||
// HDCP enabled outputs.
|
||||
optional uint32 min_hdcp_srm_version = 8;
|
||||
}
|
||||
|
||||
enum ProtocolVersion {
|
||||
@@ -209,13 +226,13 @@ enum ProtocolVersion {
|
||||
|
||||
message LicenseRequest {
|
||||
message ContentIdentification {
|
||||
message CENC {
|
||||
message CencDeprecated {
|
||||
repeated bytes pssh = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
}
|
||||
|
||||
message WebM {
|
||||
message WebmDeprecated {
|
||||
optional bytes header = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
@@ -228,10 +245,25 @@ message LicenseRequest {
|
||||
optional bytes session_usage_table_entry = 4;
|
||||
}
|
||||
|
||||
// Exactly one of these must be present.
|
||||
optional CENC cenc_id = 1;
|
||||
optional WebM webm_id = 2;
|
||||
optional ExistingLicense license = 3;
|
||||
message InitData {
|
||||
enum InitDataType {
|
||||
CENC = 1;
|
||||
WEBM = 2;
|
||||
}
|
||||
|
||||
optional InitDataType init_data_type = 1 [default = CENC];
|
||||
optional bytes init_data = 2;
|
||||
optional LicenseType license_type = 3;
|
||||
optional bytes request_id = 4;
|
||||
}
|
||||
|
||||
//oneof content_id_variant {
|
||||
// Exactly one of these must be present.
|
||||
optional CencDeprecated cenc_id_deprecated = 1;
|
||||
optional WebmDeprecated webm_id_deprecated = 2;
|
||||
optional ExistingLicense existing_license = 3;
|
||||
optional InitData init_data = 4;
|
||||
//}
|
||||
}
|
||||
|
||||
enum RequestType {
|
||||
@@ -247,6 +279,7 @@ message LicenseRequest {
|
||||
optional ClientIdentification client_id = 1;
|
||||
optional ContentIdentification content_id = 2;
|
||||
optional RequestType type = 3;
|
||||
// Time of the request in seconds (UTC) as set by the client.
|
||||
optional int64 request_time = 4;
|
||||
// Old-style decimal-encoded string key control nonce.
|
||||
optional bytes key_control_nonce_deprecated = 5;
|
||||
@@ -261,10 +294,10 @@ message LicenseRequest {
|
||||
message LicenseError {
|
||||
enum Error {
|
||||
// The device credentials are invalid. The device must re-provision.
|
||||
INVALID_DEVICE_CERTIFICATE = 1;
|
||||
INVALID_DRM_DEVICE_CERTIFICATE = 1;
|
||||
// The device credentials have been revoked. Re-provisioning is not
|
||||
// possible.
|
||||
REVOKED_DEVICE_CERTIFICATE = 2;
|
||||
REVOKED_DRM_DEVICE_CERTIFICATE = 2;
|
||||
// The service is currently unavailable due to the backend being down
|
||||
// or similar circumstances.
|
||||
SERVICE_UNAVAILABLE = 3;
|
||||
@@ -326,17 +359,38 @@ message SignedMessage {
|
||||
}
|
||||
|
||||
message GroupKeys {
|
||||
repeated License.KeyContainer key = 1;
|
||||
enum GroupLicenseVersion {
|
||||
GROUP_LICENSE_VERSION_1 = 0;
|
||||
GROUP_LICENSE_VERSION_2 = 1;
|
||||
}
|
||||
|
||||
message GroupKeyData {
|
||||
// Required track type. This indicates the track type to which this key
|
||||
// belongs.
|
||||
optional string track_type = 1;
|
||||
// A required signed message. The message body contains a serialized group
|
||||
// msg.
|
||||
optional bytes key = 2;
|
||||
}
|
||||
|
||||
// Optional key container array used in group licensing V1. This is not used
|
||||
// in V2.
|
||||
repeated License.KeyContainer key = 1 [deprecated = true];
|
||||
|
||||
// Byte string that identifies the group to which this license material
|
||||
// belongs.
|
||||
optional bytes group_id = 2;
|
||||
|
||||
// Required version id beginning with version 2. If not present version 1
|
||||
// should be assumed.
|
||||
optional GroupLicenseVersion version = 3 [default = GROUP_LICENSE_VERSION_1];
|
||||
// Optional key container array for group licensing V2.
|
||||
repeated GroupKeyData key_data = 4;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// certificate_provisioning.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Public protocol buffer definitions for Widevine Device Certificate
|
||||
// Provisioning protocol.
|
||||
@@ -349,53 +403,80 @@ message ProvisioningOptions {
|
||||
X509 = 1; // X.509 certificate.
|
||||
}
|
||||
|
||||
optional CertificateType certificate_type = 1;
|
||||
optional CertificateType certificate_type = 1 [default = WIDEVINE_DRM];
|
||||
|
||||
// It is recommended that the certificate_authority specify the X.509
|
||||
// Subject of the signing certificate.
|
||||
// Contains the application-specific name used to identify the certificate
|
||||
// authority for signing the generated certificate. This is required iff the
|
||||
// certificate type is X509.
|
||||
optional string certificate_authority = 2;
|
||||
}
|
||||
|
||||
// Provisioning request sent by client devices to provisioning service.
|
||||
message ProvisioningRequest {
|
||||
// Device root of trust and other client identification. Required.
|
||||
optional ClientIdentification client_id = 1;
|
||||
//oneof clear_or_encrypted_client_id {
|
||||
// Device root of trust and other client identification. Required.
|
||||
optional ClientIdentification client_id = 1;
|
||||
optional EncryptedClientIdentification encrypted_client_id = 5;
|
||||
//}
|
||||
// Nonce value used to prevent replay attacks. Required.
|
||||
optional bytes nonce = 2;
|
||||
// Options for type of certificate to generate. Optional.
|
||||
optional ProvisioningOptions options = 3;
|
||||
// Stable identifier, unique for each device + application (or origin).
|
||||
// Required if doing per-origin provisioning.
|
||||
optional bytes stable_id = 4;
|
||||
//oneof spoid_param {
|
||||
// Stable identifier, unique for each device + application (or origin).
|
||||
// To be deprecated.
|
||||
optional bytes stable_id = 4;
|
||||
// Service provider ID from the service certificate's provider_id field.
|
||||
// Preferred parameter.
|
||||
optional bytes provider_id = 6;
|
||||
// Client-generated stable per-origin identifier to be copied directly
|
||||
// to the client certificater serial number.
|
||||
optional bytes spoid = 7;
|
||||
//}
|
||||
}
|
||||
|
||||
// Provisioning response sent by the provisioning server to client devices.
|
||||
// This message is used for both regular Widevine DRM certificates and for
|
||||
// application-specific X.509 certificates.
|
||||
message ProvisioningResponse {
|
||||
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||
// Required.
|
||||
// Required. For X.509 certificates, the private RSA key may also include
|
||||
// a prefix as specified by private_key_prefix in the X509CertificateMetadata
|
||||
// proto message.
|
||||
optional bytes device_rsa_key = 1;
|
||||
// Initialization vector used to encrypt device_rsa_key. Required.
|
||||
optional bytes device_rsa_key_iv = 2;
|
||||
// Serialized SignedDeviceCertificate. Required.
|
||||
// For Widevine DRM certificates, this contains the serialized
|
||||
// SignedDrmDeviceCertificate. For X.509 certificates, this contains the PEM
|
||||
// encoded X.509 certificate. Required.
|
||||
optional bytes device_certificate = 3;
|
||||
// Nonce value matching nonce in ProvisioningRequest. Required.
|
||||
optional bytes nonce = 4;
|
||||
// Key used to wrap device_rsa_key when DRM provisioning an OEM factory
|
||||
// provisioned device. Encrypted with the device OEM public key using
|
||||
// RSA-OAEP.
|
||||
optional bytes wrapping_key = 5;
|
||||
}
|
||||
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse signed with
|
||||
// The message authentication key.
|
||||
message SignedProvisioningMessage {
|
||||
enum ProtocolVersion {
|
||||
VERSION_2 = 2; // Keybox factory-provisioned devices.
|
||||
VERSION_3 = 3; // OEM certificate factory-provisioned devices.
|
||||
}
|
||||
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse. Required.
|
||||
optional bytes message = 1;
|
||||
// HMAC-SHA256 signature of message. Required.
|
||||
// HMAC-SHA256 (Keybox) or RSASSA-PSS (OEM) signature of message. Required.
|
||||
optional bytes signature = 2;
|
||||
// Version number of provisioning protocol.
|
||||
optional ProtocolVersion protocol_version = 3 [default = VERSION_2];
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// client_identification.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// ClientIdentification messages used by provisioning and license protocols.
|
||||
|
||||
@@ -403,8 +484,9 @@ message SignedProvisioningMessage {
|
||||
message ClientIdentification {
|
||||
enum TokenType {
|
||||
KEYBOX = 0;
|
||||
DEVICE_CERTIFICATE = 1;
|
||||
DRM_DEVICE_CERTIFICATE = 1;
|
||||
REMOTE_ATTESTATION_CERTIFICATE = 2;
|
||||
OEM_DEVICE_CERTIFICATE = 3;
|
||||
}
|
||||
|
||||
message NameValue {
|
||||
@@ -429,6 +511,9 @@ message ClientIdentification {
|
||||
optional bool video_resolution_constraints = 3 [default = false];
|
||||
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
|
||||
optional uint32 oem_crypto_api_version = 5;
|
||||
// Client has hardware support for protecting the usage table, such as
|
||||
// storing the generation number in secure memory. For Details, see:
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
optional bool anti_rollback_usage_table = 6 [default = false];
|
||||
}
|
||||
|
||||
@@ -445,43 +530,43 @@ message ClientIdentification {
|
||||
optional uint32 license_counter = 5;
|
||||
// List of non-baseline client capabilities.
|
||||
optional ClientCapabilities client_capabilities = 6;
|
||||
// Serialized VmpData message. Optional.
|
||||
optional bytes vmp_data = 7;
|
||||
}
|
||||
|
||||
// EncryptedClientIdentification message used to hold ClientIdentification
|
||||
// messages encrypted for privacy purposes.
|
||||
message EncryptedClientIdentification {
|
||||
// Service ID for which the ClientIdentifcation is encrypted (owner of service
|
||||
// certificate).
|
||||
optional string service_id = 1;
|
||||
// Provider ID for which the ClientIdentifcation is encrypted (owner of
|
||||
// service certificate).
|
||||
optional string provider_id = 1;
|
||||
// Serial number for the service certificate for which ClientIdentification is
|
||||
// encrypted.
|
||||
optional bytes service_certificate_serial_number = 2;
|
||||
// Serialized ClientIdentification message, encrypted with the privacy key using
|
||||
// AES-128-CBC with PKCS#5 padding.
|
||||
// Serialized ClientIdentification message, encrypted with the privacy key
|
||||
// using AES-128-CBC with PKCS#5 padding.
|
||||
optional bytes encrypted_client_id = 3;
|
||||
// Initialization vector needed to decrypt encrypted_client_id.
|
||||
optional bytes encrypted_client_id_iv = 4;
|
||||
// AES-128 privacy key, encrypted with the service public public key using
|
||||
// RSA-OAEP.
|
||||
// AES-128 privacy key, encrypted with the service public key using RSA-OAEP.
|
||||
optional bytes encrypted_privacy_key = 5;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// device_certificate.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Device certificate and certificate status list format definitions.
|
||||
|
||||
// Certificate definition for user devices, intermediate, service, and root
|
||||
// DRM certificate definition for user devices, intermediate, service, and root
|
||||
// certificates.
|
||||
message DeviceCertificate {
|
||||
message DrmDeviceCertificate {
|
||||
enum CertificateType {
|
||||
ROOT = 0;
|
||||
INTERMEDIATE = 1;
|
||||
USER_DEVICE = 2;
|
||||
DRM_INTERMEDIATE = 1;
|
||||
DRM_USER_DEVICE = 2;
|
||||
SERVICE = 3;
|
||||
PROVISIONER = 4;
|
||||
}
|
||||
|
||||
// Type of certificate. Required.
|
||||
@@ -500,28 +585,60 @@ message DeviceCertificate {
|
||||
// (non-production) device. The test_device field in ProvisionedDeviceInfo
|
||||
// below should be observed instead.
|
||||
optional bool test_device_deprecated = 6 [deprecated = true];
|
||||
// Service identifier (web origin) for the service which owns the certificate.
|
||||
// Required for service certificates.
|
||||
optional string service_id = 7;
|
||||
// Service identifier (web origin) for the provider which owns the
|
||||
// certificate. Required for service and provisioner certificates.
|
||||
optional string provider_id = 7;
|
||||
}
|
||||
|
||||
// DeviceCertificate signed with intermediate or root certificate private key.
|
||||
message SignedDeviceCertificate {
|
||||
// Serialized DeviceCertificate. Required.
|
||||
optional bytes device_certificate = 1;
|
||||
// Signature of device_certificate. Signed with root or intermediate
|
||||
// certificate private key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
// Intermediate signing certificate. Present only for user device
|
||||
// certificates. All others signed with root certificate private key.
|
||||
optional SignedDeviceCertificate signer = 3;
|
||||
// Contains DRM and OEM certificate status and device information for a
|
||||
// specific system ID.
|
||||
message DeviceCertificateStatus {
|
||||
enum Status {
|
||||
VALID = 0;
|
||||
REVOKED = 1;
|
||||
};
|
||||
|
||||
// Serial number of the intermediate DrmDeviceCertificate to which this
|
||||
// message refers. Required.
|
||||
optional bytes drm_serial_number = 1;
|
||||
// Status of the certificate. Optional.
|
||||
optional Status status = 2 [default = VALID];
|
||||
// Device model information about the device to which the intermediate
|
||||
// certificate(s) correspond.
|
||||
optional ProvisionedDeviceInfo device_info = 4;
|
||||
// Serial number of the OEM X.509 intermediate certificate for this type
|
||||
// of device. Present only if the device is OEM-provisioned.
|
||||
optional bytes oem_serial_number = 5;
|
||||
}
|
||||
|
||||
// List of DeviceCertificateStatus. Used to propagate certificate revocation
|
||||
// status and device information.
|
||||
message DeviceCertificateStatusList {
|
||||
// POSIX time, in seconds, when the list was created. Required.
|
||||
optional uint32 creation_time_seconds = 1;
|
||||
// DeviceCertificateStatus for each system ID.
|
||||
repeated DeviceCertificateStatus certificate_status = 2;
|
||||
}
|
||||
|
||||
// Signed CertificateStatusList
|
||||
message SignedCertificateStatusList {
|
||||
// Serialized DeviceCertificateStatusList. Required.
|
||||
optional bytes certificate_status_list = 1;
|
||||
// Signature of certificate_status_list. Signed with root certificate private
|
||||
// key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// provisioned_device_info.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Description:
|
||||
// Provisioned device info format definitions.
|
||||
|
||||
// Contains device model information for a provisioned device.
|
||||
message ProvisionedDeviceInfo {
|
||||
enum WvSecurityLevel {
|
||||
// Defined in "WV Modular DRM Security Integration Guide for
|
||||
// Common Encryption (CENC)"
|
||||
// Defined in "Widevine Security Integration Guide for DASH on Android"
|
||||
LEVEL_UNSPECIFIED = 0;
|
||||
LEVEL_1 = 1;
|
||||
LEVEL_2 = 2;
|
||||
@@ -547,49 +664,13 @@ message ProvisionedDeviceInfo {
|
||||
optional bool test_device = 8 [default = false];
|
||||
}
|
||||
|
||||
// Contains the status of the root or an intermediate DeviceCertificate.
|
||||
message DeviceCertificateStatus {
|
||||
enum CertificateStatus {
|
||||
VALID = 0;
|
||||
REVOKED = 1;
|
||||
};
|
||||
|
||||
// Serial number of the DeviceCertificate to which this message refers.
|
||||
// Required.
|
||||
optional bytes serial_number = 1;
|
||||
// Status of the certificate. Optional.
|
||||
optional CertificateStatus status = 2 [default = VALID];
|
||||
// Device model information about the device to which the certificate
|
||||
// corresponds. Required.
|
||||
optional ProvisionedDeviceInfo device_info = 4;
|
||||
}
|
||||
|
||||
// List of DeviceCertificateStatus. Used to propagate certificate revocation and
|
||||
// update list.
|
||||
message DeviceCertificateStatusList {
|
||||
// POSIX time, in seconds, when the list was created. Required.
|
||||
optional uint32 creation_time_seconds = 1;
|
||||
// DeviceCertificateStatus for each certifificate.
|
||||
repeated DeviceCertificateStatus certificate_status = 2;
|
||||
}
|
||||
|
||||
// Signed CertificateStatusList
|
||||
message SignedCertificateStatusList {
|
||||
// Serialized DeviceCertificateStatusList. Required.
|
||||
optional bytes certificate_status_list = 1;
|
||||
// Signature of certificate_status_list. Signed with root certificate private
|
||||
// key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// widevine_header.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Public protocol buffer definitions for Widevine Cenc Header
|
||||
// protocol.
|
||||
|
||||
message WidevineCencHeader {
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
@@ -626,3 +707,15 @@ message WidevineCencHeader {
|
||||
// 'cens' (AES-CTR subsample), 'cbcs' (AES-CBC subsample).
|
||||
optional uint32 protection_scheme = 9;
|
||||
}
|
||||
|
||||
// Signed device certificate definition.
|
||||
// DrmDeviceCertificate signed by a higher (CA) DRM certificate.
|
||||
message SignedDrmDeviceCertificate {
|
||||
// Serialized certificate. Required.
|
||||
optional bytes drm_certificate = 1;
|
||||
// Signature of certificate. Signed with root or intermediate
|
||||
// certificate specified below. Required.
|
||||
optional bytes signature = 2;
|
||||
// SignedDrmDeviceCertificate used to sign this certificate.
|
||||
optional SignedDrmDeviceCertificate signer = 3;
|
||||
}
|
||||
|
||||
@@ -76,4 +76,10 @@ OEMCryptoResult OEMCrypto_CopyBuffer(
|
||||
return ::OEMCrypto_CopyBuffer(data_addr, data_length, out_buffer,
|
||||
subsample_flags);
|
||||
}
|
||||
|
||||
OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(
|
||||
SecurityLevel level) {
|
||||
return ::OEMCrypto_GetProvisioningMethod();
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
33
core/src/oemcrypto_adapter_static_v12.cpp
Normal file
33
core/src/oemcrypto_adapter_static_v12.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Wrapper of OEMCrypto APIs for platforms that support Level 1 only.
|
||||
// This should be used when liboemcrypto.so is linked with the CDM code at
|
||||
// compile time.
|
||||
//
|
||||
// Defines APIs introduced in newer version (v12) which is not available in v11
|
||||
// to allow an older oemcrypto implementation to be linked with CDM.
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
extern "C" OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() {
|
||||
return OEMCrypto_Keybox;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
|
||||
OEMCrypto_SESSION session, uint8_t* public_cert,
|
||||
size_t* public_cert_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30(
|
||||
OEMCrypto_SESSION session,
|
||||
const uint32_t *nonce,
|
||||
const uint8_t* encrypted_message_key,
|
||||
size_t encrypted_message_key_length,
|
||||
const uint8_t* enc_rsa_key,
|
||||
size_t enc_rsa_key_length,
|
||||
const uint8_t* enc_rsa_key_iv,
|
||||
uint8_t* wrapped_rsa_key,
|
||||
size_t* wrapped_rsa_key_length) {
|
||||
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature_V8(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length);
|
||||
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
|
||||
extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V9_or_V10(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const uint8_t* signature, size_t signature_length,
|
||||
const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys,
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine::License;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -31,9 +31,10 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
playback_start_time_(0),
|
||||
last_playback_time_(0),
|
||||
last_expiry_time_(0),
|
||||
grace_period_end_time_(0),
|
||||
last_expiry_time_set_(false),
|
||||
was_expired_on_load_(false),
|
||||
next_renewal_time_(0),
|
||||
policy_max_duration_seconds_(0),
|
||||
session_id_(session_id),
|
||||
event_listener_(event_listener),
|
||||
license_keys_(new LicenseKeys),
|
||||
@@ -43,11 +44,11 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
|
||||
|
||||
PolicyEngine::~PolicyEngine() {}
|
||||
|
||||
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
|
||||
bool PolicyEngine::CanDecryptContent(const KeyId& key_id) {
|
||||
if (license_keys_->IsContentKey(key_id)) {
|
||||
return license_keys_->CanDecryptContent(key_id);
|
||||
} else {
|
||||
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
|
||||
LOGE("PolicyEngine::CanDecryptContent Key '%s' not in license.",
|
||||
b2a_hex(key_id).c_str());
|
||||
return false;
|
||||
}
|
||||
@@ -78,8 +79,14 @@ void PolicyEngine::CheckDevice(int64_t current_time) {
|
||||
void PolicyEngine::OnTimerEvent() {
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
|
||||
// If we have passed the grace period, the expiration will update.
|
||||
if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) {
|
||||
grace_period_end_time_ = playback_start_time_;
|
||||
NotifyExpirationUpdate(current_time);
|
||||
}
|
||||
|
||||
// License expiration trumps all.
|
||||
if (IsLicenseOrPlaybackDurationExpired(current_time) &&
|
||||
if (HasLicenseOrPlaybackDurationExpired(current_time) &&
|
||||
license_state_ != kLicenseStateExpired) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
@@ -94,7 +101,7 @@ void PolicyEngine::OnTimerEvent() {
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateCanPlay: {
|
||||
if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
|
||||
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
|
||||
// HDCP may change, so force a check.
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
break;
|
||||
@@ -106,7 +113,7 @@ void PolicyEngine::OnTimerEvent() {
|
||||
}
|
||||
|
||||
case kLicenseStateWaitingLicenseUpdate: {
|
||||
if (IsRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
|
||||
if (HasRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -179,23 +186,9 @@ void PolicyEngine::UpdateLicense(const License& license) {
|
||||
license_start_time_ = license.license_start_time();
|
||||
next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds();
|
||||
|
||||
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
|
||||
// will be set to the minimum of the following policies :
|
||||
// rental_duration_seconds and license_duration_seconds.
|
||||
// The value is used to determine when the license expires.
|
||||
policy_max_duration_seconds_ = 0;
|
||||
|
||||
if (policy_.has_rental_duration_seconds())
|
||||
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
||||
|
||||
if ((policy_.license_duration_seconds() > 0) &&
|
||||
((policy_.license_duration_seconds() < policy_max_duration_seconds_) ||
|
||||
policy_max_duration_seconds_ == 0)) {
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
if (!policy_.can_play() || IsLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
if (!policy_.can_play() ||
|
||||
HasLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
return;
|
||||
@@ -209,7 +202,7 @@ void PolicyEngine::UpdateLicense(const License& license) {
|
||||
license_state_ = kLicenseStatePending;
|
||||
NotifyKeysChange(kKeyStatusPending);
|
||||
}
|
||||
NotifyExpirationUpdate();
|
||||
NotifyExpirationUpdate(current_time);
|
||||
}
|
||||
|
||||
void PolicyEngine::BeginDecryption() {
|
||||
@@ -220,11 +213,13 @@ void PolicyEngine::BeginDecryption() {
|
||||
case kLicenseStateWaitingLicenseUpdate:
|
||||
playback_start_time_ = clock_->GetCurrentTime();
|
||||
last_playback_time_ = playback_start_time_;
|
||||
if (policy_.play_start_grace_period_seconds() == 0)
|
||||
grace_period_end_time_ = playback_start_time_;
|
||||
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
NotifyExpirationUpdate();
|
||||
NotifyExpirationUpdate(playback_start_time_);
|
||||
break;
|
||||
case kLicenseStateInitial:
|
||||
case kLicenseStatePending:
|
||||
@@ -258,7 +253,7 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
|
||||
}
|
||||
|
||||
(*query_response)[QUERY_KEY_LICENSE_TYPE] =
|
||||
license_id_.type() == video_widevine_server::sdk::STREAMING
|
||||
license_id_.type() == video_widevine::STREAMING
|
||||
? QUERY_VALUE_STREAMING
|
||||
: QUERY_VALUE_OFFLINE;
|
||||
(*query_response)[QUERY_KEY_PLAY_ALLOWED] =
|
||||
@@ -267,7 +262,7 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
|
||||
policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
(*query_response)[QUERY_KEY_RENEW_ALLOWED] =
|
||||
policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
|
||||
ss << GetLicenseDurationRemaining(current_time);
|
||||
ss << GetLicenseOrRentalDurationRemaining(current_time);
|
||||
(*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
|
||||
ss.str("");
|
||||
ss << GetPlaybackDurationRemaining(current_time);
|
||||
@@ -304,16 +299,34 @@ bool PolicyEngine::GetSecondsSinceLastPlayed(
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() {
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
return std::min(GetLicenseDurationRemaining(current_time),
|
||||
GetPlaybackDurationRemaining(current_time));
|
||||
const int64_t current_time = clock_->GetCurrentTime();
|
||||
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
|
||||
if (expiry_time == NEVER_EXPIRES) return LLONG_MAX;
|
||||
if (expiry_time < current_time) return 0;
|
||||
return expiry_time - current_time;
|
||||
}
|
||||
|
||||
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time) {
|
||||
int64_t last_playback_time,
|
||||
int64_t grace_period_end_time) {
|
||||
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
|
||||
last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0;
|
||||
NotifyExpirationUpdate();
|
||||
grace_period_end_time_ = grace_period_end_time;
|
||||
|
||||
if (policy_.play_start_grace_period_seconds() != 0) {
|
||||
// If we are using grace period, we may need to override some of the values
|
||||
// given to us by OEMCrypto. |grace_period_end_time| will be 0 if the grace
|
||||
// period has not expired (effectively playback has not begun). Otherwise,
|
||||
// |grace_period_end_time| contains the playback start time we should use.
|
||||
playback_start_time_ = grace_period_end_time;
|
||||
}
|
||||
|
||||
const int64_t current_time = clock_->GetCurrentTime();
|
||||
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ true);
|
||||
was_expired_on_load_ =
|
||||
expiry_time != NEVER_EXPIRES && expiry_time < current_time;
|
||||
|
||||
NotifyExpirationUpdate(current_time);
|
||||
}
|
||||
|
||||
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
@@ -321,61 +334,87 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
|
||||
next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds();
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsLicenseOrPlaybackDurationExpired(int64_t current_time) {
|
||||
int64_t expiry_time =
|
||||
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
|
||||
return (expiry_time == NEVER_EXPIRES) ? false : (expiry_time <= current_time);
|
||||
bool PolicyEngine::HasLicenseOrPlaybackDurationExpired(int64_t current_time) {
|
||||
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
|
||||
return expiry_time != NEVER_EXPIRES && expiry_time <= current_time;
|
||||
}
|
||||
|
||||
// For the policy time fields checked in the following methods, a value of 0
|
||||
// indicates that there is no limit to the duration. These methods
|
||||
// will always return false if the value is 0.
|
||||
int64_t PolicyEngine::GetLicenseExpiryTime() {
|
||||
return policy_max_duration_seconds_ > 0
|
||||
? license_start_time_ + policy_max_duration_seconds_
|
||||
// indicates that there is no limit to the duration. If the fields are zero
|
||||
// (including the hard limit) then these methods will return NEVER_EXPIRES.
|
||||
|
||||
int64_t PolicyEngine::GetHardLicenseExpiryTime() {
|
||||
return policy_.license_duration_seconds() > 0
|
||||
? license_start_time_ + policy_.license_duration_seconds()
|
||||
: NEVER_EXPIRES;
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackExpiryTime() {
|
||||
return (playback_start_time_ > 0 && policy_.playback_duration_seconds() > 0)
|
||||
? (playback_start_time_ + policy_.playback_duration_seconds())
|
||||
: NEVER_EXPIRES;
|
||||
int64_t PolicyEngine::GetRentalExpiryTime() {
|
||||
const int64_t hard_limit = GetHardLicenseExpiryTime();
|
||||
if (policy_.rental_duration_seconds() == 0) return hard_limit;
|
||||
const int64_t expiry_time =
|
||||
license_start_time_ + policy_.rental_duration_seconds();
|
||||
if (hard_limit == NEVER_EXPIRES) return expiry_time;
|
||||
return std::min(hard_limit, expiry_time);
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
|
||||
int64_t license_expiry_time = GetLicenseExpiryTime();
|
||||
int64_t PolicyEngine::GetExpiryTime(int64_t current_time, bool is_load) {
|
||||
if (!HasPlaybackStarted(current_time))
|
||||
return GetRentalExpiryTime();
|
||||
|
||||
const int64_t hard_limit = GetHardLicenseExpiryTime();
|
||||
if (policy_.playback_duration_seconds() == 0) return hard_limit;
|
||||
if (!is_load && !was_expired_on_load_ &&
|
||||
policy_.soft_enforce_playback_duration()) {
|
||||
return hard_limit;
|
||||
}
|
||||
const int64_t expiry_time =
|
||||
playback_start_time_ + policy_.playback_duration_seconds();
|
||||
|
||||
if (hard_limit == NEVER_EXPIRES)
|
||||
return expiry_time;
|
||||
return std::min(hard_limit, expiry_time);
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetLicenseOrRentalDurationRemaining(
|
||||
int64_t current_time) {
|
||||
// This is only used in Query. This should return the time remaining on
|
||||
// license_duration_seconds for streaming licenses and rental_duration_seconds
|
||||
// for offline licenses.
|
||||
if (HasLicenseOrPlaybackDurationExpired(current_time)) return 0;
|
||||
const int64_t license_expiry_time = GetRentalExpiryTime();
|
||||
if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX;
|
||||
if (license_expiry_time < current_time) return 0;
|
||||
return std::min(license_expiry_time - current_time,
|
||||
policy_max_duration_seconds_);
|
||||
policy_.license_duration_seconds());
|
||||
}
|
||||
|
||||
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
|
||||
int64_t playback_expiry_time = GetPlaybackExpiryTime();
|
||||
if (playback_expiry_time == NEVER_EXPIRES) {
|
||||
return (policy_.playback_duration_seconds() != 0)
|
||||
? policy_.playback_duration_seconds()
|
||||
: LLONG_MAX;
|
||||
}
|
||||
// This is only used in Query. This should return playback_duration_seconds,
|
||||
// or the time remaining on it if playing.
|
||||
const int64_t playback_duration = policy_.playback_duration_seconds();
|
||||
if (playback_duration == 0) return LLONG_MAX;
|
||||
if (playback_start_time_ == 0) return playback_duration;
|
||||
|
||||
const int64_t playback_expiry_time = playback_duration + playback_start_time_;
|
||||
if (playback_expiry_time < current_time) return 0;
|
||||
return std::min(playback_expiry_time - current_time,
|
||||
policy_.playback_duration_seconds());
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
|
||||
bool PolicyEngine::HasRenewalDelayExpired(int64_t current_time) {
|
||||
return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) &&
|
||||
license_start_time_ + policy_.renewal_delay_seconds() <= current_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalRecoveryDurationExpired(int64_t current_time) {
|
||||
bool PolicyEngine::HasRenewalRecoveryDurationExpired(int64_t current_time) {
|
||||
// NOTE: Renewal Recovery Duration is currently not used.
|
||||
return (policy_.renewal_recovery_duration_seconds() > 0) &&
|
||||
license_start_time_ + policy_.renewal_recovery_duration_seconds() <=
|
||||
current_time;
|
||||
}
|
||||
|
||||
bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
return policy_.can_renew() &&
|
||||
(policy_.renewal_retry_interval_seconds() > 0) &&
|
||||
next_renewal_time_ <= current_time;
|
||||
@@ -394,9 +433,8 @@ void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyExpirationUpdate() {
|
||||
int64_t expiry_time =
|
||||
IsPlaybackStarted() ? GetPlaybackExpiryTime() : GetLicenseExpiryTime();
|
||||
void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) {
|
||||
const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false);
|
||||
if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) {
|
||||
last_expiry_time_ = expiry_time;
|
||||
if (event_listener_)
|
||||
|
||||
@@ -31,7 +31,9 @@ const int kOaepMinPadding = 2 * CC_SHA1_DIGEST_LENGTH + 1;
|
||||
template<typename T>
|
||||
struct CFDeleter {
|
||||
void operator()(T arg) {
|
||||
CFRelease(arg);
|
||||
if (arg) {
|
||||
CFRelease(arg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,6 +56,8 @@ SecKeyRef ImportPublicKey(const std::string& key) {
|
||||
kSecAttrKeyTypeRSA);
|
||||
CFDictionarySetValue(deleteAttributes.get(), kSecAttrApplicationTag,
|
||||
peerData.get());
|
||||
CFDictionarySetValue(deleteAttributes.get(), kSecAttrAccessible,
|
||||
kSecAttrAccessibleAfterFirstUnlock);
|
||||
SecItemDelete(deleteAttributes.get());
|
||||
|
||||
// Create attributes to add to the keystore.
|
||||
@@ -68,11 +72,13 @@ SecKeyRef ImportPublicKey(const std::string& key) {
|
||||
keyData.get());
|
||||
CFDictionarySetValue(addAttributes.get(), kSecAttrKeyClass,
|
||||
kSecAttrKeyClassPublic);
|
||||
CFDictionarySetValue(addAttributes.get(), kSecAttrAccessible,
|
||||
kSecAttrAccessibleAfterFirstUnlock);
|
||||
CFDictionarySetValue(addAttributes.get(), kSecReturnPersistentRef,
|
||||
kCFBooleanTrue);
|
||||
|
||||
// Add the key to the keystore.
|
||||
CFTypeRef temp;
|
||||
CFTypeRef temp = NULL;
|
||||
OSStatus status = SecItemAdd(addAttributes.get(), &temp);
|
||||
CF<CFTypeRef> peer(temp);
|
||||
if (!peer || (status != noErr && status != errSecDuplicateItem)) {
|
||||
@@ -90,6 +96,8 @@ SecKeyRef ImportPublicKey(const std::string& key) {
|
||||
kSecAttrKeyTypeRSA);
|
||||
CFDictionarySetValue(queryAttributes.get(), kSecAttrKeyClass,
|
||||
kSecAttrKeyClassPublic);
|
||||
CFDictionarySetValue(queryAttributes.get(), kSecAttrAccessible,
|
||||
kSecAttrAccessibleAfterFirstUnlock);
|
||||
CFDictionarySetValue(queryAttributes.get(), kSecReturnRef, kCFBooleanTrue);
|
||||
|
||||
// Query the keychain to get the public key ref.
|
||||
@@ -152,7 +160,6 @@ std::string ApplyOAEPPadding(const std::string& messageStr, size_t rsaSize) {
|
||||
ret.resize(rsaSize);
|
||||
size_t messageLength = messageStr.length();
|
||||
size_t paddingLength = rsaSize - messageLength;
|
||||
size_t psLen = paddingLength - kOaepMinPadding;
|
||||
const uint8_t *message = reinterpret_cast<const uint8_t*>(messageStr.data());
|
||||
uint8_t *result = reinterpret_cast<uint8_t*>(&ret[0]);
|
||||
uint8_t *seed = result + 1;
|
||||
|
||||
@@ -62,7 +62,7 @@ bool Properties::GetServiceCertificate(const CdmSessionId& session_id,
|
||||
std::string* service_certificate) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
if (property_set == NULL) {
|
||||
return false;
|
||||
}
|
||||
*service_certificate = property_set->service_certificate();
|
||||
@@ -73,7 +73,7 @@ bool Properties::SetServiceCertificate(const CdmSessionId& session_id,
|
||||
const std::string& service_certificate) {
|
||||
CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
if (property_set == NULL) {
|
||||
return false;
|
||||
}
|
||||
property_set->set_service_certificate(service_certificate);
|
||||
|
||||
216
core/src/service_certificate.cpp
Normal file
216
core/src/service_certificate.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "service_certificate.h"
|
||||
|
||||
#include "crypto_key.h"
|
||||
#include "crypto_session.h"
|
||||
#include "log.h"
|
||||
#include "privacy_crypto.h"
|
||||
#include "properties.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
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};
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine::ClientIdentification;
|
||||
using video_widevine::DrmDeviceCertificate;
|
||||
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& raw_certificate) {
|
||||
return VerifyAndExtract(raw_certificate);
|
||||
}
|
||||
|
||||
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 (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());
|
||||
|
||||
std::string iv(KEY_IV_SIZE, 0);
|
||||
std::string key(KEY_SIZE, 0);
|
||||
|
||||
if (!crypto_session->GetRandom(key.size(),
|
||||
reinterpret_cast<uint8_t*>(&key[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
if (!crypto_session->GetRandom(iv.size(),
|
||||
reinterpret_cast<uint8_t*>(&iv[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
std::string id, enc_id, enc_key;
|
||||
clear_client_id->SerializeToString(&id);
|
||||
|
||||
AesCbcKey aes;
|
||||
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;
|
||||
|
||||
encrypted_client_id->set_encrypted_client_id_iv(iv);
|
||||
encrypted_client_id->set_encrypted_privacy_key(enc_key);
|
||||
encrypted_client_id->set_encrypted_client_id(enc_id);
|
||||
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
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#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"
|
||||
@@ -80,9 +81,11 @@ class WvCdmEngineTest : public testing::Test {
|
||||
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));
|
||||
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);
|
||||
@@ -147,7 +150,8 @@ class WvCdmEngineTest : public testing::Test {
|
||||
EXPECT_TRUE(ok);
|
||||
|
||||
int status_code = url_request.GetStatusCode(response);
|
||||
if (expect_success) EXPECT_EQ(kHttpOk, status_code);
|
||||
if (expect_success) EXPECT_EQ(kHttpOk, status_code)
|
||||
<< "Error response: " << response;
|
||||
|
||||
if (status_code != kHttpOk) {
|
||||
return "";
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "crypto_key.h"
|
||||
#include "properties.h"
|
||||
#include "scoped_ptr.h"
|
||||
#include "service_certificate.h"
|
||||
#include "string_conversions.h"
|
||||
#include "test_printers.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
@@ -14,6 +15,8 @@ namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kEmptyString;
|
||||
|
||||
const std::string kToken = a2bs_hex(
|
||||
"0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02"
|
||||
"82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77"
|
||||
@@ -93,12 +96,15 @@ class MockDeviceFiles : public DeviceFiles {
|
||||
MockDeviceFiles() : DeviceFiles(NULL) {}
|
||||
|
||||
MOCK_METHOD1(Init, bool(CdmSecurityLevel));
|
||||
MOCK_METHOD2(RetrieveCertificate, bool(std::string*, std::string*));
|
||||
MOCK_METHOD4(RetrieveCertificate, bool(std::string*, std::string*,
|
||||
std::string*, uint32_t*));
|
||||
};
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD1(GetToken, bool(std::string*));
|
||||
MOCK_METHOD1(GetClientToken, bool(std::string*));
|
||||
MOCK_METHOD1(GetProvisioningToken, bool(std::string*));
|
||||
MOCK_METHOD0(GetPreProvisionTokenType, CdmClientTokenType());
|
||||
MOCK_METHOD0(GetSecurityLevel, CdmSecurityLevel());
|
||||
MOCK_METHOD0(Open, CdmResponseType());
|
||||
MOCK_METHOD1(Open, CdmResponseType(SecurityLevel));
|
||||
@@ -118,7 +124,9 @@ class MockCdmLicense : public CdmLicense {
|
||||
MockCdmLicense(const CdmSessionId& session_id)
|
||||
: CdmLicense(session_id) {}
|
||||
|
||||
MOCK_METHOD3(Init, bool(const std::string&, CryptoSession*, PolicyEngine*));
|
||||
MOCK_METHOD6(Init, bool(ServiceCertificate*, const std::string&,
|
||||
CdmClientTokenType, const std::string&,
|
||||
CryptoSession*, PolicyEngine*));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -135,6 +143,7 @@ using ::testing::StrEq;
|
||||
class CdmSessionTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
service_cert_ = new ServiceCertificate;
|
||||
cdm_session_.reset(new CdmSession(NULL));
|
||||
// Inject testing mocks.
|
||||
license_parser_ = new MockCdmLicense(cdm_session_->session_id());
|
||||
@@ -152,8 +161,38 @@ class CdmSessionTest : public ::testing::Test {
|
||||
MockCryptoSession* crypto_session_;
|
||||
MockPolicyEngine* policy_engine_;
|
||||
MockDeviceFiles* file_handle_;
|
||||
ServiceCertificate* service_cert_;
|
||||
};
|
||||
|
||||
TEST_F(CdmSessionTest, InitWithBuiltInCertificate) {
|
||||
Sequence crypto_session_seq;
|
||||
CdmSecurityLevel level = kSecurityLevelL1;
|
||||
EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault)))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
|
||||
.WillOnce(Return(kClientTokenDrmCert));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
|
||||
NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*license_parser_,
|
||||
Init(NULL, Eq(kToken), Eq(kClientTokenDrmCert),
|
||||
Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL));
|
||||
}
|
||||
|
||||
TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
Sequence crypto_session_seq;
|
||||
CdmSecurityLevel level = kSecurityLevelL1;
|
||||
@@ -163,15 +202,19 @@ TEST_F(CdmSessionTest, InitWithCertificate) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
|
||||
.WillOnce(Return(kClientTokenKeybox));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
|
||||
NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(*license_parser_,
|
||||
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
Init(NULL, Eq(kToken), Eq(kClientTokenDrmCert),
|
||||
Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
@@ -188,12 +231,15 @@ TEST_F(CdmSessionTest, InitWithKeybox) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*crypto_session_, GetToken(NotNull()))
|
||||
EXPECT_CALL(*crypto_session_, GetClientToken(NotNull()))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
|
||||
.WillOnce(Return(kClientTokenKeybox));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*license_parser_,
|
||||
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
Init(NULL, Eq(kToken), Eq(kClientTokenKeybox),
|
||||
Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
Properties::set_use_certificates_as_identification(false);
|
||||
@@ -210,15 +256,19 @@ TEST_F(CdmSessionTest, ReInitFail) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
|
||||
.WillOnce(Return(kClientTokenKeybox));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
|
||||
NotNull(), NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kToken), SetArgPointee<1>(kWrappedKey),
|
||||
Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, LoadCertificatePrivateKey(StrEq(kWrappedKey)))
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(*license_parser_,
|
||||
Init(Eq(kToken), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
Init(NULL, Eq(kToken), Eq(kClientTokenDrmCert),
|
||||
Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_)))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
@@ -245,8 +295,11 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) {
|
||||
EXPECT_CALL(*crypto_session_, GetSecurityLevel())
|
||||
.InSequence(crypto_session_seq)
|
||||
.WillOnce(Return(level));
|
||||
EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType())
|
||||
.WillOnce(Return(kClientTokenKeybox));
|
||||
EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true));
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull()))
|
||||
EXPECT_CALL(*file_handle_, RetrieveCertificate(NotNull(), NotNull(),
|
||||
NotNull(), NotNull()))
|
||||
.WillOnce(Return(false));
|
||||
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
|
||||
36
core/test/default_service_certificate.h
Normal file
36
core/test/default_service_certificate.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// 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_
|
||||
@@ -118,6 +118,7 @@ struct LicenseInfo {
|
||||
std::string key_release_url;
|
||||
int64_t playback_start_time;
|
||||
int64_t last_playback_time;
|
||||
int64_t grace_period_end_time;
|
||||
std::string app_parameters;
|
||||
std::string file_data;
|
||||
};
|
||||
@@ -225,9 +226,9 @@ LicenseInfo license_test_data[] = {
|
||||
"0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF"
|
||||
"A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0"
|
||||
"BEA6CABACA1C2C"),
|
||||
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, "",
|
||||
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "",
|
||||
a2bs_hex(
|
||||
"0AA8150802100122A1150801121408011210303132333435363738394142434445461"
|
||||
"0AAA150802100122A3150801121408011210303132333435363738394142434445461"
|
||||
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
|
||||
"7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302"
|
||||
"5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B"
|
||||
@@ -306,8 +307,8 @@ LicenseInfo license_test_data[] = {
|
||||
"106B63746C0000000000ECDCBE0000000020DBDFA68F051A20182F029E35047A3841F"
|
||||
"A176C74E5B387350E8D58DEA6878FF0BEA6CABACA1C2C3A2E68747470733A2F2F7465"
|
||||
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
|
||||
"365400048001220CD0599C2B85D9F2D573AC7893CE77CB5A10B326828BA8C89047505"
|
||||
"A8C9B606AC")},
|
||||
"365400048005800122066F4FFF1CB2C0F978149A9402F3E4FF8D49B19635E646A0678"
|
||||
"71AA08E7A8FDC3")},
|
||||
|
||||
// license 1
|
||||
{"ksidC8EAA2579A282EB0", DeviceFiles::kLicenseStateReleasing,
|
||||
@@ -406,9 +407,9 @@ LicenseInfo license_test_data[] = {
|
||||
"A68F051A20BDA6A56F7CBFD0942198F87C23A34AA5CBD64AFEB134277774"
|
||||
"CCF8E789D815DD"),
|
||||
"https://test.google.com/license/GetCencLicense", 0x12345678, 0x12348765,
|
||||
"Name1 Value1",
|
||||
0x0, "Name1 Value1",
|
||||
a2bs_hex(
|
||||
"0AC1150802100122BA150802121408011210303132333435363738394142434445461"
|
||||
"0AC3150802100122BC150802121408011210303132333435363738394142434445461"
|
||||
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
|
||||
"7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302"
|
||||
"5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B"
|
||||
@@ -487,8 +488,8 @@ LicenseInfo license_test_data[] = {
|
||||
"106B63746C00000000CA3A6A75000000002083E5A68F051A20BDA6A56F7CBFD094219"
|
||||
"8F87C23A34AA5CBD64AFEB134277774CCF8E789D815DD3A2E68747470733A2F2F7465"
|
||||
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
|
||||
"36540F8ACD1910148E58ED29101520F0A054E616D6531120656616C7565311220BE69"
|
||||
"AAB25B481BCAF57B741518D9F9DB8E3A7A6911D10C53D4F4649D78393C65")},
|
||||
"36540F8ACD1910148E58ED29101520F0A054E616D6531120656616C75653158001220"
|
||||
"9C0315FC1812C6A0E5936E36D04ECE2FA56AF4AB544ECDF3C9135D54B4A26167")},
|
||||
|
||||
// license 2
|
||||
{"ksidE8C37662C88DC673", DeviceFiles::kLicenseStateReleasing,
|
||||
@@ -587,9 +588,9 @@ LicenseInfo license_test_data[] = {
|
||||
"A68F051A2041EF0A9267D613D17AA90E1D1DA5BE091860E5E296D41D6D0F"
|
||||
"75E73660C279B3"),
|
||||
"https://test.google.com/license/GetCencLicense", 0x0123456789abcdef,
|
||||
0x123456789abfedc, "Name1 Value1 Name2 Param2",
|
||||
0x123456789abfedc, 0x0, "Name1 Value1 Name2 Param2",
|
||||
a2bs_hex(
|
||||
"0AE7150802100122E0150802121408011210303132333435363738394142434445461"
|
||||
"0AE9150802100122E2150802121408011210303132333435363738394142434445461"
|
||||
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
|
||||
"7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302"
|
||||
"5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B"
|
||||
@@ -669,9 +670,9 @@ LicenseInfo license_test_data[] = {
|
||||
"90E1D1DA5BE091860E5E296D41D6D0F75E73660C279B33A2E68747470733A2F2F7465"
|
||||
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
|
||||
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910152150A054E616D6531120C5661"
|
||||
"6C756531204E616D653252160A0C4E616D653220506172616D321206506172616D321"
|
||||
"2203653BA57F16FE28D66D9F7A76128B7AD7F33680815FF70A3684617DE1FBB0F9"
|
||||
"F")}};
|
||||
"6C756531204E616D653252160A0C4E616D653220506172616D321206506172616D325"
|
||||
"8001220616E6AC4AF6EB4F7147E98CF7302425E2390B293BBC01F9F8E89B49F653EA3"
|
||||
"45")}};
|
||||
|
||||
// Sample license data and related data for storage and use for offline
|
||||
// playback. The license data and URLs in this test are not real.
|
||||
@@ -774,9 +775,9 @@ LicenseInfo license_update_test_data[] = {
|
||||
"B68F051A2000351030900858FCFD6977B67803ADFD1280AA661E6B0BD30B"
|
||||
"08B2C467355129"),
|
||||
"https://test.google.com/license/GetCencLicense", 0x0123456789abcdef,
|
||||
0x123456789abfedc, "Name1 Value1 Name2 Value2 Name3 Value3",
|
||||
0x123456789abfedc, 0x0, "Name1 Value1 Name2 Value2 Name3 Value3",
|
||||
a2bs_hex(
|
||||
"0AB8150802100122B1150801121408011210303132333435363738394142434445461"
|
||||
"0ABA150802100122B3150801121408011210303132333435363738394142434445461"
|
||||
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
|
||||
"7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82"
|
||||
"6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD"
|
||||
@@ -855,13 +856,14 @@ LicenseInfo license_update_test_data[] = {
|
||||
"106B63746C0000000071FEF30B0000000020F4DFB68F051A2000351030900858FCFD6"
|
||||
"977B67803ADFD1280AA661E6B0BD30B08B2C4673551293A2E68747470733A2F2F7465"
|
||||
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
|
||||
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191011220C7ACA00F6877DAAE2E8F50"
|
||||
"126C3222C2E584A50D08EFA75BC4FC091E7034E1DD")},
|
||||
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191015800122051F15CDA5B9414919D"
|
||||
"B67769A781CC4F43138D314DAFFCBFBD620E53167E4AF2")},
|
||||
// license being released. all fields are identical except for license
|
||||
// state and hashed file data
|
||||
{"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "", 0, 0, "",
|
||||
{"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "", 0, 0, 0,
|
||||
"",
|
||||
a2bs_hex(
|
||||
"0AB8150802100122B1150802121408011210303132333435363738394142434445461"
|
||||
"0ABA150802100122B3150802121408011210303132333435363738394142434445461"
|
||||
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
|
||||
"7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82"
|
||||
"6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD"
|
||||
@@ -940,8 +942,8 @@ LicenseInfo license_update_test_data[] = {
|
||||
"106B63746C0000000071FEF30B0000000020F4DFB68F051A2000351030900858FCFD6"
|
||||
"977B67803ADFD1280AA661E6B0BD30B08B2C4673551293A2E68747470733A2F2F7465"
|
||||
"73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7"
|
||||
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910112203D8933A735A22FE27AA956"
|
||||
"802B597529E8FFCB91A5F3CBBB3CE0C38E4AF3DC88")}};
|
||||
"36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191015800122093FDE0D42BC60D3932"
|
||||
"02E0D5A49775E08093BF01560EF72C298321E921716E24")}};
|
||||
|
||||
// Application parameters were added to the License message. This data
|
||||
// is used to verify that a License saved without application parameters can
|
||||
@@ -1043,7 +1045,7 @@ LicenseInfo license_app_parameters_backwards_compatibility_test_data = {
|
||||
"0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF"
|
||||
"A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0"
|
||||
"BEA6CABACA1C2C"),
|
||||
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, "",
|
||||
"https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "",
|
||||
a2bs_hex(
|
||||
"0AA8150802100122A1150801121408011210303132333435363738394142434445461"
|
||||
"A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0"
|
||||
@@ -1452,6 +1454,7 @@ class MockFileSystem : public FileSystem {
|
||||
MOCK_METHOD1(Exists, bool(const std::string&));
|
||||
MOCK_METHOD1(Remove, bool(const std::string&));
|
||||
MOCK_METHOD1(FileSize, ssize_t(const std::string&));
|
||||
MOCK_METHOD2(List, bool(const std::string&, std::vector<std::string>*));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -1489,7 +1492,7 @@ class DeviceFilesTest : public ::testing::Test {
|
||||
return sizeof(DeviceFiles::LicenseState) + data.pssh_data.size() +
|
||||
data.key_request.size() + data.key_response.size() +
|
||||
data.key_renewal_request.size() + data.key_renewal_response.size() +
|
||||
data.key_release_url.size() + 2 * sizeof(int64_t);
|
||||
data.key_release_url.size() + 3 * sizeof(int64_t);
|
||||
}
|
||||
|
||||
CdmAppParameterMap GetAppParameters(std::string str) {
|
||||
@@ -1654,10 +1657,15 @@ TEST_F(DeviceCertificateTest, ReadCertificate) {
|
||||
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
|
||||
|
||||
std::string certificate, wrapped_private_key;
|
||||
std::string serial_number;
|
||||
uint32_t system_id = 0;
|
||||
ASSERT_TRUE(
|
||||
device_files.RetrieveCertificate(&certificate, &wrapped_private_key));
|
||||
device_files.RetrieveCertificate(&certificate, &wrapped_private_key,
|
||||
&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("", b2a_hex(serial_number));
|
||||
}
|
||||
|
||||
TEST_F(DeviceCertificateTest, HasCertificate) {
|
||||
@@ -1747,7 +1755,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
|
||||
license_test_data[license_num].key_renewal_response,
|
||||
license_test_data[license_num].key_release_url,
|
||||
license_test_data[license_num].playback_start_time,
|
||||
license_test_data[license_num].last_playback_time, app_parameters));
|
||||
license_test_data[license_num].last_playback_time,
|
||||
license_test_data[license_num].grace_period_end_time, app_parameters));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, ::testing::Bool());
|
||||
@@ -1794,7 +1803,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
|
||||
license_test_data[i].key_renewal_response,
|
||||
license_test_data[i].key_release_url,
|
||||
license_test_data[i].playback_start_time,
|
||||
license_test_data[i].last_playback_time, app_parameters));
|
||||
license_test_data[i].last_playback_time,
|
||||
license_test_data[i].grace_period_end_time, app_parameters));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1832,6 +1842,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
|
||||
CdmKeyMessage key_renewal_request;
|
||||
CdmKeyResponse key_renewal_response;
|
||||
int64_t playback_start_time, last_playback_time;
|
||||
int64_t grace_period_end_time;
|
||||
std::string release_server_url;
|
||||
CdmAppParameterMap app_parameters;
|
||||
|
||||
@@ -1841,7 +1852,7 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
|
||||
license_test_data[i].key_set_id, &license_state, &pssh_data,
|
||||
&key_request, &key_response, &key_renewal_request,
|
||||
&key_renewal_response, &release_server_url, &playback_start_time,
|
||||
&last_playback_time, &app_parameters));
|
||||
&last_playback_time, &grace_period_end_time, &app_parameters));
|
||||
EXPECT_EQ(license_test_data[i].license_state, license_state);
|
||||
EXPECT_EQ(license_test_data[i].pssh_data, pssh_data);
|
||||
EXPECT_EQ(license_test_data[i].key_request, key_request);
|
||||
@@ -1850,6 +1861,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
|
||||
EXPECT_EQ(license_test_data[i].key_response, key_response);
|
||||
EXPECT_EQ(license_test_data[i].playback_start_time, playback_start_time);
|
||||
EXPECT_EQ(license_test_data[i].last_playback_time, last_playback_time);
|
||||
EXPECT_EQ(license_test_data[i].grace_period_end_time,
|
||||
grace_period_end_time);
|
||||
|
||||
std::map<std::string, std::string>::iterator itr;
|
||||
for (itr = app_parameters.begin(); itr != app_parameters.end(); ++itr) {
|
||||
@@ -1894,6 +1907,7 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
|
||||
CdmKeyMessage key_renewal_request;
|
||||
CdmKeyResponse key_renewal_response;
|
||||
int64_t playback_start_time, last_playback_time;
|
||||
int64_t grace_period_end_time;
|
||||
std::string release_server_url;
|
||||
CdmAppParameterMap app_parameters;
|
||||
|
||||
@@ -1901,7 +1915,7 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
|
||||
test_data->key_set_id, &license_state, &pssh_data, &key_request,
|
||||
&key_response, &key_renewal_request, &key_renewal_response,
|
||||
&release_server_url, &playback_start_time, &last_playback_time,
|
||||
&app_parameters));
|
||||
&grace_period_end_time, &app_parameters));
|
||||
EXPECT_EQ(test_data->license_state, license_state);
|
||||
EXPECT_EQ(test_data->pssh_data, pssh_data);
|
||||
EXPECT_EQ(test_data->key_request, key_request);
|
||||
@@ -1910,6 +1924,7 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) {
|
||||
EXPECT_EQ(test_data->key_response, key_response);
|
||||
EXPECT_EQ(test_data->playback_start_time, playback_start_time);
|
||||
EXPECT_EQ(test_data->last_playback_time, last_playback_time);
|
||||
EXPECT_EQ(test_data->grace_period_end_time, grace_period_end_time);
|
||||
EXPECT_EQ(0u, app_parameters.size());
|
||||
}
|
||||
|
||||
@@ -1945,6 +1960,7 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
|
||||
license_update_test_data[0].key_release_url,
|
||||
license_update_test_data[0].playback_start_time,
|
||||
license_update_test_data[0].last_playback_time,
|
||||
license_update_test_data[0].grace_period_end_time,
|
||||
GetAppParameters(license_test_data[0].app_parameters)));
|
||||
|
||||
EXPECT_TRUE(device_files.StoreLicense(
|
||||
@@ -1958,6 +1974,7 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
|
||||
license_update_test_data[0].key_release_url,
|
||||
license_update_test_data[0].playback_start_time,
|
||||
license_update_test_data[0].last_playback_time,
|
||||
license_update_test_data[0].grace_period_end_time,
|
||||
GetAppParameters(license_test_data[0].app_parameters)));
|
||||
}
|
||||
|
||||
@@ -1996,14 +2013,14 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
|
||||
CdmKeyMessage key_renewal_request;
|
||||
CdmKeyResponse key_renewal_response;
|
||||
std::string release_server_url;
|
||||
int64_t playback_start_time, last_playback_time;
|
||||
int64_t playback_start_time, last_playback_time, grace_period_end_time;
|
||||
CdmAppParameterMap app_parameters;
|
||||
|
||||
EXPECT_TRUE(device_files.RetrieveLicense(
|
||||
license_test_data[0].key_set_id, &license_state, &pssh_data, &key_request,
|
||||
&key_response, &key_renewal_request, &key_renewal_response,
|
||||
&release_server_url, &playback_start_time, &last_playback_time,
|
||||
&app_parameters));
|
||||
&grace_period_end_time, &app_parameters));
|
||||
EXPECT_EQ(license_test_data[0].license_state, license_state);
|
||||
EXPECT_EQ(license_test_data[0].pssh_data, pssh_data);
|
||||
EXPECT_EQ(license_test_data[0].key_request, key_request);
|
||||
@@ -2012,6 +2029,7 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
|
||||
EXPECT_EQ(license_test_data[0].key_response, key_response);
|
||||
EXPECT_EQ(license_test_data[0].playback_start_time, playback_start_time);
|
||||
EXPECT_EQ(license_test_data[0].last_playback_time, last_playback_time);
|
||||
EXPECT_EQ(license_test_data[0].grace_period_end_time, grace_period_end_time);
|
||||
std::map<std::string, std::string>::iterator itr;
|
||||
for (itr = app_parameters.begin(); itr != app_parameters.end(); ++itr) {
|
||||
EXPECT_NE(license_test_data[0].app_parameters.find(itr->first),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "file_store.h"
|
||||
@@ -11,7 +12,9 @@ namespace {
|
||||
const std::string kTestDirName = "test_dir";
|
||||
const std::string kTestFileName = "test.txt";
|
||||
const std::string kTestFileName2 = "test2.txt";
|
||||
const std::string kTestFileName3 = "test3.other";
|
||||
const std::string kTestFileNameExt = ".txt";
|
||||
const std::string kTestFileNameExt3 = ".other";
|
||||
const std::string kWildcard = "*";
|
||||
} // namespace
|
||||
|
||||
@@ -135,4 +138,56 @@ TEST_F(FileTest, WriteReadBinaryFile) {
|
||||
EXPECT_EQ(write_data, read_data);
|
||||
}
|
||||
|
||||
TEST_F(FileTest, ListFiles) {
|
||||
std::vector<std::string> names;
|
||||
|
||||
std::string not_path("zzz");
|
||||
std::string path1 = test_vectors::kTestDir + kTestFileName;
|
||||
std::string path2 = test_vectors::kTestDir + kTestFileName2;
|
||||
std::string path3 = test_vectors::kTestDir + kTestFileName3;
|
||||
std::string path_dir = test_vectors::kTestDir;
|
||||
|
||||
File* file = file_system.Open(path1, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
file = file_system.Open(path2, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
file = file_system.Open(path3, FileSystem::kCreate);
|
||||
ASSERT_TRUE(file);
|
||||
file->Close();
|
||||
|
||||
EXPECT_TRUE(file_system.Exists(path1));
|
||||
EXPECT_TRUE(file_system.Exists(path2));
|
||||
EXPECT_TRUE(file_system.Exists(path3));
|
||||
|
||||
// Ask for non-existent path.
|
||||
EXPECT_FALSE(file_system.List(not_path, &names));
|
||||
|
||||
// Valid path, but no way to return names.
|
||||
EXPECT_FALSE(file_system.List(path_dir, NULL));
|
||||
|
||||
// Valid path, valid return.
|
||||
EXPECT_TRUE(file_system.List(path_dir, &names));
|
||||
|
||||
// Should find three files. Order not important.
|
||||
EXPECT_EQ(3, names.size());
|
||||
EXPECT_THAT(names, ::testing::UnorderedElementsAre(kTestFileName,
|
||||
kTestFileName2,
|
||||
kTestFileName3));
|
||||
|
||||
std::string wild_card_path = path_dir + kWildcard + kTestFileNameExt;
|
||||
EXPECT_TRUE(file_system.Remove(wild_card_path));
|
||||
EXPECT_TRUE(file_system.List(path_dir, &names));
|
||||
|
||||
EXPECT_EQ(1, 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());
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "cdm_engine.h"
|
||||
|
||||
#include "default_service_certificate.h"
|
||||
#include "license_request.h"
|
||||
#include "log.h"
|
||||
#include "oec_session_util.h"
|
||||
@@ -33,6 +34,7 @@ class WvGenericOperationsTest : public testing::Test {
|
||||
virtual void SetUp() {
|
||||
::testing::Test::SetUp();
|
||||
|
||||
// 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.
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox());
|
||||
@@ -72,7 +74,8 @@ class WvGenericOperationsTest : public testing::Test {
|
||||
void OecSessionSetup(uint32_t oec_session_id) {
|
||||
buffer_size_ = 160;
|
||||
oec_util_session_.SetSessionId(oec_session_id);
|
||||
oec_util_session_.GenerateTestSessionKeys();
|
||||
// TODO(fredgc or gmorgan): This should be updated for provisioning 3.0
|
||||
oec_util_session_.GenerateDerivedKeysFromKeybox();
|
||||
MakeFourKeys();
|
||||
}
|
||||
|
||||
@@ -159,6 +162,7 @@ class WvGenericOperationsTest : public testing::Test {
|
||||
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,
|
||||
@@ -202,7 +206,8 @@ TEST_F(WvGenericOperationsTest, NormalSessionOpenClose) {
|
||||
TEST_F(WvGenericOperationsTest, GenerateSessionKeys) {
|
||||
wvoec::Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.GenerateTestSessionKeys());
|
||||
// TODO(fredgc or gmorgan): This should be updated for provisioning 3.0
|
||||
ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox());
|
||||
ASSERT_NO_FATAL_FAILURE(s.close());
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
namespace wvcdm {
|
||||
|
||||
// Protobuf generated classes.
|
||||
using video_widevine_server::sdk::WidevineCencHeader;
|
||||
using video_widevine::WidevineCencHeader;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -600,7 +600,7 @@ TEST_P(HlsConstructionTest, InitData) {
|
||||
if (param.success_) {
|
||||
WidevineCencHeader cenc_header;
|
||||
EXPECT_TRUE(cenc_header.ParseFromString(value));
|
||||
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
|
||||
EXPECT_EQ(video_widevine::WidevineCencHeader_Algorithm_AESCTR,
|
||||
cenc_header.algorithm());
|
||||
for (size_t i = 0; i < param.key_ids_.size(); ++i) {
|
||||
bool key_id_found = false;
|
||||
@@ -624,7 +624,7 @@ TEST_P(HlsConstructionTest, InitData) {
|
||||
case kHlsMethodSampleAes: protection_scheme = kFourCcCbcs; break;
|
||||
default: break;
|
||||
}
|
||||
EXPECT_EQ(protection_scheme, ntohl(cenc_header.protection_scheme()));
|
||||
EXPECT_EQ(protection_scheme, cenc_header.protection_scheme());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,7 +701,7 @@ TEST_P(HlsParseTest, Parse) {
|
||||
|
||||
WidevineCencHeader cenc_header;
|
||||
EXPECT_TRUE(cenc_header.ParseFromString(init_data.data()));
|
||||
EXPECT_EQ(video_widevine_server::sdk::WidevineCencHeader_Algorithm_AESCTR,
|
||||
EXPECT_EQ(video_widevine::WidevineCencHeader_Algorithm_AESCTR,
|
||||
cenc_header.algorithm());
|
||||
if (param.key_.compare(kJsonProvider) == 0) {
|
||||
EXPECT_EQ(param.value_, cenc_header.provider());
|
||||
|
||||
@@ -54,12 +54,12 @@ static const KeyId ck_NO_HDCP_dual_res = "ck_NO_HDCP_dual_res";
|
||||
} // namespace
|
||||
|
||||
// protobuf generated classes.
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::LicenseIdentification;
|
||||
using video_widevine_server::sdk::STREAMING;
|
||||
using video_widevine_server::sdk::OFFLINE;
|
||||
using video_widevine::License;
|
||||
using video_widevine::LicenseIdentification;
|
||||
using video_widevine::STREAMING;
|
||||
using video_widevine::OFFLINE;
|
||||
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
|
||||
|
||||
class LicenseKeysTest : public ::testing::Test {
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "default_service_certificate.h"
|
||||
#include "initialization_data.h"
|
||||
#include "license.h"
|
||||
#include "policy_engine.h"
|
||||
#include "properties.h"
|
||||
#include "service_certificate.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
@@ -16,6 +18,7 @@ namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kEmptyString;
|
||||
const std::string kAesKey = a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kAesIv = a2bs_hex("000102030405060708090a0b0c0d0e0f");
|
||||
const std::string kCencInitDataHdr = a2bs_hex(
|
||||
@@ -110,10 +113,10 @@ class MockInitializationData : public InitializationData {
|
||||
} // namespace
|
||||
|
||||
// Protobuf generated classes
|
||||
using video_widevine_server::sdk::LicenseRequest_ContentIdentification;
|
||||
using video_widevine_server::sdk::ClientIdentification;
|
||||
using video_widevine_server::sdk::LicenseRequest;
|
||||
using video_widevine_server::sdk::SignedMessage;
|
||||
using video_widevine::LicenseRequest_ContentIdentification;
|
||||
using video_widevine::ClientIdentification;
|
||||
using video_widevine::LicenseRequest;
|
||||
using video_widevine::SignedMessage;
|
||||
|
||||
// gmock methods
|
||||
using ::testing::_;
|
||||
@@ -150,30 +153,44 @@ class CdmLicenseTest : public ::testing::Test {
|
||||
MockCryptoSession* crypto_session_;
|
||||
MockInitializationData* init_data_;
|
||||
MockPolicyEngine* policy_engine_;
|
||||
ServiceCertificate service_cert_;
|
||||
};
|
||||
|
||||
TEST_F(CdmLicenseTest, InitSuccess) {
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
|
||||
CreateCdmLicense();
|
||||
EXPECT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
|
||||
EXPECT_TRUE(cdm_license_->Init(
|
||||
&service_cert_, kToken, kClientTokenDrmCert, kEmptyString,
|
||||
crypto_session_, policy_engine_));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, InitFail_EmptyToken) {
|
||||
CreateCdmLicense();
|
||||
EXPECT_FALSE(cdm_license_->Init("", crypto_session_, policy_engine_));
|
||||
EXPECT_FALSE(cdm_license_->Init(&service_cert_, "", kClientTokenDrmCert,
|
||||
"", crypto_session_, policy_engine_));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) {
|
||||
CreateCdmLicense();
|
||||
EXPECT_FALSE(cdm_license_->Init(kToken, NULL, policy_engine_));
|
||||
EXPECT_FALSE(cdm_license_->Init(&service_cert_, kToken, kClientTokenDrmCert,
|
||||
"", NULL, policy_engine_));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) {
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
|
||||
CreateCdmLicense();
|
||||
EXPECT_FALSE(cdm_license_->Init(kToken, crypto_session_, NULL));
|
||||
EXPECT_FALSE(cdm_license_->Init(&service_cert_, kToken, kClientTokenDrmCert,
|
||||
"", crypto_session_, NULL));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, InitWithNullServiceCert) {
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
|
||||
CreateCdmLicense();
|
||||
EXPECT_TRUE(cdm_license_->Init(NULL, kToken, kClientTokenDrmCert,
|
||||
"", crypto_session_, policy_engine_));
|
||||
}
|
||||
|
||||
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
@@ -182,7 +199,8 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1;
|
||||
uint32_t crypto_session_api_version = 9;
|
||||
|
||||
EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
|
||||
EXPECT_CALL(*crypto_session_, IsOpen())
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(*crypto_session_, GenerateRequestId(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kCryptoRequestId), Return(true)));
|
||||
EXPECT_CALL(*crypto_session_, UsageInformationSupport(NotNull()))
|
||||
@@ -202,15 +220,18 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true)));
|
||||
|
||||
CreateCdmLicense();
|
||||
EXPECT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
|
||||
service_cert_.Init(kDefaultServiceCertificate);
|
||||
EXPECT_TRUE(cdm_license_->Init(
|
||||
&service_cert_, kToken, kClientTokenDrmCert, kEmptyString,
|
||||
crypto_session_, policy_engine_));
|
||||
|
||||
CdmAppParameterMap app_parameters;
|
||||
CdmKeyMessage signed_request;
|
||||
Properties::set_use_certificates_as_identification(true);
|
||||
std::string server_url;
|
||||
EXPECT_TRUE(cdm_license_->PrepareKeyRequest(
|
||||
EXPECT_EQ(cdm_license_->PrepareKeyRequest(
|
||||
*init_data_, kLicenseTypeStreaming, app_parameters,
|
||||
&signed_request, &server_url));
|
||||
&signed_request, &server_url), KEY_MESSAGE);
|
||||
|
||||
EXPECT_TRUE(!signed_request.empty());
|
||||
|
||||
@@ -230,15 +251,15 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
|
||||
// Verify Client Identification
|
||||
const ClientIdentification& client_id = license_request.client_id();
|
||||
EXPECT_EQ(video_widevine_server::sdk::
|
||||
ClientIdentification_TokenType_DEVICE_CERTIFICATE,
|
||||
EXPECT_EQ(video_widevine::
|
||||
ClientIdentification_TokenType_DRM_DEVICE_CERTIFICATE,
|
||||
client_id.type());
|
||||
EXPECT_TRUE(std::equal(client_id.token().begin(), client_id.token().end(),
|
||||
kToken.begin()));
|
||||
|
||||
EXPECT_LT(0, client_id.client_info_size());
|
||||
for (int i = 0; i < client_id.client_info_size(); ++i) {
|
||||
const ::video_widevine_server::sdk::ClientIdentification_NameValue&
|
||||
const ::video_widevine::ClientIdentification_NameValue&
|
||||
name_value = client_id.client_info(i);
|
||||
EXPECT_TRUE(!name_value.name().empty());
|
||||
EXPECT_TRUE(!name_value.value().empty());
|
||||
@@ -247,12 +268,12 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
EXPECT_FALSE(client_id.has_provider_client_token());
|
||||
EXPECT_FALSE(client_id.has_license_counter());
|
||||
|
||||
const ::video_widevine_server::sdk::ClientIdentification_ClientCapabilities&
|
||||
const ::video_widevine::ClientIdentification_ClientCapabilities&
|
||||
client_capabilities = client_id.client_capabilities();
|
||||
EXPECT_FALSE(client_capabilities.has_client_token());
|
||||
EXPECT_TRUE(client_capabilities.has_session_token());
|
||||
EXPECT_FALSE(client_capabilities.video_resolution_constraints());
|
||||
EXPECT_EQ(video_widevine_server::sdk::
|
||||
EXPECT_EQ(video_widevine::
|
||||
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1,
|
||||
client_capabilities.max_hdcp_version());
|
||||
EXPECT_EQ(crypto_session_api_version,
|
||||
@@ -261,23 +282,23 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
// Verify Content Identification
|
||||
const LicenseRequest_ContentIdentification& content_id =
|
||||
license_request.content_id();
|
||||
EXPECT_TRUE(content_id.has_cenc_id());
|
||||
EXPECT_FALSE(content_id.has_webm_id());
|
||||
EXPECT_FALSE(content_id.has_license());
|
||||
ASSERT_TRUE(content_id.has_cenc_id_deprecated());
|
||||
EXPECT_FALSE(content_id.has_webm_id_deprecated());
|
||||
EXPECT_FALSE(content_id.has_existing_license());
|
||||
|
||||
const ::video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC&
|
||||
cenc_id = content_id.cenc_id();
|
||||
const ::video_widevine::LicenseRequest_ContentIdentification_CencDeprecated&
|
||||
cenc_id = content_id.cenc_id_deprecated();
|
||||
EXPECT_TRUE(std::equal(cenc_id.pssh(0).begin(), cenc_id.pssh(0).end(),
|
||||
kCencPssh.begin()));
|
||||
EXPECT_EQ(video_widevine_server::sdk::STREAMING, cenc_id.license_type());
|
||||
EXPECT_EQ(video_widevine::STREAMING, cenc_id.license_type());
|
||||
EXPECT_TRUE(std::equal(cenc_id.request_id().begin(),
|
||||
cenc_id.request_id().end(), kCryptoRequestId.begin()));
|
||||
|
||||
// Verify other license request fields
|
||||
EXPECT_EQ(::video_widevine_server::sdk::LicenseRequest_RequestType_NEW,
|
||||
EXPECT_EQ(::video_widevine::LicenseRequest_RequestType_NEW,
|
||||
license_request.type());
|
||||
EXPECT_EQ(kLicenseStartTime, license_request.request_time());
|
||||
EXPECT_EQ(video_widevine_server::sdk::VERSION_2_1,
|
||||
EXPECT_EQ(video_widevine::VERSION_2_1,
|
||||
license_request.protocol_version());
|
||||
EXPECT_EQ(kNonce, license_request.key_control_nonce());
|
||||
}
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
// protobuf generated classes.
|
||||
using video_widevine_server::sdk::License;
|
||||
using video_widevine_server::sdk::License_Policy;
|
||||
using video_widevine_server::sdk::STREAMING;
|
||||
using video_widevine::License;
|
||||
using video_widevine::License_Policy;
|
||||
using video_widevine::STREAMING;
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
typedef ::video_widevine_server::sdk::License License;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::OutputProtection
|
||||
typedef ::video_widevine::License License;
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
typedef ::video_widevine::License::KeyContainer::OutputProtection
|
||||
OutputProtection;
|
||||
typedef ::video_widevine_server::sdk::License::KeyContainer::
|
||||
typedef ::video_widevine::License::KeyContainer::
|
||||
VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<KeyContainer> KeyList;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
@@ -217,12 +217,12 @@ TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->OnTimerEvent();
|
||||
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
|
||||
@@ -251,30 +251,30 @@ TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->NotifyResolution(1, kTargetRes1);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes2);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes3);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest,
|
||||
@@ -341,21 +341,21 @@ TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->NotifyResolution(1, kTargetRes1);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes2);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
|
||||
@@ -388,21 +388,21 @@ TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes1);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
|
||||
policy_engine_->NotifyResolution(1, kTargetRes2);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
|
||||
@@ -419,12 +419,12 @@ TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
|
||||
|
||||
policy_engine_->SetLicense(license_);
|
||||
policy_engine_->OnTimerEvent();
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId6));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId2));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId3));
|
||||
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId4));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId5));
|
||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
158
core/test/service_certificate_unittest.cpp
Normal file
158
core/test/service_certificate_unittest.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
#include "service_certificate.h"
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include "crypto_session.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_types.h"
|
||||
|
||||
namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const CdmSessionId kTestSessionId1 = "sid1";
|
||||
const CdmSessionId kTestSessionId2 = "sid2";
|
||||
const std::string kAppId = "com.example.test";
|
||||
|
||||
const std::string kTestSignedCertificate = a2bs_hex(
|
||||
"0AC102080312101705B917CC1204868B06333A2F772A8C1882B4829205228E023082010A02"
|
||||
"8201010099ED5B3B327DAB5E24EFC3B62A95B598520AD5BCCB37503E0645B814D876B8DF40"
|
||||
"510441AD8CE3ADB11BB88C4E725A5E4A9E0795291D58584023A7E1AF0E38A9127939300861"
|
||||
"0B6F158C878C7E21BFFBFEEA77E1019E1E5781E8A45F46263D14E60E8058A8607ADCE04FAC"
|
||||
"8457B137A8D67CCDEB33705D983A21FB4EECBD4A10CA47490CA47EAA5D438218DDBAF1CADE"
|
||||
"3392F13D6FFB6442FD31E1BF40B0C604D1C4BA4C9520A4BF97EEBD60929AFCEEF55BBAF564"
|
||||
"E2D0E76CD7C55C73A082B996120B8359EDCE24707082680D6F67C6D82C4AC5F3134490A74E"
|
||||
"EC37AF4B2F010C59E82843E2582F0B6B9F5DB0FC5E6EDF64FBD308B4711BCF1250019C9F5A"
|
||||
"0902030100013A146C6963656E73652E7769646576696E652E636F6D128003AE347314B5A8"
|
||||
"35297F271388FB7BB8CB5277D249823CDDD1DA30B93339511EB3CCBDEA04B944B927C12134"
|
||||
"6EFDBDEAC9D413917E6EC176A10438460A503BC1952B9BA4E4CE0FC4BFC20A9808AAAF4BFC"
|
||||
"D19C1DCFCDF574CCAC28D1B410416CF9DE8804301CBDB334CAFCD0D40978423A642E54613D"
|
||||
"F0AFCF96CA4A9249D855E42B3A703EF1767F6A9BD36D6BF82BE76BBF0CBA4FDE59D2ABCC76"
|
||||
"FEB64247B85C431FBCA52266B619FC36979543FCA9CBBDBBFAFA0E1A55E755A3C7BCE655F9"
|
||||
"646F582AB9CF70AA08B979F867F63A0B2B7FDB362C5BC4ECD555D85BCAA9C593C383C857D4"
|
||||
"9DAAB77E40B7851DDFD24998808E35B258E75D78EAC0CA16F7047304C20D93EDE4E8FF1C6F"
|
||||
"17E6243E3F3DA8FC1709870EC45FBA823A263F0CEFA1F7093B1909928326333705043A29BD"
|
||||
"A6F9B4342CC8DF543CB1A1182F7C5FFF33F10490FACA5B25360B76015E9C5A06AB8EE02F00"
|
||||
"D2E8D5986104AACC4DD475FD96EE9CE4E326F21B83C7058577B38732CDDABC6A6BED13FB0D"
|
||||
"49D38A45EB87A5F4");
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
class MockCryptoSession : public CryptoSession {
|
||||
public:
|
||||
MOCK_METHOD2(GetRandom, bool(size_t, uint8_t*));
|
||||
};
|
||||
|
||||
class ServiceCertificateTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() { crypto_session_ = new MockCryptoSession(); }
|
||||
|
||||
virtual void TearDown() {
|
||||
if (crypto_session_) delete crypto_session_;
|
||||
}
|
||||
|
||||
void CreateServiceCertificate() {
|
||||
service_certificate_ = new ServiceCertificate();
|
||||
}
|
||||
|
||||
ServiceCertificate* service_certificate_;
|
||||
MockCryptoSession* crypto_session_;
|
||||
};
|
||||
|
||||
class StubCdmClientPropertySet : public CdmClientPropertySet {
|
||||
public:
|
||||
StubCdmClientPropertySet()
|
||||
: security_level_(QUERY_VALUE_SECURITY_LEVEL_L1),
|
||||
use_privacy_mode_(false),
|
||||
is_session_sharing_enabled_(false),
|
||||
session_sharing_id_(0),
|
||||
app_id_(kAppId) {}
|
||||
|
||||
virtual const std::string& security_level() const { return security_level_; }
|
||||
|
||||
virtual bool use_privacy_mode() const { return use_privacy_mode_; }
|
||||
|
||||
virtual const std::string& service_certificate() const {
|
||||
return service_certificate_;
|
||||
}
|
||||
|
||||
virtual void set_service_certificate(const std::string& cert) {
|
||||
service_certificate_ = cert;
|
||||
}
|
||||
|
||||
virtual bool is_session_sharing_enabled() const {
|
||||
return is_session_sharing_enabled_;
|
||||
}
|
||||
|
||||
virtual uint32_t session_sharing_id() const { return session_sharing_id_; }
|
||||
|
||||
virtual void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; }
|
||||
|
||||
virtual const std::string& app_id() const { return app_id_; }
|
||||
|
||||
void enable_privacy_mode() { use_privacy_mode_ = true; }
|
||||
|
||||
private:
|
||||
std::string security_level_;
|
||||
std::string service_certificate_;
|
||||
bool use_privacy_mode_;
|
||||
bool is_session_sharing_enabled_;
|
||||
uint32_t session_sharing_id_;
|
||||
std::string app_id_;
|
||||
};
|
||||
|
||||
TEST_F(ServiceCertificateTest, InitSuccess) {
|
||||
MockCryptoSession crypto_session;
|
||||
|
||||
CreateServiceCertificate();
|
||||
service_certificate_->Init(kTestSessionId1);
|
||||
EXPECT_FALSE(service_certificate_->HasCertificate());
|
||||
}
|
||||
|
||||
TEST_F(ServiceCertificateTest, InitPrivacyModeRequired) {
|
||||
StubCdmClientPropertySet property_set;
|
||||
|
||||
property_set.enable_privacy_mode();
|
||||
|
||||
Properties::Init();
|
||||
Properties::AddSessionPropertySet(kTestSessionId1, &property_set);
|
||||
|
||||
CreateServiceCertificate();
|
||||
service_certificate_->Init(kTestSessionId1);
|
||||
EXPECT_FALSE(service_certificate_->HasCertificate());
|
||||
}
|
||||
|
||||
TEST_F(ServiceCertificateTest, InitServiceCertificatePresent) {
|
||||
StubCdmClientPropertySet property_set;
|
||||
|
||||
property_set.enable_privacy_mode();
|
||||
property_set.set_service_certificate(kTestSignedCertificate);
|
||||
|
||||
Properties::Init();
|
||||
Properties::AddSessionPropertySet(kTestSessionId1, &property_set);
|
||||
|
||||
CreateServiceCertificate();
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
service_certificate_->Init(
|
||||
Properties::service_certificate(kTestSessionId1));
|
||||
EXPECT_TRUE(service_certificate_->HasCertificate());
|
||||
}
|
||||
|
||||
TEST_F(ServiceCertificateTest, SetServiceCertificate) {
|
||||
StubCdmClientPropertySet property_set;
|
||||
|
||||
property_set.enable_privacy_mode();
|
||||
|
||||
Properties::Init();
|
||||
Properties::AddSessionPropertySet(kTestSessionId1, &property_set);
|
||||
|
||||
CreateServiceCertificate();
|
||||
EXPECT_EQ(NO_ERROR, service_certificate_->Init(kTestSignedCertificate);
|
||||
EXPECT_TRUE(service_certificate_->HasCertificate());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -268,9 +268,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
case RESTORE_OFFLINE_LICENSE_ERROR_2:
|
||||
*os << "RESTORE_OFFLINE_LICENSE_ERROR_2";
|
||||
break;
|
||||
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
|
||||
case SESSION_INIT_ERROR_1: *os << "SESSION_INIT_ERROR_1";
|
||||
break;
|
||||
case SESSION_INIT_GET_KEYBOX_ERROR: *os << "SESSION_INIT_GET_KEYBOX_ERROR";
|
||||
case SESSION_INIT_ERROR_2: *os << "SESSION_INIT_ERROR_2";
|
||||
break;
|
||||
case SESSION_NOT_FOUND_1: *os << "SESSION_NOT_FOUND_1";
|
||||
break;
|
||||
@@ -302,6 +302,8 @@ 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";
|
||||
@@ -445,6 +447,17 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_16: *os << "INVALID_PARAMETERS_ENG_16";
|
||||
break;
|
||||
case INVALID_PARAMETERS_ENG_17: *os << "INVALID_PARAMETERS_ENG_17";
|
||||
break;
|
||||
case CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1:
|
||||
*os << "CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1";
|
||||
break;
|
||||
case CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2:
|
||||
*os << "CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2";
|
||||
break;
|
||||
case LICENSING_CLIENT_TOKEN_ERROR_1:
|
||||
*os << "LICENSING_CLIENT_TOKEN_ERROR_1";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown CdmResponseType";
|
||||
break;
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
|
||||
const int kMaxConnectAttempts = 3;
|
||||
const int kReadBufferSize = 1024;
|
||||
const int kConnectTimeoutMs = 15000;
|
||||
const int kWriteTimeoutMs = 12000;
|
||||
@@ -79,12 +80,14 @@ UrlRequest::UrlRequest(const std::string& url)
|
||||
UrlRequest::~UrlRequest() {}
|
||||
|
||||
void UrlRequest::Reconnect() {
|
||||
socket_.CloseSocket();
|
||||
if (socket_.Connect(kConnectTimeoutMs)) {
|
||||
is_connected_ = true;
|
||||
} else {
|
||||
LOGE("failed to connect to %s, port=%d", socket_.domain_name().c_str(),
|
||||
socket_.port());
|
||||
for(uint32_t i = 0; i < kMaxConnectAttempts && !is_connected_; ++i) {
|
||||
socket_.CloseSocket();
|
||||
if (socket_.Connect(kConnectTimeoutMs)) {
|
||||
is_connected_ = true;
|
||||
} else {
|
||||
LOGE("failed to connect to %s, port=%d", socket_.domain_name().c_str(),
|
||||
socket_.port());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user