Source release v2.2.0-0-903 + third_party libs

Change-Id: I03f670eaeb052bc741abb347be06f8ddc58418e7
This commit is contained in:
Joey Parrish
2014-12-15 10:35:08 -08:00
parent 5318232d46
commit 1955c9c2c9
85 changed files with 5594 additions and 2830 deletions

12
README
View File

@@ -1,5 +1,5 @@
README for Widevine CDM Partner Kit v2.1 README for Widevine CDM Partner Kit v2.2
Date: 6/03/2014 Date: 12/16/2014
This document provides additional details on installation, system This document provides additional details on installation, system
setup, building, and testing components of the Widevine Content setup, building, and testing components of the Widevine Content
@@ -230,16 +230,12 @@ Here's a current list of GYP files and test-related targets:
wvcdm_shared Build CDM shared library wvcdm_shared Build CDM shared library
cdm/cdm_unittests.gyp cdm/cdm_unittests.gyp
wvcdm_shared_api_unittest Build CDM unit tests wvcdm_shared_api_unittest Build CDM unit tests
cdm/test/gtest.gyp third_party/gmock.gyp
gtest Provides gtest modules for unit tests
cdm/test/gmock.gyp
gmock Provides gmock modules for unit tests gmock Provides gmock modules for unit tests
gmock_main Provides main routine for unit tests gmock_main Provides main routine for unit tests
oemcrypto/mock/oec_mock.gyp gtest Provides gtest modules for unit tests
oemcrypto Top level target to build/link OEMCrypto
oemcrypto/oemcrypto.gyp oemcrypto/oemcrypto.gyp
oec_lib Build OEMCrypto library oec_lib Build OEMCrypto library
oec_mock Build mock OEMCrypto library
oec_unittest Build OEMCrypto unit tests oec_unittest Build OEMCrypto unit tests
oemcrypto/mock/oec_mock.gyp oemcrypto/mock/oec_mock.gyp
oec_mock Build mock OEMCrypto sources oec_mock Build mock OEMCrypto sources

View File

@@ -1,5 +1,5 @@
README.upgrading for Widevine CDM Partner Kit v2.1 README.upgrading for Widevine CDM Partner Kit v2.2
Date: 7/10/2014 Date: 12/16/2014
This document provides details on important changes between versions of the This document provides details on important changes between versions of the
Widevine CDM. Some upgrades require you to make changes to your application, Widevine CDM. Some upgrades require you to make changes to your application,
@@ -9,6 +9,29 @@ NOTE: All gyp variables have default values in platforms/global_config.gypi.
You may override these defaults in your platform-specific gypi. You may override these defaults in your platform-specific gypi.
New in v2.2.0:
=====
New gyp variables have been introduced to make the build more configurable:
* privacy_crypto_impl
This replaces the variable 'disable_privacy_crypto'. To achieve the same
effect, set the value to 'dummy'. Defaults to 'openssl'.
ContentDecryptionModule_1::GenerateKeyRequest did not previously require the
'type' parameter to be supplied. This is now required, and must be set to the
content's MIME type.
ContentDecryptionModule_1::Decrypt* previously required subsamples. This is no
longer required. WebM content, for example, does not contain subsamples.
ContentDecryptionModule_1::Decrypt* did not previously set the size of a
decrypted Buffer. This has been fixed.
New CDM/Host interfaces, CDM_4/Host_4, have been introduced. CDM_1/Host_1 will
continue to be supported until the end of Q1, 2015. The new interface includes
many fixes and improvements, updates to reflect the evolving EME standard, and
the introduction of offline playback APIs.
New in v2.1.6: New in v2.1.6:
===== =====
The following methods have been removed from the CDM interface (class The following methods have been removed from the CDM interface (class

View File

@@ -35,6 +35,7 @@
'../third_party/stringencoders/src', '../third_party/stringencoders/src',
], ],
'sources': [ 'sources': [
'../cdm/src/host_4_file_io_client.cpp',
'../cdm/src/file_store.cpp', '../cdm/src/file_store.cpp',
'../cdm/src/properties_common.cpp', '../cdm/src/properties_common.cpp',
'../core/src/string_conversions.cpp', '../core/src/string_conversions.cpp',
@@ -67,9 +68,11 @@
], ],
'sources': [ 'sources': [
# uses common published api # uses common published api
'../cdm/src/cdm_library_init.cpp',
'../cdm/src/clock.cpp', '../cdm/src/clock.cpp',
'../cdm/src/host_event_listener.cpp', '../cdm/src/host_event_listener.cpp',
'../cdm/src/wv_content_decryption_module.cpp', '../cdm/src/wv_content_decryption_module_1.cpp',
'../cdm/src/wv_content_decryption_module_4.cpp',
'../core/src/buffer_reader.cpp', '../core/src/buffer_reader.cpp',
'../core/src/cdm_engine.cpp', '../core/src/cdm_engine.cpp',
'../core/src/cdm_session.cpp', '../core/src/cdm_session.cpp',
@@ -80,18 +83,10 @@
'../core/src/license.cpp', '../core/src/license.cpp',
'../core/src/oemcrypto_adapter_static.cpp', '../core/src/oemcrypto_adapter_static.cpp',
'../core/src/policy_engine.cpp', '../core/src/policy_engine.cpp',
'../core/src/privacy_crypto.cpp', '../core/src/privacy_crypto_<(privacy_crypto_impl).cpp',
'../core/src/properties.cpp', '../core/src/properties.cpp',
], ],
'conditions': [ 'conditions': [
['disable_privacy_crypto=="true"', {
'sources!': [ # exclude
'../core/src/privacy_crypto.cpp',
],
'sources': [ # include
'../core/src/privacy_crypto_dummy.cpp',
],
}],
['oemcrypto_v8=="true"', { ['oemcrypto_v8=="true"', {
'sources!': [ # exclude 'sources!': [ # exclude
'../core/src/oemcrypto_adapter_static.cpp', '../core/src/oemcrypto_adapter_static.cpp',

View File

@@ -9,10 +9,17 @@
'target_name': 'wvcdm_shared_api_unittest', 'target_name': 'wvcdm_shared_api_unittest',
'type': 'executable', 'type': 'executable',
'sources': [ 'sources': [
'../cdm/test/cdm_api_test.cpp', '../cdm/test/cdm_api_1_test.cpp',
'../cdm/test/cdm_api_4_test.cpp',
'../cdm/test/cdm_test_main.cpp',
'../cdm/test/test_host_1.cpp',
'../cdm/test/test_host_4.cpp',
'../cdm/test/test_host_4_file_io.cpp',
'../cdm/test/test_util.cpp',
'../core/test/config_test_env.cpp', '../core/test/config_test_env.cpp',
'../core/test/license_request.cpp', '../core/test/license_request.cpp',
'../core/test/http_socket.cpp', '../core/test/http_socket.cpp',
'../core/test/test_printers.cpp',
'../core/test/url_request.cpp', '../core/test/url_request.cpp',
], ],
'include_dirs': [ 'include_dirs': [
@@ -22,6 +29,9 @@
'../core/test', '../core/test',
'../oemcrypto/include', '../oemcrypto/include',
], ],
'defines': [
'UNIT_TEST',
],
'libraries': [ 'libraries': [
'-lssl', '-lssl',
'-lcrypto', '-lcrypto',
@@ -29,9 +39,9 @@
], ],
'dependencies': [ 'dependencies': [
'cdm.gyp:wvcdm_shared', 'cdm.gyp:wvcdm_shared',
'test/gmock.gyp:gmock', '../third_party/gmock.gyp:gmock',
'test/gmock.gyp:gmock_main', '../third_party/gmock.gyp:gmock_main',
'test/gtest.gyp:gtest', '../third_party/gmock.gyp:gtest',
], ],
}, },
], ],

View File

@@ -3,42 +3,87 @@
#ifndef WVCDM_CDM_CDM_HOST_FILE_H_ #ifndef WVCDM_CDM_CDM_HOST_FILE_H_
#define WVCDM_CDM_CDM_HOST_FILE_H_ #define WVCDM_CDM_CDM_HOST_FILE_H_
#include "file_store.h"
#include "content_decryption_module.h" #include "content_decryption_module.h"
#include "file_store.h"
#include "host_4_file_io_client.h"
#include "scoped_ptr.h"
namespace wvcdm { namespace wvcdm {
class IFileFactory; class IFileFactory;
class IHostFile {
public:
virtual ~IHostFile() {}
virtual bool Open(const std::string& name) = 0;
virtual ssize_t Read(char* buffer, size_t bytes) = 0;
virtual ssize_t Write(const char* buffer, size_t bytes) = 0;
virtual bool Close() = 0;
virtual bool Remove(const std::string& name) = 0;
virtual ssize_t FileSize(const std::string& name) = 0;
};
class File1Impl : public IHostFile {
public:
explicit File1Impl(cdm::Host_1* const host_1) : host_1_(host_1) {}
virtual ~File1Impl() {}
virtual bool Open(const std::string& name) OVERRIDE;
virtual ssize_t Read(char* buffer, size_t bytes) OVERRIDE;
virtual ssize_t Write(const char* buffer, size_t bytes) OVERRIDE;
virtual bool Close() OVERRIDE;
virtual bool Remove(const std::string& name) OVERRIDE;
virtual ssize_t FileSize(const std::string& name) OVERRIDE;
private:
cdm::Host_1* const host_1_;
std::string fname_;
};
class File4Impl : public IHostFile {
public:
explicit File4Impl(cdm::Host_4* const host_4) :
host_4_(host_4), host_4_file_io_client_(host_4) {}
virtual ~File4Impl() {}
virtual bool Open(const std::string& name) OVERRIDE;
virtual ssize_t Read(char* buffer, size_t bytes) OVERRIDE;
virtual ssize_t Write(const char* buffer, size_t bytes) OVERRIDE;
virtual bool Close() OVERRIDE;
virtual bool Remove(const std::string& name) OVERRIDE;
virtual ssize_t FileSize(const std::string& name) OVERRIDE;
private:
cdm::Host_4* const host_4_;
Host4FileIOClient host_4_file_io_client_;
};
class File::Impl { class File::Impl {
public: public:
explicit Impl(cdm::Host* const host) : host_(host) {} explicit Impl(cdm::Host_1* const host_1) :
file_api_(new File1Impl(host_1)) {}
static void RegisterFileFactory(IFileFactory* factory) { explicit Impl(cdm::Host_4* const host_4) :
factory_ = factory; file_api_(new File4Impl(host_4)) {}
}
~Impl() {}
static void RegisterFileFactory(IFileFactory* factory) { factory_ = factory; }
virtual bool Exists(const std::string& name);
virtual bool Open(const std::string& name); virtual bool Open(const std::string& name);
virtual bool Close();
virtual bool Remove(const std::string& name);
virtual ssize_t Read(char* buffer, size_t bytes); virtual ssize_t Read(char* buffer, size_t bytes);
virtual ssize_t Write(const char* buffer, size_t bytes); virtual ssize_t Write(const char* buffer, size_t bytes);
virtual bool Close();
virtual bool Exists(const std::string& name);
virtual bool Remove(const std::string& name);
virtual ssize_t FileSize(const std::string& name); virtual ssize_t FileSize(const std::string& name);
private: private:
static IFileFactory* factory_; static IFileFactory* factory_;
friend class File; friend class File;
cdm::Host* const host_; scoped_ptr<IHostFile> file_api_;
std::string fname_;
}; };
class IFileFactory { class IFileFactory {
protected: protected:
IFileFactory() { IFileFactory() { File::Impl::RegisterFileFactory(this); }
File::Impl::RegisterFileFactory(this);
}
virtual ~IFileFactory() {} virtual ~IFileFactory() {}
@@ -46,6 +91,6 @@ class IFileFactory {
virtual File::Impl* NewFileImpl() = 0; virtual File::Impl* NewFileImpl() = 0;
}; };
} // namespace wvcdm } // namespace wvcdm
#endif // WVCDM_CDM_CDM_HOST_FILE_H_ #endif // WVCDM_CDM_CDM_HOST_FILE_H_

View File

@@ -37,13 +37,11 @@ typedef __int64 int64_t;
#endif // defined(WIN32) #endif // defined(WIN32)
// The version number must be rolled when the exported functions are updated! // We maintain this macro for backward compatibility only.
// If the CDM and the adapter use different versions of these functions, the #define INITIALIZE_CDM_MODULE InitializeCdmModule
// adapter will fail to load or crash!
#define INITIALIZE_CDM_MODULE InitializeCdmModule_1
extern "C" { extern "C" {
CDM_EXPORT void INITIALIZE_CDM_MODULE(); CDM_EXPORT void InitializeCdmModule();
CDM_EXPORT void DeinitializeCdmModule(); CDM_EXPORT void DeinitializeCdmModule();
@@ -62,38 +60,47 @@ typedef void* (*GetCdmHostFunc)(int host_interface_version, void* user_data);
// object. // object.
CDM_EXPORT void* CreateCdmInstance( CDM_EXPORT void* CreateCdmInstance(
int cdm_interface_version, int cdm_interface_version,
const char* key_system, int key_system_size, const char* key_system, uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func, void* user_data); GetCdmHostFunc get_cdm_host_func, void* user_data);
CDM_EXPORT int GetCdmVersion(); CDM_EXPORT const char* GetCdmVersion();
} }
namespace cdm { namespace cdm {
class AudioFrames; class Host_1;
class DecryptedBlock; class Host_4;
class VideoFrame;
enum Status { enum Status {
kSuccess = 0, kSuccess = 0,
kNeedMoreData, // Decoder needs more data to produce a decoded frame/sample. kNoKey = 2, // The required decryption key is not available.
kNoKey, // The required decryption key is not available. kSessionError = 3, // Session management error.
kSessionError, // Session management error. kDecryptError = 4, // Decryption failed.
kDecryptError, // Decryption failed. kDecodeError = 5, // Error decoding audio or video.
kDecodeError, // Error decoding audio or video. kRetry = 6, // Buffer temporarily cannot be accepted, delay and retry.
kRetry, // Buffer temporarily cannot be accepted. Retry after a short delay. kNeedsDeviceCertificate = 7 // A certificate is required for licensing.
kNeedsDeviceCertificate // Requires Device Certificate for content licensing
}; };
// This must be consistent with MediaKeyError defined in the // This must be consistent with MediaKeyError defined in the
// Encrypted media Extensions (EME) specification: http://goo.gl/3Df8h // Encrypted media Extensions (EME) specification: http://goo.gl/IBjNCP
enum MediaKeyError { enum MediaKeyError {
kUnknownError = 1, kUnknownError = 1,
kClientError, kClientError = 2,
kServiceError, kOutputError = 4,
kOutputError, };
kHardwareChangeError,
kDomainError // The type of session to create. The valid types are defined in the spec:
// http://goo.gl/vmc3pd
enum SessionType {
kTemporary = 0,
kPersistent = 1,
kProvisioning = 2,
};
// The type of stream. Used in DecryptDecodeAndRender.
enum StreamType {
kStreamTypeAudio = 0,
kStreamTypeVideo = 1
}; };
// An input buffer can be split into several continuous subsamples. // An input buffer can be split into several continuous subsamples.
@@ -118,11 +125,11 @@ enum MediaKeyError {
// | clear1 | decrypted1| clear2 | decrypted2 | clear3 | decrypted3 | // | clear1 | decrypted1| clear2 | decrypted2 | clear3 | decrypted3 |
// //
struct SubsampleEntry { struct SubsampleEntry {
SubsampleEntry(int32_t clear_bytes, int32_t cipher_bytes) SubsampleEntry(uint32_t clear_bytes, uint32_t cipher_bytes)
: clear_bytes(clear_bytes), cipher_bytes(cipher_bytes) {} : clear_bytes(clear_bytes), cipher_bytes(cipher_bytes) {}
int32_t clear_bytes; uint32_t clear_bytes;
int32_t cipher_bytes; uint32_t cipher_bytes;
}; };
// Represents an input buffer to be decrypted (and possibly decoded). It // Represents an input buffer to be decrypted (and possibly decoded). It
@@ -141,116 +148,173 @@ struct InputBuffer {
timestamp(0) {} timestamp(0) {}
const uint8_t* data; // Pointer to the beginning of the input data. const uint8_t* data; // Pointer to the beginning of the input data.
int32_t data_size; // Size (in bytes) of |data|. uint32_t data_size; // Size (in bytes) of |data|.
int32_t data_offset; // Number of bytes to be discarded before decryption. uint32_t data_offset; // Number of bytes to be discarded before decryption.
const uint8_t* key_id; // Key ID to identify the decryption key. const uint8_t* key_id; // Key ID to identify the decryption key.
int32_t key_id_size; // Size (in bytes) of |key_id|. uint32_t key_id_size; // Size (in bytes) of |key_id|.
const uint8_t* iv; // Initialization vector. const uint8_t* iv; // Initialization vector.
int32_t iv_size; // Size (in bytes) of |iv|. uint32_t iv_size; // Size (in bytes) of |iv|.
const struct SubsampleEntry* subsamples; const struct SubsampleEntry* subsamples;
int32_t num_subsamples; // Number of subsamples in |subsamples|. uint32_t num_subsamples; // Number of subsamples in |subsamples|.
int64_t timestamp; // Presentation timestamp in microseconds. int64_t timestamp; // Presentation timestamp in microseconds.
}; };
struct AudioDecoderConfig { // Represents a buffer created by the Host.
enum AudioCodec { class Buffer {
kUnknownAudioCodec = 0, public:
kCodecVorbis, // Destroys the buffer in the same context as it was created.
kCodecAac virtual void Destroy() = 0;
virtual int32_t Capacity() const = 0;
virtual uint8_t* Data() = 0;
virtual void SetSize(int32_t size) = 0;
virtual int32_t Size() const = 0;
protected:
Buffer() {}
virtual ~Buffer() {}
private:
Buffer(const Buffer&);
void operator=(const Buffer&);
};
// Represents a key-value map.
// Both created and destroyed by the Host.
// Data is filled in by the CDM.
// Need not be implemented if QueryKeyStatus() is not called.
class KeyValueMap {
public:
virtual void Set(const char* key, void* value, size_t value_size) = 0;
protected:
KeyValueMap() {}
virtual ~KeyValueMap() {}
private:
KeyValueMap(const KeyValueMap&);
void operator=(const KeyValueMap&);
};
// Represents a decrypted block that has not been decoded.
class DecryptedBlock {
public:
virtual void SetDecryptedBuffer(Buffer* buffer) = 0;
virtual Buffer* DecryptedBuffer() = 0;
virtual void SetTimestamp(int64_t timestamp) = 0;
virtual int64_t Timestamp() const = 0;
protected:
DecryptedBlock() {}
virtual ~DecryptedBlock() {}
};
// The FileIO interface provides a way for the CDM to store data in a file in
// persistent storage. This interface aims only at providing basic read/write
// capabilities and should not be used as a full fledged file IO API.
//
// All methods that report their result via calling a method on FileIOClient
// (currently, this is Open, Read, and Write) must call into FileIOClient on the
// same thread they were called on and must do so before returning. This
// restriction may be lifted in the future.
//
// Each domain (e.g. "example.com") and each CDM has it's own persistent
// storage. All instances of a given CDM associated with a given domain share
// the same persistent storage.
//
// Note to implementors of this interface:
// Per-origin storage and the ability for users to clear it are important.
// See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo.
class FileIO {
public:
// Opens the file with |file_name| for read and write.
// FileIOClient::OnOpenComplete() will be called after the opening
// operation finishes.
// - When the file is opened by a CDM instance, it will be classified as "in
// use". In this case other CDM instances in the same domain may receive
// kInUse status when trying to open it.
// - |file_name| should not include path separators.
virtual void Open(const char* file_name, uint32_t file_name_size) = 0;
// Reads the contents of the file. FileIOClient::OnReadComplete() will be
// called with the read status. Read() should not be called if a previous
// Read() or Write() call is still pending; otherwise OnReadComplete() will
// be called with kInUse.
virtual void Read() = 0;
// Writes |data_size| bytes of |data| into the file.
// FileIOClient::OnWriteComplete() will be called with the write status.
// All existing contents in the file will be overwritten. Calling Write() with
// NULL |data| will clear all contents in the file. Write() should not be
// called if a previous Write() or Read() call is still pending; otherwise
// OnWriteComplete() will be called with kInUse.
virtual void Write(const uint8_t* data, uint32_t data_size) = 0;
// Closes the file if opened, destroys this FileIO object and releases any
// resources allocated. The CDM must call this method when it finished using
// this object. A FileIO object must not be used after Close() is called.
virtual void Close() = 0;
protected:
FileIO() {}
virtual ~FileIO() {}
};
// Responses to FileIO calls.
class FileIOClient {
public:
enum Status {
kSuccess = 0,
kInUse,
kError
}; };
AudioDecoderConfig() // Response to a FileIO::Open() call with the open |status|.
: codec(kUnknownAudioCodec), virtual void OnOpenComplete(Status status) = 0;
channel_count(0),
bits_per_channel(0),
samples_per_second(0),
extra_data(NULL),
extra_data_size(0) {}
AudioCodec codec; // Response to a FileIO::Read() call to provide |data_size| bytes of |data|
int32_t channel_count; // read from the file.
int32_t bits_per_channel; // - kSuccess indicates that all contents of the file has been successfully
int32_t samples_per_second; // read. In this case, 0 |data_size| means that the file is empty.
// - kInUse indicates that there are other read/write operations pending.
// - kError indicates read failure, e.g. the storage isn't open or cannot be
// fully read.
virtual void OnReadComplete(Status status,
const uint8_t* data, uint32_t data_size) = 0;
// Optional byte data required to initialize audio decoders, such as the // Response to a FileIO::Write() call.
// vorbis setup header. // - kSuccess indicates that all the data has been written into the file
uint8_t* extra_data; // successfully.
int32_t extra_data_size; // - kInUse indicates that there are other read/write operations pending.
}; // - kError indicates write failure, e.g. the storage isn't open or cannot be
// fully written. Upon write failure, the contents of the file should be
// regarded as corrupt and should not used.
virtual void OnWriteComplete(Status status) = 0;
// Surface formats based on FOURCC labels, see: http://www.fourcc.org/yuv.php protected:
enum VideoFormat { FileIOClient() {}
kUnknownVideoFormat = 0, // Unknown format value. Used for error reporting. virtual ~FileIOClient() {}
kYv12, // 12bpp YVU planar 1x1 Y, 2x2 VU samples.
kI420 // 12bpp YVU planar 1x1 Y, 2x2 UV samples.
};
struct Size {
Size() : width(0), height(0) {}
Size(int32_t width, int32_t height) : width(width), height(height) {}
int32_t width;
int32_t height;
};
struct VideoDecoderConfig {
enum VideoCodec {
kUnknownVideoCodec = 0,
kCodecVp8,
kCodecH264
};
enum VideoCodecProfile {
kUnknownVideoCodecProfile = 0,
kVp8ProfileMain,
kH264ProfileBaseline,
kH264ProfileMain,
kH264ProfileExtended,
kH264ProfileHigh,
kH264ProfileHigh10,
kH264ProfileHigh422,
kH264ProfileHigh444Predictive
};
VideoDecoderConfig()
: codec(kUnknownVideoCodec),
profile(kUnknownVideoCodecProfile),
format(kUnknownVideoFormat),
extra_data(NULL),
extra_data_size(0) {}
VideoCodec codec;
VideoCodecProfile profile;
VideoFormat format;
// Width and height of video frame immediately post-decode. Not all pixels
// in this region are valid.
Size coded_size;
// Optional byte data required to initialize video decoders, such as H.264
// AAVC data.
uint8_t* extra_data;
int32_t extra_data_size;
};
enum StreamType {
kStreamTypeAudio = 0,
kStreamTypeVideo = 1
}; };
// ContentDecryptionModule interface that all CDMs need to implement. // ContentDecryptionModule interface that all CDMs need to implement.
// The interface is versioned for backward compatibility. // CDM interfaces are versioned for backward compatibility.
// Note: ContentDecryptionModule implementations must use the allocator // Note: ContentDecryptionModule implementations must use the Host
// provided in CreateCdmInstance() to allocate any Buffer that needs to // to allocate any Buffer that needs to be passed back to the caller.
// be passed back to the caller. Implementations must call Buffer::Destroy() // Host implementations must call Buffer::Destroy() when a Buffer is created
// when a Buffer is created that will never be returned to the caller. // that will never be returned to the caller.
// Based on chromium's ContentDecryptionModule_1.
class ContentDecryptionModule_1 { class ContentDecryptionModule_1 {
public: public:
static const int kVersion = 1002;
typedef Host_1 Host;
// Generates a |key_request| given |type| and |init_data|. // Generates a |key_request| given |type| and |init_data|.
// //
// Returns kSuccess if the key request was successfully generated, in which // Returns kSuccess if the key request was successfully generated, in which
@@ -296,9 +360,9 @@ class ContentDecryptionModule_1 {
virtual Status Decrypt(const InputBuffer& encrypted_buffer, virtual Status Decrypt(const InputBuffer& encrypted_buffer,
DecryptedBlock* decrypted_buffer) = 0; DecryptedBlock* decrypted_buffer) = 0;
// Decrypts the |encrypted_buffer|, decodes the decrypted buffer into a // Decrypts the |encrypted_buffer|, decodes the decrypted buffer, and passes
// video frame, and passes the frame to the rendering FW/HW. No data // the video frame or audio samples to the rendering FW/HW. No data is
// is returned. // returned to the caller.
// //
// Returns kSuccess if decryption, decoding, and rendering all succeeded. // Returns kSuccess if decryption, decoding, and rendering all succeeded.
// Returns kNoKey if the CDM did not have the necessary decryption key // Returns kNoKey if the CDM did not have the necessary decryption key
@@ -312,9 +376,9 @@ class ContentDecryptionModule_1 {
virtual Status DecryptDecodeAndRenderFrame( virtual Status DecryptDecodeAndRenderFrame(
const InputBuffer& encrypted_buffer) = 0; const InputBuffer& encrypted_buffer) = 0;
// Decrypts the |encrypted_buffer|, decodes the decrypted buffer into // Decrypts the |encrypted_buffer|, decodes the decrypted buffer into a
// audio frames, and passes the samples to the rendering FW/HW. No // video frame, and passes the frame to the rendering FW/HW. No data
// data is returned. // is returned.
// //
// Returns kSuccess if decryption, decoding, and rendering all succeeded. // Returns kSuccess if decryption, decoding, and rendering all succeeded.
// Returns kNoKey if the CDM did not have the necessary decryption key // Returns kNoKey if the CDM did not have the necessary decryption key
@@ -331,11 +395,11 @@ class ContentDecryptionModule_1 {
// Destroys the object in the same context as it was created. // Destroys the object in the same context as it was created.
virtual void Destroy() = 0; virtual void Destroy() = 0;
// Provisioning related methods // Provisioning-related methods.
virtual Status GetProvisioningRequest( virtual Status GetProvisioningRequest(
std::string* request, std::string* default_url) = 0; std::string* request, std::string* default_url) = 0;
virtual cdm::Status HandleProvisioningResponse( virtual Status HandleProvisioningResponse(
std::string& response) = 0; std::string& response) = 0;
protected: protected:
@@ -343,36 +407,139 @@ class ContentDecryptionModule_1 {
virtual ~ContentDecryptionModule_1() {} virtual ~ContentDecryptionModule_1() {}
}; };
const int kWidevineCdmInterfaceVersion_1 = 1002; // Based on chromium's ContentDecryptionModule_4 and ContentDecryptionModule_5.
class ContentDecryptionModule_4 {
typedef ContentDecryptionModule_1 ContentDecryptionModule;
const int kCdmInterfaceVersion = kWidevineCdmInterfaceVersion_1;
// Represents a buffer created by Allocator implementations.
class Buffer {
public: public:
// Destroys the buffer in the same context as it was created. static const int kVersion = 1004;
typedef Host_4 Host;
// The non-decryption methods on this class, such as CreateSession(),
// get passed a |session_id| for a MediaKeySession object. It must be used in
// the reply via Host methods (e.g. Host::OnSessionMessage()).
// Note: |session_id| is different from MediaKeySession's sessionId attribute,
// which is referred to as |web_session_id| in this file.
// Creates a new session and generates a key request given |init_data| and
// |session_type|. OnSessionCreated() will be called with a web session ID
// once the session exists. OnSessionMessage() will subsequently be called
// with the key request. A session represents hardware crypto resources,
// (if any exist for the platform), which may be in limited supply.
// For sessions of type kProvisioning, |mime_type| and |init_data| will be
// ignored and may be NULL.
virtual void CreateSession(uint32_t session_id,
const char* mime_type, uint32_t mime_type_size,
const uint8_t* init_data, uint32_t init_data_size,
SessionType session_type) = 0;
// Creates a new session and loads a previous persistent session into it that
// has a web session ID of |web_session_id|. OnSessionCreated() will be
// called once the session is loaded.
virtual void LoadSession(
uint32_t session_id,
const char* web_session_id, uint32_t web_session_id_length) = 0;
// Updates the session with |response|.
virtual void UpdateSession(
uint32_t session_id,
const uint8_t* response, uint32_t response_size) = 0;
// Tests whether |key_id| is known to any current session.
virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) = 0;
// Releases the resources for the session |session_id|.
// After calling this, it is invalid to refer to this session any more.
// If any hardware crypto resources were being used by this session, they will
// be released.
// If this session was a persistent session, this will NOT delete the
// persisted data. The persisted data will be preserved so that the session
// can be reloaded later with LoadSession(). To delete the persisted session,
// use RemoveSession().
virtual void ReleaseSession(uint32_t session_id) = 0;
// Creates a new session and generates a key release request for the
// existing persistent session identified by |web_session_id|.
// OnSessionCreated() will be called once the session exists.
// OnSessionMessage() will subsequently be called with the key release
// request.
virtual void RemoveSession(
uint32_t session_id,
const char* web_session_id, uint32_t web_session_id_length) = 0;
// Signals to the CDM that it should use server certificates to protect the
// privacy of the user. The primary use of this is when the application
// driving the CDM is untrusted code, such as when a web browser allows a web
// page's JavaScript to access the CDM. By using server certificates to
// encrypt communication with the license server, device-identifying
// information cannot be extracted from the license exchange process by a
// malicious caller.
// Unless you also call SetServerCertificate() to set a pre-cached server
// certificate, the CDM will perform a certificate exchange with the server
// prior to any key exchanges.
// This method may not be called if any sessions are open. It is typically
// called before any sessions have been opened, but may also be called if all
// open sessions have been released.
// Note that calling SetServerCertificate() implicitly calls this method as
// well.
virtual Status UsePrivacyMode() = 0;
// Provides a server certificate to be used to encrypt messages to the
// license server. Calling this is like calling UsePrivacyMode(), except that
// because the certificate is provided up-front, the CDM does not have to
// perform a certificate exchange with the server.
// This method may not be called if any sessions are open. It is typically
// called before any sessions have been opened, but may also be called if all
// open sessions have been released.
// Note that calling this method also implicitly calls UsePrivacyMode().
virtual Status SetServerCertificate(
const uint8_t* server_certificate_data,
uint32_t server_certificate_data_size) = 0;
// Performs scheduled operation with |context| when the timer fires.
virtual void TimerExpired(void* context) = 0;
// Decrypts the |encrypted_buffer|.
//
// Returns kSuccess if decryption succeeded, in which case the callee
// should have filled the |decrypted_buffer| and passed the ownership of
// |data| in |decrypted_buffer| to the caller.
// Returns kNoKey if the CDM did not have the necessary decryption key
// to decrypt.
// Returns kDecryptError if any other error happened.
// If the return value is not kSuccess, |decrypted_buffer| should be ignored
// by the caller.
virtual Status Decrypt(const InputBuffer& encrypted_buffer,
DecryptedBlock* decrypted_buffer) = 0;
// Decrypts the |encrypted_buffer|, decodes the decrypted buffer, and passes
// the video or audio frames to the rendering FW/HW. No data is returned to
// the caller.
//
// Returns kSuccess if decryption, decoding, and rendering all succeeded.
// Returns kNoKey if the CDM did not have the necessary decryption key
// to decrypt.
// Returns kRetry if |encrypted_buffer| cannot be accepted (e.g, video
// pipeline is full). Caller should retry after a short delay.
// Returns kDecryptError if any decryption error happened.
// Returns kDecodeError if any decoding error happened.
virtual Status DecryptDecodeAndRender(const InputBuffer& encrypted_buffer,
StreamType stream_type) = 0;
// Destroys the object in the same context as it was created.
virtual void Destroy() = 0; virtual void Destroy() = 0;
virtual int32_t Capacity() const = 0;
virtual uint8_t* Data() = 0;
virtual void SetSize(int32_t size) = 0;
virtual int32_t Size() const = 0;
protected: protected:
Buffer() {} ContentDecryptionModule_4() {}
virtual ~Buffer() {} virtual ~ContentDecryptionModule_4() {}
private:
Buffer(const Buffer&);
void operator=(const Buffer&);
}; };
// Host interface that the CDM can call into to access browser side services. // Host interface that the CDM can call into to access browser side services.
// Host interfaces are versioned for backward compatibility. CDM should use // Host interfaces are versioned for backward compatibility.
// HostFactory object to request a Host interface of a particular version.
// Based on chromium's Host_1.
class Host_1 { class Host_1 {
public: public:
static const int kVersion = 1002;
// Returns a Buffer* containing non-zero members upon success, or NULL on // Returns a Buffer* containing non-zero members upon success, or NULL on
// failure. The caller owns the Buffer* after this call. The buffer is not // failure. The caller owns the Buffer* after this call. The buffer is not
// guaranteed to be zero initialized. The capacity of the allocated Buffer // guaranteed to be zero initialized. The capacity of the allocated Buffer
@@ -418,77 +585,69 @@ class Host_1 {
virtual ~Host_1() {} virtual ~Host_1() {}
}; };
const int kWidevineHostInterfaceVersion_1 = 1002; // Based on chromium's Host_4 and Host_5.
class Host_4 {
typedef Host_1 Host;
const int kHostInterfaceVersion = kWidevineHostInterfaceVersion_1;
// Represents a decrypted block that has not been decoded.
class DecryptedBlock {
public: public:
virtual void SetDecryptedBuffer(Buffer* buffer) = 0; static const int kVersion = 1004;
virtual Buffer* DecryptedBuffer() = 0;
virtual void SetTimestamp(int64_t timestamp) = 0; // Returns a Buffer* containing non-zero members upon success, or NULL on
virtual int64_t Timestamp() const = 0; // failure. The caller owns the Buffer* after this call. The buffer is not
// guaranteed to be zero initialized. The capacity of the allocated Buffer
// is guaranteed to be not less than |capacity|.
virtual Buffer* Allocate(uint32_t capacity) = 0;
// Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
// from now with |context|.
virtual void SetTimer(int64_t delay_ms, void* context) = 0;
// Returns the current epoch wall time in seconds.
virtual double GetCurrentWallTimeInSeconds() = 0;
// Called by the CDM when a session is created or loaded and the value for the
// MediaKeySession's sessionId attribute is available (|web_session_id|).
// This must be called before OnSessionMessage() or OnSessionUpdated() is
// called for |session_id|. |web_session_id_length| should not include null
// termination.
// When called in response to LoadSession(), the |web_session_id| must be the
// same as the |web_session_id| passed in LoadSession().
virtual void OnSessionCreated(
uint32_t session_id,
const char* web_session_id, uint32_t web_session_id_length) = 0;
// Called by the CDM when it has a message for session |session_id|.
// Length parameters should not include null termination.
virtual void OnSessionMessage(
uint32_t session_id,
const char* message, uint32_t message_length,
const char* destination_url, uint32_t destination_url_length) = 0;
// Called by the CDM when session |session_id| has been updated.
virtual void OnSessionUpdated(uint32_t session_id) = 0;
// Called by the CDM when session |session_id| is closed.
virtual void OnSessionClosed(uint32_t session_id) = 0;
// Called by the CDM when an error occurs in session |session_id|.
virtual void OnSessionError(uint32_t session_id,
Status error_code,
uint32_t system_code) = 0;
// Creates a FileIO object from the host to do file IO operation. Returns NULL
// if a FileIO object cannot be obtained. Once a valid FileIO object is
// returned, |client| must be valid until FileIO::Close() is called. The
// CDM can call this method multiple times to operate on different files.
virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
protected: protected:
DecryptedBlock() {} Host_4() {}
virtual ~DecryptedBlock() {} virtual ~Host_4() {}
}; };
class VideoFrame { typedef ContentDecryptionModule_1 ContentDecryptionModule;
public: const int kCdmInterfaceVersion = ContentDecryptionModule::kVersion;
enum VideoPlane {
kYPlane = 0,
kUPlane = 1,
kVPlane = 2,
kMaxPlanes = 3,
};
virtual void SetFormat(VideoFormat format) = 0; typedef ContentDecryptionModule::Host Host;
virtual VideoFormat Format() const = 0; const int kHostInterfaceVersion = Host::kVersion;
virtual void SetSize(cdm::Size size) = 0;
virtual cdm::Size Size() const = 0;
virtual void SetFrameBuffer(Buffer* frame_buffer) = 0;
virtual Buffer* FrameBuffer() = 0;
virtual void SetPlaneOffset(VideoPlane plane, int32_t offset) = 0;
virtual int32_t PlaneOffset(VideoPlane plane) = 0;
virtual void SetStride(VideoPlane plane, int32_t stride) = 0;
virtual int32_t Stride(VideoPlane plane) = 0;
virtual void SetTimestamp(int64_t timestamp) = 0;
virtual int64_t Timestamp() const = 0;
protected:
VideoFrame() {}
virtual ~VideoFrame() {}
};
// Represents decrypted and decoded audio frames. AudioFrames can contain
// multiple audio output buffers, which are serialized into this format:
//
// |<------------------- serialized audio buffer ------------------->|
// | int64_t timestamp | int64_t length | length bytes of audio data |
//
// For example, with three audio output buffers, the AudioFrames will look
// like this:
//
// |<----------------- AudioFrames ------------------>|
// | audio buffer 0 | audio buffer 1 | audio buffer 2 |
class AudioFrames {
public:
virtual void SetFrameBuffer(Buffer* buffer) = 0;
virtual Buffer* FrameBuffer() = 0;
protected:
AudioFrames() {}
virtual ~AudioFrames() {}
};
} // namespace cdm } // namespace cdm

View File

@@ -0,0 +1,54 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WVCDM_HOST_4_FILE_IO_CLIENT_H_
#define WVCDM_HOST_4_FILE_IO_CLIENT_H_
#include "content_decryption_module.h"
#include "wv_cdm_common.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class Host4FileIOClient : public cdm::FileIOClient {
public:
explicit Host4FileIOClient(cdm::Host_4* host)
: host_(host),
file_io_(NULL),
status_(kSuccess),
data_size_(0),
buffer_(NULL),
buffer_size_(0) {}
~Host4FileIOClient();
bool Open(const std::string& name);
bool Read(char* buffer, size_t buffer_size);
bool ReadFileSize() { return Read(NULL, 0); }
bool Write(const char* data, size_t data_size);
bool Close();
// cdm::FileIOClient implementation
virtual void OnOpenComplete(Status status) OVERRIDE;
virtual void OnReadComplete(Status status, const uint8_t* data,
uint32_t data_size) OVERRIDE;
virtual void OnWriteComplete(Status status) OVERRIDE;
// Get the result of the last operation
Status status() const { return status_; }
uint32_t data_size() const { return data_size_; }
private:
cdm::Host_4* host_;
cdm::FileIO* file_io_;
// These hold the result of the last operation
Status status_;
uint32_t data_size_;
char* buffer_;
size_t buffer_size_;
CORE_DISALLOW_COPY_AND_ASSIGN(Host4FileIOClient);
};
} // namespace wvcdm
#endif // WVCDM_HOST_4_FILE_IO_CLIENT_H_

View File

@@ -1,3 +1,3 @@
// Widevine CDM Kit Version // Widevine CDM Kit Version
#define WV_CDM_VERSION "v2.1.8-0-829" #define WV_CDM_VERSION "v2.2.0-0-903"

View File

@@ -0,0 +1,73 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_WV_CLIENT_PROPERTY_SET_H_
#define WVCDM_CDM_WV_CLIENT_PROPERTY_SET_H_
#include "cdm_client_property_set.h"
#include "wv_cdm_types.h"
namespace wvcdm {
class WVClientPropertySet : public CdmClientPropertySet {
public:
WVClientPropertySet()
: use_privacy_mode_(false) {}
virtual ~WVClientPropertySet() {}
void set_security_level(const std::string& securityLevel) {
security_level_ = securityLevel;
}
virtual const std::string& security_level() const {
return security_level_;
}
void set_use_privacy_mode(bool usePrivacyMode) {
use_privacy_mode_ = usePrivacyMode;
}
virtual bool use_privacy_mode() const {
return use_privacy_mode_;
}
void set_service_certificate(const std::string& serviceCertificate) {
service_certificate_ = serviceCertificate;
}
virtual const std::string& service_certificate() const {
return service_certificate_;
}
virtual bool is_session_sharing_enabled() const {
return true; // This is unused by common cdm but we need a definition
// for the pure virtual methods.
}
void set_is_session_sharing_enabled(bool shareKeys) {
return; // This is unused by common cdm but we need a definition
// for the pure virtual methods.
}
virtual uint32_t session_sharing_id() const {
return 1; // This is unused by common cdm but we need a
// definition for the pure virtual methods.
}
virtual void set_session_sharing_id(uint32_t id) {
return; // This is unused by common cdm but we need a
// definition for the pure virtual methods.
}
private:
CORE_DISALLOW_COPY_AND_ASSIGN(WVClientPropertySet);
std::string security_level_;
bool use_privacy_mode_;
std::string service_certificate_;
};
} // namespace wvcdm
#endif // WVCDM_CDM_WV_CLIENT_PROPERTY_SET_H_

View File

@@ -1,7 +1,7 @@
// Copyright 2013 Google Inc. All Rights Reserved. // Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_H_ #ifndef WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_1_H_
#define WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_H_ #define WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_1_H_
#include "cdm_client_property_set.h" #include "cdm_client_property_set.h"
#include "cdm_engine.h" #include "cdm_engine.h"
@@ -14,77 +14,19 @@
#include "wv_cdm_common.h" #include "wv_cdm_common.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
#include "wv_client_property_set.h"
namespace wvcdm { namespace wvcdm {
class WVClientPropertySet : public wvcdm::CdmClientPropertySet { class WvContentDecryptionModule_1 : public cdm::ContentDecryptionModule_1,
public IFileFactory,
public IClock {
public: public:
WVClientPropertySet() explicit WvContentDecryptionModule_1(cdm::Host_1* host);
: use_privacy_mode_(false) {}
virtual ~WVClientPropertySet() {} virtual ~WvContentDecryptionModule_1();
void set_security_level(const std::string& securityLevel) { // cdm::ContentDecryptionModule_1 implementation.
security_level_ = securityLevel;
}
virtual const std::string& security_level() const {
return security_level_;
}
void set_use_privacy_mode(bool usePrivacyMode) {
use_privacy_mode_ = usePrivacyMode;
}
virtual bool use_privacy_mode() const {
return use_privacy_mode_;
}
void set_service_certificate(const std::string& serviceCertificate) {
service_certificate_ = serviceCertificate;
}
virtual const std::string& service_certificate() const {
return service_certificate_;
}
virtual bool is_session_sharing_enabled() const {
return true; // This is unused by common cdm but we need a definition
// for the pure virtual methods.
}
void set_is_session_sharing_enabled(bool shareKeys) {
return; // This is unused by common cdm but we need a definition
// for the pure virtual methods.
}
virtual uint32_t session_sharing_id() const {
return 1; // This is unused by common cdm but we need a
// definition for the pure virtual methods.
}
virtual void set_session_sharing_id(uint32_t id) {
return; // This is unused by common cdm but we need a
// definition for the pure virtual methods.
}
private:
CORE_DISALLOW_COPY_AND_ASSIGN(WVClientPropertySet);
std::string security_level_;
bool use_privacy_mode_;
std::string service_certificate_;
};
class WvContentDecryptionModule : public cdm::ContentDecryptionModule,
public IFileFactory,
public IClock {
public:
explicit WvContentDecryptionModule(cdm::Host* host);
virtual ~WvContentDecryptionModule();
// cdm::ContentDecryptionModule implementation.
virtual cdm::Status GenerateKeyRequest(const char* type, int type_size, virtual cdm::Status GenerateKeyRequest(const char* type, int type_size,
const uint8_t* init_data, const uint8_t* init_data,
int init_data_size) OVERRIDE; int init_data_size) OVERRIDE;
@@ -125,20 +67,46 @@ class WvContentDecryptionModule : public cdm::ContentDecryptionModule,
virtual int64_t GetCurrentTimeInSeconds() OVERRIDE; virtual int64_t GetCurrentTimeInSeconds() OVERRIDE;
/* |parameters| is expected to be initialized with anything not related to
* subsample parsing. |iv| is initialized by the caller, but may be modified
* during decryption. |decrypted_block| may be NULL for L1 decrypts, since
* no data is passed back to the caller. */
cdm::Status DoSubsampleDecrypt(CdmDecryptionParameters& parameters, cdm::Status DoSubsampleDecrypt(CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer, const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block); cdm::DecryptedBlock* decrypted_block);
cdm::Status DoDecrypt(CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block);
/* |parameters| is expected to be initialized with everything required by
* DoSubsampleDecrypt and DoDecrypt, plus |is_encrypted| and
* |subsample_flags|. Counters and |iv| will be updated to prepare for
* subsequent calls. */
cdm::Status DecryptAndUpdateCounters(CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
const size_t bytes,
cdm::DecryptedBlock* decrypted_block,
size_t& offset,
size_t& encrypted_offset,
uint32_t& block_ctr);
void SetSizesAndAllocate(size_t output_size,
CdmDecryptionParameters& parameters,
cdm::DecryptedBlock* decrypted_block);
cdm::Host_1* const host_;
HostEventListener host_event_listener_;
CdmEngine cdm_engine_; CdmEngine cdm_engine_;
cdm::Host* const host_;
HostEventListener host_event_listener_;
WVClientPropertySet property_set_; WVClientPropertySet property_set_;
bool timer_enabled_; bool timer_enabled_;
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule); CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule_1);
}; };
} // namespace wvcdm } // namespace wvcdm
#endif // WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_H_ #endif // WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_1_H_

View File

@@ -0,0 +1,155 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_4_H_
#define WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_4_H_
#include "content_decryption_module.h"
#include "cdm_client_property_set.h"
#include "cdm_engine.h"
#include "cdm_host_clock.h"
#include "cdm_host_file.h"
#include "clock.h"
#include "wv_cdm_common.h"
#include "wv_cdm_event_listener.h"
#include "wv_cdm_types.h"
#include "wv_client_property_set.h"
#if defined(UNIT_TEST)
# include "gtest/gtest_prod.h"
#endif
namespace wvcdm {
class WvContentDecryptionModule_4 : public cdm::ContentDecryptionModule_4,
public IFileFactory,
public IClock,
public WvCdmEventListener {
public:
explicit WvContentDecryptionModule_4(cdm::Host_4* host);
virtual ~WvContentDecryptionModule_4();
virtual void CreateSession(uint32_t session_id,
const char* mime_type, uint32_t mime_type_size,
const uint8_t* init_data, uint32_t init_data_size,
cdm::SessionType session_type) OVERRIDE;
virtual void LoadSession(uint32_t session_id, const char* web_session_id,
uint32_t web_session_id_length) OVERRIDE;
virtual void UpdateSession(uint32_t session_id, const uint8_t* response,
uint32_t response_size) OVERRIDE;
virtual bool IsKeyValid(const uint8_t* key_id, int key_id_size) OVERRIDE;
virtual void ReleaseSession(uint32_t session_id) OVERRIDE;
virtual void RemoveSession(uint32_t session_id, const char* web_session_id,
uint32_t web_session_id_length) OVERRIDE;
virtual cdm::Status UsePrivacyMode() OVERRIDE;
virtual cdm::Status SetServerCertificate(
const uint8_t* server_certificate_data,
uint32_t server_certificate_data_size) OVERRIDE;
virtual void TimerExpired(void* context) OVERRIDE;
virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_buffer) OVERRIDE;
virtual cdm::Status DecryptDecodeAndRender(
const cdm::InputBuffer& encrypted_buffer,
cdm::StreamType stream_type) OVERRIDE;
virtual void Destroy() OVERRIDE;
// wvcdm::WvCdmEventListener implementation.
virtual void OnEvent(const CdmSessionId& session_id,
CdmEventType cdm_event) OVERRIDE;
private:
class InternalSession {
public:
enum SessionType {
kDecrypt = 1,
kRelease = 2,
kProvision = 3,
};
InternalSession() : webid_(), session_type_(kDecrypt) {}
InternalSession(const std::string& webid, SessionType session_type)
: webid_(webid), session_type_(session_type) {}
const std::string& webid() const { return webid_; }
bool is_decrypt() const { return session_type_ == kDecrypt; }
bool is_release() const { return session_type_ == kRelease; }
bool is_provision() const { return session_type_ == kProvision; }
private:
std::string webid_;
SessionType session_type_;
};
void EnablePolicyTimer();
void DisablePolicyTimer();
void CreateProvisionSession(uint32_t session_id);
void UpdateProvisionSession(uint32_t session_id, const uint8_t* response,
uint32_t response_size);
bool CallOpenSession(uint32_t session_id);
virtual File::Impl* NewFileImpl() OVERRIDE;
virtual int64_t GetCurrentTimeInSeconds() OVERRIDE;
/* |parameters| is expected to be initialized with anything not related to
* subsample parsing. |iv| is initialized by the caller, but may be modified
* during decryption. |decrypted_block| may be NULL for L1 decrypts, since
* no data is passed back to the caller. */
cdm::Status DoSubsampleDecrypt(CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block);
cdm::Status DoDecrypt(CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block);
/* |parameters| is expected to be initialized with everything required by
* DoSubsampleDecrypt and DoDecrypt, plus |is_encrypted| and
* |subsample_flags|. Counters and |iv| will be updated to prepare for
* subsequent calls. */
cdm::Status DecryptAndUpdateCounters(CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
const size_t bytes,
cdm::DecryptedBlock* decrypted_block,
size_t& offset, size_t& encrypted_offset,
uint32_t& block_ctr);
void SetSizesAndAllocate(size_t output_size,
CdmDecryptionParameters& parameters,
cdm::DecryptedBlock* decrypted_block);
#if defined(UNIT_TEST)
FRIEND_TEST(CdmApi4Test, UsePrivacyMode);
FRIEND_TEST(CdmApi4Test, UsePrivacyModeFailsWithOpenSessions);
FRIEND_TEST(CdmApi4Test, SetExplicitServerCertificate);
FRIEND_TEST(CdmApi4Test, SetServerCertificateFailsWithOpenSessions);
#endif
cdm::Host_4* const host_;
WVClientPropertySet property_set_;
CdmEngine cdm_engine_;
std::map<uint32_t, InternalSession> session_map_;
bool timer_enabled_;
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule_4);
};
} // namespace wvcdm
#endif // WVCDM_CDM_WV_CONTENT_DECRYPTION_MODULE_4_H_

View File

@@ -0,0 +1,48 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include <assert.h>
#include "wv_content_decryption_module_1.h"
#include "wv_content_decryption_module_4.h"
#include "wv_cdm_version.h"
void InitializeCdmModule() {}
void DeinitializeCdmModule() {}
void* CreateCdmInstance(int cdm_interface_version, const char* key_system,
uint32_t key_system_size,
GetCdmHostFunc get_cdm_host_func, void* user_data) {
void *host = NULL;
switch (cdm_interface_version) {
case cdm::ContentDecryptionModule_1::kVersion:
host = get_cdm_host_func(cdm::Host_1::kVersion, user_data);
break;
case cdm::ContentDecryptionModule_4::kVersion:
host = get_cdm_host_func(cdm::Host_4::kVersion, user_data);
break;
}
if (!host)
return NULL;
switch (cdm_interface_version) {
case cdm::ContentDecryptionModule_1::kVersion:
return new wvcdm::WvContentDecryptionModule_1(
static_cast<cdm::Host_1*>(host));
case cdm::ContentDecryptionModule_4::kVersion:
return new wvcdm::WvContentDecryptionModule_4(
static_cast<cdm::Host_4*>(host));
}
assert(false); // NOT REACHED
return NULL;
}
const char* GetCdmVersion() {
return WV_CDM_VERSION;
}

View File

@@ -1,73 +1,168 @@
// Copyright 2013 Google Inc. All Rights Reserved. // Copyright 2013 Google Inc. All Rights Reserved.
#include <string.h>
#include <string>
#include "cdm_host_file.h" #include "cdm_host_file.h"
#include <string.h>
#include <algorithm>
#include <string>
namespace wvcdm { namespace wvcdm {
IFileFactory* File::Impl::factory_ = NULL; IFileFactory* File::Impl::factory_ = NULL;
// File::Impl() Section // File::Impl() Section
// The file handler for cert.bin, aka DeviceCertificate is all we're // The file handler for cert.bin, aka DeviceCertificate, and usage.bin,
// setting up for now. // aka UsageTable, are all we're setting up for now.
bool File::Impl::Exists(const std::string& name) { namespace {
const char* const kDeviceCertificateKey = "DeviceCertificate";
const char* const kUsageInfoKey = "UsageInfo";
const char* const kUsageTableKey = "MockOemCryptoUsageTable";
const char* const kGenerationNumberKey = "MockOemCryptoGenerationNumber";
const char* GetKeyForFileName(const std::string& name) {
if (name == "cert.bin") { if (name == "cert.bin") {
return kDeviceCertificateKey;
} else if (name == "usage.bin") {
return kUsageInfoKey;
} else if (name == "UsageTable.dat") {
return kUsageTableKey;
} else if (name == "GenerationNumber.dat") {
return kGenerationNumberKey;
} else {
return "";
}
}
} // namespace
// File1 implmentation.
bool File1Impl::Open(const std::string& name) {
if (name.empty())
return false;
fname_ = name;
return true;
}
ssize_t File1Impl::Read(char* buffer, size_t bytes) {
std::string key(GetKeyForFileName(fname_));
if (key.length() > 0) {
std::string value; std::string value;
host_->GetPlatformString("DeviceCertificate", &value); host_1_->GetPlatformString(key, &value);
return !value.empty(); size_t bytes_to_copy = std::min(bytes, value.size());
memcpy(buffer, value.data(), bytes_to_copy);
return value.size() ? bytes_to_copy : -1;
} else {
return -1;
} }
return false;
} }
bool File::Impl::Open(const std::string& name) { bool File1Impl::Remove(const std::string& name) {
if (name == "cert.bin") { std::string key(GetKeyForFileName(name));
fname_ = name; if (key.length() > 0) {
host_1_->SetPlatformString(key, "");
return true; return true;
} else {
return false;
} }
return false;
} }
bool File::Impl::Close() { ssize_t File1Impl::FileSize(const std::string& name) {
std::string key(GetKeyForFileName(name));
if (key.length() > 0) {
std::string value;
host_1_->GetPlatformString(key, &value);
return value.empty() ? -1 : value.size();
} else {
return -1;
}
}
ssize_t File1Impl::Write(const char* buffer, size_t bytes) {
std::string key(GetKeyForFileName(fname_));
if (key.length() > 0) {
std::string value(buffer, bytes);
host_1_->SetPlatformString(key, value);
return bytes;
} else {
return -1;
}
}
bool File1Impl::Close() {
fname_.clear(); fname_.clear();
return true; return true;
} }
bool File::Impl::Remove(const std::string& name) { // File 4 Implementation.
if (name == "cert.bin") { bool File4Impl::Open(const std::string& name) {
host_->SetPlatformString("DeviceCertificate", ""); return host_4_file_io_client_.Open(name);
return true; }
ssize_t File4Impl::Read(char* buffer, size_t bytes) {
if (host_4_file_io_client_.Read(buffer, bytes) &&
host_4_file_io_client_.data_size() > 0) {
return std::min<size_t>(host_4_file_io_client_.data_size(), bytes);
} else {
return -1;
} }
return false; }
ssize_t File4Impl::Write(const char* buffer, size_t bytes) {
if (host_4_file_io_client_.Write(buffer, bytes)) {
return bytes;
} else {
return -1;
}
}
bool File4Impl::Close() {
return host_4_file_io_client_.Close();
}
bool File4Impl::Remove(const std::string& name) {
Host4FileIOClient file_io_client(host_4_);
return file_io_client.Open(name) && file_io_client.Write("", 0);
}
ssize_t File4Impl::FileSize(const std::string& name) {
Host4FileIOClient file_io_client(host_4_);
if (file_io_client.Open(name) && file_io_client.ReadFileSize() &&
file_io_client.data_size() > 0) {
return file_io_client.data_size();
} else {
return -1;
}
}
// Common file implementation.
bool File::Impl::Open(const std::string& name) {
return file_api_->Open(name);
} }
ssize_t File::Impl::Read(char* buffer, size_t bytes) { ssize_t File::Impl::Read(char* buffer, size_t bytes) {
if (fname_ == "cert.bin") { return file_api_->Read(buffer, bytes);
std::string value;
host_->GetPlatformString("DeviceCertificate", &value);
memcpy(buffer, value.data(), std::min(bytes, value.size()));
return value.size() ? value.size() : -1;
}
return -1;
} }
ssize_t File::Impl::Write(const char* buffer, size_t bytes) { ssize_t File::Impl::Write(const char* buffer, size_t bytes) {
if (fname_ == "cert.bin") { return file_api_->Write(buffer, bytes);
std::string value(buffer, bytes); }
host_->SetPlatformString("DeviceCertificate", value);
return bytes; bool File::Impl::Close() {
} return file_api_->Close();
return -1; }
bool File::Impl::Exists(const std::string& name) {
return FileSize(name) > 0;
}
bool File::Impl::Remove(const std::string& name) {
return file_api_->Remove(name);
} }
ssize_t File::Impl::FileSize(const std::string& name) { ssize_t File::Impl::FileSize(const std::string& name) {
if (name == "cert.bin") { return file_api_->FileSize(name);
std::string value;
host_->GetPlatformString("DeviceCertificate", &value);
return value.size() ? value.size() : -1;
}
return -1;
} }
File::File() : impl_(File::Impl::factory_->NewFileImpl()) {} File::File() : impl_(File::Impl::factory_->NewFileImpl()) {}
@@ -81,9 +176,7 @@ bool File::Open(const std::string& name, int flags) {
return impl_->Open(name); return impl_->Open(name);
} }
void File::Close() { void File::Close() { impl_->Close(); }
impl_->Close();
}
ssize_t File::Read(char* buffer, size_t bytes) { ssize_t File::Read(char* buffer, size_t bytes) {
return impl_->Read(buffer, bytes); return impl_->Read(buffer, bytes);
@@ -93,13 +186,9 @@ ssize_t File::Write(const char* buffer, size_t bytes) {
return impl_->Write(buffer, bytes); return impl_->Write(buffer, bytes);
} }
bool File::Exists(const std::string& path) { bool File::Exists(const std::string& path) { return impl_->Exists(path); }
return impl_->Exists(path);
}
bool File::Remove(const std::string& path) { bool File::Remove(const std::string& path) { return impl_->Remove(path); }
return impl_->Remove(path);
}
bool File::Copy(const std::string& from, const std::string& to) { bool File::Copy(const std::string& from, const std::string& to) {
// Required for linkage only - no API implementation is needed by the CDM // Required for linkage only - no API implementation is needed by the CDM

View File

@@ -0,0 +1,66 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "host_4_file_io_client.h"
#include <string.h>
#include <algorithm>
namespace wvcdm {
Host4FileIOClient::~Host4FileIOClient() {
if (file_io_ != NULL) Close();
}
bool Host4FileIOClient::Open(const std::string& name) {
if (host_ == NULL) return false;
if (file_io_ != NULL) return false;
file_io_ = host_->CreateFileIO(this);
file_io_->Open(name.data(), name.length());
return status_ == kSuccess;
}
bool Host4FileIOClient::Read(char* buffer, size_t buffer_size) {
if (file_io_ == NULL) return false;
buffer_ = buffer;
buffer_size_ = buffer_size;
file_io_->Read();
return status_ == kSuccess;
}
bool Host4FileIOClient::Write(const char* data, size_t data_size) {
if (file_io_ == NULL) return false;
file_io_->Write(reinterpret_cast<const uint8_t*>(data), data_size);
return status_ == kSuccess;
}
bool Host4FileIOClient::Close() {
if (file_io_ == NULL) return false;
file_io_->Close();
file_io_ = NULL;
status_ = kSuccess;
data_size_ = 0;
return true;
}
void Host4FileIOClient::OnOpenComplete(Status status) {
status_ = status;
data_size_ = 0;
}
void Host4FileIOClient::OnReadComplete(Status status, const uint8_t* data,
uint32_t data_size) {
status_ = status;
data_size_ = data_size;
if (buffer_ != NULL) {
memcpy(buffer_, data, std::min<size_t>(data_size, buffer_size_));
buffer_ = NULL;
buffer_size_ = 0;
}
}
void Host4FileIOClient::OnWriteComplete(Status status) {
status_ = status;
data_size_ = 0;
}
} // namespace wvcdm

View File

@@ -1,20 +1,19 @@
// Copyright 2013 Google Inc. All Rights Reserved. // Copyright 2013 Google Inc. All Rights Reserved.
#include "wv_content_decryption_module.h" #include "wv_content_decryption_module_1.h"
#include <assert.h>
#include <string.h> #include <string.h>
#include <iostream>
#include "initialization_data.h" #include "initialization_data.h"
#include "log.h" #include "log.h"
#include "OEMCryptoCENC.h" #include "OEMCryptoCENC.h"
#include "properties.h" #include "properties.h"
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
#include "wv_cdm_version.h"
namespace { namespace {
enum { enum {
// individual error codes // individual error codes
kAttachEventListenerError = 0x0001, kAttachEventListenerError = 0x0001,
@@ -24,31 +23,6 @@ enum {
kGenerateKeyRequestErrorBase = 0x0200, kGenerateKeyRequestErrorBase = 0x0200,
kAddKeyErrorBase = 0x0300, kAddKeyErrorBase = 0x0300,
}; };
} // namespace
void INITIALIZE_CDM_MODULE() {}
void DeinitializeCdmModule() {}
void* CreateCdmInstance(int cdm_interface_version, const char* key_system,
int key_system_size, GetCdmHostFunc get_cdm_host_func,
void* user_data) {
if (cdm_interface_version != cdm::kCdmInterfaceVersion) return NULL;
cdm::Host* host = static_cast<cdm::Host*>(
get_cdm_host_func(cdm::kHostInterfaceVersion, user_data));
if (!host) return NULL;
return static_cast<cdm::ContentDecryptionModule*>(
new wvcdm::WvContentDecryptionModule(host));
}
int GetCdmVersion() { return cdm::kCdmInterfaceVersion; }
namespace {
static const std::string kWvCdmVersionString(WV_CDM_VERSION);
const int kCdmPolicyTimerDurationSeconds = 5; const int kCdmPolicyTimerDurationSeconds = 5;
@@ -58,10 +32,8 @@ const uint32_t kCencIvSize = 8;
const uint32_t kIvSize = 16; const uint32_t kIvSize = 16;
bool Ctr128Add(size_t block_count, uint8_t* counter) { bool Ctr128Add(size_t block_count, uint8_t* counter) {
if (NULL == counter) if (NULL == counter) return false;
return false; if (0 == block_count) return true;
if (0 == block_count)
return true;
uint8_t carry = 0; uint8_t carry = 0;
uint8_t n = kIvSize - 1; uint8_t n = kIvSize - 1;
while (n >= kCencIvSize) { while (n >= kCencIvSize) {
@@ -79,35 +51,33 @@ bool Ctr128Add(size_t block_count, uint8_t* counter) {
return true; return true;
} }
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
// An empty iv string signals that the frame is unencrypted. // cdm::ContentDecryptionModule_1 implementation.
bool IsBufferEncrypted(const cdm::InputBuffer& input_buffer) {
return input_buffer.iv_size != 0;
}
// cdm::ContentDecryptionModule implementation. WvContentDecryptionModule_1::WvContentDecryptionModule_1(cdm::Host_1* host)
WvContentDecryptionModule::WvContentDecryptionModule(cdm::Host* host)
: host_(host), : host_(host),
host_event_listener_(host, &cdm_engine_), host_event_listener_(host, &cdm_engine_),
cdm_engine_(),
property_set_(),
timer_enabled_(false) { timer_enabled_(false) {
HostClock::SetClockInterface(this); HostClock::SetClockInterface(this);
} }
WvContentDecryptionModule::~WvContentDecryptionModule() { WvContentDecryptionModule_1::~WvContentDecryptionModule_1() {
DisablePolicyTimer(); DisablePolicyTimer();
} }
cdm::Status WvContentDecryptionModule::GenerateKeyRequest( cdm::Status WvContentDecryptionModule_1::GenerateKeyRequest(
const char* type, int type_size, const uint8_t* init_data, const char* type, int type_size, const uint8_t* init_data,
int init_data_size) { int init_data_size) {
LOGI("WvContentDecryptionModule::GenerateKeyRequest()"); LOGI("WvContentDecryptionModule_1::GenerateKeyRequest()");
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data), CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
init_data_size); init_data_size);
InitializationData initialization_data(ISO_BMFF_VIDEO_MIME_TYPE, std::string type_string(type, type_size);
InitializationData initialization_data(type_string,
init_data_internal); init_data_internal);
CdmKeyMessage key_request; CdmKeyMessage key_request;
CdmSessionId session_id; CdmSessionId session_id;
@@ -124,8 +94,8 @@ cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
property_set_.set_use_privacy_mode(privacy_mode == "True" ? 1 : 0); property_set_.set_use_privacy_mode(privacy_mode == "True" ? 1 : 0);
property_set_.set_service_certificate(service_certificate); property_set_.set_service_certificate(service_certificate);
CdmResponseType result = CdmResponseType result = cdm_engine_.OpenSession("com.widevine.alpha",
cdm_engine_.OpenSession("com.widevine.alpha", &property_set_, &session_id); &property_set_, &session_id);
if (result == NEED_PROVISIONING) { if (result == NEED_PROVISIONING) {
LOGI("Need to aquire a Device Certificate from the Provisioning Server"); LOGI("Need to aquire a Device Certificate from the Provisioning Server");
@@ -150,7 +120,7 @@ cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
result = cdm_engine_.GenerateKeyRequest( result = cdm_engine_.GenerateKeyRequest(
session_id, key_set_id, initialization_data, kLicenseTypeStreaming, session_id, key_set_id, initialization_data, kLicenseTypeStreaming,
app_parameters, &key_request, &server_url); app_parameters, &key_request, &server_url, NULL);
if (KEY_MESSAGE != result) { if (KEY_MESSAGE != result) {
cdm_engine_.CloseSession(session_id); cdm_engine_.CloseSession(session_id);
host_->SendKeyError("", 0, cdm::kClientError, host_->SendKeyError("", 0, cdm::kClientError,
@@ -164,18 +134,16 @@ cdm::Status WvContentDecryptionModule::GenerateKeyRequest(
return cdm::kSuccess; return cdm::kSuccess;
} }
cdm::Status WvContentDecryptionModule::AddKey(const char* session_id, cdm::Status WvContentDecryptionModule_1::AddKey(
int session_id_size, const char* session_id, int session_id_size, const uint8_t* key,
const uint8_t* key, int key_size, int key_size, const uint8_t* key_id, int key_id_size) {
const uint8_t* key_id, LOGI("WvContentDecryptionModule_1::AddKey()");
int key_id_size) {
LOGI("WvContentDecryptionModule::AddKey()");
CdmSessionId session_id_internal(session_id, session_id_size); CdmSessionId session_id_internal(session_id, session_id_size);
CdmKeyResponse key_data((const char*)key, key_size); CdmKeyResponse key_data((const char*)key, key_size);
CdmKeySetId key_set_id; CdmKeySetId key_set_id;
CdmResponseType response = cdm_engine_.AddKey(session_id_internal, CdmResponseType response =
key_data, &key_set_id); cdm_engine_.AddKey(session_id_internal, key_data, &key_set_id);
if (response == KEY_ADDED) { if (response == KEY_ADDED) {
EnablePolicyTimer(); EnablePolicyTimer();
@@ -187,66 +155,58 @@ cdm::Status WvContentDecryptionModule::AddKey(const char* session_id,
return cdm::kSessionError; return cdm::kSessionError;
} }
bool WvContentDecryptionModule::IsKeyValid(const uint8_t* key_id, bool WvContentDecryptionModule_1::IsKeyValid(const uint8_t* key_id,
int key_id_size) { int key_id_size) {
KeyId key(reinterpret_cast<const char*>(key_id), key_id_size); KeyId key(reinterpret_cast<const char*>(key_id), key_id_size);
return cdm_engine_.IsKeyLoaded(key); return cdm_engine_.IsKeyLoaded(key);
} }
cdm::Status WvContentDecryptionModule::CloseSession(const char* session_id, cdm::Status WvContentDecryptionModule_1::CloseSession(const char* session_id,
int session_id_size) { int session_id_size) {
LOGI("WvContentDecryptionModule::CloseSession()"); LOGI("WvContentDecryptionModule_1::CloseSession()");
CdmSessionId session_id_internal(session_id, session_id_size); CdmSessionId session_id_internal(session_id, session_id_size);
return cdm_engine_.CloseSession(session_id_internal) == NO_ERROR return cdm_engine_.CloseSession(session_id_internal) == NO_ERROR
? cdm::kSuccess ? cdm::kSuccess
: cdm::kSessionError; : cdm::kSessionError;
} }
void WvContentDecryptionModule::TimerExpired(void* context) { void WvContentDecryptionModule_1::TimerExpired(void* context) {
LOGI("WvContentDecryptionModule::TimerExpired()"); LOGI("WvContentDecryptionModule_1::TimerExpired()");
if (timer_enabled_) { if (timer_enabled_) {
cdm_engine_.OnTimerEvent(); cdm_engine_.OnTimerEvent();
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL); host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
} }
} }
cdm::Status WvContentDecryptionModule::Decrypt( cdm::Status WvContentDecryptionModule_1::Decrypt(
const cdm::InputBuffer& encrypted_buffer, const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) { cdm::DecryptedBlock* decrypted_block) {
LOGI("WvContentDecryptionModule::Decrypt()"); LOGI("WvContentDecryptionModule_1::Decrypt()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE) if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError; return cdm::kDecryptError;
std::vector < uint8_t > iv(KEY_IV_SIZE); std::vector<uint8_t> iv(KEY_IV_SIZE);
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size); memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id), KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size); encrypted_buffer.key_id_size);
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id? CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
if (!encrypted_buffer.subsamples ||
encrypted_buffer.num_subsamples <= 0)
return cdm::kDecryptError;
CdmDecryptionParameters parameters(&key_id,
encrypted_buffer.data, 0, &iv, 0,
NULL); NULL);
parameters.is_secure = Properties::oem_crypto_use_secure_buffers(); parameters.is_secure = Properties::oem_crypto_use_secure_buffers();
return DoSubsampleDecrypt(parameters, if (encrypted_buffer.num_subsamples)
iv, return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
encrypted_buffer, else
decrypted_block); return DoDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
} }
// This is the Level 1 API. When the host application calls the CDM's // This is the Level 1 API. When the host application calls the CDM's
// DecryptDecodeAndRenderFrame(), rather than the CDM's Decrypt(), // DecryptDecodeAndRenderFrame(), rather than the CDM's Decrypt(),
// OEMCrypto_DecryptCTR() will be told to use direct rendering with no // OEMCrypto_DecryptCTR() will be told to use direct rendering with no
// cleartext in the return. // cleartext in the return.
cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderFrame( cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame(
const cdm::InputBuffer& encrypted_buffer) { const cdm::InputBuffer& encrypted_buffer) {
LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderFrame()"); LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderFrame()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE) if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError; return cdm::kDecryptError;
@@ -255,24 +215,23 @@ cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderFrame(
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size); memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id), KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size); encrypted_buffer.key_id_size);
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
if (!encrypted_buffer.subsamples ||
encrypted_buffer.num_subsamples <= 0)
return cdm::kDecryptError;
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0, CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL); NULL);
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
else
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
} }
// This is the Level 1 API. When the host application calls the CDM's // This is the Level 1 API. When the host application calls the CDM's
// DecryptDecodeAndRenderSamples(), rather than the CDM's Decrypt(), // DecryptDecodeAndRenderSamples(), rather than the CDM's Decrypt(),
// OEMCrypto_DecryptCTR() will be told to use direct rendering with no cleartext // OEMCrypto_DecryptCTR() will be told to use direct rendering with no cleartext
// in the return. // in the return.
cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderSamples( cdm::Status WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples(
const cdm::InputBuffer& encrypted_buffer) { const cdm::InputBuffer& encrypted_buffer) {
LOGI("WvContentDecryptionModule::DecryptDecodeAndRenderSamples()"); LOGI("WvContentDecryptionModule_1::DecryptDecodeAndRenderSamples()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE) if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError; return cdm::kDecryptError;
@@ -281,92 +240,78 @@ cdm::Status WvContentDecryptionModule::DecryptDecodeAndRenderSamples(
memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size); memcpy(&iv[0], encrypted_buffer.iv, encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id), KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size); encrypted_buffer.key_id_size);
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
if (!encrypted_buffer.subsamples ||
encrypted_buffer.num_subsamples <= 0)
return cdm::kDecryptError;
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0, CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL); NULL);
parameters.is_video = false; // override the default true value for audio. parameters.is_video = false; // override the default true value for audio.
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
else
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
} }
void WvContentDecryptionModule::Destroy() { delete this; } void WvContentDecryptionModule_1::Destroy() { delete this; }
// Provisioning related methods // Provisioning related methods
cdm::Status WvContentDecryptionModule::GetProvisioningRequest( cdm::Status WvContentDecryptionModule_1::GetProvisioningRequest(
std::string* request, std::string* provisioning_server_url) { std::string* request, std::string* provisioning_server_url) {
CdmCertificateType cert_type = kCertificateWidevine; CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority; std::string cert_authority;
if (cdm_engine_.GetProvisioningRequest(cert_type, cert_authority, if (cdm_engine_.GetProvisioningRequest(
static_cast<CdmProvisioningRequest*>(request), cert_type, cert_authority,
provisioning_server_url) == NO_ERROR) { static_cast<CdmProvisioningRequest*>(request),
provisioning_server_url) == NO_ERROR) {
return cdm::kSuccess; return cdm::kSuccess;
} }
return cdm::kSessionError; return cdm::kSessionError;
} }
cdm::Status WvContentDecryptionModule::HandleProvisioningResponse( cdm::Status WvContentDecryptionModule_1::HandleProvisioningResponse(
std::string& response) { std::string& response) {
std::string cert, wrapped_key; std::string cert, wrapped_key;
if (cdm_engine_.HandleProvisioningResponse( if (cdm_engine_.HandleProvisioningResponse(
static_cast<CdmProvisioningRequest&>(response), &cert, &wrapped_key) == static_cast<CdmProvisioningRequest&>(response), &cert,
NO_ERROR) { &wrapped_key) == NO_ERROR) {
return cdm::kSuccess; return cdm::kSuccess;
} }
return cdm::kSessionError; return cdm::kSessionError;
} }
void WvContentDecryptionModule::EnablePolicyTimer() { void WvContentDecryptionModule_1::EnablePolicyTimer() {
LOGI("WvContentDecryptionModule::EnablePolicyTimer()"); LOGI("WvContentDecryptionModule_1::EnablePolicyTimer()");
if (!timer_enabled_) { if (!timer_enabled_) {
timer_enabled_ = true; timer_enabled_ = true;
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL); host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
} }
} }
void WvContentDecryptionModule::DisablePolicyTimer() { void WvContentDecryptionModule_1::DisablePolicyTimer() {
LOGI("WvContentDecryptionModule::DisablePolicyTimer()"); LOGI("WvContentDecryptionModule_1::DisablePolicyTimer()");
timer_enabled_ = false; timer_enabled_ = false;
} }
cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt( cdm::Status WvContentDecryptionModule_1::DoSubsampleDecrypt(
CdmDecryptionParameters& parameters, CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
std::vector < uint8_t >& iv,
const cdm::InputBuffer& encrypted_buffer, const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) { cdm::DecryptedBlock* decrypted_block) {
if (!encrypted_buffer.subsamples)
/* This routine assumes session_id and iv have already return cdm::kDecryptError;
been initialized by the caller and encrypted_buffer contains subsample
information. Also, parameters is expected to be pre-initialized with any
needed parameters not related to subsample parsing.
decrypted_block may be NULL. */
CdmSessionId session_id; // it's empty but cdm_engine will locate via key_id.
CdmResponseType status = NO_ERROR;
size_t output_size = 0; size_t output_size = 0;
for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) { for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i]; const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i];
output_size += subsample.cipher_bytes + subsample.clear_bytes; output_size += subsample.cipher_bytes + subsample.clear_bytes;
} }
parameters.decrypt_buffer_length = output_size; assert(output_size ==
encrypted_buffer.data_size - encrypted_buffer.data_offset);
if (decrypted_block) { SetSizesAndAllocate(output_size, parameters, decrypted_block);
cdm::Buffer* buffer = host_->Allocate(output_size);
decrypted_block->SetDecryptedBuffer(buffer);
parameters.decrypt_buffer = buffer->Data();
} else {
parameters.decrypt_buffer = NULL;
}
size_t offset = 0; size_t offset = 0;
size_t encrypted_offset = 0; size_t encrypted_offset = 0;
uint32_t block_ctr = 0; uint32_t block_ctr = 0;
const cdm::SubsampleEntry *subsamples = encrypted_buffer.subsamples; const cdm::SubsampleEntry* subsamples = encrypted_buffer.subsamples;
bool first = true;
for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) { for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = subsamples[i]; const cdm::SubsampleEntry& subsample = subsamples[i];
@@ -374,53 +319,103 @@ cdm::Status WvContentDecryptionModule::DoSubsampleDecrypt(
for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) { for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) {
size_t bytes = size_t bytes =
is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes; is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes;
if (0 == bytes) if (bytes == 0) continue;
continue;
if (is_encrypted) {
uint32_t counter = encrypted_offset / kIvSize;
Ctr128Add(counter - block_ctr, &iv[0]);
block_ctr = counter;
}
parameters.encrypt_buffer =
&encrypted_buffer.data[encrypted_buffer.data_offset + offset];
parameters.decrypt_buffer_offset = offset;
parameters.encrypt_length = bytes;
parameters.block_offset = encrypted_offset % kIvSize;
offset += bytes;
if (is_encrypted)
encrypted_offset += bytes;
parameters.is_encrypted = is_encrypted; parameters.is_encrypted = is_encrypted;
parameters.subsample_flags = parameters.subsample_flags =
first ? OEMCrypto_FirstSubsample : 0; ((offset == 0) ? OEMCrypto_FirstSubsample : 0) |
parameters.subsample_flags |= ( ((offset + bytes == output_size) ? OEMCrypto_LastSubsample : 0);
offset == static_cast<size_t>(encrypted_buffer.data_size)
? OEMCrypto_LastSubsample : 0);
first = false; cdm::Status status =
DecryptAndUpdateCounters(parameters, iv, encrypted_buffer,
status = cdm_engine_.Decrypt(session_id, parameters); bytes, decrypted_block, offset,
encrypted_offset, block_ctr);
switch (status) { if (status != cdm::kSuccess)
case wvcdm::NEED_KEY: return status;
return cdm::kNoKey;
case wvcdm::NO_ERROR:
break;
default:
return cdm::kDecryptError;
}
} }
} }
return cdm::kSuccess; return cdm::kSuccess;
} }
int64_t WvContentDecryptionModule::GetCurrentTimeInSeconds() { cdm::Status WvContentDecryptionModule_1::DoDecrypt(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
assert(!encrypted_buffer.subsamples);
size_t output_size =
encrypted_buffer.data_size - encrypted_buffer.data_offset;
SetSizesAndAllocate(output_size, parameters, decrypted_block);
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
parameters.is_encrypted = true;
parameters.subsample_flags =
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
return DecryptAndUpdateCounters(parameters, iv, encrypted_buffer,
output_size, decrypted_block, offset,
encrypted_offset, block_ctr);
}
void WvContentDecryptionModule_1::SetSizesAndAllocate(
size_t output_size,
CdmDecryptionParameters& parameters,
cdm::DecryptedBlock* decrypted_block) {
parameters.decrypt_buffer_length = output_size;
if (decrypted_block) {
cdm::Buffer* buffer = host_->Allocate(output_size);
buffer->SetSize(output_size);
decrypted_block->SetDecryptedBuffer(buffer);
parameters.decrypt_buffer = buffer->Data();
} else {
parameters.decrypt_buffer = NULL;
}
}
cdm::Status WvContentDecryptionModule_1::DecryptAndUpdateCounters(
CdmDecryptionParameters& parameters,
std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
const size_t bytes,
cdm::DecryptedBlock* decrypted_block,
size_t& offset,
size_t& encrypted_offset,
uint32_t& block_ctr) {
if (parameters.is_encrypted) {
uint32_t counter = encrypted_offset / kIvSize;
Ctr128Add(counter - block_ctr, &iv[0]);
block_ctr = counter;
}
parameters.encrypt_buffer =
&encrypted_buffer.data[encrypted_buffer.data_offset + offset];
parameters.decrypt_buffer_offset = offset;
parameters.encrypt_length = bytes;
parameters.block_offset = encrypted_offset % kIvSize;
offset += bytes;
if (parameters.is_encrypted) encrypted_offset += bytes;
CdmSessionId session_id; // cdm_engine will locate via key_id.
CdmResponseType status = cdm_engine_.Decrypt(session_id, parameters);
switch (status) {
case wvcdm::NEED_KEY:
return cdm::kNoKey;
case wvcdm::NO_ERROR:
return cdm::kSuccess;
default:
return cdm::kDecryptError;
}
}
int64_t WvContentDecryptionModule_1::GetCurrentTimeInSeconds() {
return host_->GetCurrentWallTimeInSeconds(); return host_->GetCurrentWallTimeInSeconds();
} }
File::Impl* WvContentDecryptionModule::NewFileImpl() { File::Impl* WvContentDecryptionModule_1::NewFileImpl() {
return new File::Impl(host_); return new File::Impl(host_);
} }

View File

@@ -0,0 +1,597 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "wv_content_decryption_module_4.h"
#include <assert.h>
#include "log.h"
#include "properties.h"
#include "wv_cdm_constants.h"
namespace {
enum {
// individual error codes
kAttachEventListenerError = 0x0001,
kLicenseExpiredNotification = 0x0002,
kSessionNotFoundError = 0x0003,
kSessionAlreadyExistsError = 0x0004,
kInvalidParameter = 0x0005,
kNotImplemented = 0x0006,
// error classes to be OR'd with cdm engine result values
kOpenSessionErrorBase = 0x0100,
kGenerateKeyRequestErrorBase = 0x0200,
kGenerateRenewalRequestErrorBase = 0x0300,
kUpdateSessionErrorBase = 0x0400,
kRestoreKeyErrorBase = 0x0500,
kOpenKeySetSessionErrorBase = 0x0600,
kGetProvisioningRequestErrorBase = 0x0700,
kHandleProvisioningResponseErrorBase = 0x0800,
};
const int kCdmPolicyTimerDurationSeconds = 5;
// The iso spec only uses the lower 8 bytes of the iv as
// the counter.
const uint32_t kCencIvSize = 8;
const uint32_t kIvSize = 16;
void Ctr128Add(size_t block_count, uint8_t* counter) {
assert(NULL != counter);
if (0 == block_count) return;
uint8_t carry = 0;
uint8_t n = kIvSize - 1;
while (n >= kCencIvSize) {
uint32_t temp = block_count & 0xff;
temp += counter[n];
temp += carry;
counter[n] = temp & 0xff;
carry = (temp & 0x100) ? 1 : 0;
block_count = block_count >> 8;
n--;
if (!block_count && !carry) {
break;
}
}
}
} // namespace
namespace wvcdm {
// cdm::ContentDecryptionModule_4 implementation.
WvContentDecryptionModule_4::WvContentDecryptionModule_4(cdm::Host_4* host)
: host_(host), timer_enabled_(false) {
HostClock::SetClockInterface(this);
}
WvContentDecryptionModule_4::~WvContentDecryptionModule_4() {
DisablePolicyTimer();
}
void WvContentDecryptionModule_4::CreateSession(uint32_t session_id,
const char* mime_type,
uint32_t mime_type_size,
const uint8_t* init_data,
uint32_t init_data_size,
cdm::SessionType session_type) {
if (session_type == cdm::kProvisioning) {
// CreateProvisionSession() dispatches its own errors to the host.
CreateProvisionSession(session_id);
} else {
CdmLicenseType license_type;
switch (session_type) {
case cdm::kTemporary:
license_type = kLicenseTypeStreaming;
break;
case cdm::kPersistent:
license_type = kLicenseTypeOffline;
break;
default:
LOGE(
"WvContentDecryptionModule_4::CreateSession: Unsupported session "
"type ",
session_type);
host_->OnSessionError(session_id, cdm::kSessionError,
kInvalidParameter);
return;
}
if (!CallOpenSession(session_id)) {
// CallOpenSession() dispatches its own errors to the host.
return;
}
CdmSessionId internal_session_id = session_map_[session_id].webid();
CdmKeySetId empty_key_set_id;
std::string mime_type_string(mime_type, mime_type_size);
CdmInitData init_data_internal(reinterpret_cast<const char*>(init_data),
init_data_size);
InitializationData initialization_data(mime_type_string,
init_data_internal);
CdmAppParameterMap empty_app_parameters;
CdmKeyMessage key_request;
std::string server_url;
CdmKeySetId key_set_id;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
internal_session_id, empty_key_set_id, initialization_data,
license_type, empty_app_parameters, &key_request, &server_url,
&key_set_id);
if (KEY_MESSAGE == result) {
if (session_type == cdm::kPersistent) {
host_->OnSessionCreated(session_id, key_set_id.c_str(),
key_set_id.length());
} else {
host_->OnSessionCreated(session_id, internal_session_id.c_str(),
internal_session_id.length());
}
host_->OnSessionMessage(session_id, key_request.c_str(),
key_request.length(), server_url.c_str(),
server_url.length());
} else {
cdm_engine_.CloseSession(internal_session_id);
session_map_.erase(session_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kGenerateKeyRequestErrorBase | result);
}
}
}
void WvContentDecryptionModule_4::LoadSession(uint32_t session_id,
const char* web_session_id,
uint32_t web_session_id_length) {
if (!CallOpenSession(session_id)) {
// CallOpenSession() dispatches its own errors to the host.
return;
}
CdmSessionId internal_session_id = session_map_[session_id].webid();
CdmKeySetId key_set_id(web_session_id, web_session_id_length);
CdmResponseType result =
cdm_engine_.RestoreKey(internal_session_id, key_set_id);
if (result != KEY_ADDED) {
cdm_engine_.CloseSession(internal_session_id);
session_map_.erase(session_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kRestoreKeyErrorBase | result);
return;
}
host_->OnSessionCreated(session_id, web_session_id, web_session_id_length);
}
void WvContentDecryptionModule_4::UpdateSession(uint32_t session_id,
const uint8_t* response,
uint32_t response_size) {
LOGI("WvContentDecryptionModule_4::UpdateSession()");
if (session_map_.count(session_id) == 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionNotFoundError);
return;
}
const InternalSession& session = session_map_[session_id];
if (session.is_provision()) {
// UpdateProvisionSession() dispatches its own errors to the host.
UpdateProvisionSession(session_id, response, response_size);
} else {
bool is_release = session.is_release();
CdmSessionId session_id_internal;
CdmKeySetId key_set_id;
if (!is_release) {
session_id_internal = session.webid();
} else {
key_set_id = session.webid();
}
CdmKeyResponse key_data((const char*)response, response_size);
CdmResponseType status =
cdm_engine_.AddKey(session_id_internal, key_data, &key_set_id);
if (status != KEY_ADDED) {
host_->OnSessionError(session_id, cdm::kSessionError,
status | kUpdateSessionErrorBase);
return;
}
if (!is_release) EnablePolicyTimer();
host_->OnSessionUpdated(session_id);
}
}
bool WvContentDecryptionModule_4::IsKeyValid(const uint8_t* key_id,
int key_id_size) {
KeyId cdm_key_id(reinterpret_cast<const char*>(key_id), key_id_size);
return cdm_engine_.IsKeyLoaded(cdm_key_id);
}
void WvContentDecryptionModule_4::ReleaseSession(uint32_t session_id) {
if (session_map_.count(session_id) == 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionNotFoundError);
return;
}
if (session_map_[session_id].is_release()) {
cdm_engine_.CloseKeySetSession(session_map_[session_id].webid());
} else if (session_map_[session_id].is_decrypt()) {
cdm_engine_.CloseSession(session_map_[session_id].webid());
}
host_->OnSessionClosed(session_id);
session_map_.erase(session_id);
}
void WvContentDecryptionModule_4::RemoveSession(
uint32_t session_id, const char* web_session_id,
uint32_t web_session_id_length) {
if (session_map_.count(session_id) != 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionAlreadyExistsError);
return;
}
CdmKeySetId key_set_id(web_session_id, web_session_id_length);
CdmResponseType result = cdm_engine_.OpenKeySetSession(key_set_id);
if (result != NO_ERROR) {
host_->OnSessionError(session_id, cdm::kSessionError,
kOpenKeySetSessionErrorBase | result);
return;
}
session_map_[session_id] =
InternalSession(key_set_id, InternalSession::kRelease);
CdmSessionId empty_session_id;
InitializationData empty_initialization_data;
CdmAppParameterMap empty_app_parameters;
CdmKeyMessage key_request;
std::string server_url;
result = cdm_engine_.GenerateKeyRequest(
empty_session_id, key_set_id, empty_initialization_data,
kLicenseTypeRelease, empty_app_parameters, &key_request, &server_url,
NULL);
if (KEY_MESSAGE == result) {
host_->OnSessionCreated(session_id, key_set_id.c_str(),
key_set_id.length());
host_->OnSessionMessage(session_id, key_request.c_str(),
key_request.length(), server_url.c_str(),
server_url.length());
} else {
cdm_engine_.CloseKeySetSession(key_set_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kGenerateKeyRequestErrorBase | result);
}
}
cdm::Status WvContentDecryptionModule_4::UsePrivacyMode() {
if (!session_map_.empty()) return cdm::kSessionError;
property_set_.set_use_privacy_mode(true);
return cdm::kSuccess;
}
cdm::Status WvContentDecryptionModule_4::SetServerCertificate(
const uint8_t* server_certificate_data,
uint32_t server_certificate_data_size) {
if (!session_map_.empty()) return cdm::kSessionError;
cdm::Status result = UsePrivacyMode();
if (result != cdm::kSuccess) return result;
std::string server_certificate(
reinterpret_cast<const char*>(server_certificate_data),
server_certificate_data_size);
property_set_.set_service_certificate(server_certificate);
return cdm::kSuccess;
}
void WvContentDecryptionModule_4::TimerExpired(void* context) {
LOGI("WvContentDecryptionModule_4::TimerExpired()");
if (timer_enabled_) {
cdm_engine_.OnTimerEvent();
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
}
}
cdm::Status WvContentDecryptionModule_4::Decrypt(
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
LOGI("WvContentDecryptionModule_4::Decrypt()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError;
std::vector<uint8_t> iv(encrypted_buffer.iv,
encrypted_buffer.iv + encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size);
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
parameters.is_secure = Properties::oem_crypto_use_secure_buffers();
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer,
decrypted_block);
else
return DoDecrypt(parameters, iv, encrypted_buffer, decrypted_block);
}
cdm::Status WvContentDecryptionModule_4::DecryptDecodeAndRender(
const cdm::InputBuffer& encrypted_buffer, cdm::StreamType stream_type) {
LOGI("WvContentDecryptionModule_4::DecryptDecodeAndRender()");
if (static_cast<size_t>(encrypted_buffer.iv_size) != KEY_IV_SIZE)
return cdm::kDecryptError;
std::vector<uint8_t> iv(encrypted_buffer.iv,
encrypted_buffer.iv + encrypted_buffer.iv_size);
KeyId key_id(reinterpret_cast<const char*>(encrypted_buffer.key_id),
encrypted_buffer.key_id_size);
CdmDecryptionParameters parameters(&key_id, encrypted_buffer.data, 0, &iv, 0,
NULL);
parameters.is_video = (stream_type == cdm::kStreamTypeVideo);
if (encrypted_buffer.num_subsamples)
return DoSubsampleDecrypt(parameters, iv, encrypted_buffer, NULL);
else
return DoDecrypt(parameters, iv, encrypted_buffer, NULL);
}
void WvContentDecryptionModule_4::Destroy() { delete this; }
File::Impl* WvContentDecryptionModule_4::NewFileImpl() {
return new File::Impl(host_);
}
cdm::Status WvContentDecryptionModule_4::DoSubsampleDecrypt(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
if (!encrypted_buffer.subsamples) return cdm::kDecryptError;
size_t output_size = 0;
for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = encrypted_buffer.subsamples[i];
output_size += subsample.cipher_bytes + subsample.clear_bytes;
}
assert(output_size ==
encrypted_buffer.data_size - encrypted_buffer.data_offset);
SetSizesAndAllocate(output_size, parameters, decrypted_block);
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
const cdm::SubsampleEntry* subsamples = encrypted_buffer.subsamples;
for (int i = 0; i < encrypted_buffer.num_subsamples; ++i) {
const cdm::SubsampleEntry& subsample = subsamples[i];
for (int is_encrypted = 0; is_encrypted < 2; ++is_encrypted) {
size_t bytes =
is_encrypted ? subsample.cipher_bytes : subsample.clear_bytes;
if (bytes == 0) continue;
parameters.is_encrypted = is_encrypted;
parameters.subsample_flags =
((offset == 0) ? OEMCrypto_FirstSubsample : 0) |
((offset + bytes == output_size) ? OEMCrypto_LastSubsample : 0);
cdm::Status status = DecryptAndUpdateCounters(
parameters, iv, encrypted_buffer, bytes, decrypted_block, offset,
encrypted_offset, block_ctr);
if (status != cdm::kSuccess) return status;
}
}
return cdm::kSuccess;
}
cdm::Status WvContentDecryptionModule_4::DoDecrypt(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer,
cdm::DecryptedBlock* decrypted_block) {
assert(!encrypted_buffer.subsamples);
size_t output_size =
encrypted_buffer.data_size - encrypted_buffer.data_offset;
SetSizesAndAllocate(output_size, parameters, decrypted_block);
size_t offset = 0;
size_t encrypted_offset = 0;
uint32_t block_ctr = 0;
parameters.is_encrypted = true;
parameters.subsample_flags =
OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample;
return DecryptAndUpdateCounters(parameters, iv, encrypted_buffer, output_size,
decrypted_block, offset, encrypted_offset,
block_ctr);
}
void WvContentDecryptionModule_4::SetSizesAndAllocate(
size_t output_size, CdmDecryptionParameters& parameters,
cdm::DecryptedBlock* decrypted_block) {
parameters.decrypt_buffer_length = output_size;
if (decrypted_block) {
cdm::Buffer* buffer = host_->Allocate(output_size);
buffer->SetSize(output_size);
decrypted_block->SetDecryptedBuffer(buffer);
parameters.decrypt_buffer = buffer->Data();
} else {
parameters.decrypt_buffer = NULL;
}
}
cdm::Status WvContentDecryptionModule_4::DecryptAndUpdateCounters(
CdmDecryptionParameters& parameters, std::vector<uint8_t>& iv,
const cdm::InputBuffer& encrypted_buffer, const size_t bytes,
cdm::DecryptedBlock* decrypted_block, size_t& offset,
size_t& encrypted_offset, uint32_t& block_ctr) {
if (parameters.is_encrypted) {
uint32_t counter = encrypted_offset / kIvSize;
Ctr128Add(counter - block_ctr, &iv[0]);
block_ctr = counter;
}
parameters.encrypt_buffer =
&encrypted_buffer.data[encrypted_buffer.data_offset + offset];
parameters.decrypt_buffer_offset = offset;
parameters.encrypt_length = bytes;
parameters.block_offset = encrypted_offset % kIvSize;
offset += bytes;
if (parameters.is_encrypted) encrypted_offset += bytes;
CdmSessionId session_id; // cdm_engine will locate via key_id.
CdmResponseType status = cdm_engine_.Decrypt(session_id, parameters);
switch (status) {
case wvcdm::NEED_KEY:
return cdm::kNoKey;
case wvcdm::NO_ERROR:
return cdm::kSuccess;
default:
return cdm::kDecryptError;
}
}
void WvContentDecryptionModule_4::EnablePolicyTimer() {
LOGI("WvContentDecryptionModule_4::EnablePolicyTimer()");
if (!timer_enabled_) {
timer_enabled_ = true;
host_->SetTimer(kCdmPolicyTimerDurationSeconds * 1000, NULL);
}
}
void WvContentDecryptionModule_4::DisablePolicyTimer() {
LOGI("WvContentDecryptionModule_4::DisablePolicyTimer()");
timer_enabled_ = false;
}
void WvContentDecryptionModule_4::CreateProvisionSession(uint32_t session_id) {
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
CdmProvisioningRequest request;
std::string default_url;
CdmResponseType status = cdm_engine_.GetProvisioningRequest(
cert_type, cert_authority, &request, &default_url);
if (status != NO_ERROR) {
host_->OnSessionError(session_id, cdm::kSessionError,
kGetProvisioningRequestErrorBase | status);
return;
}
std::string blank_web_id;
session_map_[session_id] =
InternalSession(blank_web_id, InternalSession::kProvision);
host_->OnSessionCreated(session_id, blank_web_id.c_str(),
blank_web_id.length());
host_->OnSessionMessage(session_id, request.c_str(), request.length(),
default_url.c_str(), default_url.length());
}
void WvContentDecryptionModule_4::UpdateProvisionSession(
uint32_t session_id, const uint8_t* response, uint32_t response_size) {
CdmProvisioningResponse cdm_response(reinterpret_cast<const char*>(response),
response_size);
std::string cert;
std::string wrapped_key;
CdmResponseType status =
cdm_engine_.HandleProvisioningResponse(cdm_response, &cert, &wrapped_key);
if (status != NO_ERROR) {
host_->OnSessionError(session_id, cdm::kSessionError,
kHandleProvisioningResponseErrorBase | status);
return;
}
host_->OnSessionUpdated(session_id);
}
bool WvContentDecryptionModule_4::CallOpenSession(uint32_t session_id) {
CdmSessionId internal_session_id;
if (session_map_.count(session_id) != 0) {
host_->OnSessionError(session_id, cdm::kSessionError,
kSessionAlreadyExistsError);
return false;
}
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, &internal_session_id);
if (result != NO_ERROR) {
if (result == NEED_PROVISIONING) {
host_->OnSessionError(session_id, cdm::kNeedsDeviceCertificate,
kOpenSessionErrorBase | result);
} else {
host_->OnSessionError(session_id, cdm::kSessionError,
kOpenSessionErrorBase | result);
}
return false;
}
if (!cdm_engine_.AttachEventListener(internal_session_id, this)) {
cdm_engine_.CloseSession(internal_session_id);
host_->OnSessionError(session_id, cdm::kSessionError,
kAttachEventListenerError);
return false;
}
session_map_[session_id] =
InternalSession(internal_session_id, InternalSession::kDecrypt);
return true;
}
int64_t WvContentDecryptionModule_4::GetCurrentTimeInSeconds() {
return host_->GetCurrentWallTimeInSeconds();
}
void WvContentDecryptionModule_4::OnEvent(const CdmSessionId& session_id,
CdmEventType cdm_event) {
bool found = false;
std::map<uint32_t, InternalSession>::iterator session_pair_iterator;
uint32_t external_session_id = 0;
for (session_pair_iterator = session_map_.begin();
session_pair_iterator != session_map_.end(); ++session_pair_iterator) {
if (session_pair_iterator->second.webid() == session_id) {
found = true;
external_session_id = session_pair_iterator->first;
break;
}
}
if (!found) {
LOGE("Unmapped Session Event on Session %s", session_id.c_str());
host_->OnSessionError(0, cdm::kSessionError, kSessionNotFoundError);
return;
}
switch (cdm_event) {
case LICENSE_RENEWAL_NEEDED_EVENT: {
wvcdm::CdmKeyMessage cdm_message;
std::string server_url;
CdmResponseType result = cdm_engine_.GenerateRenewalRequest(
session_id, &cdm_message, &server_url);
if (result == wvcdm::KEY_MESSAGE) {
LOGI("Renewal created");
host_->OnSessionMessage(external_session_id, cdm_message.c_str(),
cdm_message.length(), server_url.c_str(),
server_url.length());
} else {
LOGD("Error on Generating a Renewal Request!");
host_->OnSessionError(external_session_id, cdm::kSessionError,
kGenerateRenewalRequestErrorBase | result);
}
break;
}
case LICENSE_EXPIRED_EVENT: {
LOGD("License Expired Event");
host_->OnSessionError(external_session_id, cdm::kSessionError,
kLicenseExpiredNotification);
break;
}
}
}
} // namespace wvcdm

709
cdm/test/cdm_api_1_test.cpp Normal file
View File

@@ -0,0 +1,709 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// This source file provides a basic set of unit tests for the Content
// Decryption Module (CDM). It exercises much of the API that will be
// required by the host application to get the license and keys for
// rendering protected content.
#include "cdm_test_config.h"
#include "test_host_1.h"
#include "test_util.h"
#include <getopt.h>
#include <gtest/gtest.h>
#include "clock.h"
#include "config_test_env.h"
#include "content_decryption_module.h"
#include "device_cert.h"
#include "license_request.h"
#include "log.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "url_request.h"
static const int kTestPolicyRenewalDelaySeconds = 180;
static const int kDelayWaitToForRenewalMessageSeconds = 2;
static const int kHttpOk = 200;
static const int kHttpBadGateway = 502;
static const int kNumRetries = 5;
static const int kRetryBaseDelaySeconds = 3;
namespace {
// Default key system identifier.
const char kKeySystemWidevine[] = "com.widevine.alpha";
// Default mime type for key request generation.
const char kMimeType[] = "video/mp4";
// Key ID of key used to encrypt the test content.
// This is used to look up the content key.
const std::vector<uint8_t> kTestKeyId =
wvcdm::a2b_hex("371ea35e1a985d75d198a7f41020dc23");
// Dummy encrypted data.
const std::vector<uint8_t> kInputVector1 = wvcdm::a2b_hex(
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
const std::vector<uint8_t> kIv1 =
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
// Expected output for kInputVector1.
const std::vector<uint8_t> kOutputVector1 = wvcdm::a2b_hex(
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659");
// Dummy encrypted data. This is a combination of clear and encrypted data.
const std::vector<uint8_t> kInputVector2 = wvcdm::a2b_hex(
// subsample 0
"abcdef"
"53cc758763904ea5870458e6b23d36db1e6d7f7aaa2f3eeebb5393a7264991e7"
"ce4f57b198326e1a208a821799b2a29c90567ab57321b06e51fc20dc9bc5fc55"
"10720a8bb1f5e002c3e50ff70d2d806a9432cad237050d09581f5b0d59b00090"
"b3ad69b4087f5a155b17e13c44d33fa007475d207fc4ac2ef3b571ecb9"
// subsample 1
"0123456789"
"f3c852"
"ce00dc4806f0c6856ae1732e20308096478e1d822d75c2bb768119565d3bd6e6"
"901e36164f4802355ee758fc46ef6cf5f852dd5256c7b1e5f96d29"
// subsample 2
"deadbeefbaadf00d"
"3b20525d5e"
"78b8e5aa344d5c4e425e67ddf889ea7c4bb1d49af67eba67718b765e0a940402"
"8d306f4ce693ad6dc0a931d507fa14fff4d293d4170280b3e0fca2d628f722e8"
);
const std::vector<uint8_t> kIv2 =
wvcdm::a2b_hex("6ba18dd40f49da7f64c368e4db43fc88");
// Expected output for kInputVector2.
const std::vector<uint8_t> kOutputVector2 = wvcdm::a2b_hex(
// subsample 0
"abcdef"
"52e65334501acadf78e2b26460def3ac973771ed7c64001a2e82917342a7eab3"
"047f5e85449692fae8f677be425a47bdea850df5a3ffff17043afb1f2b437ab2"
"b1d5e0784c4ed8f97fc24b8f565e85ed63fb7d1365980d9aea7b8b58f488f83c"
"1ce80b6096c60f3b113c988ff185b26e798da8fc6f327e4ff00e4b3fbf"
// subsample 1
"0123456789"
"b1ed0a"
"a054bce40ccb0ebc70b181d1a12055f46ac55e29c7c2473a29d2a366d240ec48"
"7cede274f012813a877f99159e7062b6a37cfc9327a7bc2195814e"
// subsample 2
"deadbeefbaadf00d"
"653b818d1d"
"4ab9a9128361d8ca6a9d2766df5c096ee29f4f5204febdf217a94a5b560cd692"
"cc36d3e071df789fdeac2fb7ec6dcd7af94bb1f85c22025b25e702e38212b927"
);
// Dummy encrypted data. This will be decrypted with a data_offset
// instead of subsamples.
const std::vector<uint8_t> kInputVector3 = wvcdm::a2b_hex(
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
const std::vector<uint8_t> kIv3 =
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
// The data_offset for kInputVector3.
const uint32_t kInputOffset3 = 9;
// Expected output for kInputVector3 offset by kInputOffset3.
const std::vector<uint8_t> kOutputVector3 = wvcdm::a2b_hex(
"19ab304b49908e2395b32f26bf471adf4a4bc92f9e999cca8476d24a257931b4"
"c5fd177693ed55e31cd2b85dc196b2b722cd8854eb9334f3dab0b5bd26aa5e66"
"a9d1cfbba877c9456b11dc99a6bdc7015ca1544f7ce66171a8179eca19efe515"
"8c4c1d0612dff64100387065da108fdbfcc14738202ac3d27520eb48c020ddb7"
"714dca22e5e2241aff6932dba1587a97ac1a952827d411d8582dfecc2e9e1494"
"644046ca7044bc41c3c5e0a3a405d5551f3f5bdd6f36042e2f0f3693778b9277"
"6ed8d106647a7539df7d30288803cd9ca1c274bebe688151c72b451f571a441f"
"83d0ff77d8d57dcb395122e175f4944569917627d6c3dc");
void* GetCdmHost(int host_interface_version, void* user_data) {
if (host_interface_version != cdm::Host_1::kVersion) return NULL;
return user_data;
}
} // namespace
namespace wvcdm {
class CdmApi1Test : public testing::Test {
public:
CdmApi1Test() : cdm_(NULL) {}
~CdmApi1Test() {}
protected:
virtual void SetUp() {
// Create the Host.
host_.reset(new TestHost_1());
// Set various parameters that the CDM will query.
host_->SetPlatformString("SecurityLevel", "L1");
host_->SetPlatformString("PrivacyOn", "False");
std::string cert(reinterpret_cast<const char*>(kDeviceCert),
sizeof(kDeviceCert));
host_->SetPlatformString("DeviceCertificate", cert);
// Initialize the CDM module before creating a CDM instance.
InitializeCdmModule();
// Create the CDM.
cdm_ =
reinterpret_cast<cdm::ContentDecryptionModule_1*>(::CreateCdmInstance(
cdm::ContentDecryptionModule_1::kVersion, kKeySystemWidevine,
strlen(kKeySystemWidevine), GetCdmHost, host_.get()));
// Tell the Host about the CDM.
host_->SetCdmPtr(cdm_);
}
cdm::Status GenerateKeyRequest(const std::string& init_data) {
cdm::Status status = cdm_->GenerateKeyRequest(
kMimeType, strlen(kMimeType),
(const uint8_t*)init_data.data(), init_data.length());
return status;
}
cdm::Status GenerateKeyRequestWithMimeType(const std::string& mime_type) {
cdm::Status status = cdm_->GenerateKeyRequest(
mime_type.c_str(), mime_type.length(),
(const uint8_t*)g_key_id.data(), g_key_id.length());
return status;
}
// posts a request and extracts the drm message from the response
std::string GetKeyRequestResponse(const TestHost_1::KeyMessage& key_msg) {
std::string url;
if (key_msg.default_url.empty()) {
url = g_license_server + g_client_auth;
} else {
// Note that the client auth string is not appended when the CDM tells
// us what URL to use.
url = key_msg.default_url;
}
int status_code;
std::string response;
UrlRequest url_request(url);
for (int retries = 0; retries < kNumRetries; ++retries) {
EXPECT_TRUE(url_request.is_connected());
if (!url_request.is_connected()) {
return "";
}
url_request.PostRequest(key_msg.message);
int resp_bytes = url_request.GetResponse(&response);
status_code = url_request.GetStatusCode(response);
// Sometimes, the server returns "HTTP 502 bad gateway".
// If we treat this as a non-fatal error, we reduce test flakiness.
if (status_code != kHttpBadGateway) {
// Move on with normal processing.
break;
}
// Reconnect to the server and try again. Since the server's 502
// response could indicate a temporary failure due to load, we use
// an exponential backoff. Each time we reconnect, we delay by
// exactly twice as long as the last time. This is a simplified
// version of the delay strategy recommended by Google in the
// internal document "Rate Limiting in Google Applications" under
// the heading "Settings for client exponential backoff". We do
// not bother to fuzz the delay, since unit tests are not running
// simultaneously in large numbers like real clients would be.
LOGE("Bad gateway, retrying.");
sleep(kRetryBaseDelaySeconds << retries);
url_request.Reconnect();
}
// Some license servers return 400 for invalid message, some
// return 500; treat anything other than 200 as an invalid message.
EXPECT_EQ(kHttpOk, status_code);
if (status_code != kHttpOk) {
return "";
} else {
std::string drm_msg;
LicenseRequest lic_request;
lic_request.GetDrmMessage(response, drm_msg);
LOGV("drm msg: %u bytes\n%s", drm_msg.size(),
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str());
return drm_msg;
}
}
void ProcessKeyResponse() {
TestHost_1::KeyMessage key_msg = host_->GetLastKeyMessage();
ASSERT_FALSE(key_msg.message.empty());
EXPECT_TRUE(key_msg.default_url.empty());
std::string drm_msg = GetKeyRequestResponse(key_msg);
EXPECT_EQ(cdm::kSuccess, AddKey(key_msg.session_id, drm_msg));
}
void ProcessKeyRenewalResponse() {
TestHost_1::KeyMessage key_msg = host_->GetLastKeyMessage();
ASSERT_FALSE(key_msg.message.empty());
EXPECT_FALSE(key_msg.default_url.empty());
std::string drm_msg = GetKeyRequestResponse(key_msg);
EXPECT_EQ(cdm::kSuccess, AddKey(key_msg.session_id, drm_msg));
}
void CloseSession(const std::string& session_id) {
cdm::Status status =
cdm_->CloseSession(session_id.data(), session_id.length());
EXPECT_EQ(cdm::kSuccess, status);
}
cdm::Status AddKey(const std::string& session_id,
const std::string& drm_msg) {
cdm::Status status =
cdm_->AddKey(session_id.data(), session_id.size(),
(const uint8_t*)drm_msg.data(), drm_msg.size(), NULL, 0);
return status;
}
cdm::InputBuffer BuildInputBuffer(const std::vector<uint8_t>& encrypted,
const std::vector<uint8_t>& iv) {
cdm::InputBuffer buf;
buf.data = &encrypted[0];
buf.data_size = encrypted.size();
buf.key_id = &kTestKeyId[0];
buf.key_id_size = kTestKeyId.size();
buf.iv = &iv[0];
buf.iv_size = iv.size();
buf.data_offset = 0;
buf.timestamp = 10;
return buf;
}
cdm::InputBuffer BuildInputBuffer(
const std::vector<uint8_t>& encrypted,
const std::vector<uint8_t>& iv,
const std::vector<cdm::SubsampleEntry>& sub) {
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.subsamples = &sub[0];
buf.num_subsamples = sub.size();
return buf;
}
cdm::InputBuffer BuildInputBuffer(
const std::vector<uint8_t>& encrypted,
const std::vector<uint8_t>& iv,
const uint32_t offset) {
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.data_offset = offset;
return buf;
}
std::vector<cdm::SubsampleEntry> BuildMultipleSubsamples() {
std::vector<cdm::SubsampleEntry> sub;
sub.push_back(cdm::SubsampleEntry(3, 125));
sub.push_back(cdm::SubsampleEntry(5, 62));
sub.push_back(cdm::SubsampleEntry(8, 69));
return sub;
}
std::vector<cdm::SubsampleEntry> BuildSingleSubsample(size_t size) {
std::vector<cdm::SubsampleEntry> sub;
sub.push_back(cdm::SubsampleEntry(0, size));
return sub;
}
cdm::ContentDecryptionModule_1* cdm_; // owned by host_
wvcdm::scoped_ptr<TestHost_1> host_;
};
namespace {
class DummyCDM : public cdm::ContentDecryptionModule_1 {
public:
DummyCDM() : timer_fired_(false), last_context_(NULL) {}
virtual cdm::Status GenerateKeyRequest(const char*, int, const uint8_t*,
int) OVERRIDE {
return cdm::kSessionError;
}
virtual cdm::Status AddKey(const char*, int, const uint8_t*, int,
const uint8_t*, int) OVERRIDE {
return cdm::kSessionError;
}
virtual bool IsKeyValid(const uint8_t*, int) OVERRIDE { return false; }
virtual cdm::Status CloseSession(const char*, int) OVERRIDE {
return cdm::kSessionError;
}
virtual void TimerExpired(void* context) OVERRIDE {
timer_fired_ = true;
last_context_ = context;
}
virtual cdm::Status Decrypt(const cdm::InputBuffer&,
cdm::DecryptedBlock*) OVERRIDE {
return cdm::kSessionError;
}
virtual cdm::Status DecryptDecodeAndRenderFrame(
const cdm::InputBuffer&) OVERRIDE {
return cdm::kSessionError;
}
virtual cdm::Status DecryptDecodeAndRenderSamples(
const cdm::InputBuffer&) OVERRIDE {
return cdm::kSessionError;
}
virtual void Destroy() OVERRIDE { delete this; }
virtual cdm::Status GetProvisioningRequest(std::string*,
std::string*) OVERRIDE {
return cdm::kSessionError;
}
virtual cdm::Status HandleProvisioningResponse(std::string&) OVERRIDE {
return cdm::kSessionError;
}
bool TimerFired() const { return timer_fired_; }
void* LastTimerContext() const { return last_context_; }
void ResetTimerStatus() {
timer_fired_ = false;
last_context_ = NULL;
}
private:
bool timer_fired_;
void* last_context_;
};
} // namespace
TEST_F(CdmApi1Test, TestHostTimer) {
// Validate that the TestHost timers are processed in the correct order.
// To do this, we replace the cdm with a dummy that only tracks timers.
DummyCDM* cdm = new DummyCDM();
// The old CDM is destroyed by SetCdmPtr.
cdm_ = cdm;
host_->SetCdmPtr(cdm);
const double kTimerDelaySeconds = 1.0;
const int64_t kTimerDelayMs = kTimerDelaySeconds * 1000;
void* kCtx1 = reinterpret_cast<void*>(0x1);
void* kCtx2 = reinterpret_cast<void*>(0x2);
host_->SetTimer(kTimerDelayMs * 1, kCtx1);
host_->SetTimer(kTimerDelayMs * 2, kCtx2);
host_->FastForwardTime(kTimerDelaySeconds);
EXPECT_TRUE(cdm->TimerFired());
EXPECT_EQ(kCtx1, cdm->LastTimerContext());
cdm->ResetTimerStatus();
host_->FastForwardTime(kTimerDelaySeconds);
EXPECT_TRUE(cdm->TimerFired());
EXPECT_EQ(kCtx2, cdm->LastTimerContext());
cdm->ResetTimerStatus();
host_->FastForwardTime(kTimerDelaySeconds);
EXPECT_FALSE(cdm->TimerFired());
}
TEST_F(CdmApi1Test, DeviceCertificateTest) {
if (Properties::use_certificates_as_identification()) {
// Clear any existing device cert.
host_->SetPlatformString("DeviceCertificate", "");
ASSERT_EQ(cdm::kNeedsDeviceCertificate, GenerateKeyRequest(g_key_id));
// The Host must handle the certificate provisioning request.
std::string server_url;
std::string request;
cdm::Status status = cdm_->GetProvisioningRequest(&request, &server_url);
ASSERT_EQ(cdm::kSuccess, status);
UrlRequest url_request(server_url);
url_request.PostCertRequestInQueryString(request);
std::string message;
bool ok = url_request.GetResponse(&message);
ASSERT_TRUE(ok);
status = cdm_->HandleProvisioningResponse(message);
ASSERT_EQ(cdm::kSuccess, status);
// Now we are provisioned, so GKR should succeed.
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
} else {
LOGI(
"Skipping CdmApi1Test::DeviceCertificateTest because this platform "
"does not support device certificates.");
}
}
// Note that these tests, BaseMessageTest, NormalDecryption and TimeTest,
// are dependent on getting back a license from the license server where the
// url for the license server is defined in the conf_test_env.cpp. If these
// tests fail immediately, verify that the license server URL is correct
// and works in your test environment.
TEST_F(CdmApi1Test, BaseMessageTest) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
}
TEST_F(CdmApi1Test, NormalDecryption) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
std::vector<uint8_t> encrypted = kInputVector1;
std::vector<uint8_t> iv = kIv1;
std::vector<uint8_t> expected = kOutputVector1;
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
}
TEST_F(CdmApi1Test, NormalSubSampleDecryptionWithSubsampleInfo) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<uint8_t> expected = kOutputVector2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
}
TEST_F(CdmApi1Test, NormalSubSampleDecryptionWithMissingSubsampleInfo) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<uint8_t> expected = kOutputVector2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
// Don't add these subsamples yet!
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.num_subsamples = sub.size();
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kDecryptError, status);
// Add the subsamples pointer and expect success.
buf.subsamples = &sub[0];
status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
}
TEST_F(CdmApi1Test, DecryptWithDataOffset) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
std::vector<uint8_t> encrypted = kInputVector3;
std::vector<uint8_t> iv = kIv3;
std::vector<uint8_t> expected = kOutputVector3;
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, kInputOffset3);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
cdm::Buffer *output_buf = output.DecryptedBuffer();
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(buf.data_size - buf.data_offset, output_buf->Size());
EXPECT_EQ(0, memcmp(output.DecryptedBuffer()->Data(), &expected[0],
output_buf->Size()));
}
TEST_F(CdmApi1Test, DecryptReturnsSizedBuffer) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
std::vector<uint8_t> encrypted = kInputVector1;
std::vector<uint8_t> iv = kIv1;
std::vector<uint8_t> expected = kOutputVector1;
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
cdm::Buffer* buffer = output.DecryptedBuffer();
EXPECT_NE((void*)NULL, buffer);
if (buffer) {
EXPECT_EQ(expected.size(), output.DecryptedBuffer()->Size());
}
}
TEST_F(CdmApi1Test, TimeTest) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
// We expect that by the time we've added a key, the CDM has set a timer.
// Otherwise, it couldn't correctly handle renewal.
EXPECT_NE(0, host_->NumTimers());
host_->FastForwardTime(kTestPolicyRenewalDelaySeconds +
kDelayWaitToForRenewalMessageSeconds);
// When the timer expired, we should have sent a renewal, so we can
// add this renewed key now, assuming things are working as expected.
ProcessKeyRenewalResponse();
}
TEST_F(CdmApi1Test, SecureDecryptionLevel1) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
// Level 1 passes encrypted payload straight through. By calling the
// CDM's DecryptDecodeAndRenderSamples, and/or DecryptDecodeAndRenderFrame,
// OEMCrypto_DecryptCTR will be told to use Direct Rendering.
std::vector<uint8_t> encrypted = kInputVector1;
std::vector<uint8_t> iv = kIv1;
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
cdm::Status status;
status = cdm_->DecryptDecodeAndRenderSamples(buf);
EXPECT_EQ(cdm::kSuccess, status);
status = cdm_->DecryptDecodeAndRenderFrame(buf);
EXPECT_EQ(cdm::kSuccess, status);
}
TEST_F(CdmApi1Test, SecureDecryptionLevel1WithSubsampleInfo) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
// Level 1 passes encrypted payload straight through. By calling the
// CDM's DecryptDecodeAndRenderSamples, and/or DecryptDecodeAndRenderFrame,
// OEMCrypto_DecryptCTR will be told to use Direct Rendering.
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
cdm::Status status;
status = cdm_->DecryptDecodeAndRenderSamples(buf);
EXPECT_EQ(cdm::kSuccess, status);
status = cdm_->DecryptDecodeAndRenderFrame(buf);
EXPECT_EQ(cdm::kSuccess, status);
}
TEST_F(CdmApi1Test, SecureDecryptionLevel1WithMissingSubsampleInfo) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
ProcessKeyResponse();
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
// Don't add these subsamples yet!
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.num_subsamples = sub.size();
cdm::Status status;
status = cdm_->DecryptDecodeAndRenderSamples(buf);
EXPECT_EQ(cdm::kDecryptError, status);
status = cdm_->DecryptDecodeAndRenderFrame(buf);
EXPECT_EQ(cdm::kDecryptError, status);
// Add the subsamples pointer and expect success.
buf.subsamples = &sub[0];
status = cdm_->DecryptDecodeAndRenderSamples(buf);
EXPECT_EQ(cdm::kSuccess, status);
status = cdm_->DecryptDecodeAndRenderFrame(buf);
EXPECT_EQ(cdm::kSuccess, status);
}
TEST_F(CdmApi1Test, GenerateKeyRequestFailureSendsKeyError) {
// Pass a bogus key id and expect failure.
EXPECT_EQ(cdm::kSessionError, GenerateKeyRequest(""));
// Expect the CDM to pass a key error back to the host.
EXPECT_EQ(1, host_->KeyErrorsSize());
}
TEST_F(CdmApi1Test, AddKeyFailureSendsKeyError) {
EXPECT_EQ(cdm::kSuccess, GenerateKeyRequest(g_key_id));
// Get the message and response.
TestHost_1::KeyMessage key_msg = host_->GetLastKeyMessage();
EXPECT_TRUE(key_msg.default_url.empty());
std::string drm_msg = GetKeyRequestResponse(key_msg);
// Call AddKey with a bad session id and expect failure.
EXPECT_EQ(cdm::kSessionError, AddKey("BLAH", drm_msg));
// Expect the CDM to pass a key error back to the host.
EXPECT_EQ(1, host_->KeyErrorsSize());
// Call AddKey with a bad license and expect failure.
EXPECT_EQ(cdm::kSessionError, AddKey(key_msg.session_id, "BLAH"));
// Expect the CDM to pass one more key error back to the host.
EXPECT_EQ(2, host_->KeyErrorsSize());
}
TEST_F(CdmApi1Test, MimeTypeMatters) {
cdm::Status status;
status = GenerateKeyRequestWithMimeType("video/mp4");
ASSERT_EQ(cdm::kSuccess, status);
status = GenerateKeyRequestWithMimeType("video/webm");
ASSERT_EQ(cdm::kSuccess, status);
status = GenerateKeyRequestWithMimeType("video/blah");
ASSERT_EQ(cdm::kSessionError, status);
}
} // namespace wvcdm

922
cdm/test/cdm_api_4_test.cpp Normal file
View File

@@ -0,0 +1,922 @@
// Copyright 2013 Google Inc. All Rights Reserved.
//
// This source file provides a basic set of unit tests for the Content
// Decryption Module (CDM). It exercises much of the API that will be
// required by the host application to get the license and keys for
// rendering protected content.
#include "cdm_test_config.h"
#include "test_host_4.h"
#include "test_util.h"
#include <getopt.h>
#include <gtest/gtest.h>
#include "cdm_client_property_set.h"
#include "clock.h"
#include "config_test_env.h"
#include "content_decryption_module.h"
#include "device_cert.h"
#include "license_request.h"
#include "log.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "url_request.h"
#include "wv_content_decryption_module_4.h"
static const int kTestPolicyRenewalDelaySeconds = 180;
static const int kDelayWaitToForRenewalMessageSeconds = 2;
static const int kHttpOk = 200;
static const int kSessionId1 = 1;
static const int kSessionId2 = 2;
static const int kSessionId3 = 3;
namespace {
// Default key system identifier.
const char kKeySystemWidevine[] = "com.widevine.alpha";
// Default mime type for session creation.
const char kMimeType[] = "video/mp4";
// Known filename for certificate manipulation.
const std::string kCertFilename = "cert.bin";
// Key ID of key used to encrypt the test content.
// This is used to look up the content key.
const std::vector<uint8_t> kTestKeyId =
wvcdm::a2b_hex("371ea35e1a985d75d198a7f41020dc23");
// Dummy encrypted data.
const std::vector<uint8_t> kInputVector1 = wvcdm::a2b_hex(
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
const std::vector<uint8_t> kIv1 =
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
// Expected output for kInputVector1.
const std::vector<uint8_t> kOutputVector1 = wvcdm::a2b_hex(
"217ce9bde99bd91e9733a1a00b9b557ac3a433dc92633546156817fae26b6e1c"
"942ac20a89ff79f4c2f25fba99d6a44618a8c0420b27d54e3da17b77c9d43cca"
"595d259a1e4a8b6d7744cd98c5d3f921adc252eb7d8af6b916044b676a574747"
"8df21fdc42f166880d97a2225cd5c9ea5e7b752f4cf81bbdbe98e542ee10e1c6"
"ad868a6ac55c10d564fc23b8acff407daaf4ed2743520e02cda9680d9ea88e91"
"029359c4cf5906b6ab5bf60fbb3f1a1c7c59acfc7e4fb4ad8e623c04d503a3dd"
"4884604c8da8a53ce33db9ff8f1c5bb6bb97f37b39906bf41596555c1bcce9ed"
"08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659");
// Dummy encrypted data. This is a combination of clear and encrypted data.
const std::vector<uint8_t> kInputVector2 = wvcdm::a2b_hex(
// subsample 0
"abcdef"
"53cc758763904ea5870458e6b23d36db1e6d7f7aaa2f3eeebb5393a7264991e7"
"ce4f57b198326e1a208a821799b2a29c90567ab57321b06e51fc20dc9bc5fc55"
"10720a8bb1f5e002c3e50ff70d2d806a9432cad237050d09581f5b0d59b00090"
"b3ad69b4087f5a155b17e13c44d33fa007475d207fc4ac2ef3b571ecb9"
// subsample 1
"0123456789"
"f3c852"
"ce00dc4806f0c6856ae1732e20308096478e1d822d75c2bb768119565d3bd6e6"
"901e36164f4802355ee758fc46ef6cf5f852dd5256c7b1e5f96d29"
// subsample 2
"deadbeefbaadf00d"
"3b20525d5e"
"78b8e5aa344d5c4e425e67ddf889ea7c4bb1d49af67eba67718b765e0a940402"
"8d306f4ce693ad6dc0a931d507fa14fff4d293d4170280b3e0fca2d628f722e8"
);
const std::vector<uint8_t> kIv2 =
wvcdm::a2b_hex("6ba18dd40f49da7f64c368e4db43fc88");
// Expected output for kInputVector2.
const std::vector<uint8_t> kOutputVector2 = wvcdm::a2b_hex(
// subsample 0
"abcdef"
"52e65334501acadf78e2b26460def3ac973771ed7c64001a2e82917342a7eab3"
"047f5e85449692fae8f677be425a47bdea850df5a3ffff17043afb1f2b437ab2"
"b1d5e0784c4ed8f97fc24b8f565e85ed63fb7d1365980d9aea7b8b58f488f83c"
"1ce80b6096c60f3b113c988ff185b26e798da8fc6f327e4ff00e4b3fbf"
// subsample 1
"0123456789"
"b1ed0a"
"a054bce40ccb0ebc70b181d1a12055f46ac55e29c7c2473a29d2a366d240ec48"
"7cede274f012813a877f99159e7062b6a37cfc9327a7bc2195814e"
// subsample 2
"deadbeefbaadf00d"
"653b818d1d"
"4ab9a9128361d8ca6a9d2766df5c096ee29f4f5204febdf217a94a5b560cd692"
"cc36d3e071df789fdeac2fb7ec6dcd7af94bb1f85c22025b25e702e38212b927"
);
// Dummy encrypted data. This will be decrypted with a data_offset
// instead of subsamples.
const std::vector<uint8_t> kInputVector3 = wvcdm::a2b_hex(
"64ab17b3e3dfab47245c7cce4543d4fc7a26dcf248f19f9b59f3c92601440b36"
"17c8ed0c96c656549e461f38708cd47a434066f8df28ccc28b79252eee3f9c2d"
"7f6c68ebe40141fe818fe082ca523c03d69ddaf183a93c022327fedc5582c5ab"
"ca9d342b71263a67f9cb2336f12108aaaef464f17177e44e9b0c4e56e61da53c"
"2150b4405cc82d994dfd9bf4087c761956d6688a9705db4cf350381085f383c4"
"9666d4aed135c519c1f0b5cba06e287feea96ea367bf54e7368dcf998276c6e4"
"6497e0c50e20fef74e42cb518fe7f22ef27202428688f86404e8278587017012"
"c1d65537c6cbd7dde04aae338d68115a9f430afc100ab83cdadf45dca39db685");
const std::vector<uint8_t> kIv3 =
wvcdm::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f");
// The data_offset for kInputVector3.
const uint32_t kInputOffset3 = 9;
// Expected output for kInputVector3 offset by kInputOffset3.
const std::vector<uint8_t> kOutputVector3 = wvcdm::a2b_hex(
"19ab304b49908e2395b32f26bf471adf4a4bc92f9e999cca8476d24a257931b4"
"c5fd177693ed55e31cd2b85dc196b2b722cd8854eb9334f3dab0b5bd26aa5e66"
"a9d1cfbba877c9456b11dc99a6bdc7015ca1544f7ce66171a8179eca19efe515"
"8c4c1d0612dff64100387065da108fdbfcc14738202ac3d27520eb48c020ddb7"
"714dca22e5e2241aff6932dba1587a97ac1a952827d411d8582dfecc2e9e1494"
"644046ca7044bc41c3c5e0a3a405d5551f3f5bdd6f36042e2f0f3693778b9277"
"6ed8d106647a7539df7d30288803cd9ca1c274bebe688151c72b451f571a441f"
"83d0ff77d8d57dcb395122e175f4944569917627d6c3dc");
// Dummy data used as a server certificate. Not a real cert.
const std::vector<uint8_t> kFakeServerCertficiate = wvcdm::a2b_hex(
"65393939a0e4a28fb07aa8237bdc9e2fd30fdf763061ed9ed91b368a2b78fc2d"
"15f0b6add5d56d05f7e5852aeae67de6127d6b61b39bb5d6e9657f352ba75e72"
"c436f334878568504f697ad01aa329efbadebc3b9aaf502ada9b8e6fcf066252"
"76690a0f50cd3852dd39f7c5444402f86831d8bb2cbbd7cba11ab1caa35445fe"
"35a332529845f2f4b5d5a0b1e2c0855fc2d644443eb967e1f9030fd0d9e6375b"
"a5100a997ff8a958606d59a00151d251eaf69f9e00465b5aa4c23ef33a11b05b"
"212c1ff830fcd095c681ef25a18db08d7bddfeee16763e9fec06daa8275de2b0"
"554a0bec821fb7bb6fbda081d8cecce6c51195e6b6a1c0fcbb2470bcff2a962e"
"57e767f83cc8b6c31d41d7c59526128f19cbf625fa4f5f382393a3bd2b76463a"
"fe97e4a6f30f631c83308aa5fdccc2d1765c113d474bf2496e03c030c18e5ca8"
"84cb98fa120804baa245682966926ccbf555450437de10e549afc088f8c36f63"
"8a943178bdd58e4ef1f7d501e2296bbe2df57ce8816d6ae9e7ab18b1e01dac9b"
"8c312298356cad58c6ed46b1cd3a895e496c66f5229da39e260a7c1f782653bd"
"ade5f6132fc4771bb8caca80eb063abb47144abc44aaba8d23fcc721199291b3"
"0b95bc7d310c3f90756916552151a6008feba73ddab87454822914732d6ee78a"
"3587f33c698b0ab90f01b38a71abd01660a5ef1473e4556aa8c17d34679065c5"
"689dd21026601e94146254346f4ccd979bc378046473b3de64b8654e18406e94"
"b673dc13fdcc70213365bd098f212ed7a973ef35da18e16b1c118f8d4eda0b39"
"ffb8084ca93a44923df8475e3feec6c0941a2a37d5df514e686dd182dc9ebbff"
"41d8d80aa39d059de3b7849d4e5946b09937c6e7a6f6392ea5e9370ba1867553"
"b716e31433c2e3ca3eff1e7c08a31b201fd18c363b8daeed59df7afb68ad5166"
"659914a2e137c9d2e5940aaa921694c8d84d5ea1c83486e473e3021cb825c0ed"
"f778c621598d35e44843cd53e32f90925e74bc8eb469889fb221a2c592b43d94"
"2079d6393ab76e47d30dbf837fa5ca07d7186710973c5bb17c04b3207a85a166"
"153053f6b0ec35c6b124efd43be274cdf8300234806a72231272d13b331cafd3"
"dab01002b5e1961a5f3221d4c589fac3e42c1d1de9f244090e31a08999d3434d"
"15a5159f09fa3307e0d9466ac40af63c0a62f8d2719e1df80bb24d4d7ab256f7"
"0906ee342a47a9bd6805d796cf928f0d5251701ba8e6888675b1b6fd03e77df0"
"8150495c778cb7942d8c060ace4b080ad22e44854988d5e2232f5dbcf2db559f"
"24bae8667c33cea77f1c6a58d9dd010363c233cff6d5d26f5f77230ee681456c"
"35a8f90d37c2fc0ab07a2a795431f829cca574fd37d8822e04fc500ba468f08a"
"556e53ba8992dd1ccbd44559a7b93bebc27cec81a834755cf110bce183481e42");
void* GetCdmHost(int host_interface_version, void* user_data) {
if (host_interface_version != cdm::Host_4::kVersion) return NULL;
return user_data;
}
} // namespace
namespace wvcdm {
class CdmApi4Test : public testing::Test {
public:
CdmApi4Test() : cdm_(NULL) {}
~CdmApi4Test() {}
protected:
virtual void SetUp() {
// Create the Host.
host_.reset(new TestHost_4());
// Load the device cert that was already "saved" to the device.
std::string cert(reinterpret_cast<const char*>(kDeviceCert),
sizeof(kDeviceCert));
host_->file_store[kCertFilename] = cert;
// Initialize the CDM module before creating a CDM instance.
InitializeCdmModule();
// Create the CDM.
cdm_ =
reinterpret_cast<cdm::ContentDecryptionModule_4*>(::CreateCdmInstance(
cdm::ContentDecryptionModule_4::kVersion, kKeySystemWidevine,
strlen(kKeySystemWidevine), GetCdmHost, host_.get()));
if (cdm_ == NULL) {
FAIL() << "Fatal CDM creation error!";
}
// Tell the Host about the CDM.
host_->SetCdmPtr(cdm_);
}
void CreateSession(const uint32_t session_id, const std::string& init_data,
cdm::SessionType session_type) {
cdm_->CreateSession(session_id, kMimeType, strlen(kMimeType),
reinterpret_cast<const uint8_t*>(init_data.data()),
init_data.length(), session_type);
}
void CreateSessionWithMimeType(const uint32_t session_id,
const std::string& mime_type) {
cdm_->CreateSession(session_id, mime_type.c_str(), mime_type.length(),
reinterpret_cast<const uint8_t*>(g_key_id.data()),
g_key_id.length(), cdm::kTemporary);
}
void LoadSession(uint32_t session_id, const std::string& web_session_id) {
cdm_->LoadSession(session_id, web_session_id.data(),
web_session_id.length());
}
// posts a request and extracts the drm message from the response
std::string GetKeyRequestResponse(
const TestHost_4::SessionMessage& session_msg,
const std::string& default_url, bool is_provision) {
std::string url;
if (session_msg.default_url.empty()) {
url = default_url;
} else {
// Note that the client auth string is assumed to already be appended when
// the CDM tells us what URL to use.
url = session_msg.default_url;
}
UrlRequest url_request(url);
EXPECT_TRUE(url_request.is_connected());
if (!url_request.is_connected()) {
return "";
}
if (!is_provision) {
url_request.PostRequest(session_msg.message);
} else {
url_request.PostCertRequestInQueryString(session_msg.message);
}
std::string response;
int resp_bytes = url_request.GetResponse(&response);
// Some license servers return 400 for invalid message, some
// return 500; treat anything other than 200 as an invalid message.
int status_code = url_request.GetStatusCode(response);
EXPECT_EQ(kHttpOk, status_code);
if (status_code != kHttpOk) {
return "";
} else {
std::string drm_msg;
LicenseRequest lic_request;
lic_request.GetDrmMessage(response, drm_msg);
LOGV("drm msg: %u bytes\n%s", drm_msg.size(),
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str());
return drm_msg;
}
}
void ProcessKeyResponse(bool expect_url_in_message) {
ProcessKeyResponse(expect_url_in_message, g_license_server + g_client_auth);
}
void ProcessKeyResponse(bool expect_url_in_message,
const std::string& default_url) {
ProcessServerResponse(expect_url_in_message, false, default_url);
}
void ProcessProvisionResponse() { ProcessServerResponse(true, true, ""); }
void ProcessServerResponse(bool expect_url_in_message, bool is_provision,
const std::string& default_url) {
TestHost_4::SessionMessage session_msg = host_->GetLastSessionMessage();
ASSERT_FALSE(session_msg.message.empty());
// Use EXPECT_bool instead of EXPECT_EQ to get better error printing.
if (expect_url_in_message) {
EXPECT_FALSE(session_msg.default_url.empty());
} else {
EXPECT_TRUE(session_msg.default_url.empty());
}
std::string drm_msg =
GetKeyRequestResponse(session_msg, default_url, is_provision);
UpdateSession(session_msg.session_id, (const uint8_t*)drm_msg.c_str(),
drm_msg.length());
}
void ReleaseSession(uint32_t session_id) { cdm_->ReleaseSession(session_id); }
void RemoveSession(uint32_t session_id, const std::string& web_session_id) {
cdm_->RemoveSession(session_id, web_session_id.data(),
web_session_id.length());
}
void UpdateSession(uint32_t session_id, const uint8_t* response,
uint32_t response_size) {
cdm_->UpdateSession(session_id, response, response_size);
}
cdm::Status UsePrivacyMode() { return cdm_->UsePrivacyMode(); }
cdm::Status SetServerCertificate(const std::vector<uint8_t>& cert) {
return cdm_->SetServerCertificate(&cert[0], cert.size());
}
bool SessionErrorPresent(uint32_t session_id) {
TestHost_4::SessionError serr = host_->GetLastSessionError();
if (serr.session_id == session_id) {
return true;
}
return false;
}
bool SessionErrorPresent(uint32_t session_id, cdm::Status error_code) {
TestHost_4::SessionError serr = host_->GetLastSessionError();
if (serr.session_id == session_id && serr.error_code == error_code) {
return true;
}
return false;
}
cdm::InputBuffer BuildInputBuffer(const std::vector<uint8_t>& encrypted,
const std::vector<uint8_t>& iv) {
cdm::InputBuffer buf;
buf.data = &encrypted[0];
buf.data_size = encrypted.size();
buf.key_id = &kTestKeyId[0];
buf.key_id_size = kTestKeyId.size();
buf.iv = &iv[0];
buf.iv_size = iv.size();
buf.data_offset = 0;
buf.timestamp = 10;
return buf;
}
cdm::InputBuffer BuildInputBuffer(
const std::vector<uint8_t>& encrypted, const std::vector<uint8_t>& iv,
const std::vector<cdm::SubsampleEntry>& sub) {
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.subsamples = &sub[0];
buf.num_subsamples = sub.size();
return buf;
}
cdm::InputBuffer BuildInputBuffer(const std::vector<uint8_t>& encrypted,
const std::vector<uint8_t>& iv,
const uint32_t data_offset) {
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.data_offset = data_offset;
return buf;
}
std::vector<cdm::SubsampleEntry> BuildMultipleSubsamples() {
std::vector<cdm::SubsampleEntry> sub;
sub.push_back(cdm::SubsampleEntry(3, 125));
sub.push_back(cdm::SubsampleEntry(5, 62));
sub.push_back(cdm::SubsampleEntry(8, 69));
return sub;
}
std::vector<cdm::SubsampleEntry> BuildSingleSubsample(size_t size) {
std::vector<cdm::SubsampleEntry> sub;
sub.push_back(cdm::SubsampleEntry(0, size));
return sub;
}
void GetOfflineConfiguration(std::string* key_id, std::string* license_server,
std::string* client_auth) {
// This method compares three different places settings could be found:
// 1) A configuration object using the default settings.
// 2) The global variables prefixed with g_.
// 3) A configuration object configured for offline playback.
// The desire is to use 3 unless the user customized the settings on the
// command line, in which case we want to defer to them and use 2. The "if"
// statements check if 1 and 2 are the same. If so, we know the user
// did not customize the settings on the command line, and therefore we can
// use 3. Otherwise, we use 2. This will never return the values from 1;
// 1 is only used to check if 2 has been altered by the user.
ConfigTestEnv std_config(kLicenseServerId);
// TODO (juce): Switch this to kLicenseServerId once
// kContentProtectionServer is the default.
ConfigTestEnv config(wvcdm::kContentProtectionServer, false);
if (g_key_id.compare(a2bs_hex(std_config.key_id())) == 0)
key_id->assign(wvcdm::a2bs_hex(config.key_id()));
else
key_id->assign(g_key_id);
if (g_license_server.compare(std_config.license_server()) == 0)
license_server->assign(config.license_server());
else
license_server->assign(g_license_server);
if (g_client_auth.compare(std_config.client_auth()) == 0)
client_auth->assign(config.client_auth());
else
client_auth->assign(g_client_auth);
}
cdm::ContentDecryptionModule_4* cdm_; // owned by host_
wvcdm::scoped_ptr<TestHost_4> host_;
};
namespace {
class DummyCDM : public cdm::ContentDecryptionModule_4 {
public:
DummyCDM() : timer_fired_(false), last_context_(NULL) {}
virtual void CreateSession(uint32_t session_id,
const char* mime_type, uint32_t mime_type_size,
const uint8_t* init_data, uint32_t init_data_size,
cdm::SessionType session_type) OVERRIDE {}
virtual void LoadSession(uint32_t session_id, const char* web_session_id,
uint32_t web_session_id_length) OVERRIDE {}
virtual void UpdateSession(uint32_t session_id, const uint8_t* response,
uint32_t response_size) OVERRIDE {}
virtual bool IsKeyValid(const uint8_t*, int) OVERRIDE { return false; }
virtual void ReleaseSession(uint32_t session_id) OVERRIDE {}
virtual void RemoveSession(uint32_t session_id, const char* web_session_id,
uint32_t web_session_id_length) {}
virtual cdm::Status UsePrivacyMode() OVERRIDE { return cdm::kSuccess; }
virtual cdm::Status SetServerCertificate(
const uint8_t* server_certificate_data,
uint32_t server_certificate_data_size) OVERRIDE {
return cdm::kSuccess;
}
virtual void TimerExpired(void* context) OVERRIDE {
timer_fired_ = true;
last_context_ = context;
}
virtual cdm::Status Decrypt(const cdm::InputBuffer&,
cdm::DecryptedBlock*) OVERRIDE {
return cdm::kSessionError;
}
virtual cdm::Status DecryptDecodeAndRender(
const cdm::InputBuffer& encrypted_buffer,
cdm::StreamType stream_type) OVERRIDE {
return cdm::kDecryptError;
}
virtual void Destroy() OVERRIDE { delete this; }
bool TimerFired() const { return timer_fired_; }
void* LastTimerContext() const { return last_context_; }
void ResetTimerStatus() {
timer_fired_ = false;
last_context_ = NULL;
}
private:
bool timer_fired_;
void* last_context_;
};
} // namespace
TEST_F(CdmApi4Test, TestHostTimer) {
// Validate that the TestHost timers are processed in the correct order.
// To do this, we replace the cdm with a dummy that only tracks timers.
DummyCDM* cdm = new DummyCDM();
// The old CDM is destroyed by SetCdmPtr.
cdm_ = cdm;
host_->SetCdmPtr(cdm);
const double kTimerDelaySeconds = 1.0;
const int64_t kTimerDelayMs = kTimerDelaySeconds * 1000;
void* kCtx1 = reinterpret_cast<void*>(0x1);
void* kCtx2 = reinterpret_cast<void*>(0x2);
host_->SetTimer(kTimerDelayMs * 1, kCtx1);
host_->SetTimer(kTimerDelayMs * 2, kCtx2);
host_->FastForwardTime(kTimerDelaySeconds);
EXPECT_TRUE(cdm->TimerFired());
EXPECT_EQ(kCtx1, cdm->LastTimerContext());
cdm->ResetTimerStatus();
host_->FastForwardTime(kTimerDelaySeconds);
EXPECT_TRUE(cdm->TimerFired());
EXPECT_EQ(kCtx2, cdm->LastTimerContext());
cdm->ResetTimerStatus();
host_->FastForwardTime(kTimerDelaySeconds);
EXPECT_FALSE(cdm->TimerFired());
}
TEST_F(CdmApi4Test, DeviceCertificateTest) {
if (Properties::use_certificates_as_identification()) {
// Clear any existing certificates
host_->file_store.erase(kCertFilename);
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ASSERT_TRUE(SessionErrorPresent(kSessionId1, cdm::kNeedsDeviceCertificate));
// The Host must handle the certificate provisioning request.
CreateSession(kSessionId2, "", cdm::kProvisioning);
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
ProcessProvisionResponse();
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
CreateSession(kSessionId3, g_key_id, cdm::kTemporary);
ASSERT_FALSE(SessionErrorPresent(kSessionId3));
} else {
LOGI(
"Skipping CdmApi4Test::DeviceCertificateTest because this platform "
"does not support device certificates.");
}
}
// Note that these tests, BaseMessageTest, NormalDecryption, TimeTest, and
// others are dependent on getting back a license from the license server where
// the url for the license server is defined in the conf_test_env.cpp. If these
// tests fail immediately, verify that the license server URL is correct
// and works in your test environment.
TEST_F(CdmApi4Test, BaseMessageTest) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, NormalDecryption) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
std::vector<uint8_t> encrypted = kInputVector1;
std::vector<uint8_t> iv = kIv1;
std::vector<uint8_t> expected = kOutputVector1;
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, NormalSubSampleDecryptionWithSubsampleInfo) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
// Level 1 / Level 2 payload comes back in the cpu memory as cleartext.
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<uint8_t> expected = kOutputVector2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, NormalSubSampleDecryptionWithMissingSubsampleInfo) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<uint8_t> expected = kOutputVector2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
// Don't add these subsamples yet!
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.num_subsamples = sub.size();
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kDecryptError, status);
// Add the subsamples pointer and expect success.
buf.subsamples = &sub[0];
status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(
0, memcmp(output.DecryptedBuffer()->Data(), &expected[0], buf.data_size));
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, DecryptWithDataOffset) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
std::vector<uint8_t> encrypted = kInputVector3;
std::vector<uint8_t> iv = kIv3;
std::vector<uint8_t> expected = kOutputVector3;
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, kInputOffset3);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
cdm::Buffer* output_buf = output.DecryptedBuffer();
EXPECT_EQ(cdm::kSuccess, status);
EXPECT_EQ(buf.data_size - buf.data_offset, output_buf->Size());
EXPECT_EQ(0, memcmp(output.DecryptedBuffer()->Data(), &expected[0],
output_buf->Size()));
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, DecryptReturnsSizedBuffer) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
std::vector<uint8_t> encrypted = kInputVector1;
std::vector<uint8_t> iv = kIv1;
std::vector<uint8_t> expected = kOutputVector1;
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
TestDecryptedBlock output;
cdm::Status status = cdm_->Decrypt(buf, &output);
EXPECT_EQ(cdm::kSuccess, status);
cdm::Buffer* buffer = output.DecryptedBuffer();
EXPECT_NE((void*)NULL, buffer);
if (buffer) {
EXPECT_EQ(expected.size(), output.DecryptedBuffer()->Size());
}
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, TimeTest) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
// We expect that by the time we've added a key, the CDM has set a timer.
// Otherwise, it couldn't correctly handle renewal.
EXPECT_NE(0, host_->NumTimers());
host_->FastForwardTime(kTestPolicyRenewalDelaySeconds +
kDelayWaitToForRenewalMessageSeconds);
// When the timer expired, we should have sent a renewal, so we can
// add this renewed key now, assuming things are working as expected.
ProcessKeyResponse(true);
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, SecureDecryptionLevel1) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
// Level 1 passes encrypted payload straight through. By calling the CDM's
// DecryptDecodeAndRender, OEMCrypto_DecryptCTR will be told to use Direct
// Rendering.
std::vector<uint8_t> encrypted = kInputVector1;
std::vector<uint8_t> iv = kIv1;
std::vector<cdm::SubsampleEntry> sub = BuildSingleSubsample(encrypted.size());
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
cdm::Status status;
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
EXPECT_EQ(cdm::kSuccess, status);
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
EXPECT_EQ(cdm::kSuccess, status);
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, SecureDecryptionLevel1WithSubsampleInfo) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
// Level 1 passes encrypted payload straight through. By calling the
// CDM's DecryptDecodeAndRender, OEMCrypto_DecryptCTR will be told
// to use Direct Rendering.
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv, sub);
cdm::Status status;
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
EXPECT_EQ(cdm::kSuccess, status);
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
EXPECT_EQ(cdm::kSuccess, status);
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, SecureDecryptionLevel1WithMissingSubsampleInfo) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
std::vector<uint8_t> encrypted = kInputVector2;
std::vector<uint8_t> iv = kIv2;
std::vector<cdm::SubsampleEntry> sub = BuildMultipleSubsamples();
// Don't add these subsamples yet!
cdm::InputBuffer buf = BuildInputBuffer(encrypted, iv);
buf.num_subsamples = sub.size();
cdm::Status status;
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
EXPECT_EQ(cdm::kDecryptError, status);
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
EXPECT_EQ(cdm::kDecryptError, status);
// Add the subsamples pointer and expect success.
buf.subsamples = &sub[0];
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeVideo);
EXPECT_EQ(cdm::kSuccess, status);
status = cdm_->DecryptDecodeAndRender(buf, cdm::kStreamTypeAudio);
EXPECT_EQ(cdm::kSuccess, status);
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, GenerateKeyRequestFailureSendsKeyError) {
// Pass a bogus key id and expect failure.
// Expect the CDM to pass a key error back to the host.
CreateSession(kSessionId1, g_wrong_key_id, cdm::kTemporary);
EXPECT_EQ(1, host_->SessionErrorsSize());
EXPECT_EQ(0, host_->SessionMessagesSize());
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, UpdateSessionFailureSendsKeyError) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
TestHost_4::SessionMessage session_msg = host_->GetLastSessionMessage();
EXPECT_TRUE(session_msg.default_url.empty());
std::string drm_msg = GetKeyRequestResponse(
session_msg, g_license_server + g_client_auth, false);
// Call UpdateSession with a bad session id, and expect the CDM to pass a key
// error back to the host.
UpdateSession(kSessionId2, reinterpret_cast<const uint8_t*>(drm_msg.c_str()),
drm_msg.length());
EXPECT_EQ(1, host_->SessionErrorsSize());
// Call UpdateSession with a bad license, and expect the CDM to pass a key
// error back to the host.
static const std::string kBadLicense = "!kGoodLicense";
UpdateSession(kSessionId1,
reinterpret_cast<const uint8_t*>(kBadLicense.c_str()),
kBadLicense.length());
EXPECT_EQ(2, host_->SessionErrorsSize());
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, IsKeyValidDetectsValidKey) {
EXPECT_FALSE(cdm_->IsKeyValid(
reinterpret_cast<const uint8_t*>(kTestKeyId.data()), kTestKeyId.size()));
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ProcessKeyResponse(false);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
EXPECT_TRUE(cdm_->IsKeyValid(
reinterpret_cast<const uint8_t*>(kTestKeyId.data()), kTestKeyId.size()));
ReleaseSession(kSessionId1);
EXPECT_FALSE(cdm_->IsKeyValid(
reinterpret_cast<const uint8_t*>(kTestKeyId.data()), kTestKeyId.size()));
}
TEST_F(CdmApi4Test, OfflineLicense) {
// override default settings unless configured through the command line
std::string key_id;
std::string license_server;
std::string client_auth;
GetOfflineConfiguration(&key_id, &license_server, &client_auth);
CreateSession(kSessionId1, key_id, cdm::kPersistent);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
ProcessKeyResponse(false, license_server + client_auth);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, RestoreOfflineLicense) {
// override default settings unless configured through the command line
std::string key_id;
std::string license_server;
std::string client_auth;
GetOfflineConfiguration(&key_id, &license_server, &client_auth);
CreateSession(kSessionId1, key_id, cdm::kPersistent);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
ProcessKeyResponse(false, license_server + client_auth);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
std::string web_session_id = host_->session_map[kSessionId1];
EXPECT_FALSE(web_session_id.empty());
ReleaseSession(kSessionId1);
LoadSession(kSessionId2, web_session_id);
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
ReleaseSession(kSessionId2);
}
TEST_F(CdmApi4Test, ReleaseOfflineLicense) {
// override default settings unless configured through the command line
std::string key_id;
std::string license_server;
std::string client_auth;
GetOfflineConfiguration(&key_id, &license_server, &client_auth);
CreateSession(kSessionId1, key_id, cdm::kPersistent);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
ProcessKeyResponse(false, license_server + client_auth);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
std::string web_session_id = host_->session_map[kSessionId1];
EXPECT_FALSE(web_session_id.empty());
ReleaseSession(kSessionId1);
RemoveSession(kSessionId2, web_session_id);
ProcessKeyResponse(true, license_server + client_auth);
ASSERT_FALSE(SessionErrorPresent(kSessionId2));
}
TEST_F(CdmApi4Test, MimeTypeMatters) {
CreateSessionWithMimeType(kSessionId1, "video/mp4");
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
ReleaseSession(kSessionId1);
CreateSessionWithMimeType(kSessionId1, "video/webm");
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
ReleaseSession(kSessionId1);
CreateSessionWithMimeType(kSessionId1, "video/blah");
ASSERT_TRUE(SessionErrorPresent(kSessionId1));
}
TEST_F(CdmApi4Test, UsePrivacyMode) {
ASSERT_EQ(cdm::kSuccess, UsePrivacyMode());
const CdmClientPropertySet& property_set =
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
EXPECT_TRUE(property_set.use_privacy_mode());
}
TEST_F(CdmApi4Test, UsePrivacyModeFailsWithOpenSessions) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
EXPECT_NE(cdm::kSuccess, UsePrivacyMode());
const CdmClientPropertySet& property_set =
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
EXPECT_FALSE(property_set.use_privacy_mode());
ReleaseSession(kSessionId1);
}
TEST_F(CdmApi4Test, SetExplicitServerCertificate) {
ASSERT_EQ(cdm::kSuccess, SetServerCertificate(kFakeServerCertficiate));
const CdmClientPropertySet& property_set =
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
EXPECT_TRUE(property_set.use_privacy_mode());
const std::string& set_cert = property_set.service_certificate();
ASSERT_EQ(kFakeServerCertficiate.size(), set_cert.size());
EXPECT_EQ(0,
memcmp(&kFakeServerCertficiate[0], &set_cert[0], set_cert.size()));
}
TEST_F(CdmApi4Test, SetServerCertificateFailsWithOpenSessions) {
CreateSession(kSessionId1, g_key_id, cdm::kTemporary);
ASSERT_FALSE(SessionErrorPresent(kSessionId1));
EXPECT_NE(cdm::kSuccess, SetServerCertificate(kFakeServerCertficiate));
const CdmClientPropertySet& property_set =
reinterpret_cast<WvContentDecryptionModule_4*>(cdm_)->property_set_;
EXPECT_FALSE(property_set.use_privacy_mode());
ReleaseSession(kSessionId1);
}
} // namespace wvcdm

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_TEST_CDM_TEST_CONFIG_H_
#define WVCDM_CDM_TEST_CDM_TEST_CONFIG_H_
#include <string>
#include "config_test_env.h"
extern std::string g_client_auth;
extern std::string g_key_id;
extern std::string g_license_server;
extern std::string g_wrong_key_id;
static const wvcdm::LicenseServerId kLicenseServerId =
wvcdm::kContentProtectionServer;
#endif // WVCDM_CDM_TEST_CDM_TEST_CONFIG_H_

View File

@@ -0,0 +1,96 @@
// Copyright 2014 Google Inc. All Rights Reserved.
#include "cdm_test_config.h"
#include <getopt.h>
#include <gtest/gtest.h>
#include "log.h"
#include "string_conversions.h"
// Default license server, can be configured using --server command line option
// Default key id (pssh), can be configured using --keyid command line option
std::string g_client_auth;
std::string g_key_id;
std::string g_license_server;
std::string g_wrong_key_id;
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
wvcdm::InitLogging(argc, argv);
wvcdm::ConfigTestEnv config(kLicenseServerId);
g_client_auth.assign(config.client_auth());
g_wrong_key_id.assign(config.wrong_key_id());
// The following variables are configurable through command line options.
g_license_server.assign(config.license_server());
g_key_id.assign(config.key_id());
std::string license_server(g_license_server);
int show_usage = 0;
static const struct option long_options[] = {
{"keyid", required_argument, NULL, 'k'},
{"server", required_argument, NULL, 's'},
{NULL, 0, NULL, '\0'}};
int option_index = 0;
int opt = 0;
while ((opt = getopt_long(argc, argv, "k:s:v", long_options,
&option_index)) != -1) {
switch (opt) {
case 'k': {
g_key_id.clear();
g_key_id.assign(optarg);
break;
}
case 's': {
g_license_server.clear();
g_license_server.assign(optarg);
break;
}
case 'v': {
// This option has already been consumed by wvcdm::InitLogging() above.
// We only tell getopt about it so that it is not an error. We ignore
// the option here when seen.
// TODO: Stop passing argv to InitLogging, and instead set the log
// level here through the logging API. We should keep all command-line
// parsing at the application level, rather than split between various
// apps and various platform-specific logging implementations.
break;
}
case '?': {
show_usage = 1;
break;
}
}
}
if (show_usage) {
std::cout << std::endl;
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout
<< "configure the license server url, please include http[s] in the url"
<< std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << license_server << std::endl;
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
std::cout << "configure the key id or pssh, in hex format" << std::endl;
std::cout << std::setw(30) << std::left << " default keyid:";
std::cout << g_key_id << std::endl;
return 0;
}
std::cout << std::endl;
std::cout << "Server: " << g_license_server << std::endl;
std::cout << "KeyID: " << g_key_id << std::endl << std::endl;
g_key_id = wvcdm::a2bs_hex(g_key_id);
config.set_license_server(g_license_server);
config.set_key_id(g_key_id);
return RUN_ALL_TESTS();
}

View File

@@ -1,6 +1,9 @@
// Copyright 2014 Google Inc. All Rights Reserved. // Copyright 2014 Google Inc. All Rights Reserved.
const char kDeviceCert[] = { #ifndef WVCDM_CDM_TEST_DEVICE_CERT_H_
#define WVCDM_CDM_TEST_DEVICE_CERT_H_
const uint8_t kDeviceCert[] = {
0x0A, 0x9A, 0x14, 0x08, 0x01, 0x10, 0x01, 0x1A, 0x0A, 0x9A, 0x14, 0x08, 0x01, 0x10, 0x01, 0x1A,
0x93, 0x14, 0x0A, 0xED, 0x09, 0x0A, 0xAE, 0x02, 0x93, 0x14, 0x0A, 0xED, 0x09, 0x0A, 0xAE, 0x02,
0x08, 0x02, 0x12, 0x10, 0x19, 0x77, 0x50, 0x4D, 0x08, 0x02, 0x12, 0x10, 0x19, 0x77, 0x50, 0x4D,
@@ -330,3 +333,5 @@ const char kDeviceCert[] = {
0xA6, 0x95, 0xCC, 0x54, 0x82, 0xB5, 0x8B, 0xF3, 0xA6, 0x95, 0xCC, 0x54, 0x82, 0xB5, 0x8B, 0xF3,
0xE6, 0xBD, 0xA1, 0xD8, 0xE8, 0x19, 0xB8, 0xE6, 0xBD, 0xA1, 0xD8, 0xE8, 0x19, 0xB8,
}; };
#endif // WVCDM_CDM_TEST_DEVICE_CERT_H_

View File

@@ -1,16 +0,0 @@
# Copyright 2013 Google Inc. All Rights Reserved.
{
'targets': [
{
'target_name': 'gtest',
'type': 'static_library',
'include_dirs': [
'../../third_party/gmock/gtest',
'../../third_party/gmock/gtest/include',
],
'sources': [
'../../third_party/gmock/gtest/src/gtest-all.cc',
],
},
],
}

134
cdm/test/test_host_1.cpp Normal file
View File

@@ -0,0 +1,134 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// Review the TestHost_1 class below to observe how the CDM interfaces with
// the host application.
#include "test_host_1.h"
#include <gtest/gtest.h>
#include <sys/time.h>
#include "test_util.h"
static double GetCurrentTime() {
struct timeval tv;
tv.tv_sec = tv.tv_usec = 0;
gettimeofday(&tv, NULL);
return tv.tv_sec + (tv.tv_usec / (1000.0 * 1000.0));
}
TestHost_1::TestHost_1()
: current_time_(GetCurrentTime()),
has_new_key_message_(false),
has_new_key_error_(false),
cdm_(NULL) {
}
TestHost_1::~TestHost_1() {
if (cdm_)
cdm_->Destroy();
}
cdm::Buffer* TestHost_1::Allocate(int32_t capacity) {
return TestBuffer::Create(capacity);
}
void TestHost_1::SetTimer(int64_t delay_ms, void* context) {
double expiry_time = current_time_ + (delay_ms / 1000.0);
timers_.push(Timer(expiry_time, context));
}
double TestHost_1::GetCurrentWallTimeInSeconds() {
return current_time_;
}
void TestHost_1::SendKeyMessage(const char* session_id,
int32_t session_id_length, const char* message,
int32_t message_length, const char* default_url,
int32_t default_url_length) {
KeyMessage key_message;
key_message.session_id.assign(session_id, session_id_length);
key_message.message.assign(message, message_length);
key_message.default_url.assign(default_url, default_url_length);
key_messages_.push_back(key_message);
has_new_key_message_ = true;
}
void TestHost_1::SendKeyError(const char* session_id, int32_t session_id_length,
cdm::MediaKeyError error_code,
uint32_t system_code) {
KeyError key_error;
key_error.session_id.assign(session_id, session_id_length);
key_error.error_code = error_code;
key_error.system_code = system_code;
key_errors_.push_back(key_error);
has_new_key_error_ = true;
}
void TestHost_1::FastForwardTime(double seconds) {
double goal_time = current_time_ + seconds;
while (current_time_ < goal_time) {
if (timers_.empty()) {
current_time_ = goal_time;
} else {
Timer t = timers_.top();
timers_.pop();
ASSERT_GE(t.expiry_time, current_time_);
current_time_ = t.expiry_time;
cdm_->TimerExpired(t.context);
}
}
}
void TestHost_1::GetPlatformString(const std::string& name,
std::string* value) {
*value = platform_strings_[name];
}
void TestHost_1::SetPlatformString(const std::string& name,
const std::string& value) {
platform_strings_[name] = value;
}
int TestHost_1::KeyMessagesSize() const { return key_messages_.size(); }
int TestHost_1::KeyErrorsSize() const { return key_errors_.size(); }
int TestHost_1::NumTimers() const { return timers_.size(); }
TestHost_1::KeyMessage TestHost_1::GetLastKeyMessage() {
if (!has_new_key_message_) {
return KeyMessage();
}
if (key_messages_.empty()) {
return KeyMessage();
}
has_new_key_message_ = false;
return key_messages_.back();
}
TestHost_1::KeyError TestHost_1::GetLastKeyError() {
if (!has_new_key_error_) return KeyError();
if (key_errors_.empty()) return KeyError();
has_new_key_error_ = false;
return key_errors_.back();
}
TestHost_1::KeyMessage TestHost_1::GetKeyMessage(int index) const {
return key_messages_[index];
}
TestHost_1::KeyError TestHost_1::GetKeyError(int index) const {
return key_errors_[index];
}
void TestHost_1::SetCdmPtr(cdm::ContentDecryptionModule_1* cdm) {
if (cdm_) {
cdm_->Destroy();
}
cdm_ = cdm;
}

105
cdm/test/test_host_1.h Normal file
View File

@@ -0,0 +1,105 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_TEST_TEST_HOST_1_H_
#define WVCDM_CDM_TEST_TEST_HOST_1_H_
#include "content_decryption_module.h"
#include <map>
#include <queue>
#include "wv_cdm_common.h"
#include "wv_cdm_types.h"
class TestHost_1 : public cdm::Host_1 {
public:
// These structs are used to store the KeyMessages and KeyErrors passed to
// this class' objects.
struct KeyMessage {
std::string session_id;
std::string message;
std::string default_url;
};
struct KeyError {
KeyError() : error_code(cdm::kUnknownError), system_code(0) {}
std::string session_id;
cdm::MediaKeyError error_code;
uint32_t system_code;
};
TestHost_1();
virtual ~TestHost_1();
// cdm::Host implementation.
virtual cdm::Buffer* Allocate(int32_t capacity) OVERRIDE;
virtual void SetTimer(int64_t delay_ms, void* context) OVERRIDE;
virtual double GetCurrentWallTimeInSeconds() OVERRIDE;
virtual void SendKeyMessage(const char* session_id, int32_t session_id_length,
const char* message, int32_t message_length,
const char* default_url,
int32_t default_url_length) OVERRIDE;
virtual void SendKeyError(const char* session_id, int32_t session_id_length,
cdm::MediaKeyError error_code,
uint32_t system_code) OVERRIDE;
virtual void GetPlatformString(const std::string& name,
std::string* value) OVERRIDE;
virtual void SetPlatformString(const std::string& name,
const std::string& value) OVERRIDE;
// Methods only for this test.
void FastForwardTime(double seconds);
int KeyMessagesSize() const;
int KeyErrorsSize() const;
int NumTimers() const;
// Returns Key{Message,Error} (replace Message with Error for KeyError). It
// returns the most recent message passed to SendKeyMessage(). Another call
// to this method without a new SendKeyMessage() call will return an empty
// KeyMessage struct.
KeyMessage GetLastKeyMessage();
KeyError GetLastKeyError();
KeyMessage GetKeyMessage(int index) const;
KeyError GetKeyError(int index) const;
void SetCdmPtr(cdm::ContentDecryptionModule_1* cdm);
private:
struct Timer {
Timer(double expiry_time, void* context)
: expiry_time(expiry_time), context(context) {}
bool operator<(const Timer& other) const {
// We want to reverse the order so that the smallest expiry times go to
// the top of the priority queue.
return expiry_time > other.expiry_time;
}
double expiry_time;
void* context;
};
double current_time_;
std::priority_queue<Timer> timers_;
std::vector<KeyMessage> key_messages_;
std::vector<KeyError> key_errors_;
bool has_new_key_message_;
bool has_new_key_error_;
std::map<std::string, std::string> platform_strings_;
cdm::ContentDecryptionModule_1* cdm_;
CORE_DISALLOW_COPY_AND_ASSIGN(TestHost_1);
};
#endif // WVCDM_CDM_TEST_TEST_HOST_1_H_

137
cdm/test/test_host_4.cpp Normal file
View File

@@ -0,0 +1,137 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// Review the TestHost_4 class below to observe how the CDM interfaces with
// the host application.
#include "test_host_4.h"
#include <gtest/gtest.h>
#include <sys/time.h>
#include "test_host_4_file_io.h"
#include "test_util.h"
static double GetCurrentTime() {
struct timeval tv;
tv.tv_sec = tv.tv_usec = 0;
gettimeofday(&tv, NULL);
return tv.tv_sec + (tv.tv_usec / (1000.0 * 1000.0));
}
TestHost_4::TestHost_4()
: current_time_(GetCurrentTime()),
has_new_session_message_(false),
has_new_session_error_(false),
cdm_(NULL) {}
TestHost_4::~TestHost_4() {
if (cdm_) cdm_->Destroy();
}
cdm::Buffer* TestHost_4::Allocate(uint32_t capacity) {
return TestBuffer::Create(capacity);
}
void TestHost_4::SetTimer(int64_t delay_ms, void* context) {
double expiry_time = current_time_ + (delay_ms / 1000.0);
timers_.push(Timer(expiry_time, context));
}
double TestHost_4::GetCurrentWallTimeInSeconds() { return current_time_; }
void TestHost_4::FastForwardTime(double seconds) {
double goal_time = current_time_ + seconds;
while (current_time_ < goal_time) {
if (timers_.empty()) {
current_time_ = goal_time;
} else {
Timer t = timers_.top();
timers_.pop();
ASSERT_GE(t.expiry_time, current_time_);
current_time_ = t.expiry_time;
cdm_->TimerExpired(t.context);
}
}
}
int TestHost_4::SessionMessagesSize() const { return session_messages_.size(); }
int TestHost_4::SessionErrorsSize() const { return session_errors_.size(); }
int TestHost_4::NumTimers() const { return timers_.size(); }
TestHost_4::SessionMessage TestHost_4::GetLastSessionMessage() {
if (!has_new_session_message_) {
return SessionMessage();
}
if (session_messages_.empty()) {
return SessionMessage();
}
has_new_session_message_ = false;
return session_messages_.back();
}
TestHost_4::SessionError TestHost_4::GetLastSessionError() {
if (!has_new_session_error_) return SessionError();
if (session_errors_.empty()) return SessionError();
has_new_session_error_ = false;
return session_errors_.back();
}
TestHost_4::SessionMessage TestHost_4::GetSessionMessage(int index) const {
return session_messages_[index];
}
TestHost_4::SessionError TestHost_4::GetSessionError(int index) const {
return session_errors_[index];
}
void TestHost_4::SetCdmPtr(cdm::ContentDecryptionModule_4* cdm) {
if (cdm_) {
cdm_->Destroy();
}
cdm_ = cdm;
}
void TestHost_4::OnSessionCreated(uint32_t session_id,
const char* web_session_id,
uint32_t web_session_id_length) {
std::string webid(web_session_id, web_session_id_length);
session_map[session_id] = webid; // keep a parallel map with cdm.
}
void TestHost_4::OnSessionMessage(uint32_t session_id, const char* message,
uint32_t message_length,
const char* destination_url,
uint32_t destination_url_length) {
SessionMessage session_message;
session_message.session_id = session_id;
session_message.message.assign(message, message_length);
session_message.default_url.assign(destination_url, destination_url_length);
session_messages_.push_back(session_message);
has_new_session_message_ = true;
}
void TestHost_4::OnSessionUpdated(uint32_t session_id) {}
void TestHost_4::OnSessionClosed(uint32_t session_id) {
session_map.erase(session_id);
}
void TestHost_4::OnSessionError(uint32_t session_id, cdm::Status error_code,
uint32_t system_code) {
SessionError session_error;
session_error.session_id = session_id;
session_error.error_code = error_code;
session_error.system_code = system_code;
session_errors_.push_back(session_error);
has_new_session_error_ = true;
}
cdm::FileIO* TestHost_4::CreateFileIO(cdm::FileIOClient* client) {
return new TestHost_4_FileIO(this, client);
}

108
cdm/test/test_host_4.h Normal file
View File

@@ -0,0 +1,108 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_TEST_TEST_HOST_4_H_
#define WVCDM_CDM_TEST_TEST_HOST_4_H_
#include "content_decryption_module.h"
#include "gmock/gmock.h"
#include <queue>
#include "wv_cdm_common.h"
#include "wv_cdm_types.h"
class TestHost_4 : public cdm::Host_4 {
public:
// These structs are used to store the SessionMessages and SessionErrors
// passed to this class' objects.
struct SessionMessage {
uint32_t session_id;
std::string message;
std::string default_url;
};
struct SessionError {
uint32_t session_id;
cdm::Status error_code;
uint32_t system_code;
};
TestHost_4();
virtual ~TestHost_4();
// cdm::Host implementation.
virtual cdm::Buffer* Allocate(uint32_t capacity) OVERRIDE;
virtual void SetTimer(int64_t delay_ms, void* context) OVERRIDE;
virtual double GetCurrentWallTimeInSeconds() OVERRIDE;
virtual void OnSessionCreated(uint32_t session_id, const char* web_session_id,
uint32_t web_session_id_length) OVERRIDE;
virtual void OnSessionMessage(uint32_t session_id, const char* message,
uint32_t message_length,
const char* destination_url,
uint32_t destination_url_length) OVERRIDE;
virtual void OnSessionUpdated(uint32_t session_id) OVERRIDE;
virtual void OnSessionClosed(uint32_t session_id) OVERRIDE;
virtual void OnSessionError(uint32_t session_id, cdm::Status error_code,
uint32_t system_code) OVERRIDE;
virtual cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) OVERRIDE;
// Methods only for this test.
void FastForwardTime(double seconds);
int SessionMessagesSize() const;
int SessionErrorsSize() const;
int NumTimers() const;
// Returns Key{Message,Error} (replace Message with Error for SessionError).
// It returns the most recent message passed to SendSessionMessage(). Another
// call to this method without a new SendSessionMessage() call will return an
// empty SessionMessage struct.
SessionMessage GetLastSessionMessage();
SessionError GetLastSessionError();
SessionMessage GetSessionMessage(int index) const;
SessionError GetSessionError(int index) const;
void SetCdmPtr(cdm::ContentDecryptionModule_4* cdm);
std::map<uint32_t, std::string> session_map;
// Accessed by all FileIO objects.
std::map<std::string, std::string> file_store;
private:
struct Timer {
Timer(double expiry_time, void* context)
: expiry_time(expiry_time), context(context) {}
bool operator<(const Timer& other) const {
// We want to reverse the order so that the smallest expiry times go to
// the top of the priority queue.
return expiry_time > other.expiry_time;
}
double expiry_time;
void* context;
};
double current_time_;
std::priority_queue<Timer> timers_;
std::vector<SessionMessage> session_messages_;
std::vector<SessionError> session_errors_;
bool has_new_session_message_;
bool has_new_session_error_;
cdm::ContentDecryptionModule_4* cdm_;
CORE_DISALLOW_COPY_AND_ASSIGN(TestHost_4);
};
#endif // WVCDM_CDM_TEST_TEST_HOST_4_H_

View File

@@ -0,0 +1,31 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// Review the TestHost_4 class below to observe how the CDM interfaces with
// the host application.
#include "test_host_4_file_io.h"
#include <gtest/gtest.h>
void TestHost_4_FileIO::Open(const char* file_name, uint32_t file_name_size) {
ASSERT_EQ(0, file_name_.size());
file_name_.assign(file_name, file_name_size);
client_->OnOpenComplete(cdm::FileIOClient::kSuccess);
}
void TestHost_4_FileIO::Read() {
ASSERT_NE(0, file_name_.size());
const std::string& data = host_->file_store[file_name_];
client_->OnReadComplete(cdm::FileIOClient::kSuccess,
reinterpret_cast<const uint8_t*>(data.data()),
data.size());
}
void TestHost_4_FileIO::Write(const uint8_t* data, uint32_t data_size) {
ASSERT_NE(0, file_name_.size());
host_->file_store[file_name_].assign(reinterpret_cast<const char*>(data),
data_size);
client_->OnWriteComplete(cdm::FileIOClient::kSuccess);
}
void TestHost_4_FileIO::Close() { delete this; }

View File

@@ -0,0 +1,34 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_TEST_TEST_HOST_4_FILE_IO_H_
#define WVCDM_CDM_TEST_TEST_HOST_4_FILE_IO_H_
#include <string>
#include "content_decryption_module.h"
#include "test_host_4.h"
#include "wv_cdm_common.h"
#include "wv_cdm_types.h"
class TestHost_4_FileIO : public cdm::FileIO {
public:
TestHost_4_FileIO(TestHost_4* host, cdm::FileIOClient* client)
: host_(host), client_(client) {}
virtual ~TestHost_4_FileIO() {}
// cdm::FileIO implementation.
virtual void Open(const char* file_name, uint32_t file_name_size) OVERRIDE;
virtual void Read() OVERRIDE;
virtual void Write(const uint8_t* data, uint32_t data_size) OVERRIDE;
virtual void Close() OVERRIDE;
private:
TestHost_4* host_;
cdm::FileIOClient* client_;
std::string file_name_;
CORE_DISALLOW_COPY_AND_ASSIGN(TestHost_4_FileIO);
};
#endif // WVCDM_CDM_TEST_TEST_HOST_4_FILE_IO_H_

52
cdm/test/test_util.cpp Normal file
View File

@@ -0,0 +1,52 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "test_util.h"
TestBuffer* TestBuffer::Create(uint32_t capacity) {
return new TestBuffer(capacity);
}
void TestBuffer::Destroy() {
delete this;
}
int32_t TestBuffer::Capacity() const { return capacity_; }
uint8_t* TestBuffer::Data() { return buffer_; }
void TestBuffer::SetSize(int32_t size) { size_ = size; }
int32_t TestBuffer::Size() const { return size_; }
TestBuffer::TestBuffer(uint32_t capacity)
: buffer_(new uint8_t[capacity]),
capacity_(capacity) {}
TestBuffer::~TestBuffer() {
if (buffer_) {
delete[] buffer_;
buffer_ = NULL;
}
}
TestDecryptedBlock::TestDecryptedBlock() : buffer_(NULL), timestamp_(0) {}
TestDecryptedBlock::~TestDecryptedBlock() {
if (buffer_) {
buffer_->Destroy();
buffer_ = NULL;
}
}
void TestDecryptedBlock::SetDecryptedBuffer(cdm::Buffer* buffer) {
if (buffer_) buffer_->Destroy();
buffer_ = buffer;
}
cdm::Buffer* TestDecryptedBlock::DecryptedBuffer() { return buffer_; }
void TestDecryptedBlock::SetTimestamp(int64_t timestamp) {
timestamp_ = timestamp;
}
int64_t TestDecryptedBlock::Timestamp() const { return timestamp_; }

58
cdm/test/test_util.h Normal file
View File

@@ -0,0 +1,58 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef WVCDM_CDM_TEST_TEST_UTIL_H_
#define WVCDM_CDM_TEST_TEST_UTIL_H_
#include "content_decryption_module.h"
#include "wv_cdm_common.h"
#include "wv_cdm_types.h"
// These classes below are naive implementation of the abstract classes defined
// in the CDM interface (content_decryptiom_module.h), which are used for tests
// only.
class TestBuffer : public cdm::Buffer {
public:
static TestBuffer* Create(uint32_t capacity);
virtual void Destroy() OVERRIDE;
virtual int32_t Capacity() const OVERRIDE;
virtual uint8_t* Data() OVERRIDE;
virtual void SetSize(int32_t size) OVERRIDE;
virtual int32_t Size() const OVERRIDE;
private:
// TestBuffer can only be created by calling Create().
explicit TestBuffer(uint32_t capacity);
// TestBuffer can only be destroyed by calling Destroy().
virtual ~TestBuffer();
uint8_t* buffer_;
int32_t capacity_;
int32_t size_;
CORE_DISALLOW_COPY_AND_ASSIGN(TestBuffer);
};
class TestDecryptedBlock : public cdm::DecryptedBlock {
public:
TestDecryptedBlock();
virtual ~TestDecryptedBlock();
virtual void SetDecryptedBuffer(cdm::Buffer* buffer) OVERRIDE;
virtual cdm::Buffer* DecryptedBuffer() OVERRIDE;
virtual void SetTimestamp(int64_t timestamp) OVERRIDE;
virtual int64_t Timestamp() const OVERRIDE;
private:
cdm::Buffer* buffer_;
int64_t timestamp_;
CORE_DISALLOW_COPY_AND_ASSIGN(TestDecryptedBlock);
};
#endif // WVCDM_CDM_TEST_TEST_UTIL_H_

View File

@@ -24,13 +24,13 @@ namespace wvcdm {
class BufferReader { class BufferReader {
public: public:
BufferReader(const uint8_t* buf, size_t size) BufferReader(const uint8_t* buf, size_t size)
: buf_(buf), size_(size), pos_(0) {} : buf_(buf), size_(size), pos_(0) {}
bool HasBytes(int count) { return (pos() + count <= size()); } bool HasBytes(int count) { return (pos() + count <= size()); }
// Read a value from the stream, performing endian correction, // Read a value from the stream, performing endian correction,
// and advance the stream pointer. // and advance the stream pointer.
bool Read1(uint8_t* v) WARN_UNUSED_RESULT; bool Read1(uint8_t* v) WARN_UNUSED_RESULT;
bool Read2(uint16_t* v) WARN_UNUSED_RESULT; bool Read2(uint16_t* v) WARN_UNUSED_RESULT;
bool Read2s(int16_t* v) WARN_UNUSED_RESULT; bool Read2s(int16_t* v) WARN_UNUSED_RESULT;
bool Read4(uint32_t* v) WARN_UNUSED_RESULT; bool Read4(uint32_t* v) WARN_UNUSED_RESULT;
@@ -58,7 +58,8 @@ class BufferReader {
size_t size_; size_t size_;
size_t pos_; size_t pos_;
template<typename T> bool Read(T* t) WARN_UNUSED_RESULT; template <typename T>
bool Read(T* t) WARN_UNUSED_RESULT;
CORE_DISALLOW_COPY_AND_ASSIGN(BufferReader); CORE_DISALLOW_COPY_AND_ASSIGN(BufferReader);
}; };

View File

@@ -3,7 +3,6 @@
#ifndef WVCDM_CORE_CDM_ENGINE_H_ #ifndef WVCDM_CORE_CDM_ENGINE_H_
#define WVCDM_CORE_CDM_ENGINE_H_ #define WVCDM_CORE_CDM_ENGINE_H_
#include "cdm_session.h"
#include "certificate_provisioning.h" #include "certificate_provisioning.h"
#include "initialization_data.h" #include "initialization_data.h"
#include "oemcrypto_adapter.h" #include "oemcrypto_adapter.h"
@@ -13,6 +12,7 @@
namespace wvcdm { namespace wvcdm {
class CdmClientPropertySet; class CdmClientPropertySet;
class CdmSession;
class CryptoEngine; class CryptoEngine;
class WvCdmEventListener; class WvCdmEventListener;
@@ -34,12 +34,33 @@ class CdmEngine {
virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id); virtual CdmResponseType CloseKeySetSession(const CdmKeySetId& key_set_id);
// License related methods // License related methods
// Construct a valid license request
// Construct a valid license request. The arguments are used as follows:
// session_id: The Session ID of the session the request is being generated
// for. This is ignored for license release requests.
// key_set_id: The Key Set ID of the key set the request is being generated
// for. This is ignored except for license release requests.
// init_data: The initialization data from the media file, which is used to
// build the key request. This is ignored for release and renewal
// requests.
// license_type: The type of license being requested. Never ignored.
// app_parameters: Additional, application-specific parameters that factor
// into the request generation. This is ignored for release
// and renewal requests.
// key_request: This must be non-null and point to a CdmKeyMessage. The buffer
// will have its contents replaced with the key request.
// server_url: This must be non-null and point to a string. The string will
// have its contents replaced with the default URL (if one is
// known) to send this key request to.
// key_set_id_out: May be null. If it is non-null, the CdmKeySetId pointed to
// will have its contents replaced with the key set ID of the
// session. Note that for non-offline license requests, the
// key set ID is empty, so the CdmKeySetId will be cleared.
virtual CdmResponseType GenerateKeyRequest( virtual CdmResponseType GenerateKeyRequest(
const CdmSessionId& session_id, const CdmKeySetId& key_set_id, const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const InitializationData& init_data, const CdmLicenseType license_type, const InitializationData& init_data, const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url); std::string* server_url, CdmKeySetId* key_set_id_out);
// Accept license response and extract key info. // Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmSessionId& session_id, virtual CdmResponseType AddKey(const CdmSessionId& session_id,

View File

@@ -36,7 +36,7 @@ class CdmSession {
virtual CdmResponseType GenerateKeyRequest( virtual CdmResponseType GenerateKeyRequest(
const InitializationData& init_data, const CdmLicenseType license_type, const InitializationData& init_data, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url); std::string* server_url, CdmKeySetId* key_set_id);
// AddKey() - Accept license response and extract key info. // AddKey() - Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmKeyResponse& key_response, virtual CdmResponseType AddKey(const CdmKeyResponse& key_response,
@@ -81,7 +81,8 @@ class CdmSession {
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
virtual SecurityLevel GetRequestedSecurityLevel() { virtual SecurityLevel GetRequestedSecurityLevel() {
return requested_security_level_; } return requested_security_level_;
}
virtual CdmSecurityLevel GetSecurityLevel() { return security_level_; } virtual CdmSecurityLevel GetSecurityLevel() { return security_level_; }
virtual CdmResponseType UpdateUsageInformation(); virtual CdmResponseType UpdateUsageInformation();

View File

@@ -31,8 +31,7 @@ class CertificateProvisioning {
CdmProvisioningRequest* request); CdmProvisioningRequest* request);
bool ParseJsonResponse(const CdmProvisioningResponse& json_str, bool ParseJsonResponse(const CdmProvisioningResponse& json_str,
const std::string& start_substr, const std::string& start_substr,
const std::string& end_substr, const std::string& end_substr, std::string* result);
std::string* result);
CryptoSession crypto_session_; CryptoSession crypto_session_;
CdmCertificateType cert_type_; CdmCertificateType cert_type_;

View File

@@ -11,7 +11,6 @@ namespace wvcdm {
// Provides time related information. The implementation is platform dependent. // Provides time related information. The implementation is platform dependent.
class Clock { class Clock {
public: public:
Clock() {} Clock() {}
virtual ~Clock() {} virtual ~Clock() {}

View File

@@ -8,7 +8,7 @@
namespace wvcdm { namespace wvcdm {
class CryptoKey { class CryptoKey {
public: public:
CryptoKey() {}; CryptoKey() {};
~CryptoKey() {}; ~CryptoKey() {};
@@ -27,7 +27,7 @@ public:
bool HasKeyControl() const { return key_control_.size() >= 16; } bool HasKeyControl() const { return key_control_.size() >= 16; }
private: private:
std::string key_id_; std::string key_id_;
std::string key_data_iv_; std::string key_data_iv_;
std::string key_data_; std::string key_data_;
@@ -35,6 +35,6 @@ private:
std::string key_control_iv_; std::string key_control_iv_;
}; };
}; // namespace wvcdm }; // namespace wvcdm
#endif // WVCDM_CORE_CRYPTO_KEY_H_ #endif // WVCDM_CORE_CRYPTO_KEY_H_

View File

@@ -18,6 +18,17 @@ typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
class CryptoSession { class CryptoSession {
public: public:
// This enum should be kept in sync with the values specified for
// HDCP capabilities in OEMCryptoCENC.h. (See comments for
// OEMCrypto_GetHDCPCapability)
typedef enum {
kOemCryptoHdcpNotSupported = 0,
kOemCryptoHdcpVersion1 = 1,
kOemCryptoHdcpVersion2 = 2,
kOemCryptoHdcpVersion2_1 = 3,
kOemCryptoHdcpVersion2_2 = 4,
kOemCryptoNoHdcpDeviceAttached = 0xff,
} OemCryptoHdcpVersion;
CryptoSession(); CryptoSession();
virtual ~CryptoSession(); virtual ~CryptoSession();
@@ -37,7 +48,7 @@ class CryptoSession {
virtual CryptoSessionId oec_session_id() { return oec_session_id_; } virtual CryptoSessionId oec_session_id() { return oec_session_id_; }
// Key request/response // Key request/response
virtual void GenerateRequestId(std::string& req_id_str); virtual bool GenerateRequestId(std::string* req_id_str);
virtual bool PrepareRequest(const std::string& key_deriv_message, virtual bool PrepareRequest(const std::string& key_deriv_message,
bool is_provisioning, std::string* signature); bool is_provisioning, std::string* signature);
virtual bool PrepareRenewalRequest(const std::string& message, virtual bool PrepareRenewalRequest(const std::string& message,
@@ -66,6 +77,7 @@ class CryptoSession {
// Media data path // Media data path
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters); virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
virtual bool UsageInformationSupport(bool* has_support);
virtual CdmResponseType UpdateUsageInformation(); virtual CdmResponseType UpdateUsageInformation();
virtual CdmResponseType GenerateUsageReport( virtual CdmResponseType GenerateUsageReport(
const std::string& provider_session_token, std::string* usage_report); const std::string& provider_session_token, std::string* usage_report);
@@ -73,6 +85,8 @@ class CryptoSession {
const std::string& message, const std::string& signature, const std::string& message, const std::string& signature,
const std::string& provider_session_token); const std::string& provider_session_token);
virtual bool GetHdcpCapabilities(OemCryptoHdcpVersion* current,
OemCryptoHdcpVersion* max);
virtual bool GetRandom(size_t data_length, uint8_t* random_data); virtual bool GetRandom(size_t data_length, uint8_t* random_data);
private: private:

View File

@@ -27,7 +27,7 @@ class DeviceFiles {
virtual bool Init(CdmSecurityLevel security_level); virtual bool Init(CdmSecurityLevel security_level);
virtual bool Reset(CdmSecurityLevel security_level) { virtual bool Reset(CdmSecurityLevel security_level) {
return Init(security_level); return Init(security_level);
} }
virtual bool StoreCertificate(const std::string& certificate, virtual bool StoreCertificate(const std::string& certificate,
@@ -44,8 +44,7 @@ class DeviceFiles {
const CdmKeyResponse& key_renewal_response, const CdmKeyResponse& key_renewal_response,
const std::string& release_server_url); const std::string& release_server_url);
virtual bool RetrieveLicense(const std::string& key_set_id, virtual bool RetrieveLicense(const std::string& key_set_id,
LicenseState* state, LicenseState* state, CdmInitData* pssh_data,
CdmInitData* pssh_data,
CdmKeyMessage* key_request, CdmKeyMessage* key_request,
CdmKeyResponse* key_response, CdmKeyResponse* key_response,
CdmKeyMessage* key_renewal_request, CdmKeyMessage* key_renewal_request,
@@ -55,6 +54,7 @@ class DeviceFiles {
virtual bool DeleteAllFiles(); virtual bool DeleteAllFiles();
virtual bool DeleteAllLicenses(); virtual bool DeleteAllLicenses();
virtual bool LicenseExists(const std::string& key_set_id); virtual bool LicenseExists(const std::string& key_set_id);
virtual bool ReserveLicenseId(const std::string& key_set_id);
virtual bool StoreUsageInfo(const std::string& provider_session_token, virtual bool StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request, const CdmKeyMessage& key_request,
@@ -65,8 +65,9 @@ class DeviceFiles {
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info); std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info);
private: private:
bool StoreFile(const char* name, const std::string& serialized_file); bool StoreFileWithHash(const char* name, const std::string& serialized_file);
bool RetrieveFile(const char* name, std::string* serialized_file); bool StoreFileRaw(const char* name, const std::string& serialized_file);
bool RetrieveHashedFile(const char* name, std::string* serialized_file);
// Certificate and offline licenses are now stored in security // Certificate and offline licenses are now stored in security
// level specific directories. In an earlier version they were // level specific directories. In an earlier version they were
@@ -77,6 +78,7 @@ class DeviceFiles {
static std::string GetCertificateFileName(); static std::string GetCertificateFileName();
static std::string GetLicenseFileNameExtension(); static std::string GetLicenseFileNameExtension();
static std::string GetUsageInfoFileName(); static std::string GetUsageInfoFileName();
static std::string GetBlankFileData();
void SetTestFile(File* file); void SetTestFile(File* file);
#if defined(UNIT_TEST) #if defined(UNIT_TEST)
FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel); FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel);
@@ -84,6 +86,7 @@ class DeviceFiles {
FRIEND_TEST(DeviceFilesStoreTest, StoreLicense); FRIEND_TEST(DeviceFilesStoreTest, StoreLicense);
FRIEND_TEST(DeviceFilesTest, DeleteLicense); FRIEND_TEST(DeviceFilesTest, DeleteLicense);
FRIEND_TEST(DeviceFilesTest, ReadCertificate); FRIEND_TEST(DeviceFilesTest, ReadCertificate);
FRIEND_TEST(DeviceFilesTest, ReserveLicenseIds);
FRIEND_TEST(DeviceFilesTest, RetrieveLicenses); FRIEND_TEST(DeviceFilesTest, RetrieveLicenses);
FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility); FRIEND_TEST(DeviceFilesTest, SecurityLevelPathBackwardCompatibility);
FRIEND_TEST(DeviceFilesTest, StoreLicenses); FRIEND_TEST(DeviceFilesTest, StoreLicenses);

View File

@@ -44,7 +44,7 @@ class File {
virtual ssize_t FileSize(const std::string& file_path); virtual ssize_t FileSize(const std::string& file_path);
private: private:
Impl *impl_; Impl* impl_;
CORE_DISALLOW_COPY_AND_ASSIGN(File); CORE_DISALLOW_COPY_AND_ASSIGN(File);
}; };

View File

@@ -13,7 +13,7 @@ class WvCdmEngineTest;
class InitializationData { class InitializationData {
public: public:
InitializationData(const std::string& type, InitializationData(const std::string& type = std::string(),
const CdmInitData& data = CdmInitData()); const CdmInitData& data = CdmInitData());
bool is_supported() const { return is_cenc_ || is_webm_; } bool is_supported() const { return is_cenc_ || is_webm_; }

View File

@@ -6,6 +6,7 @@
#include <set> #include <set>
#include "initialization_data.h" #include "initialization_data.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace video_widevine_server { namespace video_widevine_server {
@@ -16,39 +17,44 @@ class SignedMessage;
namespace wvcdm { namespace wvcdm {
class Clock;
class CryptoSession; class CryptoSession;
class PolicyEngine; class PolicyEngine;
class CdmLicense { class CdmLicense {
public: public:
CdmLicense();
CdmLicense() : session_(NULL), initialized_(false) {} virtual ~CdmLicense();
virtual ~CdmLicense() {}
virtual bool Init(const std::string& token, CryptoSession* session, virtual bool Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine); PolicyEngine* policy_engine);
virtual bool PrepareKeyRequest(const InitializationData& init_data, virtual bool PrepareKeyRequest(const InitializationData& init_data,
const CdmLicenseType license_type, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, const CdmAppParameterMap& app_parameters,
const CdmSessionId& session_id, const CdmSessionId& session_id,
CdmKeyMessage* signed_request, CdmKeyMessage* signed_request,
std::string* server_url); std::string* server_url);
virtual bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request, virtual bool PrepareKeyUpdateRequest(bool is_renewal,
std::string* server_url); CdmKeyMessage* signed_request,
virtual CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response); std::string* server_url);
virtual CdmResponseType HandleKeyResponse(
const CdmKeyResponse& license_response);
virtual CdmResponseType HandleKeyUpdateResponse( virtual CdmResponseType HandleKeyUpdateResponse(
bool is_renewal, const CdmKeyResponse& license_response); bool is_renewal, const CdmKeyResponse& license_response);
virtual bool RestoreOfflineLicense(const CdmKeyMessage& license_request, virtual bool RestoreOfflineLicense(
const CdmKeyResponse& license_response, const CdmKeyMessage& license_request,
const CdmKeyResponse& license_renewal_response); const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response);
virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request, virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response); const CdmKeyResponse& license_response);
virtual bool HasInitData() { return !stored_init_data_.empty(); } virtual bool HasInitData() { return !stored_init_data_.empty(); }
virtual bool IsKeyLoaded(const KeyId& key_id); virtual bool IsKeyLoaded(const KeyId& key_id);
virtual std::string provider_session_token() { return provider_session_token_; } virtual std::string provider_session_token() {
return provider_session_token_;
}
private: private:
bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
@@ -59,9 +65,9 @@ class CdmLicense {
CdmResponseType HandleKeyErrorResponse( CdmResponseType HandleKeyErrorResponse(
const video_widevine_server::sdk::SignedMessage& signed_message); const video_widevine_server::sdk::SignedMessage& signed_message);
template<typename T> bool PrepareContentId(const CdmLicenseType license_type, template <typename T>
const std::string& request_id, bool PrepareContentId(const CdmLicenseType license_type,
T* content_id); const std::string& request_id, T* content_id);
CryptoSession* session_; CryptoSession* session_;
PolicyEngine* policy_engine_; PolicyEngine* policy_engine_;
@@ -76,6 +82,14 @@ class CdmLicense {
// Used for certificate based licensing // Used for certificate based licensing
CdmKeyMessage key_request_; CdmKeyMessage key_request_;
scoped_ptr<Clock> clock_;
// For testing
CdmLicense(Clock* clock); // CdmLicense takes ownership of the clock.
#if defined(UNIT_TEST)
friend class CdmLicenseTest;
#endif
CORE_DISALLOW_COPY_AND_ASSIGN(CdmLicense); CORE_DISALLOW_COPY_AND_ASSIGN(CdmLicense);
}; };

View File

@@ -29,7 +29,7 @@ class Lock {
private: private:
class Impl; class Impl;
Impl *impl_; Impl* impl_;
CORE_DISALLOW_COPY_AND_ASSIGN(Lock); CORE_DISALLOW_COPY_AND_ASSIGN(Lock);
}; };
@@ -38,20 +38,14 @@ class Lock {
// is constructed and release when AutoLock goes out of scope. // is constructed and release when AutoLock goes out of scope.
class AutoLock { class AutoLock {
public: public:
explicit AutoLock(Lock& lock) : lock_(&lock) { explicit AutoLock(Lock& lock) : lock_(&lock) { lock_->Acquire(); }
lock_->Acquire();
}
explicit AutoLock(Lock* lock) : lock_(lock) { explicit AutoLock(Lock* lock) : lock_(lock) { lock_->Acquire(); }
lock_->Acquire();
}
~AutoLock() { ~AutoLock() { lock_->Release(); }
lock_->Release();
}
private: private:
Lock *lock_; Lock* lock_;
CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock); CORE_DISALLOW_COPY_AND_ASSIGN(AutoLock);
}; };

View File

@@ -7,10 +7,7 @@
namespace wvcdm { namespace wvcdm {
enum SecurityLevel { enum SecurityLevel { kLevelDefault, kLevel3 };
kLevelDefault,
kLevel3
};
/* This attempts to open a session at the desired security level. /* This attempts to open a session at the desired security level.
If one level is not available, the other will be used instead. */ If one level is not available, the other will be used instead. */

View File

@@ -52,10 +52,9 @@ class RsaPublicKey {
// Encrypt a message using RSA-OAEP. Caller retains ownership of all // Encrypt a message using RSA-OAEP. Caller retains ownership of all
// parameters. Returns true if successful, false otherwise. // parameters. Returns true if successful, false otherwise.
bool Encrypt(const std::string& plaintext, bool Encrypt(const std::string& plaintext, std::string* ciphertext);
std::string* ciphertext);
// Verify RSSASSA-PSS signature. Caller retains ownership of all parameters. // Verify RSASSA-PSS signature. Caller retains ownership of all parameters.
// Returns true if validation succeeds, false otherwise. // Returns true if validation succeeds, false otherwise.
bool VerifySignature(const std::string& message, bool VerifySignature(const std::string& message,
const std::string& signature); const std::string& signature);

View File

@@ -11,6 +11,10 @@
#include "scoped_ptr.h" #include "scoped_ptr.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
#if defined(UNIT_TEST)
# include "gtest/gtest_prod.h"
#endif
namespace wvcdm { namespace wvcdm {
typedef std::map<CdmSessionId, const CdmClientPropertySet*> typedef std::map<CdmSessionId, const CdmClientPropertySet*>
@@ -93,6 +97,7 @@ class Properties {
FRIEND_TEST(CdmSessionTest, ReInitFail); FRIEND_TEST(CdmSessionTest, ReInitFail);
FRIEND_TEST(CdmSessionTest, InitFailCryptoError); FRIEND_TEST(CdmSessionTest, InitFailCryptoError);
FRIEND_TEST(CdmSessionTest, InitNeedsProvisioning); FRIEND_TEST(CdmSessionTest, InitNeedsProvisioning);
FRIEND_TEST(CdmLicenseTest, PrepareKeyRequestValidation);
#endif #endif
private: private:

View File

@@ -53,6 +53,7 @@ class scoped_ptr {
ptr_ = p; ptr_ = p;
} }
} }
private: private:
T* ptr_; T* ptr_;

View File

@@ -17,7 +17,8 @@ bool BufferReader::Read1(uint8_t* v) {
} }
// Internal implementation of multi-byte reads // Internal implementation of multi-byte reads
template<typename T> bool BufferReader::Read(T* v) { template <typename T>
bool BufferReader::Read(T* v) {
if (!HasBytes(sizeof(T))) { if (!HasBytes(sizeof(T))) {
LOGW("BufferReader::Read<T> : Failure during parse: Not enough bytes (%u)", LOGW("BufferReader::Read<T> : Failure during parse: Not enough bytes (%u)",
sizeof(T)); sizeof(T));

View File

@@ -19,8 +19,8 @@
#include "wv_cdm_event_listener.h" #include "wv_cdm_event_listener.h"
namespace { namespace {
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
const size_t kUsageReportsPerRequest = 1; const size_t kUsageReportsPerRequest = 1;
} // unnamed namespace } // unnamed namespace
namespace wvcdm { namespace wvcdm {
@@ -47,10 +47,9 @@ CdmEngine::~CdmEngine() {
sessions_.clear(); sessions_.clear();
} }
CdmResponseType CdmEngine::OpenSession( CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
const CdmKeySystem& key_system, const CdmClientPropertySet* property_set,
const CdmClientPropertySet* property_set, CdmSessionId* session_id) {
CdmSessionId* session_id) {
LOGI("CdmEngine::OpenSession"); LOGI("CdmEngine::OpenSession");
if (!ValidateKeySystem(key_system)) { if (!ValidateKeySystem(key_system)) {
@@ -95,8 +94,7 @@ CdmResponseType CdmEngine::OpenKeySetSession(const CdmKeySetId& key_set_id) {
CdmSessionId session_id; CdmSessionId session_id;
CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id); CdmResponseType sts = OpenSession(KEY_SYSTEM, NULL, &session_id);
if (sts != NO_ERROR) if (sts != NO_ERROR) return sts;
return sts;
release_key_sets_[key_set_id] = session_id; release_key_sets_[key_set_id] = session_id;
return NO_ERROR; return NO_ERROR;
@@ -123,7 +121,7 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id); CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) { if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s", LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
key_set_id.c_str()); key_set_id.c_str());
return KEY_ERROR; return KEY_ERROR;
} }
@@ -133,13 +131,10 @@ CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
} }
CdmResponseType CdmEngine::GenerateKeyRequest( CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id, const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const CdmKeySetId& key_set_id, const InitializationData& init_data, const CdmLicenseType license_type,
const InitializationData& init_data, CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
const CdmLicenseType license_type, std::string* server_url, CdmKeySetId* key_set_id_out) {
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
std::string* server_url) {
LOGI("CdmEngine::GenerateKeyRequest"); LOGI("CdmEngine::GenerateKeyRequest");
CdmSessionId id = session_id; CdmSessionId id = session_id;
@@ -153,14 +148,14 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
if (!session_id.empty()) { if (!session_id.empty()) {
LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s", LOGE("CdmEngine::GenerateKeyRequest: invalid session ID = %s",
session_id.c_str()); session_id.c_str());
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id); CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) { if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s", LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
key_set_id.c_str()); key_set_id.c_str());
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
@@ -170,7 +165,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
CdmSessionMap::iterator iter = sessions_.find(id); CdmSessionMap::iterator iter = sessions_.find(id);
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s",
id.c_str()); id.c_str());
return KEY_ERROR; return KEY_ERROR;
} }
@@ -184,23 +179,27 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
if (license_type == kLicenseTypeRelease) { if (license_type == kLicenseTypeRelease) {
sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease); sts = iter->second->RestoreOfflineSession(key_set_id, kLicenseTypeRelease);
if (sts != KEY_ADDED) { if (sts != KEY_ADDED) {
LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed," LOGE(
"sts = %d", (int)sts); "CdmEngine::GenerateKeyRequest: key release restoration failed,"
"sts = %d",
(int)sts);
return sts; return sts;
} }
} }
sts = iter->second->GenerateKeyRequest(init_data, license_type, sts = iter->second->GenerateKeyRequest(
app_parameters, key_request, init_data, license_type, app_parameters, key_request, server_url,
server_url); key_set_id_out);
if (KEY_MESSAGE != sts) { if (KEY_MESSAGE != sts) {
if (sts == NEED_PROVISIONING) { if (sts == NEED_PROVISIONING) {
cert_provisioning_requested_security_level_ = cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel(); iter->second->GetRequestedSecurityLevel();
} }
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, " LOGE(
"sts = %d", (int)sts); "CdmEngine::GenerateKeyRequest: key request generation failed, "
"sts = %d",
(int)sts);
return sts; return sts;
} }
@@ -211,10 +210,9 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
return KEY_MESSAGE; return KEY_MESSAGE;
} }
CdmResponseType CdmEngine::AddKey( CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
const CdmSessionId& session_id, const CdmKeyResponse& key_data,
const CdmKeyResponse& key_data, CdmKeySetId* key_set_id) {
CdmKeySetId* key_set_id) {
LOGI("CdmEngine::AddKey"); LOGI("CdmEngine::AddKey");
CdmSessionId id = session_id; CdmSessionId id = session_id;
@@ -262,9 +260,8 @@ CdmResponseType CdmEngine::AddKey(
return KEY_ADDED; return KEY_ADDED;
} }
CdmResponseType CdmEngine::RestoreKey( CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id,
const CdmSessionId& session_id, const CdmKeySetId& key_set_id) {
const CdmKeySetId& key_set_id) {
LOGI("CdmEngine::RestoreKey"); LOGI("CdmEngine::RestoreKey");
if (key_set_id.empty()) { if (key_set_id.empty()) {
@@ -275,7 +272,7 @@ CdmResponseType CdmEngine::RestoreKey(
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
LOGE("CdmEngine::RestoreKey: session_id not found = %s ", LOGE("CdmEngine::RestoreKey: session_id not found = %s ",
session_id.c_str()); session_id.c_str());
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
@@ -285,6 +282,9 @@ CdmResponseType CdmEngine::RestoreKey(
cert_provisioning_requested_security_level_ = cert_provisioning_requested_security_level_ =
iter->second->GetRequestedSecurityLevel(); iter->second->GetRequestedSecurityLevel();
} }
if (sts != KEY_ADDED) {
LOGE("CdmEngine::RestoreKey: restore offline session failed = %d", sts);
}
return sts; return sts;
} }
@@ -303,8 +303,7 @@ CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) {
} }
CdmResponseType CdmEngine::GenerateRenewalRequest( CdmResponseType CdmEngine::GenerateRenewalRequest(
const CdmSessionId& session_id, const CdmSessionId& session_id, CdmKeyMessage* key_request,
CdmKeyMessage* key_request,
std::string* server_url) { std::string* server_url) {
LOGI("CdmEngine::GenerateRenewalRequest"); LOGI("CdmEngine::GenerateRenewalRequest");
@@ -322,8 +321,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
key_request->clear(); key_request->clear();
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request, CdmResponseType sts =
server_url); iter->second->GenerateRenewalRequest(key_request, server_url);
if (KEY_MESSAGE != sts) { if (KEY_MESSAGE != sts) {
LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d", LOGE("CdmEngine::GenerateRenewalRequest: key request gen. failed, sts=%d",
@@ -334,9 +333,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest(
return KEY_MESSAGE; return KEY_MESSAGE;
} }
CdmResponseType CdmEngine::RenewKey( CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id,
const CdmSessionId& session_id, const CdmKeyResponse& key_data) {
const CdmKeyResponse& key_data) {
LOGI("CdmEngine::RenewKey"); LOGI("CdmEngine::RenewKey");
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
@@ -416,9 +414,8 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id,
return iter->second->QueryStatus(key_info); return iter->second->QueryStatus(key_info);
} }
CdmResponseType CdmEngine::QueryKeyStatus( CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id,
const CdmSessionId& session_id, CdmQueryMap* key_info) {
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyStatus"); LOGI("CdmEngine::QueryKeyStatus");
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
@@ -429,9 +426,8 @@ CdmResponseType CdmEngine::QueryKeyStatus(
return iter->second->QueryKeyStatus(key_info); return iter->second->QueryKeyStatus(key_info);
} }
CdmResponseType CdmEngine::QueryKeyControlInfo( CdmResponseType CdmEngine::QueryKeyControlInfo(const CdmSessionId& session_id,
const CdmSessionId& session_id, CdmQueryMap* key_info) {
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyControlInfo"); LOGI("CdmEngine::QueryKeyControlInfo");
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
@@ -450,20 +446,15 @@ CdmResponseType CdmEngine::QueryKeyControlInfo(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails. * Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/ */
CdmResponseType CdmEngine::GetProvisioningRequest( CdmResponseType CdmEngine::GetProvisioningRequest(
CdmCertificateType cert_type, CdmCertificateType cert_type, const std::string& cert_authority,
const std::string& cert_authority, CdmProvisioningRequest* request, std::string* default_url) {
CdmProvisioningRequest* request,
std::string* default_url) {
if (!request || !default_url) { if (!request || !default_url) {
LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters"); LOGE("CdmEngine::GetProvisioningRequest: invalid input parameters");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
return cert_provisioning_.GetProvisioningRequest( return cert_provisioning_.GetProvisioningRequest(
cert_provisioning_requested_security_level_, cert_provisioning_requested_security_level_, cert_type, cert_authority,
cert_type, request, default_url);
cert_authority,
request,
default_url);
} }
/* /*
@@ -474,21 +465,22 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails. * Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/ */
CdmResponseType CdmEngine::HandleProvisioningResponse( CdmResponseType CdmEngine::HandleProvisioningResponse(
CdmProvisioningResponse& response, CdmProvisioningResponse& response, std::string* cert,
std::string* cert,
std::string* wrapped_key) { std::string* wrapped_key) {
if (response.empty()) { if (response.empty()) {
LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response."); LOGE("CdmEngine::HandleProvisioningResponse: Empty provisioning response.");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
if (NULL == cert) { if (NULL == cert) {
LOGE("CdmEngine::HandleProvisioningResponse: invalid certificate " LOGE(
"destination"); "CdmEngine::HandleProvisioningResponse: invalid certificate "
"destination");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
if (NULL == wrapped_key) { if (NULL == wrapped_key) {
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key " LOGE(
"destination"); "CdmEngine::HandleProvisioningResponse: invalid wrapped key "
"destination");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
return cert_provisioning_.HandleProvisioningResponse(response, cert, return cert_provisioning_.HandleProvisioningResponse(response, cert,
@@ -540,15 +532,16 @@ CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) {
uint32_t index = rand() % license_info.size(); uint32_t index = rand() % license_info.size();
status = usage_session_->RestoreUsageSession(license_info[index].first, status = usage_session_->RestoreUsageSession(license_info[index].first,
license_info[index].second); license_info[index].second);
if (KEY_ADDED != status) { if (KEY_ADDED != status) {
LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld", LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld", index,
index, status); status);
usage_info->clear(); usage_info->clear();
return status; return status;
} }
status = usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url); status =
usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
if (KEY_MESSAGE != status) { if (KEY_MESSAGE != status) {
LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld", LOGE("CdmEngine::GetUsageInfo: generate release request error: %ld",
@@ -573,9 +566,8 @@ CdmResponseType CdmEngine::ReleaseUsageInfo(
return status; return status;
} }
CdmResponseType CdmEngine::Decrypt( CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id,
const CdmSessionId& session_id, const CdmDecryptionParameters& parameters) {
const CdmDecryptionParameters& parameters) {
if (parameters.key_id == NULL) { if (parameters.key_id == NULL) {
LOGE("CdmEngine::Decrypt: no key_id"); LOGE("CdmEngine::Decrypt: no key_id");
return KEY_ERROR; return KEY_ERROR;
@@ -596,7 +588,7 @@ CdmResponseType CdmEngine::Decrypt(
!Properties::Properties::oem_crypto_use_fifo()) { !Properties::Properties::oem_crypto_use_fifo()) {
LOGE("CdmEngine::Decrypt: no dest decrypt buffer"); LOGE("CdmEngine::Decrypt: no dest decrypt buffer");
return KEY_ERROR; return KEY_ERROR;
} // else we must be level 1 direct and we don't need to return a buffer. } // else we must be level 1 direct and we don't need to return a buffer.
} }
CdmSessionMap::iterator iter; CdmSessionMap::iterator iter;
@@ -612,8 +604,7 @@ CdmResponseType CdmEngine::Decrypt(
} }
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d", LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d",
session_id.c_str(), session_id.c_str(), session_id.size());
session_id.size());
return KEY_ERROR; return KEY_ERROR;
} }
@@ -630,9 +621,8 @@ bool CdmEngine::IsKeyLoaded(const KeyId& key_id) {
return false; return false;
} }
bool CdmEngine::FindSessionForKey( bool CdmEngine::FindSessionForKey(const KeyId& key_id,
const KeyId& key_id, CdmSessionId* session_id) {
CdmSessionId* session_id) {
if (NULL == session_id) { if (NULL == session_id) {
LOGE("CdmEngine::FindSessionForKey: session id not provided"); LOGE("CdmEngine::FindSessionForKey: session id not provided");
return false; return false;
@@ -660,10 +650,8 @@ bool CdmEngine::FindSessionForKey(
return false; return false;
} }
bool CdmEngine::AttachEventListener( bool CdmEngine::AttachEventListener(const CdmSessionId& session_id,
const CdmSessionId& session_id, WvCdmEventListener* listener) {
WvCdmEventListener* listener) {
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
return false; return false;
@@ -672,10 +660,8 @@ bool CdmEngine::AttachEventListener(
return iter->second->AttachEventListener(listener); return iter->second->AttachEventListener(listener);
} }
bool CdmEngine::DetachEventListener( bool CdmEngine::DetachEventListener(const CdmSessionId& session_id,
const CdmSessionId& session_id, WvCdmEventListener* listener) {
WvCdmEventListener* listener) {
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
if (iter == sessions_.end()) { if (iter == sessions_.end()) {
return false; return false;
@@ -700,7 +686,7 @@ void CdmEngine::OnTimerEvent() {
} }
for (CdmSessionMap::iterator iter = sessions_.begin(); for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) { iter != sessions_.end(); ++iter) {
iter->second->OnTimerEvent(); iter->second->OnTimerEvent();
if (update_usage_information && iter->second->is_usage_update_needed()) { if (update_usage_information && iter->second->is_usage_update_needed()) {
@@ -718,7 +704,6 @@ void CdmEngine::OnTimerEvent() {
} }
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
for (CdmSessionMap::iterator iter = sessions_.begin(); for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) { iter != sessions_.end(); ++iter) {
iter->second->OnKeyReleaseEvent(key_set_id); iter->second->OnKeyReleaseEvent(key_set_id);

View File

@@ -29,22 +29,18 @@ CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) {
new DeviceFiles(), cdm_client_property_set); new DeviceFiles(), cdm_client_property_set);
} }
CdmSession::CdmSession( CdmSession::CdmSession(CdmLicense* license_parser,
CdmLicense* license_parser, CryptoSession* crypto_session,
CryptoSession* crypto_session, PolicyEngine* policy_engine, DeviceFiles* file_handle,
PolicyEngine* policy_engine, const CdmClientPropertySet* cdm_client_property_set) {
DeviceFiles* file_handle,
const CdmClientPropertySet* cdm_client_property_set) {
Create(license_parser, crypto_session, policy_engine, file_handle, Create(license_parser, crypto_session, policy_engine, file_handle,
cdm_client_property_set); cdm_client_property_set);
} }
void CdmSession::Create( void CdmSession::Create(CdmLicense* license_parser,
CdmLicense* license_parser, CryptoSession* crypto_session,
CryptoSession* crypto_session, PolicyEngine* policy_engine, DeviceFiles* file_handle,
PolicyEngine* policy_engine, const CdmClientPropertySet* cdm_client_property_set) {
DeviceFiles* file_handle,
const CdmClientPropertySet* cdm_client_property_set) {
// Just return on failures. Failures will be signaled in Init. // Just return on failures. Failures will be signaled in Init.
if (NULL == license_parser) { if (NULL == license_parser) {
LOGE("CdmSession::Create: License parser not provided"); LOGE("CdmSession::Create: License parser not provided");
@@ -76,7 +72,7 @@ void CdmSession::Create(
requested_security_level_ = kLevelDefault; requested_security_level_ = kLevelDefault;
if (NULL != cdm_client_property_set) { if (NULL != cdm_client_property_set) {
if (QUERY_VALUE_SECURITY_LEVEL_L3.compare( if (QUERY_VALUE_SECURITY_LEVEL_L3.compare(
cdm_client_property_set->security_level()) == 0) { cdm_client_property_set->security_level()) == 0) {
requested_security_level_ = kLevel3; requested_security_level_ = kLevel3;
} }
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set); Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
@@ -125,17 +121,14 @@ CdmResponseType CdmSession::RestoreOfflineSession(
key_set_id_ = key_set_id; key_set_id_ = key_set_id;
// Retrieve license information from persistent store // Retrieve license information from persistent store
if (!file_handle_->Reset(security_level_)) if (!file_handle_->Reset(security_level_)) return UNKNOWN_ERROR;
return UNKNOWN_ERROR;
DeviceFiles::LicenseState license_state; DeviceFiles::LicenseState license_state;
if (!file_handle_->RetrieveLicense(key_set_id, &license_state, if (!file_handle_->RetrieveLicense(
&offline_init_data_, key_set_id, &license_state, &offline_init_data_, &key_request_,
&key_request_, &key_response_, &key_response_, &offline_key_renewal_request_,
&offline_key_renewal_request_, &offline_key_renewal_response_, &offline_release_server_url_)) {
&offline_key_renewal_response_,
&offline_release_server_url_)) {
LOGE("CdmSession::Init failed to retrieve license. key set id = %s", LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
key_set_id.c_str()); key_set_id.c_str());
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
@@ -158,9 +151,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} }
CdmResponseType CdmSession::RestoreUsageSession( CdmResponseType CdmSession::RestoreUsageSession(
const CdmKeyMessage& key_request, const CdmKeyMessage& key_request, const CdmKeyResponse& key_response) {
const CdmKeyResponse& key_response) {
key_request_ = key_request; key_request_ = key_request;
key_response_ = key_response; key_response_ = key_response;
@@ -177,7 +168,7 @@ CdmResponseType CdmSession::RestoreUsageSession(
CdmResponseType CdmSession::GenerateKeyRequest( CdmResponseType CdmSession::GenerateKeyRequest(
const InitializationData& init_data, const CdmLicenseType license_type, const InitializationData& init_data, const CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request, const CdmAppParameterMap& app_parameters, CdmKeyMessage* key_request,
std::string* server_url) { std::string* server_url, CdmKeySetId* key_set_id) {
if (crypto_session_.get() == NULL) { if (crypto_session_.get() == NULL) {
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session"); LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
@@ -189,9 +180,15 @@ CdmResponseType CdmSession::GenerateKeyRequest(
} }
switch (license_type) { switch (license_type) {
case kLicenseTypeStreaming: is_offline_ = false; break; case kLicenseTypeStreaming:
case kLicenseTypeOffline: is_offline_ = true; break; is_offline_ = false;
case kLicenseTypeRelease: is_release_ = true; break; break;
case kLicenseTypeOffline:
is_offline_ = true;
break;
case kLicenseTypeRelease:
is_release_ = true;
break;
default: default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld", LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type); license_type);
@@ -212,6 +209,10 @@ CdmResponseType CdmSession::GenerateKeyRequest(
LOGW("CdmSession::GenerateKeyRequest: init data absent"); LOGW("CdmSession::GenerateKeyRequest: init data absent");
return KEY_ERROR; return KEY_ERROR;
} }
if (is_offline_ && !GenerateKeySetId(&key_set_id_)) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
return UNKNOWN_ERROR;
}
if (!license_parser_->PrepareKeyRequest(init_data, license_type, if (!license_parser_->PrepareKeyRequest(init_data, license_type,
app_parameters, session_id_, app_parameters, session_id_,
@@ -225,6 +226,7 @@ CdmResponseType CdmSession::GenerateKeyRequest(
offline_release_server_url_ = *server_url; offline_release_server_url_ = *server_url;
} }
if (key_set_id) *key_set_id = key_set_id_;
return KEY_MESSAGE; return KEY_MESSAGE;
} }
} }
@@ -260,7 +262,7 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
if (sts != NO_ERROR) return sts; if (sts != NO_ERROR) return sts;
} }
*key_set_id = key_set_id_; if (key_set_id) *key_set_id = key_set_id_;
return KEY_ADDED; return KEY_ADDED;
} }
} }
@@ -332,7 +334,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
} }
if (!is_usage_update_needed_) { if (!is_usage_update_needed_) {
is_usage_update_needed_ = is_usage_update_needed_ =
!license_parser_->provider_session_token().empty(); !license_parser_->provider_session_token().empty();
} }
} }
@@ -384,10 +386,9 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
// ReleaseKey() - Accept release response and release license. // ReleaseKey() - Accept release response and release license.
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(false, CdmResponseType sts =
key_response); license_parser_->HandleKeyUpdateResponse(false, key_response);
if (KEY_ADDED != sts) if (KEY_ADDED != sts) return sts;
return sts;
if (is_offline_ || !license_parser_->provider_session_token().empty()) { if (is_offline_ || !license_parser_->provider_session_token().empty()) {
DeleteLicense(); DeleteLicense();
@@ -413,8 +414,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
std::vector<uint8_t> random_data( std::vector<uint8_t> random_data(
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0); (kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
if (!file_handle_->Reset(security_level_)) if (!file_handle_->Reset(security_level_)) return false;
return false;
while (key_set_id->empty()) { while (key_set_id->empty()) {
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0])) if (!crypto_session_->GetRandom(random_data.size(), &random_data[0]))
@@ -427,13 +427,14 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
key_set_id->clear(); key_set_id->clear();
} }
} }
file_handle_->ReserveLicenseId(*key_set_id);
return true; return true;
} }
CdmResponseType CdmSession::StoreLicense() { CdmResponseType CdmSession::StoreLicense() {
if (is_offline_) { if (is_offline_) {
if (!GenerateKeySetId(&key_set_id_)) { if (key_set_id_.empty()) {
LOGE("CdmSession::StoreLicense: Unable to generate key set Id"); LOGE("CdmSession::StoreLicense: No key set ID");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
@@ -472,13 +473,12 @@ CdmResponseType CdmSession::StoreLicense() {
} }
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) { bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
if (!file_handle_->Reset(security_level_)) if (!file_handle_->Reset(security_level_)) return false;
return false;
return file_handle_->StoreLicense( return file_handle_->StoreLicense(
key_set_id_, state, offline_init_data_, key_request_, key_set_id_, state, offline_init_data_, key_request_, key_response_,
key_response_, offline_key_renewal_request_, offline_key_renewal_request_, offline_key_renewal_response_,
offline_key_renewal_response_, offline_release_server_url_); offline_release_server_url_);
} }
bool CdmSession::DeleteLicense() { bool CdmSession::DeleteLicense() {
@@ -494,7 +494,7 @@ bool CdmSession::DeleteLicense() {
return file_handle_->DeleteLicense(key_set_id_); return file_handle_->DeleteLicense(key_set_id_);
else else
return file_handle_->DeleteUsageInfo( return file_handle_->DeleteUsageInfo(
license_parser_->provider_session_token()); license_parser_->provider_session_token());
} }
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) { bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {

View File

@@ -14,9 +14,9 @@ namespace {
// The provisioning server supplies the certificate that is needed // The provisioning server supplies the certificate that is needed
// to communicate with the License Server. // to communicate with the License Server.
const std::string kProvisioningServerUrl = const std::string kProvisioningServerUrl =
"https://www.googleapis.com/" "https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create" "certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
} }
namespace wvcdm { namespace wvcdm {
@@ -41,9 +41,7 @@ using video_widevine_server::sdk::SignedProvisioningMessage;
* base64 encoded message * base64 encoded message
*/ */
void CertificateProvisioning::ComposeJsonRequestAsQueryString( void CertificateProvisioning::ComposeJsonRequestAsQueryString(
const std::string& message, const std::string& message, CdmProvisioningRequest* request) {
CdmProvisioningRequest* request) {
// Performs base64 encoding for message // Performs base64 encoding for message
std::vector<uint8_t> message_vector(message.begin(), message.end()); std::vector<uint8_t> message_vector(message.begin(), message.end());
std::string message_b64 = Base64SafeEncodeNoPad(message_vector); std::string message_b64 = Base64SafeEncodeNoPad(message_vector);
@@ -58,10 +56,8 @@ void CertificateProvisioning::ComposeJsonRequestAsQueryString(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails. * Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/ */
CdmResponseType CertificateProvisioning::GetProvisioningRequest( CdmResponseType CertificateProvisioning::GetProvisioningRequest(
SecurityLevel requested_security_level, SecurityLevel requested_security_level, CdmCertificateType cert_type,
CdmCertificateType cert_type, const std::string& cert_authority, CdmProvisioningRequest* request,
const std::string& cert_authority,
CdmProvisioningRequest* request,
std::string* default_url) { std::string* default_url) {
if (!default_url) { if (!default_url) {
LOGE("GetProvisioningRequest: pointer for returning URL is NULL"); LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
@@ -123,7 +119,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
// Derives signing and encryption keys and constructs signature. // Derives signing and encryption keys and constructs signature.
std::string request_signature; std::string request_signature;
if (!crypto_session_.PrepareRequest(serialized_message, true, if (!crypto_session_.PrepareRequest(serialized_message, true,
&request_signature)) { &request_signature)) {
LOGE("GetProvisioningRequest: fails to prepare request"); LOGE("GetProvisioningRequest: fails to prepare request");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
@@ -152,10 +148,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
* Returns true for success and false if fails. * Returns true for success and false if fails.
*/ */
bool CertificateProvisioning::ParseJsonResponse( bool CertificateProvisioning::ParseJsonResponse(
const CdmProvisioningResponse& json_str, const CdmProvisioningResponse& json_str, const std::string& start_substr,
const std::string& start_substr, const std::string& end_substr, std::string* result) {
const std::string& end_substr,
std::string* result) {
std::string b64_string; std::string b64_string;
size_t start = json_str.find(start_substr); size_t start = json_str.find(start_substr);
if (start == json_str.npos) { if (start == json_str.npos) {
@@ -186,10 +180,8 @@ bool CertificateProvisioning::ParseJsonResponse(
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails. * Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/ */
CdmResponseType CertificateProvisioning::HandleProvisioningResponse( CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
CdmProvisioningResponse& response, CdmProvisioningResponse& response, std::string* cert,
std::string* cert,
std::string* wrapped_key) { std::string* wrapped_key) {
// Extracts signed response from JSON string, decodes base64 signed response // Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \""; const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\""; const std::string kMessageEnd = "\"";
@@ -220,8 +212,7 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
error = true; error = true;
} }
if (error) if (error) return UNKNOWN_ERROR;
return UNKNOWN_ERROR;
const std::string& signed_message = signed_response.message(); const std::string& signed_message = signed_response.message();
ProvisioningResponse provisioning_response; ProvisioningResponse provisioning_response;
@@ -241,12 +232,9 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv(); const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
const std::string& signature = signed_response.signature(); const std::string& signature = signed_response.signature();
std::string wrapped_rsa_key; std::string wrapped_rsa_key;
if (!crypto_session_.RewrapDeviceRSAKey(signed_message, if (!crypto_session_.RewrapDeviceRSAKey(signed_message, signature, nonce,
signature, enc_rsa_key, rsa_key_iv,
nonce, &wrapped_rsa_key)) {
enc_rsa_key,
rsa_key_iv,
&wrapped_rsa_key)){
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails"); LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }

View File

@@ -262,13 +262,19 @@ void CryptoSession::Close() {
} }
} }
void CryptoSession::GenerateRequestId(std::string& req_id_str) { bool CryptoSession::GenerateRequestId(std::string* req_id_str) {
LOGV("CryptoSession::GenerateRequestId: Lock"); LOGV("CryptoSession::GenerateRequestId: Lock");
AutoLock auto_lock(crypto_lock_); AutoLock auto_lock(crypto_lock_);
req_id_str = HexEncode(reinterpret_cast<uint8_t*>(&request_id_base_), if (!req_id_str) {
sizeof(request_id_base_)) + LOGE("CryptoSession::GenerateRequestId: No output destination provided.");
HexEncode(reinterpret_cast<uint8_t*>(&request_id_index_), return false;
sizeof(request_id_index_)); }
*req_id_str = HexEncode(reinterpret_cast<uint8_t*>(&request_id_base_),
sizeof(request_id_base_)) +
HexEncode(reinterpret_cast<uint8_t*>(&request_id_index_),
sizeof(request_id_index_));
return true;
} }
bool CryptoSession::PrepareRequest(const std::string& message, bool CryptoSession::PrepareRequest(const std::string& message,
@@ -406,8 +412,10 @@ CdmResponseType CryptoSession::LoadKeys(
if (OEMCrypto_SUCCESS == sts) { if (OEMCrypto_SUCCESS == sts) {
return KEY_ADDED; return KEY_ADDED;
} else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) { } else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) {
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
return INSUFFICIENT_CRYPTO_RESOURCES; return INSUFFICIENT_CRYPTO_RESOURCES;
} else { } else {
LOGE("LoadKeys: OEMCrypto_LoadKeys error=%d", sts);
return KEY_ERROR; return KEY_ERROR;
} }
} }
@@ -570,8 +578,8 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message,
OEMCryptoResult sts = OEMCrypto_GenerateRSASignature( OEMCryptoResult sts = OEMCrypto_GenerateRSASignature(
oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()), oec_session_id_, reinterpret_cast<const uint8_t*>(message.data()),
message.size(), message.size(),
reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())), reinterpret_cast<uint8_t*>(const_cast<char*>(signature->data())), &length,
&length, kSign_RSASSA_PSS); kSign_RSASSA_PSS);
if (OEMCrypto_SUCCESS != sts) { if (OEMCrypto_SUCCESS != sts) {
if (OEMCrypto_ERROR_SHORT_BUFFER != sts) { if (OEMCrypto_ERROR_SHORT_BUFFER != sts) {
@@ -656,9 +664,26 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
} }
} }
bool CryptoSession::UsageInformationSupport(bool* has_support) {
LOGV("UsageInformationSupport: id=%ld", (uint32_t)oec_session_id_);
if (!initialized_) return false;
*has_support = OEMCrypto_SupportsUsageTable(
kSecurityLevelL3 == GetSecurityLevel() ? kLevel3 : kLevelDefault);
return true;
}
CdmResponseType CryptoSession::UpdateUsageInformation() { CdmResponseType CryptoSession::UpdateUsageInformation() {
return (OEMCrypto_UpdateUsageTable() == OEMCrypto_SUCCESS) ? NO_ERROR LOGV("UpdateUsageInformation: id=%ld", (uint32_t)oec_session_id_);
: UNKNOWN_ERROR; AutoLock auto_lock(crypto_lock_);
if (!initialized_) return UNKNOWN_ERROR;
OEMCryptoResult status = OEMCrypto_UpdateUsageTable();
if (status != OEMCrypto_SUCCESS) {
LOGE("CryptoSession::UsageUsageInformation: error=%ld", status);
return UNKNOWN_ERROR;
}
return NO_ERROR;
} }
CdmResponseType CryptoSession::GenerateUsageReport( CdmResponseType CryptoSession::GenerateUsageReport(
@@ -816,6 +841,23 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
return true; return true;
} }
bool CryptoSession::GetHdcpCapabilities(OemCryptoHdcpVersion* current_version,
OemCryptoHdcpVersion* max_version) {
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);
if (!initialized_) return UNKNOWN_ERROR;
OEMCrypto_HDCP_Capability current, max;
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(&current, &max);
if (OEMCrypto_SUCCESS != status) {
LOGW("OEMCrypto_GetHDCPCapability fails with %d", status);
return false;
}
*current_version = static_cast<OemCryptoHdcpVersion>(current);
*max_version = static_cast<OemCryptoHdcpVersion>(max);
return true;
}
bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) { bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) {
if (random_data == NULL) { if (random_data == NULL) {
LOGE("CryptoSession::GetRandom: random data destination not provided"); LOGE("CryptoSession::GetRandom: random data destination not provided");

View File

@@ -3,11 +3,11 @@
#include "device_files.h" #include "device_files.h"
#if defined(__APPLE__) #if defined(__APPLE__)
# include <CommonCrypto/CommonDigest.h> #include <CommonCrypto/CommonDigest.h>
# define SHA256 CC_SHA256 #define SHA256 CC_SHA256
# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH #define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
#else #else
# include <openssl/sha.h> #include <openssl/sha.h>
#endif #endif
#include <cstring> #include <cstring>
@@ -39,13 +39,15 @@ const char* kSecurityLevelPathCompatibilityExclusionList[] = {"ay64.dat"};
size_t kSecurityLevelPathCompatibilityExclusionListSize = size_t kSecurityLevelPathCompatibilityExclusionListSize =
sizeof(kSecurityLevelPathCompatibilityExclusionList) / sizeof(kSecurityLevelPathCompatibilityExclusionList) /
sizeof(*kSecurityLevelPathCompatibilityExclusionList); sizeof(*kSecurityLevelPathCompatibilityExclusionList);
// Some platforms cannot store a truly blank file, so we use a W for Widevine.
const char kBlankFileData[] = "W";
bool Hash(const std::string& data, std::string* hash) { bool Hash(const std::string& data, std::string* hash) {
if (!hash) return false; if (!hash) return false;
hash->resize(SHA256_DIGEST_LENGTH); hash->resize(SHA256_DIGEST_LENGTH);
const unsigned char* input = const unsigned char* input =
reinterpret_cast<const unsigned char*>(data.data()); reinterpret_cast<const unsigned char*>(data.data());
unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]); unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
SHA256(input, data.size(), output); SHA256(input, data.size(), output);
return true; return true;
@@ -56,9 +58,10 @@ bool Hash(const std::string& data, std::string* hash) {
namespace wvcdm { namespace wvcdm {
DeviceFiles::DeviceFiles() DeviceFiles::DeviceFiles()
: file_(NULL), security_level_(kSecurityLevelUninitialized), : file_(NULL),
initialized_(false), test_file_(false) { security_level_(kSecurityLevelUninitialized),
} initialized_(false),
test_file_(false) {}
DeviceFiles::~DeviceFiles() { DeviceFiles::~DeviceFiles() {
if (test_file_) file_.release(); if (test_file_) file_.release();
@@ -100,7 +103,7 @@ bool DeviceFiles::StoreCertificate(const std::string& certificate,
std::string serialized_file; std::string serialized_file;
file.SerializeToString(&serialized_file); file.SerializeToString(&serialized_file);
return StoreFile(kCertificateFileName, serialized_file); return StoreFileWithHash(kCertificateFileName, serialized_file);
} }
bool DeviceFiles::RetrieveCertificate(std::string* certificate, bool DeviceFiles::RetrieveCertificate(std::string* certificate,
@@ -115,8 +118,7 @@ bool DeviceFiles::RetrieveCertificate(std::string* certificate,
} }
std::string serialized_file; std::string serialized_file;
if (!RetrieveFile(kCertificateFileName, &serialized_file)) if (!RetrieveHashedFile(kCertificateFileName, &serialized_file)) return false;
return false;
video_widevine_client::sdk::File file; video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) { if (!file.ParseFromString(serialized_file)) {
@@ -189,7 +191,7 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
file.SerializeToString(&serialized_file); file.SerializeToString(&serialized_file);
std::string file_name = key_set_id + kLicenseFileNameExt; std::string file_name = key_set_id + kLicenseFileNameExt;
return StoreFile(file_name.c_str(), serialized_file); return StoreFileWithHash(file_name.c_str(), serialized_file);
} }
bool DeviceFiles::RetrieveLicense(const std::string& key_set_id, bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
@@ -206,7 +208,7 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
std::string serialized_file; std::string serialized_file;
std::string file_name = key_set_id + kLicenseFileNameExt; std::string file_name = key_set_id + kLicenseFileNameExt;
if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false; if (!RetrieveHashedFile(file_name.c_str(), &serialized_file)) return false;
video_widevine_client::sdk::File file; video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) { if (!file.ParseFromString(serialized_file)) {
@@ -310,7 +312,7 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
std::string path; std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path"); LOGW("DeviceFiles::LicenseExists: Unable to get base path");
return false; return false;
} }
path.append(key_set_id); path.append(key_set_id);
@@ -319,6 +321,16 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) {
return file_->Exists(path); return file_->Exists(path);
} }
bool DeviceFiles::ReserveLicenseId(const std::string& key_set_id) {
if (!initialized_) {
LOGW("DeviceFiles::ReserveLicenseId: not initialized");
return false;
}
std::string file_name = key_set_id + kLicenseFileNameExt;
return StoreFileRaw(file_name.c_str(), kBlankFileData);
}
bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
const CdmKeyMessage& key_request, const CdmKeyMessage& key_request,
const CdmKeyResponse& key_response) { const CdmKeyResponse& key_response) {
@@ -329,7 +341,7 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
std::string serialized_file; std::string serialized_file;
video_widevine_client::sdk::File file; video_widevine_client::sdk::File file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) { if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
file.set_type(video_widevine_client::sdk::File::USAGE_INFO); file.set_type(video_widevine_client::sdk::File::USAGE_INFO);
file.set_version(video_widevine_client::sdk::File::VERSION_1); file.set_version(video_widevine_client::sdk::File::VERSION_1);
} else { } else {
@@ -344,13 +356,11 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token,
provider_session->set_token(provider_session_token.data(), provider_session->set_token(provider_session_token.data(),
provider_session_token.size()); provider_session_token.size());
provider_session->set_license_request(key_request.data(), provider_session->set_license_request(key_request.data(), key_request.size());
key_request.size()); provider_session->set_license(key_response.data(), key_response.size());
provider_session->set_license(key_response.data(),
key_response.size());
file.SerializeToString(&serialized_file); file.SerializeToString(&serialized_file);
return StoreFile(kUsageInfoFileName, serialized_file); return StoreFileWithHash(kUsageInfoFileName, serialized_file);
} }
bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) { bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
@@ -360,7 +370,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
} }
std::string serialized_file; std::string serialized_file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) return false; if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) return false;
video_widevine_client::sdk::File file; video_widevine_client::sdk::File file;
if (!file.ParseFromString(serialized_file)) { if (!file.ParseFromString(serialized_file)) {
@@ -372,15 +382,18 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
int index = 0; int index = 0;
bool found = false; bool found = false;
for (; index < usage_info->sessions_size(); ++index) { for (; index < usage_info->sessions_size(); ++index) {
if (usage_info->sessions(index).token().compare(provider_session_token) == 0) { if (usage_info->sessions(index).token().compare(provider_session_token) ==
0) {
found = true; found = true;
break; break;
} }
} }
if (!found) { if (!found) {
LOGW("DeviceFiles::DeleteUsageInfo: Unable to find provider session " LOGW(
"token: %s", b2a_hex(provider_session_token).c_str()); "DeviceFiles::DeleteUsageInfo: Unable to find provider session "
"token: %s",
b2a_hex(provider_session_token).c_str());
return false; return false;
} }
@@ -392,7 +405,7 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) {
sessions->RemoveLast(); sessions->RemoveLast();
file.SerializeToString(&serialized_file); file.SerializeToString(&serialized_file);
return StoreFile(kUsageInfoFileName, serialized_file); return StoreFileWithHash(kUsageInfoFileName, serialized_file);
} }
bool DeviceFiles::DeleteUsageInfo() { bool DeviceFiles::DeleteUsageInfo() {
@@ -411,21 +424,22 @@ bool DeviceFiles::DeleteUsageInfo() {
return file_->Remove(path); return file_->Remove(path);
} }
bool DeviceFiles::RetrieveUsageInfo(std::vector< bool DeviceFiles::RetrieveUsageInfo(
std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) { std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
if (!initialized_) { if (!initialized_) {
LOGW("DeviceFiles::RetrieveUsageInfo: not initialized"); LOGW("DeviceFiles::RetrieveUsageInfo: not initialized");
return false; return false;
} }
if (NULL == usage_info) { if (NULL == usage_info) {
LOGW("DeviceFiles::RetrieveUsageInfo: license destination not " LOGW(
"DeviceFiles::RetrieveUsageInfo: license destination not "
"provided"); "provided");
return false; return false;
} }
std::string serialized_file; std::string serialized_file;
if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) { if (!RetrieveHashedFile(kUsageInfoFileName, &serialized_file)) {
std::string path; std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
return false; return false;
@@ -457,22 +471,22 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector<
return true; return true;
} }
bool DeviceFiles::StoreFile(const char* name, bool DeviceFiles::StoreFileWithHash(const char* name,
const std::string& serialized_file) { const std::string& serialized_file) {
if (!file_.get()) { if (!file_.get()) {
LOGW("DeviceFiles::StoreFile: Invalid file handle"); LOGW("DeviceFiles::StoreFileWithHash: Invalid file handle");
return false; return false;
} }
if (!name) { if (!name) {
LOGW("DeviceFiles::StoreFile: Unspecified file name parameter"); LOGW("DeviceFiles::StoreFileWithHash: Unspecified file name parameter");
return false; return false;
} }
// calculate SHA hash // calculate SHA hash
std::string hash; std::string hash;
if (!Hash(serialized_file, &hash)) { if (!Hash(serialized_file, &hash)) {
LOGW("DeviceFiles::StoreFile: Hash computation failed"); LOGW("DeviceFiles::StoreFileWithHash: Hash computation failed");
return false; return false;
} }
@@ -484,9 +498,24 @@ bool DeviceFiles::StoreFile(const char* name,
std::string serialized_hash_file; std::string serialized_hash_file;
hash_file.SerializeToString(&serialized_hash_file); hash_file.SerializeToString(&serialized_hash_file);
return StoreFileRaw(name, serialized_hash_file);
}
bool DeviceFiles::StoreFileRaw(const char* name,
const std::string& serialized_file) {
if (!file_.get()) {
LOGW("DeviceFiles::StoreFileRaw: Invalid file handle");
return false;
}
if (!name) {
LOGW("DeviceFiles::StoreFileRaw: Unspecified file name parameter");
return false;
}
std::string path; std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path"); LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
return false; return false;
} }
@@ -497,59 +526,62 @@ bool DeviceFiles::StoreFile(const char* name,
path += name; path += name;
if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) { if (!file_->Open(path, File::kCreate | File::kTruncate | File::kBinary)) {
LOGW("DeviceFiles::StoreFile: File open failed: %s", path.c_str()); LOGW("DeviceFiles::StoreFileRaw: File open failed: %s", path.c_str());
return false; return false;
} }
ssize_t bytes = file_->Write(serialized_hash_file.data(), ssize_t bytes = file_->Write(serialized_file.data(), serialized_file.size());
serialized_hash_file.size());
file_->Close(); file_->Close();
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) { if (bytes != static_cast<ssize_t>(serialized_file.size())) {
LOGW("DeviceFiles::StoreFile: write failed: (actual: %d, expected: %d)", LOGW(
bytes, "DeviceFiles::StoreFileRaw: write failed: (actual: %d, "
serialized_hash_file.size()); "expected: %d)",
bytes, serialized_file.size());
return false; return false;
} }
LOGV("DeviceFiles::StoreFile: success: %s (%db)", LOGV("DeviceFiles::StoreFileRaw: success: %s (%db)", path.c_str(),
path.c_str(), serialized_file.size());
serialized_hash_file.size());
return true; return true;
} }
bool DeviceFiles::RetrieveFile(const char* name, std::string* serialized_file) { bool DeviceFiles::RetrieveHashedFile(const char* name,
std::string* serialized_file) {
if (!file_.get()) { if (!file_.get()) {
LOGW("DeviceFiles::RetrieveFile: Invalid file handle"); LOGW("DeviceFiles::RetrieveHashedFile: Invalid file handle");
return false; return false;
} }
if (!name) { if (!name) {
LOGW("DeviceFiles::RetrieveFile: Unspecified file name parameter"); LOGW("DeviceFiles::RetrieveHashedFile: Unspecified file name parameter");
return false; return false;
} }
if (!serialized_file) { if (!serialized_file) {
LOGW("DeviceFiles::RetrieveFile: Unspecified serialized_file parameter"); LOGW(
"DeviceFiles::RetrieveHashedFile: Unspecified serialized_file "
"parameter");
return false; return false;
} }
std::string path; std::string path;
if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) {
LOGW("DeviceFiles::StoreFile: Unable to get base path"); LOGW("DeviceFiles::StoreFileWithHash: Unable to get base path");
return false; return false;
} }
path += name; path += name;
if (!file_->Exists(path)) { if (!file_->Exists(path)) {
LOGW("DeviceFiles::RetrieveFile: %s does not exist", path.c_str()); LOGW("DeviceFiles::RetrieveHashedFile: %s does not exist", path.c_str());
return false; return false;
} }
ssize_t bytes = file_->FileSize(path); ssize_t bytes = file_->FileSize(path);
if (bytes <= 0) { if (bytes <= 0) {
LOGW("DeviceFiles::RetrieveFile: File size invalid: %s", path.c_str()); LOGW("DeviceFiles::RetrieveHashedFile: File size invalid: %s",
path.c_str());
return false; return false;
} }
@@ -563,27 +595,27 @@ bool DeviceFiles::RetrieveFile(const char* name, std::string* serialized_file) {
file_->Close(); file_->Close();
if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) { if (bytes != static_cast<ssize_t>(serialized_hash_file.size())) {
LOGW("DeviceFiles::RetrieveFile: read failed"); LOGW("DeviceFiles::RetrieveHashedFile: read failed");
return false; return false;
} }
LOGV("DeviceFiles::RetrieveFile: success: %s (%db)", path.c_str(), LOGV("DeviceFiles::RetrieveHashedFile: success: %s (%db)", path.c_str(),
serialized_hash_file.size()); serialized_hash_file.size());
HashedFile hash_file; HashedFile hash_file;
if (!hash_file.ParseFromString(serialized_hash_file)) { if (!hash_file.ParseFromString(serialized_hash_file)) {
LOGW("DeviceFiles::RetrieveFile: Unable to parse hash file"); LOGW("DeviceFiles::RetrieveHashedFile: Unable to parse hash file");
return false; return false;
} }
std::string hash; std::string hash;
if (!Hash(hash_file.file(), &hash)) { if (!Hash(hash_file.file(), &hash)) {
LOGW("DeviceFiles::RetrieveFile: Hash computation failed"); LOGW("DeviceFiles::RetrieveHashedFile: Hash computation failed");
return false; return false;
} }
if (hash.compare(hash_file.hash())) { if (hash.compare(hash_file.hash())) {
LOGW("DeviceFiles::RetrieveFile: Hash mismatch"); LOGW("DeviceFiles::RetrieveHashedFile: Hash mismatch");
return false; return false;
} }
@@ -663,9 +695,9 @@ std::string DeviceFiles::GetLicenseFileNameExtension() {
return kLicenseFileNameExt; return kLicenseFileNameExt;
} }
std::string DeviceFiles::GetUsageInfoFileName() { std::string DeviceFiles::GetUsageInfoFileName() { return kUsageInfoFileName; }
return kUsageInfoFileName;
} std::string DeviceFiles::GetBlankFileData() { return kBlankFileData; }
void DeviceFiles::SetTestFile(File* file) { void DeviceFiles::SetTestFile(File* file) {
file_.reset(file); file_.reset(file);

View File

@@ -14,12 +14,10 @@ namespace wvcdm {
InitializationData::InitializationData(const std::string& type, InitializationData::InitializationData(const std::string& type,
const CdmInitData& data) const CdmInitData& data)
: type_(type), is_cenc_(false), is_webm_(false) { : type_(type), is_cenc_(false), is_webm_(false) {
if (type == ISO_BMFF_VIDEO_MIME_TYPE || if (type == ISO_BMFF_VIDEO_MIME_TYPE || type == ISO_BMFF_AUDIO_MIME_TYPE ||
type == ISO_BMFF_AUDIO_MIME_TYPE ||
type == CENC_INIT_DATA_FORMAT) { type == CENC_INIT_DATA_FORMAT) {
is_cenc_ = true; is_cenc_ = true;
} else if (type == WEBM_VIDEO_MIME_TYPE || } else if (type == WEBM_VIDEO_MIME_TYPE || type == WEBM_AUDIO_MIME_TYPE ||
type == WEBM_AUDIO_MIME_TYPE ||
type == WEBM_INIT_DATA_FORMAT) { type == WEBM_INIT_DATA_FORMAT) {
is_webm_ = true; is_webm_ = true;
} }
@@ -35,10 +33,10 @@ InitializationData::InitializationData(const std::string& type,
// Parse a blob of multiple concatenated PSSH atoms to extract the first // Parse a blob of multiple concatenated PSSH atoms to extract the first
// Widevine PSSH. // Widevine PSSH.
bool InitializationData::ExtractWidevinePssh( bool InitializationData::ExtractWidevinePssh(const CdmInitData& init_data,
const CdmInitData& init_data, CdmInitData* output) { CdmInitData* output) {
BufferReader reader( BufferReader reader(reinterpret_cast<const uint8_t*>(init_data.data()),
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length()); init_data.length());
// TODO(kqyang): Extracted from an actual init_data; // TODO(kqyang): Extracted from an actual init_data;
// Need to find out where it comes from. // Need to find out where it comes from.
@@ -90,12 +88,10 @@ bool InitializationData::ExtractWidevinePssh(
return false; return false;
} }
if (memcmp(&system_id[0], kWidevineSystemId, if (memcmp(&system_id[0], kWidevineSystemId, sizeof(kWidevineSystemId))) {
sizeof(kWidevineSystemId))) {
// skip the remaining contents of the atom, // skip the remaining contents of the atom,
// after size field, atom name, flags and system id // after size field, atom name, flags and system id
if (!reader.SkipBytes( if (!reader.SkipBytes(size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) {
LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom"); LOGW("CdmEngine::ExtractWidevinePssh: Unable to rest of PSSH atom");
return false; return false;
} }

View File

@@ -23,64 +23,48 @@ std::string kDeviceNameKey = "device_name";
std::string kProductNameKey = "product_name"; std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info"; std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id"; std::string kDeviceIdKey = "device_id";
std::string kOemCryptoApiVersion = "oemcrypto_api_version";
const unsigned char kServiceCertificateCAPublicKey[] = { const unsigned char kServiceCertificateCAPublicKey[] = {
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39,
0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd,
0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87,
0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6,
0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40,
0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98,
0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84,
0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab,
0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0x53, 0x3e, 0x47, 0x5f,
0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf,
0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, 0x45, 0xd6,
0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7,
0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50,
0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd,
0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf, 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0,
0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7,
0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b,
0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11,
0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8,
0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33,
0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0x60, 0x1a, 0x11, 0x3d,
0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b,
0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, 0xbe, 0x7f, 0xb0,
0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e, 0x69,
0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, 0x01,
0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24,
0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31,
0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5,
0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8,
0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d,
0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03,
0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f,
0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01,
0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0x00, 0x01};
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 wvcdm { namespace wvcdm {
// Protobuf generated classes. // Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification; 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::ClientIdentification_NameValue;
using video_widevine_server::sdk::DeviceCertificate; using video_widevine_server::sdk::DeviceCertificate;
using video_widevine_server::sdk::EncryptedClientIdentification; using video_widevine_server::sdk::EncryptedClientIdentification;
@@ -141,6 +125,19 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
return key_array; return key_array;
} }
CdmLicense::CdmLicense()
: session_(NULL), initialized_(false), clock_(new Clock()) {}
CdmLicense::CdmLicense(Clock* clock) : session_(NULL), initialized_(false) {
if (NULL == clock) {
LOGE("CdmLicense::CdmLicense: clock parameter not provided");
return;
}
clock_.reset(clock);
}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(const std::string& token, CryptoSession* session, bool CdmLicense::Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine) { PolicyEngine* policy_engine) {
if (token.size() == 0) { if (token.size() == 0) {
@@ -207,7 +204,7 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
} }
std::string request_id; std::string request_id;
session_->GenerateRequestId(request_id); session_->GenerateRequestId(&request_id);
LicenseRequest license_request; LicenseRequest license_request;
ClientIdentification* client_id = license_request.mutable_client_id(); ClientIdentification* client_id = license_request.mutable_client_id();
@@ -261,11 +258,54 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
client_info->set_name(kDeviceIdKey); client_info->set_name(kDeviceIdKey);
client_info->set_value(value); client_info->set_value(value);
} }
ClientIdentification_ClientCapabilities* client_capabilities =
client_id->mutable_client_capabilities();
bool supports_usage_information;
if (session_->UsageInformationSupport(&supports_usage_information)) {
client_capabilities->set_session_token(supports_usage_information);
}
CryptoSession::OemCryptoHdcpVersion current_version, max_version;
if (session_->GetHdcpCapabilities(&current_version, &max_version)) {
switch (max_version) {
case CryptoSession::kOemCryptoHdcpNotSupported:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
break;
case CryptoSession::kOemCryptoHdcpVersion1:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
break;
case CryptoSession::kOemCryptoHdcpVersion2:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
break;
case CryptoSession::kOemCryptoHdcpVersion2_1:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
break;
case CryptoSession::kOemCryptoHdcpVersion2_2:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
break;
case CryptoSession::kOemCryptoNoHdcpDeviceAttached:
default:
LOGW(
"CdmLicense::PrepareKeyRequest: unexpected HDCP max capability "
"version %d",
max_version);
}
}
uint32_t version = 0; uint32_t version = 0;
if (session_->GetApiVersion(&version)) { if (session_->GetApiVersion(&version)) {
client_info = client_id->add_client_info(); client_capabilities->set_oem_crypto_api_version(version);
client_info->set_name(kOemCryptoApiVersion);
client_info->set_value(UintToString(version));
} }
if (privacy_mode_enabled) { if (privacy_mode_enabled) {
@@ -362,8 +402,7 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
license_request.set_type(LicenseRequest::NEW); license_request.set_type(LicenseRequest::NEW);
Clock clock; license_request.set_request_time(clock_->GetCurrentTime());
license_request.set_request_time(clock.GetCurrentTime());
// Get/set the nonce. This value will be reflected in the Key Control Block // Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response. // of the license response.
@@ -430,8 +469,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
else else
license_request.set_type(LicenseRequest::RELEASE); license_request.set_type(LicenseRequest::RELEASE);
Clock clock; license_request.set_request_time(clock_->GetCurrentTime());
license_request.set_request_time(clock.GetCurrentTime());
LicenseRequest_ContentIdentification_ExistingLicense* current_license = LicenseRequest_ContentIdentification_ExistingLicense* current_license =
license_request.mutable_content_id()->mutable_license(); license_request.mutable_content_id()->mutable_license();
@@ -515,8 +553,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
case SignedMessage::ERROR_RESPONSE: case SignedMessage::ERROR_RESPONSE:
return HandleKeyErrorResponse(signed_response); return HandleKeyErrorResponse(signed_response);
default: default:
LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d" LOGE(
, signed_response.type()); "CdmLicense::HandleKeyResponse: unrecognized signed message type: %d",
signed_response.type());
return KEY_ERROR; return KEY_ERROR;
} }
@@ -579,18 +618,14 @@ CdmResponseType CdmLicense::HandleKeyResponse(
policy_engine_->SetLicense(license); policy_engine_->SetLicense(license);
CdmResponseType resp = session_->LoadKeys(signed_response.msg(), CdmResponseType resp = session_->LoadKeys(
signed_response.signature(), signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key,
mac_key_iv, key_array, provider_session_token_);
mac_key,
key_array,
provider_session_token_);
if (KEY_ADDED == resp) { if (KEY_ADDED == resp) {
loaded_keys_.clear(); loaded_keys_.clear();
for (std::vector<CryptoKey>::iterator it = key_array.begin(); for (std::vector<CryptoKey>::iterator it = key_array.begin();
it != key_array.end(); it != key_array.end(); ++it) {
++it) {
loaded_keys_.insert(it->key_id()); loaded_keys_.insert(it->key_id());
} }
} }
@@ -642,10 +677,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
if (!license.id().has_provider_session_token()) return KEY_ADDED; if (!license.id().has_provider_session_token()) return KEY_ADDED;
provider_session_token_ = license.id().provider_session_token(); provider_session_token_ = license.id().provider_session_token();
CdmResponseType status = CdmResponseType status = session_->ReleaseUsageInformation(
session_->ReleaseUsageInformation(signed_response.msg(), signed_response.msg(), signed_response.signature(),
signed_response.signature(), provider_session_token_);
provider_session_token_);
return (NO_ERROR == status) ? KEY_ADDED : status; return (NO_ERROR == status) ? KEY_ADDED : status;
} }
@@ -668,7 +702,6 @@ bool CdmLicense::RestoreOfflineLicense(
const CdmKeyMessage& license_request, const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response, const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response) { const CdmKeyResponse& license_renewal_response) {
if (license_request.empty() || license_response.empty()) { if (license_request.empty() || license_response.empty()) {
LOGE( LOGE(
"CdmLicense::RestoreOfflineLicense: key_request or response empty: " "CdmLicense::RestoreOfflineLicense: key_request or response empty: "
@@ -712,7 +745,6 @@ bool CdmLicense::RestoreOfflineLicense(
bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request, bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response) { const CdmKeyResponse& license_response) {
if (license_request.empty() || license_response.empty()) { if (license_request.empty() || license_response.empty()) {
LOGE( LOGE(
"CdmLicense::RestoreUsageLicense: key_request or response empty: %u %u", "CdmLicense::RestoreUsageLicense: key_request or response empty: %u %u",
@@ -738,14 +770,16 @@ bool CdmLicense::RestoreUsageLicense(const CdmKeyMessage& license_request,
SignedMessage signed_response; SignedMessage signed_response;
if (!signed_response.ParseFromString(license_response)) { if (!signed_response.ParseFromString(license_response)) {
LOGE("CdmLicense::RestoreUsageLicense: unable to parse signed license" LOGE(
"CdmLicense::RestoreUsageLicense: unable to parse signed license"
" response"); " response");
return false; return false;
} }
if (SignedMessage::LICENSE != signed_response.type()) { if (SignedMessage::LICENSE != signed_response.type()) {
LOGE("CdmLicense::RestoreUsageLicense: unrecognized signed message type: %d" LOGE(
, signed_response.type()); "CdmLicense::RestoreUsageLicense: unrecognized signed message type: %d",
signed_response.type());
return false; return false;
} }
@@ -809,7 +843,6 @@ bool CdmLicense::PrepareServiceCertificateRequest(CdmKeyMessage* signed_request,
CdmResponseType CdmLicense::HandleServiceCertificateResponse( CdmResponseType CdmLicense::HandleServiceCertificateResponse(
const video_widevine_server::sdk::SignedMessage& signed_response) { const video_widevine_server::sdk::SignedMessage& signed_response) {
SignedDeviceCertificate signed_service_certificate; SignedDeviceCertificate signed_service_certificate;
if (!signed_service_certificate.ParseFromString(signed_response.msg())) { if (!signed_service_certificate.ParseFromString(signed_response.msg())) {
LOGE( LOGE(
@@ -862,7 +895,6 @@ CdmResponseType CdmLicense::HandleServiceCertificateResponse(
CdmResponseType CdmLicense::HandleKeyErrorResponse( CdmResponseType CdmLicense::HandleKeyErrorResponse(
const SignedMessage& signed_message) { const SignedMessage& signed_message) {
LicenseError license_error; LicenseError license_error;
if (!license_error.ParseFromString(signed_message.msg())) { if (!license_error.ParseFromString(signed_message.msg())) {
LOGE("CdmLicense::HandleKeyErrorResponse: Unable to parse license error"); LOGE("CdmLicense::HandleKeyErrorResponse: Unable to parse license error");
@@ -882,7 +914,7 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
} }
} }
template<typename T> template <typename T>
bool CdmLicense::PrepareContentId(const CdmLicenseType license_type, bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
const std::string& request_id, const std::string& request_id,
T* content_id) { T* content_id) {
@@ -891,8 +923,7 @@ bool CdmLicense::PrepareContentId(const CdmLicenseType license_type,
content_id->set_license_type(video_widevine_server::sdk::OFFLINE); content_id->set_license_type(video_widevine_server::sdk::OFFLINE);
break; break;
case kLicenseTypeStreaming: case kLicenseTypeStreaming:
content_id->set_license_type( content_id->set_license_type(video_widevine_server::sdk::STREAMING);
video_widevine_server::sdk::STREAMING);
break; break;
default: default:
LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d", LOGD("CdmLicense::PrepareKeyRequest: Unknown license type = %d",

View File

@@ -287,12 +287,6 @@ message SignedMessage {
// This message is used to pass optional data on initial license issuance. // This message is used to pass optional data on initial license issuance.
message SessionInit { message SessionInit {
enum ReplayControl {
NO_SESSION_USAGE = 0;
NONCE_REQUIRED_AND_NEW_SESSION_USAGE = 1;
NONCE_REQUIRED_OR_EXISTING_SESSION_USAGE = 2;
}
optional bytes session_id = 1; optional bytes session_id = 1;
optional bytes purchase_id = 2; optional bytes purchase_id = 2;
// master_signing_key should be 128 bits in length. // master_signing_key should be 128 bits in length.
@@ -312,8 +306,6 @@ message SessionInit {
// LicenseIdentfication::provider_session_token, and sent back in all // LicenseIdentfication::provider_session_token, and sent back in all
// license renewal and release requests for the session thereafter. // license renewal and release requests for the session thereafter.
optional bytes provider_session_token = 7; optional bytes provider_session_token = 7;
// Replay control indicator which will be encoded into V9+ KeyControl blocks.
optional ReplayControl replay_control = 8 [default = NO_SESSION_USAGE];
} }
// This message is used by the server to preserve and restore session state. // This message is used by the server to preserve and restore session state.
@@ -415,6 +407,7 @@ message ClientIdentification {
optional bool session_token = 2 [default = false]; optional bool session_token = 2 [default = false];
optional bool video_resolution_constraints = 3 [default = false]; optional bool video_resolution_constraints = 3 [default = false];
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE]; optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
optional uint32 oem_crypto_api_version = 5;
} }
// Type of factory-provisioned device root of trust. Optional. // Type of factory-provisioned device root of trust. Optional.

View File

@@ -43,4 +43,8 @@ const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
return ::OEMCrypto_SecurityLevel(); return ::OEMCrypto_SecurityLevel();
} }
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
return ::OEMCrypto_SupportsUsageTable();
}
}; // namespace wvcdm }; // namespace wvcdm

View File

@@ -15,25 +15,15 @@
#include "OEMCryptoCENC.h" #include "OEMCryptoCENC.h"
#include "oemcrypto_adapter.h" #include "oemcrypto_adapter.h"
extern "C" extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V8(
OEMCryptoResult OEMCrypto_LoadKeys_V8(OEMCrypto_SESSION session, OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* message, const uint8_t* signature, size_t signature_length,
size_t message_length, const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys,
const uint8_t* signature, size_t num_keys, const OEMCrypto_KeyObject* key_array);
size_t signature_length,
const uint8_t* enc_mac_keys_iv,
const uint8_t* enc_mac_keys,
size_t num_keys,
const OEMCrypto_KeyObject* key_array);
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_GenerateRSASignature_V8(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length);
namespace wvcdm { namespace wvcdm {
@@ -70,12 +60,16 @@ const char* OEMCrypto_SecurityLevel(SecurityLevel level) {
return ::OEMCrypto_SecurityLevel(); return ::OEMCrypto_SecurityLevel();
} }
bool OEMCrypto_SupportsUsageTable(SecurityLevel level) {
return ::OEMCrypto_SupportsUsageTable();
}
extern "C" OEMCryptoResult OEMCrypto_LoadKeys( extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
const uint8_t* signature, size_t signature_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, const uint8_t* enc_mac_key_iv, const uint8_t* enc_mac_key, size_t num_keys,
const OEMCrypto_KeyObject* key_array, const OEMCrypto_KeyObject* key_array, const uint8_t* pst,
const uint8_t* pst, size_t pst_length) { size_t pst_length) {
return OEMCrypto_LoadKeys_V8(session, message, message_length, signature, return OEMCrypto_LoadKeys_V8(session, message, message_length, signature,
signature_length, enc_mac_key_iv, enc_mac_key, signature_length, enc_mac_key_iv, enc_mac_key,
num_keys, key_array); num_keys, key_array);
@@ -83,45 +77,40 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys(
extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature( extern "C" OEMCryptoResult OEMCrypto_GenerateRSASignature(
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme) { uint8_t* signature, size_t* signature_length,
RSA_Padding_Scheme padding_scheme) {
return OEMCrypto_GenerateRSASignature_V8(session, message, message_length, return OEMCrypto_GenerateRSASignature_V8(session, message, message_length,
signature, signature_length); signature, signature_length);
} }
extern "C" extern "C" OEMCryptoResult OEMCrypto_GetHDCPCapability(
OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current, OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum) {
OEMCrypto_HDCP_Capability *maximum) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED; return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} }
extern "C" bool OEMCrypto_SupportsUsageTable() { extern "C" bool OEMCrypto_SupportsUsageTable() { return false; }
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
}
extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() { extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED; return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} }
extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t *pst, extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst,
size_t pst_length) { size_t pst_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED; return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} }
extern "C" OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, extern "C" OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
const uint8_t *pst, const uint8_t* pst,
size_t pst_length, size_t pst_length,
OEMCrypto_PST_Report *buffer, OEMCrypto_PST_Report* buffer,
size_t *buffer_length) { size_t* buffer_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED; return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} }
extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(OEMCrypto_SESSION session, extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry(
const uint8_t* pst, OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length,
size_t pst_length, const uint8_t* message, size_t message_length, const uint8_t* signature,
const uint8_t *message, size_t signature_length) {
size_t message_length,
const uint8_t *signature,
size_t signature_length) {
return OEMCrypto_ERROR_NOT_IMPLEMENTED; return OEMCrypto_ERROR_NOT_IMPLEMENTED;
} }
}; // namespace wvcdm }; // namespace wvcdm

View File

@@ -16,17 +16,12 @@
namespace wvcdm { namespace wvcdm {
PolicyEngine::PolicyEngine() { PolicyEngine::PolicyEngine() { Init(new Clock()); }
Init(new Clock());
}
PolicyEngine::PolicyEngine(Clock* clock) { PolicyEngine::PolicyEngine(Clock* clock) { Init(clock); }
Init(clock);
}
PolicyEngine::~PolicyEngine() { PolicyEngine::~PolicyEngine() {
if (clock_) if (clock_) delete clock_;
delete clock_;
} }
void PolicyEngine::Init(Clock* clock) { void PolicyEngine::Init(Clock* clock) {
@@ -45,7 +40,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
// License expiration trumps all. // License expiration trumps all.
if ((IsLicenseDurationExpired(current_time) || if ((IsLicenseDurationExpired(current_time) ||
IsPlaybackDurationExpired(current_time)) && IsPlaybackDurationExpired(current_time)) &&
license_state_ != kLicenseStateExpired) { license_state_ != kLicenseStateExpired) {
license_state_ = kLicenseStateExpired; license_state_ = kLicenseStateExpired;
can_decrypt_ = false; can_decrypt_ = false;
@@ -59,8 +54,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
// Test to determine if renewal should be attempted. // Test to determine if renewal should be attempted.
switch (license_state_) { switch (license_state_) {
case kLicenseStateCanPlay: { case kLicenseStateCanPlay: {
if (IsRenewalDelayExpired(current_time)) if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
renewal_needed = true;
break; break;
} }
@@ -70,8 +64,7 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
} }
case kLicenseStateWaitingLicenseUpdate: { case kLicenseStateWaitingLicenseUpdate: {
if (IsRenewalRetryIntervalExpired(current_time)) if (IsRenewalRetryIntervalExpired(current_time)) renewal_needed = true;
renewal_needed = true;
break; break;
} }
@@ -112,8 +105,7 @@ void PolicyEngine::SetLicense(
void PolicyEngine::UpdateLicense( void PolicyEngine::UpdateLicense(
const video_widevine_server::sdk::License& license) { const video_widevine_server::sdk::License& license) {
if (!license.has_policy()) if (!license.has_policy()) return;
return;
if (kLicenseStateExpired == license_state_) { if (kLicenseStateExpired == license_state_) {
LOGD("PolicyEngine::UpdateLicense: updating an expired license"); LOGD("PolicyEngine::UpdateLicense: updating an expired license");
@@ -147,8 +139,7 @@ void PolicyEngine::UpdateLicense(
policy_max_duration_seconds_ = policy_.rental_duration_seconds(); policy_max_duration_seconds_ = policy_.rental_duration_seconds();
if ((policy_.license_duration_seconds() > 0) && if ((policy_.license_duration_seconds() > 0) &&
((policy_.license_duration_seconds() < ((policy_.license_duration_seconds() < policy_max_duration_seconds_) ||
policy_max_duration_seconds_) ||
policy_max_duration_seconds_ == 0)) { policy_max_duration_seconds_ == 0)) {
policy_max_duration_seconds_ = policy_.license_duration_seconds(); policy_max_duration_seconds_ = policy_.license_duration_seconds();
} }
@@ -197,18 +188,18 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
std::stringstream ss; std::stringstream ss;
int64_t current_time = clock_->GetCurrentTime(); int64_t current_time = clock_->GetCurrentTime();
if (license_state_ == kLicenseStateInitial) if (license_state_ == kLicenseStateInitial) return UNKNOWN_ERROR;
return UNKNOWN_ERROR;
(*key_info)[QUERY_KEY_LICENSE_TYPE] = (*key_info)[QUERY_KEY_LICENSE_TYPE] =
license_id_.type() == video_widevine_server::sdk::STREAMING ? license_id_.type() == video_widevine_server::sdk::STREAMING
QUERY_VALUE_STREAMING : QUERY_VALUE_OFFLINE; ? QUERY_VALUE_STREAMING
(*key_info)[QUERY_KEY_PLAY_ALLOWED] = policy_.can_play() ? : QUERY_VALUE_OFFLINE;
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; (*key_info)[QUERY_KEY_PLAY_ALLOWED] =
(*key_info)[QUERY_KEY_PERSIST_ALLOWED] = policy_.can_persist() ? policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; (*key_info)[QUERY_KEY_PERSIST_ALLOWED] =
(*key_info)[QUERY_KEY_RENEW_ALLOWED] = policy_.can_renew() ? policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; (*key_info)[QUERY_KEY_RENEW_ALLOWED] =
policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE;
ss << GetLicenseDurationRemaining(current_time); ss << GetLicenseDurationRemaining(current_time);
(*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str(); (*key_info)[QUERY_KEY_LICENSE_DURATION_REMAINING] = ss.str();
ss.str(""); ss.str("");
@@ -229,15 +220,14 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
// will always return false if the value is 0. // will always return false if the value is 0.
bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) { bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
return policy_max_duration_seconds_ && return policy_max_duration_seconds_ &&
license_start_time_ + policy_max_duration_seconds_ <= license_start_time_ + policy_max_duration_seconds_ <= current_time;
current_time;
} }
int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) { int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
if (0 == policy_max_duration_seconds_) return LLONG_MAX; if (0 == policy_max_duration_seconds_) return LLONG_MAX;
int64_t remaining_time = policy_max_duration_seconds_ int64_t remaining_time =
+ license_start_time_ - current_time; policy_max_duration_seconds_ + license_start_time_ - current_time;
if (remaining_time < 0) if (remaining_time < 0)
remaining_time = 0; remaining_time = 0;
@@ -247,43 +237,38 @@ int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
} }
bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) { bool PolicyEngine::IsPlaybackDurationExpired(int64_t current_time) {
return (policy_.playback_duration_seconds() > 0) && return (policy_.playback_duration_seconds() > 0) && playback_start_time_ &&
playback_start_time_ && playback_start_time_ + policy_.playback_duration_seconds() <=
playback_start_time_ + policy_.playback_duration_seconds() <= current_time;
current_time;
} }
int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) { int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
if (0 == policy_.playback_duration_seconds()) return LLONG_MAX; if (0 == policy_.playback_duration_seconds()) return LLONG_MAX;
if (0 == playback_start_time_) return policy_.playback_duration_seconds(); if (0 == playback_start_time_) return policy_.playback_duration_seconds();
int64_t remaining_time = policy_.playback_duration_seconds() int64_t remaining_time =
+ playback_start_time_ - current_time; policy_.playback_duration_seconds() + playback_start_time_ - current_time;
if (remaining_time < 0) remaining_time = 0; if (remaining_time < 0) remaining_time = 0;
return remaining_time; return remaining_time;
} }
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) { bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
return policy_.can_renew() && return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) &&
(policy_.renewal_delay_seconds() > 0) && license_start_time_ + policy_.renewal_delay_seconds() <= current_time;
license_start_time_ + policy_.renewal_delay_seconds() <=
current_time;
} }
bool PolicyEngine::IsRenewalRecoveryDurationExpired( bool PolicyEngine::IsRenewalRecoveryDurationExpired(int64_t current_time) {
int64_t current_time) { // NOTE: Renewal Recovery Duration is currently not used.
// NOTE: Renewal Recovery Duration is currently not used.
return (policy_.renewal_recovery_duration_seconds() > 0) && return (policy_.renewal_recovery_duration_seconds() > 0) &&
license_start_time_ + policy_.renewal_recovery_duration_seconds() <= license_start_time_ + policy_.renewal_recovery_duration_seconds() <=
current_time; current_time;
} }
bool PolicyEngine::IsRenewalRetryIntervalExpired( bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
int64_t current_time) {
return policy_.can_renew() && return policy_.can_renew() &&
(policy_.renewal_retry_interval_seconds() > 0) && (policy_.renewal_retry_interval_seconds() > 0) &&
next_renewal_time_ <= current_time; next_renewal_time_ <= current_time;
} }
} // wvcdm } // wvcdm

View File

@@ -13,9 +13,7 @@ AesCbcKey::AesCbcKey() {}
AesCbcKey::~AesCbcKey() {} AesCbcKey::~AesCbcKey() {}
bool AesCbcKey::Init(const std::string& key) { bool AesCbcKey::Init(const std::string& key) { return false; }
return false;
}
bool AesCbcKey::Encrypt(const std::string& in, std::string* out, bool AesCbcKey::Encrypt(const std::string& in, std::string* out,
std::string* iv) { std::string* iv) {
@@ -26,9 +24,7 @@ RsaPublicKey::RsaPublicKey() {}
RsaPublicKey::~RsaPublicKey() {} RsaPublicKey::~RsaPublicKey() {}
bool RsaPublicKey::Init(const std::string& serialized_key) { bool RsaPublicKey::Init(const std::string& serialized_key) { return false; }
return false;
}
bool RsaPublicKey::Encrypt(const std::string& clear_message, bool RsaPublicKey::Encrypt(const std::string& clear_message,
std::string* encrypted_message) { std::string* encrypted_message) {

View File

@@ -43,7 +43,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
unsigned char lsb = 0; // least significant 4 bits unsigned char lsb = 0; // least significant 4 bits
if (!CharToDigit(byte[i * 2], &msb) || if (!CharToDigit(byte[i * 2], &msb) ||
!CharToDigit(byte[i * 2 + 1], &lsb)) { !CharToDigit(byte[i * 2 + 1], &lsb)) {
LOGE("Invalid hex value %c%c at index %d", byte[i*2], byte[i*2+1], i); LOGE("Invalid hex value %c%c at index %d",
byte[i * 2], byte[i * 2 + 1], i);
return array; return array;
} }
array.push_back((msb << 4) | lsb); array.push_back((msb << 4) | lsb);
@@ -53,8 +54,8 @@ std::vector<uint8_t> a2b_hex(const std::string& byte) {
// converts an ascii hex string(2 bytes per digit) into a decimal byte string // converts an ascii hex string(2 bytes per digit) into a decimal byte string
// dump the string with the label. // dump the string with the label.
std::vector<uint8_t> a2b_hex(const std::string& label, const std::string& byte) { std::vector<uint8_t> a2b_hex(const std::string& label,
const std::string& byte) {
std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]" std::cout << std::endl << "[[DUMP: " << label << " ]= \"" << byte << "\"]"
<< std::endl << std::endl; << std::endl << std::endl;
@@ -71,7 +72,7 @@ std::string b2a_hex(const std::vector<uint8_t>& byte) {
} }
std::string b2a_hex(const std::string& byte) { std::string b2a_hex(const std::string& byte) {
return HexEncode(reinterpret_cast<const uint8_t *>(byte.data()), return HexEncode(reinterpret_cast<const uint8_t*>(byte.data()),
byte.length()); byte.length());
} }
@@ -90,9 +91,8 @@ std::string Base64SafeEncode(const std::vector<uint8_t>& bin_input) {
int in_size = bin_input.size(); int in_size = bin_input.size();
std::string b64_output(modp_b64w_encode_len(in_size), 0); std::string b64_output(modp_b64w_encode_len(in_size), 0);
int out_size = modp_b64w_encode(&b64_output[0], int out_size = modp_b64w_encode(
reinterpret_cast<const char*>(&bin_input[0]), &b64_output[0], reinterpret_cast<const char*>(&bin_input[0]), in_size);
in_size);
if (out_size == -1) { if (out_size == -1) {
LOGE("Base64SafeEncode failed"); LOGE("Base64SafeEncode failed");
return std::string(); return std::string();
@@ -119,8 +119,7 @@ std::vector<uint8_t> Base64SafeDecode(const std::string& b64_input) {
int in_size = b64_input.size(); int in_size = b64_input.size();
std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0); std::vector<uint8_t> bin_output(modp_b64w_decode_len(in_size), 0);
int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]), int out_size = modp_b64w_decode(reinterpret_cast<char*>(&bin_output[0]),
b64_input.data(), b64_input.data(), in_size);
in_size);
if (out_size == -1) { if (out_size == -1) {
LOGE("Base64SafeDecode failed"); LOGE("Base64SafeDecode failed");
return std::vector<uint8_t>(0); return std::vector<uint8_t>(0);

View File

@@ -38,28 +38,28 @@ const std::string kTwoBytesOverB64Data("SGVsbG8gRnJpZW5kISE=");
const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg="; const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = { const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
make_pair(&kNullString, &kNullString), make_pair(&kNullString, &kNullString),
make_pair(&kf, &kfB64), make_pair(&kf, &kfB64),
make_pair(&kfo, &kfoB64), make_pair(&kfo, &kfoB64),
make_pair(&kfoo, &kfooB64), make_pair(&kfoo, &kfooB64),
make_pair(&kfoob, &kfoobB64), make_pair(&kfoob, &kfoobB64),
make_pair(&kfooba, &kfoobaB64), make_pair(&kfooba, &kfoobaB64),
make_pair(&kfoobar, &kfoobarB64), make_pair(&kfoobar, &kfoobarB64),
make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data), make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data),
make_pair(&kOneByteOverData, &kOneByteOverB64Data), make_pair(&kOneByteOverData, &kOneByteOverB64Data),
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data), make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
make_pair(&kTestData, &kB64TestData), make_pair(&kTestData, &kB64TestData)};
};
} // unnamed namespace } // unnamed namespace
namespace wvcdm { namespace wvcdm {
class Base64EncodeDecodeTest : public ::testing::TestWithParam< class Base64EncodeDecodeTest
std::pair<const std::string*, const std::string*> > {}; : public ::testing::TestWithParam<
std::pair<const std::string*, const std::string*> > {};
TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) { TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
std::pair<const std::string*, const std::string*> values = GetParam(); std::pair<const std::string *, const std::string *> values = GetParam();
std::vector<uint8_t> decoded_vector = Base64SafeDecode(values.second->data()); std::vector<uint8_t> decoded_vector = Base64SafeDecode(values.second->data());
std::string decoded_string(decoded_vector.begin(), decoded_vector.end()); std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(values.first->data(), decoded_string.data()); EXPECT_STREQ(values.first->data(), decoded_string.data());

View File

@@ -1,4 +1,8 @@
// Copyright 2013 Google Inc. All Rights Reserved. // Copyright 2013 Google Inc. All Rights Reserved.
// These tests are for the cdm engine, and code below it in the stack. In
// particular, we assume that the OEMCrypo layer works, and has a valid keybox.
// This is because we need a valid RSA certificate, and will attempt to connect
// to the provisioning server to request one if we don't.
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
@@ -14,6 +18,7 @@
#include "properties.h" #include "properties.h"
#include "scoped_ptr.h" #include "scoped_ptr.h"
#include "string_conversions.h" #include "string_conversions.h"
#include "test_printers.h"
#include "url_request.h" #include "url_request.h"
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
@@ -31,60 +36,6 @@ wvcdm::CdmKeySystem g_key_system;
std::string g_license_server; std::string g_license_server;
wvcdm::KeyId g_wrong_key_id; wvcdm::KeyId g_wrong_key_id;
// This is an RSA certificate message from the provisioning server.
// The client sends this certificate to a license server for device
// authentication by the license server.
// This certificate is used to test the CDM engine's provisioning
// response handling.
static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
"{\"signedResponse\": {"
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
const std::string kCencMimeType = "video/mp4"; const std::string kCencMimeType = "video/mp4";
const std::string kWebmMimeType = "video/webm"; const std::string kWebmMimeType = "video/webm";
@@ -95,14 +46,39 @@ namespace wvcdm {
class WvCdmEngineTest : public testing::Test { class WvCdmEngineTest : public testing::Test {
public: public:
virtual void SetUp() { virtual void SetUp() {
cdm_engine_.OpenSession(g_key_system, NULL, &session_id_); CdmResponseType status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
if (status == NEED_PROVISIONING) {
Provision();
status = cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
}
ASSERT_EQ(NO_ERROR, status);
ASSERT_NE("", session_id_) << "Could not open CDM session.";
} }
virtual void TearDown() { virtual void TearDown() { cdm_engine_.CloseSession(session_id_); }
cdm_engine_.CloseSession(session_id_);
}
protected: protected:
void Provision() {
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
ASSERT_EQ(NO_ERROR,
cdm_engine_.GetProvisioningRequest(cert_type,
cert_authority,
&prov_request,
&provisioning_server_url));
UrlRequest url_request(provisioning_server_url);
url_request.PostCertRequestInQueryString(prov_request);
std::string message;
bool ok = url_request.GetResponse(&message);
EXPECT_TRUE(ok);
ASSERT_EQ(NO_ERROR,
cdm_engine_.HandleProvisioningResponse(message,
&cert, &wrapped_key));
}
void GenerateKeyRequest(const std::string& key_id, void GenerateKeyRequest(const std::string& key_id,
const std::string& init_data_type_string) { const std::string& init_data_type_string) {
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
@@ -112,25 +88,30 @@ class WvCdmEngineTest : public testing::Test {
InitializationData init_data(init_data_type_string, key_id); InitializationData init_data(init_data_type_string, key_id);
EXPECT_EQ(KEY_MESSAGE, EXPECT_EQ(KEY_MESSAGE,
cdm_engine_.GenerateKeyRequest(session_id_, cdm_engine_.GenerateKeyRequest(
key_set_id, session_id_, key_set_id, init_data, kLicenseTypeStreaming,
init_data, app_parameters, &key_msg_, &server_url, NULL));
kLicenseTypeStreaming,
app_parameters,
&key_msg_,
&server_url));
} }
void GenerateRenewalRequest() { void GenerateRenewalRequest() {
EXPECT_EQ(KEY_MESSAGE, EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateRenewalRequest(
cdm_engine_.GenerateRenewalRequest(session_id_, session_id_, &key_msg_, &server_url_));
&key_msg_, }
&server_url_));
std::string GetKeyRequestResponse(const std::string& server_url,
const std::string& client_auth) {
return GetKeyRequestResponse(server_url, client_auth, true);
}
std::string FailToGetKeyRequestResponse(const std::string& server_url,
const std::string& client_auth) {
return GetKeyRequestResponse(server_url, client_auth, false);
} }
// posts a request and extracts the drm message from the response // posts a request and extracts the drm message from the response
std::string GetKeyRequestResponse(const std::string& server_url, std::string GetKeyRequestResponse(const std::string& server_url,
const std::string& client_auth) { const std::string& client_auth,
bool expect_success) {
// Use secure connection and chunk transfer coding. // Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth); UrlRequest url_request(server_url + client_auth);
if (!url_request.is_connected()) { if (!url_request.is_connected()) {
@@ -144,7 +125,7 @@ class WvCdmEngineTest : public testing::Test {
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
int status_code = url_request.GetStatusCode(response); int status_code = url_request.GetStatusCode(response);
EXPECT_EQ(kHttpOk, status_code); if (expect_success) EXPECT_EQ(kHttpOk, status_code);
if (status_code != kHttpOk) { if (status_code != kHttpOk) {
return ""; return "";
@@ -153,24 +134,23 @@ class WvCdmEngineTest : public testing::Test {
LicenseRequest lic_request; LicenseRequest lic_request;
lic_request.GetDrmMessage(response, drm_msg); lic_request.GetDrmMessage(response, drm_msg);
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(), LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()), HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str()); drm_msg.size()).c_str());
return drm_msg; return drm_msg;
} }
} }
void VerifyNewKeyResponse(const std::string& server_url, void VerifyNewKeyResponse(const std::string& server_url,
const std::string& client_auth){ const std::string& client_auth) {
std::string resp = GetKeyRequestResponse(server_url, std::string resp = GetKeyRequestResponse(server_url, client_auth);
client_auth);
CdmKeySetId key_set_id; CdmKeySetId key_set_id;
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.AddKey(session_id_, resp, &key_set_id)); EXPECT_EQ(wvcdm::KEY_ADDED,
cdm_engine_.AddKey(session_id_, resp, &key_set_id));
} }
void VerifyRenewalKeyResponse(const std::string& server_url, void VerifyRenewalKeyResponse(const std::string& server_url,
const std::string& client_auth) { const std::string& client_auth) {
std::string resp = GetKeyRequestResponse(server_url, std::string resp = GetKeyRequestResponse(server_url, client_auth);
client_auth);
EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp)); EXPECT_EQ(wvcdm::KEY_ADDED, cdm_engine_.RenewKey(session_id_, resp));
} }
@@ -180,18 +160,9 @@ class WvCdmEngineTest : public testing::Test {
std::string server_url_; std::string server_url_;
}; };
TEST(WvCdmProvisioningTest, ProvisioningTest) { // Test that provisioning works, even if device is already provisioned.
CdmEngine cdm_engine; TEST_F(WvCdmEngineTest, ProvisioningTest) {
CdmProvisioningRequest prov_request; Provision();
std::string provisioning_server_url;
CdmCertificateType cert_type = kCertificateWidevine;
std::string cert_authority;
std::string cert, wrapped_key;
cdm_engine.GetProvisioningRequest(cert_type, cert_authority,
&prov_request, &provisioning_server_url);
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse,
&cert, &wrapped_key);
} }
TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) { TEST_F(WvCdmEngineTest, BaseIsoBmffMessageTest) {
@@ -211,7 +182,7 @@ TEST_F(WvCdmEngineTest, WrongMessageTest) {
// We should receive a response with no license, i.e. the extracted license // We should receive a response with no license, i.e. the extracted license
// response message should be empty. // response message should be empty.
ASSERT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth)); ASSERT_EQ("", FailToGetKeyRequestResponse(g_license_server, g_client_auth));
} }
TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) { TEST_F(WvCdmEngineTest, NormalDecryptionIsoBmff) {
@@ -236,11 +207,11 @@ TEST_F(WvCdmEngineTest, LicenseRenewal) {
} // namespace wvcdm } // namespace wvcdm
int main(int argc, char **argv) { int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
wvcdm::InitLogging(argc, argv); wvcdm::InitLogging(argc, argv);
wvcdm::ConfigTestEnv config(wvcdm::kGooglePlayServer); wvcdm::ConfigTestEnv config(wvcdm::kContentProtectionServer);
g_client_auth.assign(config.client_auth()); g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system()); g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id()); g_wrong_key_id.assign(config.wrong_key_id());
@@ -252,14 +223,14 @@ int main(int argc, char **argv) {
int show_usage = 0; int show_usage = 0;
static const struct option long_options[] = { static const struct option long_options[] = {
{ "keyid", required_argument, NULL, 'k' }, {"keyid", required_argument, NULL, 'k'},
{ "server", required_argument, NULL, 's' }, {"server", required_argument, NULL, 's'},
{ NULL, 0, NULL, '\0' } {NULL, 0, NULL, '\0'}};
};
int option_index = 0; int option_index = 0;
int opt = 0; int opt = 0;
while ((opt = getopt_long(argc, argv, "k:s:v", long_options, &option_index)) != -1) { while ((opt = getopt_long(argc, argv, "k:s:v", long_options,
&option_index)) != -1) {
switch (opt) { switch (opt) {
case 'k': { case 'k': {
g_key_id_pssh.clear(); g_key_id_pssh.clear();
@@ -292,11 +263,15 @@ int main(int argc, char **argv) {
if (show_usage) { if (show_usage) {
std::cout << std::endl; std::cout << std::endl;
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl; std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl; std::cout << " enclose multiple arguments in '' when using adb shell"
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl; << std::endl;
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'"
<< std::endl << std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>"; std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout << "configure the license server url, please include http[s] in the url" << std::endl; std::cout
<< "configure the license server url, please include http[s] in the url"
<< std::endl;
std::cout << std::setw(30) << std::left << " "; std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << license_server << std::endl; std::cout << "default: " << license_server << std::endl;

View File

@@ -6,6 +6,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "properties.h" #include "properties.h"
#include "string_conversions.h" #include "string_conversions.h"
#include "test_printers.h"
namespace { namespace {
const std::string kToken = wvcdm::a2bs_hex( const std::string kToken = wvcdm::a2bs_hex(
@@ -111,7 +112,7 @@ class MockCryptoSession : public CryptoSession {
class MockPolicyEngine : public PolicyEngine { class MockPolicyEngine : public PolicyEngine {
public: public:
// Leaving a place holder for when PolicyEngine methods need to be mocked // Leaving a place holder for when PolicyEngine methods need to be mocked
}; };
class MockCdmLicense : public CdmLicense { class MockCdmLicense : public CdmLicense {
@@ -132,10 +133,7 @@ class CdmSessionTest : public ::testing::Test {
if (cdm_session_) delete cdm_session_; if (cdm_session_) delete cdm_session_;
} }
void CreateSession() { void CreateSession() { CreateSession(NULL); }
cdm_session_ = new CdmSession(license_parser_, crypto_session_,
policy_engine_, file_handle_, NULL);
}
void CreateSession(const CdmClientPropertySet* cdm_client_property_set) { void CreateSession(const CdmClientPropertySet* cdm_client_property_set) {
cdm_session_ = cdm_session_ =

View File

@@ -6,25 +6,24 @@ namespace {
const std::string kWidevineKeySystem = "com.widevine.alpha"; const std::string kWidevineKeySystem = "com.widevine.alpha";
// Content Protection license server data // Content Protection license server data
const std::string kCpLicenseServer = const std::string kCpLicenseServer = "http://widevine-proxy.appspot.com/proxy";
"http://wv-ref-eme-player.appspot.com/proxy";
const std::string kCpClientAuth = ""; const std::string kCpClientAuth = "";
const std::string kCpKeyId = const std::string kCpKeyId =
"00000042" // blob size "00000042" // blob size
"70737368" // "pssh" "70737368" // "pssh"
"00000000" // flags "00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000022" // pssh data size "00000022" // pssh data size
// pssh data: // pssh data:
"08011a0d7769646576696e655f746573" "08011a0d7769646576696e655f746573"
"74220f73747265616d696e675f636c69" "74220f73747265616d696e675f636c69"
"7031"; "7031";
const std::string kCpOfflineKeyId = const std::string kCpOfflineKeyId =
"00000040" // blob size "00000040" // blob size
"70737368" // "pssh" "70737368" // "pssh"
"00000000" // flags "00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000020" // pssh data size "00000020" // pssh data size
// pssh data: // pssh data:
"08011a0d7769646576696e655f746573" "08011a0d7769646576696e655f746573"
"74220d6f66666c696e655f636c697031"; "74220d6f66666c696e655f636c697031";
@@ -40,18 +39,17 @@ const std::string kGpLicenseServer =
const std::string kGpClientAuth = const std::string kGpClientAuth =
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
const std::string kGpKeyId = const std::string kGpKeyId =
"00000034" // blob size "00000034" // blob size
"70737368" // "pssh" "70737368" // "pssh"
"00000000" // flags "00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000014" // pssh data size "00000014" // pssh data size
// pssh data: // pssh data:
"08011210e02562e04cd55351b14b3d74" "08011210e02562e04cd55351b14b3d74"
"8d36ed8e"; "8d36ed8e";
const std::string kGpOfflineKeyId = kGpKeyId; const std::string kGpOfflineKeyId = kGpKeyId;
const std::string kGpClientOfflineQueryParameters = const std::string kGpClientOfflineQueryParameters = "&offline=true";
"&offline=true";
const std::string kGpClientOfflineRenewalQueryParameters = const std::string kGpClientOfflineRenewalQueryParameters =
"&offline=true&renewal=true"; "&offline=true&renewal=true";
const std::string kGpClientOfflineReleaseQueryParameters = const std::string kGpClientOfflineReleaseQueryParameters =
@@ -59,11 +57,11 @@ const std::string kGpClientOfflineReleaseQueryParameters =
// An invalid key id, expected to fail // An invalid key id, expected to fail
const std::string kWrongKeyId = const std::string kWrongKeyId =
"00000034" // blob size "00000034" // blob size
"70737368" // "pssh" "70737368" // "pssh"
"00000000" // flags "00000000" // flags
"edef8ba979d64acea3c827dcd51d21ed" // Widevine system id "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"00000014" // pssh data size "00000014" // pssh data size
// pssh data: // pssh data:
"0901121094889920e8d6520098577df8" "0901121094889920e8d6520098577df8"
"f2dd5546"; "f2dd5546";
@@ -75,24 +73,21 @@ const std::string kProductionProvisioningServerUrl =
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = { const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{ wvcdm::kGooglePlayServer, kGpLicenseServer, {wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
kGpClientAuth, kGpKeyId, kGpOfflineKeyId }, kGpOfflineKeyId},
{ wvcdm::kContentProtectionServer, kCpLicenseServer, {wvcdm::kContentProtectionServer, kCpLicenseServer, kCpClientAuth, kCpKeyId,
kCpClientAuth, kCpKeyId, kCpOfflineKeyId }, kCpOfflineKeyId},
}; };
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) { ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) { Init(server_id); }
Init(server_id);
}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) { ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) {
Init(server_id); Init(server_id);
if (!streaming) if (!streaming) key_id_ = license_servers[server_id].offline_key_id;
key_id_ = license_servers[server_id].offline_key_id;
} }
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming, ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
@@ -119,7 +114,7 @@ void ConfigTestEnv::Init(LicenseServerId server_id) {
key_system_ = kWidevineKeySystem; key_system_ = kWidevineKeySystem;
license_server_ = license_servers[server_id].url; license_server_ = license_servers[server_id].url;
provisioning_server_url_ = kProductionProvisioningServerUrl; provisioning_server_url_ = kProductionProvisioningServerUrl;
wrong_key_id_= kWrongKeyId; wrong_key_id_ = kWrongKeyId;
} }
} // namespace wvcdm } // namespace wvcdm

View File

@@ -16,6 +16,7 @@ using ::testing::AllOf;
using ::testing::Eq; using ::testing::Eq;
using ::testing::Gt; using ::testing::Gt;
using ::testing::HasSubstr; using ::testing::HasSubstr;
using ::testing::InSequence;
using ::testing::NotNull; using ::testing::NotNull;
using ::testing::Return; using ::testing::Return;
using ::testing::ReturnArg; using ::testing::ReturnArg;
@@ -1786,6 +1787,36 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id)); EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id));
} }
TEST_F(DeviceFilesTest, ReserveLicenseIds) {
MockFile file;
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.Times(kNumberOfLicenses)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
for (size_t i = 0; i < kNumberOfLicenses; ++i) {
std::string license_path = device_base_path_ +
license_test_data[i].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
InSequence calls;
EXPECT_CALL(file, Open(StrEq(license_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(StrEq(DeviceFiles::GetBlankFileData()),
DeviceFiles::GetBlankFileData().size()))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close());
}
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
for (size_t i = 0; i < kNumberOfLicenses; i++) {
EXPECT_TRUE(device_files.ReserveLicenseId(license_test_data[i].key_set_id));
}
}
TEST_P(DeviceFilesUsageInfoTest, Read) { TEST_P(DeviceFilesUsageInfoTest, Read) {
MockFile file; MockFile file;
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(); std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
@@ -1824,8 +1855,8 @@ TEST_P(DeviceFilesUsageInfoTest, Read) {
for (size_t i = 0; i < license_info.size(); ++i) { for (size_t i = 0; i < license_info.size(); ++i) {
bool found = false; bool found = false;
for (size_t j = 0; j <= static_cast<size_t>(index); ++j) { for (size_t j = 0; j <= static_cast<size_t>(index); ++j) {
if ((license_info[i].first.compare( if ((license_info[i]
kUsageInfoTestData[j].license_request) == 0) && .first.compare(kUsageInfoTestData[j].license_request) == 0) &&
(license_info[i].second.compare(kUsageInfoTestData[j].license) == (license_info[i].second.compare(kUsageInfoTestData[j].license) ==
0)) { 0)) {
found = true; found = true;

View File

@@ -257,8 +257,7 @@ TEST_F(FileTest, ListFiles) {
EXPECT_EQ(3u, files.size()); EXPECT_EQ(3u, files.size());
for (size_t i = 0; i < files.size(); ++i) { for (size_t i = 0; i < files.size(); ++i) {
EXPECT_TRUE(files[i] == kTestDirName || EXPECT_TRUE(files[i] == kTestDirName || files[i] == kTestFileName ||
files[i] == kTestFileName ||
files[i] == kTestFileName2); files[i] == kTestFileName2);
} }
} }

View File

@@ -41,8 +41,7 @@ SSL_CTX* InitSslContext() {
SSL_load_error_strings(); SSL_load_error_strings();
method = SSLv3_client_method(); method = SSLv3_client_method();
ctx = SSL_CTX_new(method); ctx = SSL_CTX_new(method);
if (!ctx) if (!ctx) LOGE("failed to create SSL context");
LOGE("failed to create SSL context");
return ctx; return ctx;
} }
@@ -77,8 +76,8 @@ bool SocketWait(int fd, bool for_read, int timeout_in_ms) {
tv.tv_sec = timeout_in_ms / 1000; tv.tv_sec = timeout_in_ms / 1000;
tv.tv_usec = (timeout_in_ms % 1000) * 1000; tv.tv_usec = (timeout_in_ms % 1000) * 1000;
fd_set *read_fds = NULL; fd_set* read_fds = NULL;
fd_set *write_fds = NULL; fd_set* write_fds = NULL;
if (for_read) { if (for_read) {
read_fds = &fds; read_fds = &fds;
} else { } else {
@@ -104,12 +103,9 @@ namespace wvcdm {
// Parses the URL and extracts all relevant information. // Parses the URL and extracts all relevant information.
// static // static
bool HttpSocket::ParseUrl(const std::string& url, bool HttpSocket::ParseUrl(const std::string& url, std::string* scheme,
std::string* scheme, bool* secure_connect, std::string* domain_name,
bool* secure_connect, int* port, std::string* path) {
std::string* domain_name,
int* port,
std::string* path) {
size_t offset = 0; size_t offset = 0;
if (!Tokenize(url, "://", offset, scheme, &offset)) { if (!Tokenize(url, "://", offset, scheme, &offset)) {
@@ -143,8 +139,7 @@ bool HttpSocket::ParseUrl(const std::string& url,
// The domain name may optionally contain a port which overrides the default. // The domain name may optionally contain a port which overrides the default.
std::string domain_name_without_port; std::string domain_name_without_port;
size_t port_offset; size_t port_offset;
if (Tokenize(*domain_name, ":", 0, &domain_name_without_port, if (Tokenize(*domain_name, ":", 0, &domain_name_without_port, &port_offset)) {
&port_offset)) {
*port = atoi(domain_name->c_str() + port_offset); *port = atoi(domain_name->c_str() + port_offset);
if (*port <= 0 || *port >= 65536) { if (*port <= 0 || *port >= 65536) {
LOGE("Invalid URL, port not valid: %s", url.c_str()); LOGE("Invalid URL, port not valid: %s", url.c_str());
@@ -157,17 +152,13 @@ bool HttpSocket::ParseUrl(const std::string& url,
} }
HttpSocket::HttpSocket(const std::string& url) HttpSocket::HttpSocket(const std::string& url)
: socket_fd_(-1), : socket_fd_(-1), ssl_(NULL), ssl_ctx_(NULL) {
ssl_(NULL),
ssl_ctx_(NULL) {
valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_, valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_,
&resource_path_); &resource_path_);
SSL_library_init(); SSL_library_init();
} }
HttpSocket::~HttpSocket() { HttpSocket::~HttpSocket() { CloseSocket(); }
CloseSocket();
}
void HttpSocket::CloseSocket() { void HttpSocket::CloseSocket() {
if (socket_fd_ != -1) { if (socket_fd_ != -1) {
@@ -223,8 +214,8 @@ bool HttpSocket::Connect(int timeout_in_ms) {
} }
// set the port // set the port
struct sockaddr_in* addr_ipv4 = reinterpret_cast<struct sockaddr_in*>( struct sockaddr_in* addr_ipv4 =
addr_info->ai_addr); reinterpret_cast<struct sockaddr_in*>(addr_info->ai_addr);
addr_ipv4->sin_port = htons(port_); addr_ipv4->sin_port = htons(port_);
// connect to the server // connect to the server
@@ -276,8 +267,7 @@ bool HttpSocket::Connect(int timeout_in_ms) {
ret = SSL_connect(ssl_); ret = SSL_connect(ssl_);
if (ret != 1) { if (ret != 1) {
int ssl_err = SSL_get_error(ssl_, ret); int ssl_err = SSL_get_error(ssl_, ret);
if (ssl_err != SSL_ERROR_WANT_READ && if (ssl_err != SSL_ERROR_WANT_READ && ssl_err != SSL_ERROR_WANT_WRITE) {
ssl_err != SSL_ERROR_WANT_WRITE) {
char buf[256]; char buf[256];
LOGE("SSL_connect error: %s", ERR_error_string(ERR_get_error(), buf)); LOGE("SSL_connect error: %s", ERR_error_string(ERR_get_error(), buf));
CloseSocket(); CloseSocket();

View File

@@ -32,12 +32,9 @@ class HttpSocket {
int Write(const char* data, int len, int timeout_in_ms); int Write(const char* data, int len, int timeout_in_ms);
private: private:
static bool ParseUrl(const std::string& url, static bool ParseUrl(const std::string& url, std::string* scheme,
std::string* scheme, bool* secure_connect, std::string* domain_name,
bool* secure_connect, int* port, std::string* path);
std::string* domain_name,
int* port,
std::string* path);
FRIEND_TEST(HttpSocketTest, ParseUrlTest); FRIEND_TEST(HttpSocketTest, ParseUrlTest);
std::string scheme_; std::string scheme_;

View File

@@ -103,63 +103,63 @@ struct ParseUrlTests {
}; };
ParseUrlTests parse_url_tests[] = { ParseUrlTests parse_url_tests[] = {
{ {
"https://code.google.com/p/googletest/wiki/Primer", // url "https://code.google.com/p/googletest/wiki/Primer", // url
"https", // scheme "https", // scheme
true, // secure_connect true, // secure_connect
"code.google.com", // domain_name "code.google.com", // domain_name
443, // port 443, // port
"/p/googletest/wiki/Primer", // path "/p/googletest/wiki/Primer", // path
}, },
{ {
"http://code.google.com/p/googletest/wiki/Primer/", // url "http://code.google.com/p/googletest/wiki/Primer/", // url
"http", // scheme "http", // scheme
false, // secure_connect false, // secure_connect
"code.google.com", // domain_name "code.google.com", // domain_name
80, // port 80, // port
"/p/googletest/wiki/Primer/", // path "/p/googletest/wiki/Primer/", // path
}, },
{ {
"http://code.google.com/", // url "http://code.google.com/", // url
"http", // scheme "http", // scheme
false, // secure_connect false, // secure_connect
"code.google.com", // domain_name "code.google.com", // domain_name
80, // port 80, // port
"/", // path "/", // path
}, },
{ {
"http://code.google.com", // url "http://code.google.com", // url
"http", // scheme "http", // scheme
false, // secure_connect false, // secure_connect
"code.google.com", // domain_name "code.google.com", // domain_name
80, // port 80, // port
"/", // path "/", // path
}, },
{ {
"http://10.11.12.13:8888/drm", // url "http://10.11.12.13:8888/drm", // url
"http", // scheme "http", // scheme
false, // secure_connect false, // secure_connect
"10.11.12.13", // domain_name "10.11.12.13", // domain_name
8888, // port 8888, // port
"/drm", // path "/drm", // path
}, },
{ {
"http://10.11.12.13:8888", // url "http://10.11.12.13:8888", // url
"http", // scheme "http", // scheme
false, // secure_connect false, // secure_connect
"10.11.12.13", // domain_name "10.11.12.13", // domain_name
8888, // port 8888, // port
"/", // path "/", // path
}, },
{ {
"https://10.11.12.13:8888", // url "https://10.11.12.13:8888", // url
"https", // scheme "https", // scheme
true, // secure_connect true, // secure_connect
"10.11.12.13", // domain_name "10.11.12.13", // domain_name
8888, // port 8888, // port
"/", // path "/", // path
}, },
{ NULL } // list terminator {NULL} // list terminator
}; };
TEST_F(HttpSocketTest, ParseUrlTest) { TEST_F(HttpSocketTest, ParseUrlTest) {

View File

@@ -9,13 +9,13 @@ static const std::string kTwoBlankLines("\r\n\r\n");
size_t LicenseRequest::FindHeaderEndPosition( size_t LicenseRequest::FindHeaderEndPosition(
const std::string& response) const { const std::string& response) const {
return(response.find(kTwoBlankLines)); return response.find(kTwoBlankLines);
} }
// This routine parses the license server's response message and // This routine parses the license server's response message and
// extracts the drm message from the response header. // extracts the drm message from the response header.
void LicenseRequest::GetDrmMessage(const std::string& response, void LicenseRequest::GetDrmMessage(const std::string& response,
std::string& drm_msg) { std::string& drm_msg) {
if (response.empty()) { if (response.empty()) {
drm_msg.clear(); drm_msg.clear();
return; return;
@@ -27,23 +27,25 @@ void LicenseRequest::GetDrmMessage(const std::string& response,
// the drm message length as below instead of using Content-Length // the drm message length as below instead of using Content-Length
size_t header_end_pos = FindHeaderEndPosition(response); size_t header_end_pos = FindHeaderEndPosition(response);
if (header_end_pos != std::string::npos) { if (header_end_pos != std::string::npos) {
header_end_pos += kTwoBlankLines.size(); // points to response body header_end_pos += kTwoBlankLines.size(); // points to response body
drm_msg.clear(); drm_msg.clear();
size_t drm_msg_pos = response.find(kTwoBlankLines, header_end_pos); // Messages from Google Play server add a GLS wrapper. These start
if (drm_msg_pos != std::string::npos) { // with "GLS/1.0 <status>".
drm_msg_pos += kTwoBlankLines.size(); // points to drm message if (response.find("GLS/1.", header_end_pos) == header_end_pos) {
} else { // For GLS messages, we should skip past the next blank line, and use
// For backward compatibility, no blank line after error code // what's left of the message.
drm_msg_pos = response.find("\r\n", header_end_pos); size_t drm_msg_pos = response.find(kTwoBlankLines, header_end_pos);
if (drm_msg_pos != std::string::npos) { if (drm_msg_pos != std::string::npos) {
drm_msg_pos += 2; // points to drm message drm_msg_pos += kTwoBlankLines.size(); // points to drm message
drm_msg = response.substr(drm_msg_pos);
} else {
LOGE("Message had GLS marker, but did not have extra blank line.");
drm_msg = response.substr(header_end_pos);
} }
}
if (drm_msg_pos != std::string::npos) {
drm_msg = response.substr(drm_msg_pos);
} else { } else {
// For servers that do not use the GLS wrapper, we should just strip of
// the headers, and use the rest of the message.
drm_msg = response.substr(header_end_pos); drm_msg = response.substr(header_end_pos);
} }
} else { } else {
@@ -62,11 +64,10 @@ void LicenseRequest::GetHeartbeatUrl(const std::string& response,
size_t header_end_pos = FindHeaderEndPosition(response); size_t header_end_pos = FindHeaderEndPosition(response);
if (header_end_pos != std::string::npos) { if (header_end_pos != std::string::npos) {
header_end_pos += kTwoBlankLines.size(); // points to response body header_end_pos += kTwoBlankLines.size(); // points to response body
heartbeat_url.clear(); heartbeat_url.clear();
size_t heartbeat_url_pos = response.find("Heartbeat-Url: ", size_t heartbeat_url_pos = response.find("Heartbeat-Url: ", header_end_pos);
header_end_pos);
if (heartbeat_url_pos != std::string::npos) { if (heartbeat_url_pos != std::string::npos) {
heartbeat_url_pos += sizeof("Heartbeat-Url: "); heartbeat_url_pos += sizeof("Heartbeat-Url: ");
heartbeat_url.assign(response.substr(heartbeat_url_pos)); heartbeat_url.assign(response.substr(heartbeat_url_pos));
@@ -78,4 +79,4 @@ void LicenseRequest::GetHeartbeatUrl(const std::string& response,
} }
} }
} // namespace wvcdm } // namespace wvcdm

View File

@@ -1,150 +1,282 @@
// Copyright 2012 Google Inc. All Rights Reserved. // Copyright 2012 Google Inc. All Rights Reserved.
#include "clock.h"
#include "crypto_session.h" #include "crypto_session.h"
#include "license.h" #include "license.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "initialization_data.h" #include "initialization_data.h"
#include "policy_engine.h" #include "policy_engine.h"
#include "properties.h"
#include "string_conversions.h" #include "string_conversions.h"
#include "wv_cdm_constants.h"
namespace { namespace {
// The test data is based on key box Eureka-Dev-G1-0001520 const uint32_t kAesBlockSize = 16;
// This unit test should run on oemcrypto mock with the same key box const std::string kAesKey = wvcdm::a2bs_hex("000102030405060708090a0b0c0d0e0f");
static const char* kInitData = "0801121093789920E8D6520098577DF8F2DD5546"; const std::string kAesIv = wvcdm::a2bs_hex("000102030405060708090a0b0c0d0e0f");
static const char* kSignedRequest = const std::string kCencInitDataHdr = wvcdm::a2bs_hex(
"080112790A4C0800124800000002000001241F344DB9DFF087F01D917910F39B" "00000042" // blob size
"60DC7797CD97789EE82516DC07A478CB70B8C08C299293150AA8E01D2DC9808C" "70737368" // "pssh"
"98DAA16E40A0E55DFE3618C7584DD3C7BE4212250A230A140801121093789920" "00000000" // flags
"E8D6520098577DF8F2DD554610011A09393837363534333231180120001A20FA" "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id
"E2DDCD7F1ACA4B728EC957FEE802F8A5541557ACA784EE0D05BFCC0E65FEA1"; "00000022"); // pssh data size
static const char* kValidResponse = const std::string kCencPssh = wvcdm::a2bs_hex(
"080212D9020A190A093938373635343332311208C434AB9240A9EF2420012800" "08011a0d7769646576696e655f74657374220f73747265616d696e675f636c697031");
"120E0801180120809A9E0128809A9E011A461210B72EEBF582B04BDB15C2E0E3" const std::string kCdmSessionId = "sid2";
"20B21C351A30E51FC1D27F70DB8E0DDF8C051BD6E251A44599DBCE4E1BE663FD" const std::string kCryptoSessionId = "id2";
"3AFAB191A7DD5736841FB04CE558E7F17BD9812A2DBA20011A6E0A1093789920" const std::string kCryptoRequestId = wvcdm::a2bs_hex(
"E8D6520098577DF8F2DD55461210367E8714B6F10087AFDE542EDC5C91541A20" "4341444542353737444337393044394330313030303030303030303030303030");
"ED51D4E84D81C8CBD8E2046EE079F8A2016268A2F192B902FDA241FEEB10C014" const uint32_t kNonce = 0x49e81305;
"200242240A109209D46191B8752147C9F6A1CE2BEE6E12107910F39B60DC7797" const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013
"CD97789EE82516DC1A6E0A107B1328EB61B554E293F75B1E3E94CC3B1210676F" const std::string kToken = wvcdm::a2bs_hex(
"69BBDA35EE972B77BC1328A087391A20D2B9FA92B164F5F6362CAD9200A11661" "0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02"
"B8F71E9CE671A3A252D34586526B68FA200242240A109D7B13420FD6217666CC" "82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77"
"CD43860FAA3A1210DBCE4E1BE663FD3AFAB191A7DD57368420E9FDCE86051A20" "93BDA057E6BC3E0CA2348BC6831E03609445CA4D418CB98EAC98FFC87AB2364CE76BA26BEE"
"C6279E32FD2CB9067229E87AFF4B2DE14A077CDF8F061DAEE2CC2D1BCDEF62D0"; "CDB0C45BD2A6FE9FD38CC5A1C26303AEEB7E9C3CAFAB0D10E46C07E50BEDAB42BF21F40BD2"
static const char* kInvalidResponse = "E055DB0B455191D6B4CEEB11B3F1AFA42B5C0CE4C96B75A5283C0E3AE049AA7CF86D1C4EF6"
"0802128D020A190A093938373635343332311208BA68C949396C438C20012800" "6A9088B53BCF320ABC9B98A22C219DC109014EFEA72DA5FF2ED5D655DE7AE06EAC6C6B4191"
"120E0801180120809A9E0128809A9E011A4612105021EB9AEDC1F73E96DE7DCC" "523B2CD2DC1EBFF5F08B11CFE056F2826C1323F12704EC7EBBC1AF935129E5543804492AF9"
"6D7D72401A300A82E118C0BF0DB230FCADE3F49A9777DDD392322240FEF32C97" "23B848F4AF47B4BFB131C39DDDC99DBAEEE0F30AD2ADBBC63E60793D0876E37391008BB4DB"
"F85428E2F6CCFA638B5481464ADBCF199CEC2FCF3AFB20011A480A1093789920" "F7020301000128DD22128002A9E571776EA9D22A1BD14248DA88E12FD859989F613360B8D2"
"E8D6520098577DF8F2DD55461210EE52C59B99050A36E10569AFB34D1DA41A20" "DA40AF31CC071C7A138466F0EB745E3FD664C0E1A5E4F01098B8D56C34A0158DF9916D192F"
"C61FCB8019AC9ADE99FF8FCA99ED35E2331B6488A35102F9379AA42C87A22DC7" "841ADCA17FD630E1C0CBE652CAC6A52B6A1581BE4029CE6FAE0E04D2D2C7861187AF8299D8"
"20021A480A107B1328EB61B554E293F75B1E3E94CC3B12101BBF5286B859E349" "3E008DB9A2789672CA1DED903773D7E82B234CE2C799EB73CF80600C08F17EEDDDF369D2B8"
"2E4A47A24C06AC1B1A2061F21836A04E558BEE0244EF41C165F60CF23C580275" "4A08292F22D1F18FE89521905E713BA674F2217881DBD7711B8C48D5FDCE6FAB51F935E293"
"3175D48BAF1C6CA5759F200220A2BCCA86051A203FD4671075D9DEC6486A9317" "CB29191AB012B115FD2F5F23164B063D0A929C3E254BF0F4FA60051EB6B3498ED99FF77C19"
"70669993306831EDD57D77F34EFEB467470BA364"; "68E8CD83A35CEB054D05433FD0EA6AAE43C87DDA377591D1DCC1831EE130BFFF6D139A5ADA"
"738B0F257CCE2649E71AB4050AAE020801121017DCBC27D11341D497135442A188DAA6188F"
const std::string kCencMimeType = "video/mp4"; "89809105228E023082010A0282010100D21ADD7549D2748B3494526A9C3FB86C79376BBE8C"
const std::string kWebmMimeType = "video/webm"; "8856F601B8D10461F77ACC7331B10DEBF365120056CDB5662D25907B74F12382F0F4A0CA47"
"5EEA9562815C6228F6F698ADA27879D8890F2A2D96A746DDEF5316301C003519C2A2250354"
} "674169FDDA41CE14D3C52BEA7A20384515012D5952B38AA19E15E8563CC7AAA81C2122880A"
"A370A64FEA23C53FB83AC3DB5753214730A349E07F64BF32BE7EAD30D02612AF110BB44FB0"
"8E1D308173B327EF64D40C41639542B2D1A73C98A6607EC6C683B513A58470514106EF87AE"
"1E7B9C695B93A104DF7437BFC4167789748A43ED208F2C1FA710793C688885EAE732A8BFDF"
"5B423B23D75B88FC0ADC8FBDB5020301000128DD2212800372D2FB88098BA3B85B6B4354E0"
"3767DBE2D7724663FB0A62ABF7704EA910E01F221349EE16D0152C769384050CE78520668C"
"06CCFD3D789AF3EB69FF163615CD609169FDBE2E15A029D34AD2605625BC81844C9D1E2CE0"
"519039F3799ADAEF86641E20B033DC16DF2E5B9A1A2A417B8BB3B7A4D9AD1A99367448587D"
"A13DDE05A3ED9D62FA42078973B4AA40263D7BFA23F1072E94CDF323FA45F78408823E55C4"
"F4C5C723819CF44CE6D98E50C04EC24D93B1AAB8877B9108B9CA391308E1A3645EBB0E7CAC"
"BB40B5451560ED799421873BFB5ABB917FA60DB9C77CB8606AF7E3142626F5EA40E5CB8AA0"
"89D8E7D6A9361935C426A4450EA8BC2E57290D3BF0A0962991D2A91B752FC80C3E7E4E5503"
"3D71C94B325307A68815F026448F56A2741CEBEFC18E8C142F5F62BFAA67A291517DDE982D"
"8CD5A9DF6E3D3A99B806F6D60991358C5BE77117D4F3168F3348E9A048539F892F4D783152"
"C7A8095224AA56B78C5CF7BD1AB1B179C0C0D11E3C3BAC84C141A00191321E3ACC17242E68"
"3C");
const std::string kLicenseRequestSignature = wvcdm::a2bs_hex(
"4A560ACFED04787BE0D29D7396234FA2E11D6DD0B22F87FD77AEAEDAA6C8FE54AD9859AE4E"
"C9F12BCB947892D906DAEC1AD78CABD6F9D479CCF91AF5587DB6FC29CBEBF9C338BAF17790"
"90980B1F3333BC901CDBF877490C7B85DB2BF9BC559C98450C6F1E8B2E192959F59CC53BD4"
"85F2CC87D87C324750D5A8E28B821A3C55ABF27305AE4C58474D16E4FEE499D87A7D10AC84"
"8D24103EB15C63C227A0D57A9D90F5A409D2D55147EE10A35AE291D2D725C7F161FF827221"
"9AE18B91516E0CDD0B581590DDDEA2A2527E2C9ABA273629B586A9D22D451A827E332CFC3E"
"9BEDB6CF3D8713F9E11675DF1F5DB9038DBBECAB9D1683F8722CAF6E18EC8C04AEE5");
} // namespace
namespace wvcdm { namespace wvcdm {
class LicenseTest : public ::testing::Test { // 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;
// gmock methods
using ::testing::_;
using ::testing::Eq;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgPointee;
class MockCryptoSession : public CryptoSession {
public:
MOCK_METHOD0(IsOpen, bool());
MOCK_METHOD1(GenerateRequestId, bool(std::string*));
MOCK_METHOD1(UsageInformationSupport, bool(bool*));
MOCK_METHOD2(GetHdcpCapabilities,
bool(OemCryptoHdcpVersion*, OemCryptoHdcpVersion*));
MOCK_METHOD1(GetApiVersion, bool(uint32_t*));
MOCK_METHOD1(GenerateNonce, bool(uint32_t*));
MOCK_METHOD3(PrepareRequest, bool(const std::string&, bool, std::string*));
};
class MockPolicyEngine : public PolicyEngine {
public:
};
class MockClock : public Clock {
public:
MOCK_METHOD0(GetCurrentTime, int64_t());
};
class MockInitializationData : public InitializationData {
public:
MockInitializationData(const std::string& type, const CdmInitData& data)
: InitializationData(type, data) {}
MOCK_METHOD0(is_supported, bool());
MOCK_METHOD0(is_cenc, bool());
};
class CdmLicenseTest : public ::testing::Test {
protected: protected:
virtual void SetUp() { virtual void SetUp() {
session_ = new CryptoSession(); clock_ = new MockClock();
EXPECT_TRUE(session_ != NULL); crypto_session_ = new MockCryptoSession();
init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT,
std::string token; kCencInitDataHdr + kCencPssh);
EXPECT_TRUE(session_->GetToken(&token)); policy_engine_ = new MockPolicyEngine();
EXPECT_TRUE(session_->Open());
EXPECT_TRUE(license_.Init(token, session_, &policy_engine_));
} }
virtual void TearDown() { virtual void TearDown() {
session_->Close(); if (cdm_license_) delete cdm_license_;
delete session_; if (policy_engine_) delete policy_engine_;
if (init_data_) delete init_data_;
if (crypto_session_) delete crypto_session_;
if (clock_) delete clock_;
} }
CryptoSession* session_; void CreateCdmLicense() {
CdmLicense license_; cdm_license_ = new CdmLicense(clock_);
PolicyEngine policy_engine_; clock_ = NULL;
}
CdmLicense* cdm_license_;
MockClock* clock_;
MockCryptoSession* crypto_session_;
MockInitializationData* init_data_;
MockPolicyEngine* policy_engine_;
}; };
TEST(LicenseTestSession, InitNullSession) { TEST_F(CdmLicenseTest, InitSuccess) {
CdmLicense license; EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true));
EXPECT_FALSE(license.Init("Dummy", NULL, NULL));
CreateCdmLicense();
ASSERT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
} }
// TODO(rfrias): Fix or remove test. TEST_F(CdmLicenseTest, InitFail_EmptyToken) {
TEST_F(LicenseTest, DISABLED_PrepareIsoBmffKeyRequest) { CreateCdmLicense();
std::string signed_request; EXPECT_FALSE(cdm_license_->Init("", crypto_session_, policy_engine_));
}
TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) {
CreateCdmLicense();
EXPECT_FALSE(cdm_license_->Init(kToken, 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));
}
TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
bool usage_information_support = true;
CryptoSession::OemCryptoHdcpVersion current_hdcp_version =
CryptoSession::kOemCryptoNoHdcpDeviceAttached;
CryptoSession::OemCryptoHdcpVersion max_hdcp_version =
CryptoSession::kOemCryptoHdcpVersion2_1;
uint32_t crypto_session_api_version = 9;
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())).WillOnce(
DoAll(SetArgPointee<0>(usage_information_support), Return(true)));
EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version),
SetArgPointee<1>(max_hdcp_version), Return(true)));
EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())).WillOnce(
DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true)));
EXPECT_CALL(*clock_, GetCurrentTime()).WillOnce(Return(kLicenseStartTime));
EXPECT_CALL(*crypto_session_, GenerateNonce(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kNonce), Return(true)));
EXPECT_CALL(*crypto_session_, PrepareRequest(_, Eq(false), NotNull()))
.WillOnce(
DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true)));
CreateCdmLicense();
EXPECT_TRUE(cdm_license_->Init(kToken, crypto_session_, policy_engine_));
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
CdmKeyMessage signed_request;
Properties::set_use_certificates_as_identification(true);
std::string server_url; std::string server_url;
CdmSessionId session_id; EXPECT_TRUE(cdm_license_->PrepareKeyRequest(
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData)); *init_data_, kLicenseTypeStreaming, app_parameters, kCdmSessionId,
license_.PrepareKeyRequest(init_data, &signed_request, &server_url));
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
}
// TODO(rfrias): Fix or remove test. EXPECT_TRUE(!signed_request.empty());
TEST_F(LicenseTest, DISABLED_PrepareWebmKeyRequest) {
std::string signed_request;
CdmAppParameterMap app_parameters;
std::string server_url;
CdmSessionId session_id;
InitializationData init_data(kWebmMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
}
// TODO(rfrias): Fix or remove test. // Verify signed message
TEST_F(LicenseTest, DISABLED_HandleKeyResponseValid) { SignedMessage signed_message;
std::string signed_request; EXPECT_TRUE(signed_message.ParseFromString(signed_request));
CdmAppParameterMap app_parameters; EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type());
CdmSessionId session_id; EXPECT_TRUE(signed_message.has_signature());
std::string server_url; EXPECT_TRUE(std::equal(signed_message.signature().begin(),
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData)); signed_message.signature().end(),
license_.PrepareKeyRequest(init_data, kLicenseRequestSignature.begin()));
kLicenseTypeStreaming, EXPECT_TRUE(!signed_message.msg().empty());
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
EXPECT_TRUE(license_.HandleKeyResponse(a2bs_hex(kValidResponse)));
}
// TODO(rfrias): Fix or remove test. // Verify license request
TEST_F(LicenseTest, DISABLED_HandleKeyResponseInvalid) { LicenseRequest license_request;
std::string signed_request; EXPECT_TRUE(license_request.ParseFromString(signed_message.msg()));
CdmAppParameterMap app_parameters;
CdmSessionId session_id;
std::string server_url;
InitializationData init_data(kCencMimeType, a2bs_hex(kInitData));
license_.PrepareKeyRequest(init_data,
kLicenseTypeStreaming,
app_parameters,
session_id,
&signed_request,
&server_url);
EXPECT_EQ(signed_request, a2bs_hex(kSignedRequest));
EXPECT_FALSE(license_.HandleKeyResponse(a2bs_hex(kInvalidResponse)));
}
// TODO(kqyang): add unit test cases for PrepareKeyRenewalRequest // Verify Client Identification
// and HandleRenewalKeyResponse const ClientIdentification& client_id = license_request.client_id();
EXPECT_EQ(video_widevine_server::sdk::
ClientIdentification_TokenType_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&
name_value = client_id.client_info(i);
EXPECT_TRUE(!name_value.name().empty());
EXPECT_TRUE(!name_value.value().empty());
}
EXPECT_FALSE(client_id.has_provider_client_token());
EXPECT_FALSE(client_id.has_license_counter());
const ::video_widevine_server::sdk::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::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1,
client_capabilities.max_hdcp_version());
EXPECT_EQ(crypto_session_api_version,
client_capabilities.oem_crypto_api_version());
// 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());
const ::video_widevine_server::sdk::LicenseRequest_ContentIdentification_CENC&
cenc_id = content_id.cenc_id();
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_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,
license_request.type());
EXPECT_EQ(kLicenseStartTime, license_request.request_time());
EXPECT_EQ(video_widevine_server::sdk::VERSION_2_1,
license_request.protocol_version());
EXPECT_EQ(kNonce, license_request.key_control_nonce());
}
} }

View File

@@ -10,6 +10,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "license.h" #include "license.h"
#include "policy_engine.h" #include "policy_engine.h"
#include "test_printers.h"
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
namespace { namespace {

View File

@@ -0,0 +1,93 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// This file adds some print methods so that when unit tests fail, the
// will print the name of an enumeration instead of the numeric value.
#include "test_printers.h"
namespace wvcdm {
void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
switch(value) {
case NO_ERROR: *os << "NO_ERROR";
break;
case UNKNOWN_ERROR: *os << "UNKNOWN_ERROR";
break;
case KEY_ADDED: *os << "KEY_ADDED";
break;
case KEY_ERROR: *os << "KEY_ERROR";
break;
case KEY_MESSAGE: *os << "KEY_MESSAGE";
break;
case NEED_KEY: *os << "NEED_KEY";
break;
case KEY_CANCELED: *os << "KEY_CANCELED";
break;
case NEED_PROVISIONING: *os << "NEED_PROVISIONING";
break;
case DEVICE_REVOKED: *os << "DEVICE_REVOKED";
break;
case INSUFFICIENT_CRYPTO_RESOURCES: *os << "INSUFFICIENT_CRYPTO_RESOURCES";
break;
default:
*os << "Unknown CdmResponseType";
break;
}
}
void PrintTo(const enum CdmEventType& value, ::std::ostream* os) {
switch(value) {
case LICENSE_EXPIRED_EVENT: *os << "LICENSE_EXPIRED_EVENT";
break;
case LICENSE_RENEWAL_NEEDED_EVENT: *os << "LICENSE_RENEWAL_NEEDED_EVENT";
break;
default:
*os << "Unknown CdmEventType";
break;
}
};
void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os) {
switch(value) {
case kLicenseTypeOffline: *os << "kLicenseTypeOffline";
break;
case kLicenseTypeStreaming: *os << "kLicenseTypeStreaming";
break;
case kLicenseTypeRelease: *os << "kLicenseTypeRelease";
break;
default:
*os << "Unknown CdmLicenseType";
break;
}
};
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os) {
switch(value) {
case kSecurityLevelUninitialized: *os << "kSecurityLevelUninitialized";
break;
case kSecurityLevelL1: *os << "kSecurityLevelL1";
break;
case kSecurityLevelL2: *os << "kSecurityLevelL2";
break;
case kSecurityLevelL3: *os << "kSecurityLevelL3";
break;
case kSecurityLevelUnknown: *os << "kSecurityLevelUnknown";
break;
default:
*os << "Unknown CdmSecurityLevel";
break;
}
};
void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os) {
switch(value) {
case kCertificateWidevine: *os << "kCertificateWidevine";
break;
case kCertificateX509: *os << "kCertificateX509";
break;
default:
*os << "Unknown CdmCertificateType";
break;
}
};
}; // namespace wvcdm

19
core/test/test_printers.h Normal file
View File

@@ -0,0 +1,19 @@
// Copyright 2013 Google Inc. All Rights Reserved.
// This file adds some print methods so that when unit tests fail, the
// will print the name of an enumeration instead of the numeric value.
#ifndef CDM_TEST_PRINTERS_H_
#define CDM_TEST_PRINTERS_H_
#include <iostream>
#include "wv_cdm_types.h"
namespace wvcdm {
void PrintTo(const enum CdmResponseType& value, ::std::ostream* os);
void PrintTo(const enum CdmEventType& value, ::std::ostream* os);
void PrintTo(const enum CdmLicenseType& value, ::std::ostream* os);
void PrintTo(const enum CdmSecurityLevel& value, ::std::ostream* os);
void PrintTo(const enum CdmCertificateType& value, ::std::ostream* os);
}; // namespace wvcdm
#endif // CDM_TEST_PRINTERS_H_

View File

@@ -10,9 +10,7 @@ class TestTimerHandler : public TimerHandler {
TestTimerHandler() : timer_events_(0) {}; TestTimerHandler() : timer_events_(0) {};
virtual ~TestTimerHandler() {}; virtual ~TestTimerHandler() {};
virtual void OnTimerEvent() { virtual void OnTimerEvent() { timer_events_++; }
timer_events_++;
}
uint32_t timer_events() { return timer_events_; } uint32_t timer_events() { return timer_events_; }
void ResetTimerEvents() { timer_events_ = 0; } void ResetTimerEvents() { timer_events_ = 0; }
@@ -41,14 +39,13 @@ TEST(TimerTest, TimerCheck) {
EXPECT_TRUE(timer.IsRunning()); EXPECT_TRUE(timer.IsRunning());
sleep(duration); sleep(duration);
EXPECT_LE(duration-1, handler.timer_events()); EXPECT_LE(duration - 1, handler.timer_events());
EXPECT_LE(handler.timer_events(), duration+1); EXPECT_LE(handler.timer_events(), duration + 1);
timer.Stop(); timer.Stop();
EXPECT_FALSE(timer.IsRunning()); EXPECT_FALSE(timer.IsRunning());
sleep(duration); sleep(duration);
EXPECT_LE(duration-1, handler.timer_events()); EXPECT_LE(duration - 1, handler.timer_events());
EXPECT_LE(handler.timer_events(), duration+1); EXPECT_LE(handler.timer_events(), duration + 1);
} }
} }

View File

@@ -12,9 +12,9 @@
namespace { namespace {
const int kReadBufferSize = 1024; const int kReadBufferSize = 1024;
const int kConnectTimeoutMs = 5000; const int kConnectTimeoutMs = 15000;
const int kWriteTimeoutMs = 3000; const int kWriteTimeoutMs = 12000;
const int kReadTimeoutMs = 3000; const int kReadTimeoutMs = 12000;
// Concatenate all chunks into one blob and returns the response with // Concatenate all chunks into one blob and returns the response with
// header information. // header information.
@@ -69,12 +69,17 @@ void ConcatenateChunkedResponse(const std::string http_response,
} // namespace } // namespace
namespace wvcdm { namespace wvcdm {
UrlRequest::UrlRequest(const std::string& url) UrlRequest::UrlRequest(const std::string& url)
: is_connected_(false), : is_connected_(false), socket_(url) {
socket_(url) { Reconnect();
}
UrlRequest::~UrlRequest() {}
bool UrlRequest::Reconnect() {
socket_.CloseSocket();
if (socket_.Connect(kConnectTimeoutMs)) { if (socket_.Connect(kConnectTimeoutMs)) {
is_connected_ = true; is_connected_ = true;
} else { } else {
@@ -83,8 +88,6 @@ UrlRequest::UrlRequest(const std::string& url)
} }
} }
UrlRequest::~UrlRequest() {}
bool UrlRequest::GetResponse(std::string* message) { bool UrlRequest::GetResponse(std::string* message) {
std::string response; std::string response;

View File

@@ -17,6 +17,7 @@ class UrlRequest {
~UrlRequest(); ~UrlRequest();
bool is_connected() const { return is_connected_; } bool is_connected() const { return is_connected_; }
bool Reconnect();
bool PostRequest(const std::string& data); bool PostRequest(const std::string& data);
bool PostCertRequestInQueryString(const std::string& data); bool PostCertRequestInQueryString(const std::string& data);

View File

@@ -9,16 +9,35 @@
#include "log.h" #include "log.h"
namespace {
FILE* output_file = stdout;
} // namespace
namespace wvcdm { namespace wvcdm {
LogPriority g_cutoff = LOG_WARN; LogPriority g_cutoff = LOG_WARN;
void InitLogging(int argc, const char* const* argv) { void InitLogging(int argc, const char* const* argv) {
// default to stdout if no arguments set it otherwise.
output_file = stdout;
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], "-v", 2) == 0) { if (!strcmp(argv[i], "-v")) {
g_cutoff = LOG_VERBOSE; g_cutoff = LOG_VERBOSE;
} else if (!strcmp(argv[i], "-")) {
// default above is stdout, so keep that.
} else {
// open a file for output.
output_file = fopen(argv[i], "a");
if (!output_file) {
output_file = stdout;
printf("Could not open log file \"%s\"\n", argv[i]);
}
} }
} }
const char *verbose_env = getenv("VERBOSE_LOG"); const char *verbose_env = getenv("VERBOSE_LOG");
if (verbose_env && ! strncmp(verbose_env, "yes", 3) ) { if (verbose_env && ! strncmp(verbose_env, "yes", 3) ) {
g_cutoff = LOG_VERBOSE; g_cutoff = LOG_VERBOSE;
@@ -28,20 +47,21 @@ void InitLogging(int argc, const char* const* argv) {
void Log(const char* file, int line, LogPriority level, const char* fmt, ...) { void Log(const char* file, int line, LogPriority level, const char* fmt, ...) {
const char* severities[] = { "ERROR", "WARN", "INFO", "DEBUG", "VERBOSE" }; const char* severities[] = { "ERROR", "WARN", "INFO", "DEBUG", "VERBOSE" };
if (level >= sizeof(severities) / sizeof(*severities)) { if (level >= sizeof(severities) / sizeof(*severities)) {
printf("[FATAL:%s(%d)] Invalid log priority level: %d\n", file, line, fprintf(output_file, "[FATAL:%s(%d)] Invalid log priority level: %d\n",
level); file, line, level);
return; return;
} }
if (level > g_cutoff) return; if (level > g_cutoff) return;
printf("[%s:%s(%d)] ", severities[level], file, line); fprintf(output_file, "[%s:%s(%d)] ", severities[level], file, line);
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vprintf(fmt, ap); vfprintf(output_file, fmt, ap);
va_end(ap); va_end(ap);
putc('\n', stdout); putc('\n', output_file);
fflush(output_file);
} }
}; // namespace wvcdm }; // namespace wvcdm

View File

@@ -562,7 +562,7 @@ OEMCryptoResult OEMCrypto_GenerateSignature(
* as a successful comparison). * as a successful comparison).
* *
* 2. The enc_mac_keys pointer must be either null, or point inside the * 2. The enc_mac_keys pointer must be either null, or point inside the
* message. If the pointer enc_mac_keys is not null, [e]the API shall verify * message. If the pointer enc_mac_keys is not null, the API shall verify
* that the two pointers enc_mac_keys_iv and enc_mac_keys point to locations in * that the two pointers enc_mac_keys_iv and enc_mac_keys point to locations in
* the message. I.e. (message <= p && p < message+message_length)for p in * the message. I.e. (message <= p && p < message+message_length)for p in
* each of enc_mac_keys_iv, enc_mac_keys. If not, return * each of enc_mac_keys_iv, enc_mac_keys. If not, return
@@ -603,8 +603,8 @@ OEMCryptoResult OEMCrypto_GenerateSignature(
* license from being loaded more than once, and will be used for online * license from being loaded more than once, and will be used for online
* streaming. * streaming.
* *
* - If Replay_Control is 2 = Require existing Session Usage table entry or * - If Replay_Control is 2 = "Require existing Session Usage table entry or
* Nonce”, then OEMCrypto will check the Session Usage table for an existing * Nonce", then OEMCrypto will check the Session Usage table for an existing
* entry with the same pst. * entry with the same pst.
* *
* --- If the pst is not in the table yet, a new entry will be created in the * --- If the pst is not in the table yet, a new entry will be created in the
@@ -867,8 +867,8 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
* secure buffer. * secure buffer.
* *
* If the session has an entry in the Usage Table, then OEMCrypto will update the * If the session has an entry in the Usage Table, then OEMCrypto will update the
* time_of_last_decrypt. If the status of the entry is unused, then change the * time_of_last_decrypt. If the status of the entry is "unused", then change the
* status to active and set the time_of_first_decrypt.[f] * status to "active" and set the time_of_first_decrypt.
* *
* *
* Verification: * Verification:
@@ -877,7 +877,7 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
* *
* 1. If the current keys control block has a nonzero Duration field, then the * 1. If the current keys control block has a nonzero Duration field, then the
* API shall verify that the duration is greater than the sessions elapsed time * API shall verify that the duration is greater than the sessions elapsed time
* clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED[g]. * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED.
* *
* 2. If the current keys control block has the Data_Path_Type bit set, then the * 2. If the current keys control block has the Data_Path_Type bit set, then the
* API shall verify that the output buffer is secure or direct. If not, return * API shall verify that the output buffer is secure or direct. If not, return
@@ -893,8 +893,8 @@ OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session,
* current version is not at least as high as that in the control block, then * current version is not at least as high as that in the control block, then
* return OEMCrypto_ERROR_INSUFFICIENT_HDCP. * return OEMCrypto_ERROR_INSUFFICIENT_HDCP.
* *
* 1. If the current session has an entry in the Usage Table, and the status of * 5. If the current session has an entry in the Usage Table, and the status of
* that entry is inactive, then return OEMCrypto_ERROR_INVALID_SESSION.[h] * that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION.
* *
* If the flag is_encrypted is false, then no verification is performed. This * If the flag is_encrypted is false, then no verification is performed. This
* call shall copy clear data even when there are no keys loaded, or there is no * call shall copy clear data even when there are no keys loaded, or there is no
@@ -1160,11 +1160,11 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
* GenerateDerivedKeys. * GenerateDerivedKeys.
* *
* After decrypting enc_rsa_key, If the first four bytes of the buffer are the * After decrypting enc_rsa_key, If the first four bytes of the buffer are the
* string SIGN, then the actual RSA key begins on the 9th byte of the buffer. * string "SIGN", then the actual RSA key begins on the 9th byte of the buffer.
* The second four bytes of the buffer is the 32 bit field allowed_schemes, * The second four bytes of the buffer is the 32 bit field "allowed_schemes",
* of type RSA_Padding_Scheme, which is used in OEMCrypto_GenerateRSASignature. The * of type RSA_Padding_Scheme, which is used in OEMCrypto_GenerateRSASignature. The
* value of allowed_schemes must also be wrapped with RSA key. We recommend * value of allowed_schemes must also be wrapped with RSA key. We recommend
* storing the magic string SIGN with the key to distinguish keys that have a * storing the magic string "SIGN" with the key to distinguish keys that have a
* value for allowed_schemes from those that should use the default * value for allowed_schemes from those that should use the default
* allowed_schemes. Devices that do not support the alternative signing * allowed_schemes. Devices that do not support the alternative signing
* algorithms may refuse to load these keys and return an error of * algorithms may refuse to load these keys and return an error of
@@ -1173,7 +1173,7 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData,
* authentication when acting as a ChromeCast receiver. This is not needed for * authentication when acting as a ChromeCast receiver. This is not needed for
* devices that wish to send data to a ChromeCast. * devices that wish to send data to a ChromeCast.
* *
* If the first four bytes of the buffer enc_rsa_key are not the string SIGN, * If the first four bytes of the buffer enc_rsa_key are not the string "SIGN",
* then the default value of allowed_schemes = 1 will be used. * then the default value of allowed_schemes = 1 will be used.
* *
* Verification: * Verification:
@@ -1262,7 +1262,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey(OEMCrypto_SESSION session,
* the one verified and wrapped by OEMCrypto_RewrapDeviceRSAKey. The RSA private * the one verified and wrapped by OEMCrypto_RewrapDeviceRSAKey. The RSA private
* key should be stored in secure memory. * key should be stored in secure memory.
* *
* If the bit field allowed_schemes was wrapped with this RSA key, its value * If the bit field "allowed_schemes" was wrapped with this RSA key, its value
* will be loaded and stored with the RSA key. If there was not bit field * will be loaded and stored with the RSA key. If there was not bit field
* wrapped with the RSA key, the key will use a default value of 1 = RSASSA-PSS * wrapped with the RSA key, the key will use a default value of 1 = RSASSA-PSS
* with SHA1. * with SHA1.
@@ -1550,8 +1550,8 @@ bool OEMCrypto_SupportsUsageTable();
* This function encrypts a generic buffer of data using the current key. * This function encrypts a generic buffer of data using the current key.
* *
* If the session has an entry in the Usage Table, then OEMCrypto will update the * If the session has an entry in the Usage Table, then OEMCrypto will update the
* time_of_last_decrypt. If the status of the entry is unused, then change the * time_of_last_decrypt. If the status of the entry is "unused", then change the
* status to active and set the time_of_first_decrypt. * status to "active" and set the time_of_first_decrypt.
* *
* Verification: * Verification:
* The following checks should be performed. If any check fails, an error is * The following checks should be performed. If any check fails, an error is
@@ -1562,7 +1562,7 @@ bool OEMCrypto_SupportsUsageTable();
* API shall verify that the duration is greater than the sessions elapsed time * API shall verify that the duration is greater than the sessions elapsed time
* clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED.
* 3. If the current session has an entry in the Usage Table, and the status of * 3. If the current session has an entry in the Usage Table, and the status of
* that entry is inactive, then return OEMCrypto_ERROR_INVALID_SESSION. * that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION.
* *
* Parameters: * Parameters:
* session (in) - crypto session identifier. * session (in) - crypto session identifier.
@@ -1602,8 +1602,8 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
* This function decrypts a generic buffer of data using the current key. * This function decrypts a generic buffer of data using the current key.
* *
* If the session has an entry in the Usage Table, then OEMCrypto will update the * If the session has an entry in the Usage Table, then OEMCrypto will update the
* time_of_last_decrypt. If the status of the entry is unused, then change the * time_of_last_decrypt. If the status of the entry is "unused", then change the
* status to active and set the time_of_first_decrypt. * status to "active" and set the time_of_first_decrypt.
* *
* Verification: * Verification:
* The following checks should be performed. If any check fails, an error is * The following checks should be performed. If any check fails, an error is
@@ -1616,7 +1616,7 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt(OEMCrypto_SESSION session,
* API shall verify that the duration is greater than the sessions elapsed time * API shall verify that the duration is greater than the sessions elapsed time
* clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED.
* 4. If the current session has an entry in the Usage Table, and the status of * 4. If the current session has an entry in the Usage Table, and the status of
* that entry is inactive, then return OEMCrypto_ERROR_INVALID_SESSION. * that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION.
* *
* Parameters: * Parameters:
* session (in) - crypto session identifier. * session (in) - crypto session identifier.
@@ -1657,8 +1657,8 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
* This function signs a generic buffer of data using the current key. * This function signs a generic buffer of data using the current key.
* *
* If the session has an entry in the Usage Table, then OEMCrypto will update the * If the session has an entry in the Usage Table, then OEMCrypto will update the
* time_of_last_decrypt. If the status of the entry is unused, then change the * time_of_last_decrypt. If the status of the entry is "unused", then change the
* status to active and set the time_of_first_decrypt. * status to "active" and set the time_of_first_decrypt.
* *
* Verification: * Verification:
* The following checks should be performed. If any check fails, an error is * The following checks should be performed. If any check fails, an error is
@@ -1668,7 +1668,7 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt(OEMCrypto_SESSION session,
* API shall verify that the duration is greater than the sessions elapsed time * API shall verify that the duration is greater than the sessions elapsed time
* clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED.
* 3. If the current session has an entry in the Usage Table, and the status of * 3. If the current session has an entry in the Usage Table, and the status of
* that entry is inactive, then return OEMCrypto_ERROR_INVALID_SESSION. * that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION.
* *
* Parameters: * Parameters:
* session (in) - crypto session identifier. * session (in) - crypto session identifier.
@@ -1714,8 +1714,8 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
* current key. * current key.
* *
* If the session has an entry in the Usage Table, then OEMCrypto will update the * If the session has an entry in the Usage Table, then OEMCrypto will update the
* time_of_last_decrypt. If the status of the entry is unused, then change the * time_of_last_decrypt. If the status of the entry is "unused", then change the
* status to active and set the time_of_first_decrypt. * status to "active" and set the time_of_first_decrypt.
* *
* Verification: * Verification:
* The following checks should be performed. If any check fails, an error is * The following checks should be performed. If any check fails, an error is
@@ -1730,7 +1730,7 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session,
* API shall verify that the duration is greater than the sessions elapsed time * API shall verify that the duration is greater than the sessions elapsed time
* clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. * clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED.
* 5. If the current session has an entry in the Usage Table, and the status of * 5. If the current session has an entry in the Usage Table, and the status of
* that entry is inactive, then return OEMCrypto_ERROR_INVALID_SESSION. * that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION.
* *
* Parameters: * Parameters:
* session (in) - crypto session identifier. * session (in) - crypto session identifier.
@@ -1797,8 +1797,8 @@ OEMCryptoResult OEMCrypto_UpdateUsageTable();
* *
* Description: * Description:
* Find the entry in the Usage Table with a matching PST. Mark the status of * Find the entry in the Usage Table with a matching PST. Mark the status of
* that entry as inactive. If it corresponds to an open session, the status of * that entry as "inactive". If it corresponds to an open session, the status of
* that session will also be marked as inactive. Then OEMCrypto will increment * that session will also be marked as "inactive". Then OEMCrypto will increment
* Usage Tables generation number, sign, encrypt, and save the Usage Table. * Usage Tables generation number, sign, encrypt, and save the Usage Table.
* *
* If no entry in the Usage Table has a matching PST, return the error * If no entry in the Usage Table has a matching PST, return the error
@@ -1934,7 +1934,7 @@ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session,
* the computed signature matches the signature passed in. The signature will be * the computed signature matches the signature passed in. The signature will be
* computed using HMAC-SHA256 and the mac_key_server. If they do not match, * computed using HMAC-SHA256 and the mac_key_server. If they do not match,
* return OEMCrypto_ERROR_SIGNATURE_FAILURE. * return OEMCrypto_ERROR_SIGNATURE_FAILURE.
* 3. If the session is not associated with an entry in the Usage Table, return * 3. If the session is not associated with an entry in the Usage Table, return
* OEMCrypto_ERROR_UNKNOWN_FAILURE. * OEMCrypto_ERROR_UNKNOWN_FAILURE.
* 4. If the pst passed in as a parameter does not match that in the Usage Table, * 4. If the pst passed in as a parameter does not match that in the Usage Table,
* return OEMCrypto_ERROR_UNKNOWN_FAILURE. * return OEMCrypto_ERROR_UNKNOWN_FAILURE.

View File

@@ -26,6 +26,7 @@
#include "OEMCryptoCENC.h" #include "OEMCryptoCENC.h"
#include "oemcrypto_key_mock.h" #include "oemcrypto_key_mock.h"
#include "log.h"
#include "properties.h" #include "properties.h"
#include "string_conversions.h" #include "string_conversions.h"
#include "wv_cdm_constants.h" #include "wv_cdm_constants.h"
@@ -899,6 +900,7 @@ class Session {
public: public:
Session() Session()
: open_(false), : open_(false),
session_id_(0),
mac_key_server_(wvcdm::MAC_KEY_SIZE), mac_key_server_(wvcdm::MAC_KEY_SIZE),
mac_key_client_(wvcdm::MAC_KEY_SIZE), mac_key_client_(wvcdm::MAC_KEY_SIZE),
enc_key_(wvcdm::KEY_SIZE), enc_key_(wvcdm::KEY_SIZE),
@@ -1546,13 +1548,14 @@ class OEMCryptoClientTest : public ::testing::Test {
protected: protected:
OEMCryptoClientTest() {} OEMCryptoClientTest() {}
void SetUp() { virtual void SetUp() {
::testing::Test::SetUp(); ::testing::Test::SetUp();
wvcdm::Properties::Init(); wvcdm::Properties::Init();
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
wvcdm::g_cutoff = wvcdm::LOG_INFO;
} }
void TearDown() { virtual void TearDown() {
OEMCrypto_Terminate(); OEMCrypto_Terminate();
::testing::Test::TearDown(); ::testing::Test::TearDown();
} }
@@ -1766,36 +1769,88 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood) {
s.open(); s.open();
int error_counter = 0; int error_counter = 0;
uint32_t nonce; uint32_t nonce;
// More than 20 nonces should generate an error. time_t test_start = time(NULL);
// More than 20 nonces per second should generate an error.
// To allow for some slop, we actually test for more than 40. // To allow for some slop, we actually test for more than 40.
for (int i = 0; i < 60; i++) { for (int i = 0; i < 60; i++) {
s.GenerateNonce(&nonce, &error_counter); s.GenerateNonce(&nonce, &error_counter);
} }
ASSERT_LE(20, error_counter); time_t test_end = time(NULL);
// Either there should be a two second delay, or there should have been
// at least 20 errors.
if (20 > error_counter) {
EXPECT_LE(2, test_end - test_start);
} else {
EXPECT_LE(20, error_counter);
}
error_counter = 0; error_counter = 0;
sleep(2); // After a pause, we should be able to regenerate nonces. sleep(2); // After a pause, we should be able to regenerate nonces.
s.GenerateNonce(&nonce, &error_counter); s.GenerateNonce(&nonce, &error_counter);
ASSERT_EQ(0, error_counter); EXPECT_EQ(0, error_counter);
} }
// Prevent a nonce flood even if each nonce is in a different session. // Prevent a nonce flood even if each nonce is in a different session.
TEST_F(OEMCryptoClientTest, PreventNonceFlood2) { TEST_F(OEMCryptoClientTest, PreventNonceFlood2) {
int error_counter = 0; int error_counter = 0;
uint32_t nonce; uint32_t nonce;
// More than 20 nonces should generate an error. time_t test_start = time(NULL);
// More than 20 nonces per second should generate an error.
// To allow for some slop, we actually test for more than 40. // To allow for some slop, we actually test for more than 40.
for (int i = 0; i < 60; i++) { for (int i = 0; i < 60; i++) {
Session s; Session s;
s.open(); s.open();
EXPECT_TRUE(s.isOpen());
s.GenerateNonce(&nonce, &error_counter); s.GenerateNonce(&nonce, &error_counter);
} }
ASSERT_LE(20, error_counter); time_t test_end = time(NULL);
// Either there should be a two second delay, or there should have been
// at least 20 errors.
if (20 > error_counter) {
EXPECT_LE(2, test_end - test_start);
} else {
EXPECT_LE(20, error_counter);
}
error_counter = 0; error_counter = 0;
sleep(2); // After a pause, we should be able to regenerate nonces. sleep(2); // After a pause, we should be able to regenerate nonces.
Session s; Session s;
s.open(); s.open();
s.GenerateNonce(&nonce, &error_counter); s.GenerateNonce(&nonce, &error_counter);
ASSERT_EQ(0, error_counter); EXPECT_EQ(0, error_counter);
}
// Prevent a nonce flood even if some nonces are in a different session. This
// is different from the test above because there are several session open at
// the same time. We want to make sure you can't get a flood of nonces by
// opening a flood of sessions.
TEST_F(OEMCryptoClientTest, PreventNonceFlood3) {
int error_counter = 0;
uint32_t nonce;
time_t test_start = time(NULL);
// More than 20 nonces per second should generate an error.
// To allow for some slop, we actually test for more than 40.
Session s[6];
for (int i = 0; i < 6; i++) {
s[i].open();
EXPECT_TRUE(s[i].isOpen());
for (int j = 0; j < 10; j++) {
s[i].GenerateNonce(&nonce, &error_counter);
}
}
time_t test_end = time(NULL);
// Either there should be a two second delay, or there should have been
// at least 20 errors.
if (20 > error_counter) {
EXPECT_LE(2, test_end - test_start);
} else {
EXPECT_LE(20, error_counter);
}
error_counter = 0;
sleep(2); // After a pause, we should be able to regenerate nonces.
Session s7;
s7.open();
EXPECT_TRUE(s7.isOpen());
s7.GenerateNonce(&nonce, &error_counter);
EXPECT_EQ(0, error_counter);
} }
TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) { TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) {
@@ -1815,18 +1870,43 @@ TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) {
class DISABLED_TestKeybox : public OEMCryptoClientTest { class DISABLED_TestKeybox : public OEMCryptoClientTest {
protected: protected:
void SetUp() { virtual void SetUp() {
OEMCryptoClientTest::SetUp(); OEMCryptoClientTest::SetUp();
InstallKeybox(kDefaultKeybox, true); InstallKeybox(kDefaultKeybox, true);
OEMCrypto_DeleteUsageTable(); OEMCrypto_DeleteUsageTable();
} }
virtual void TearDown() {
InstallKeybox(kDefaultKeybox, true);
OEMCryptoClientTest::TearDown();
}
bool IsDefaultKeyboxInstalled() {
uint8_t key_data[256];
size_t key_data_len = sizeof(key_data);
OEMCryptoResult sts = OEMCrypto_GetKeyData(key_data, &key_data_len);
uint32_t* data = reinterpret_cast<uint32_t*>(key_data);
uint32_t system_id = htonl(data[1]);
return sts == OEMCrypto_SUCCESS && system_id == 0x1019;
}
void InstallKeybox(const wvoec_mock::WidevineKeybox& keybox, bool good) { void InstallKeybox(const wvoec_mock::WidevineKeybox& keybox, bool good) {
OEMCryptoResult sts; OEMCryptoResult sts;
uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)]; uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)];
size_t length = sizeof(wvoec_mock::WidevineKeybox); size_t length = sizeof(wvoec_mock::WidevineKeybox);
sts = OEMCrypto_WrapKeybox(reinterpret_cast<const uint8_t*>(&keybox), sts = OEMCrypto_WrapKeybox(reinterpret_cast<const uint8_t*>(&keybox),
sizeof(keybox), wrapped, &length, NULL, 0); sizeof(keybox), wrapped, &length, NULL, 0);
if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED &&
&keybox == &kDefaultKeybox &&
IsDefaultKeyboxInstalled()) {
// Allow implementations which don't support installation to continue
// so long as:
// 1) we wanted to install the default keybox AND
// 2) the default keybox is already installed.
return;
}
ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(OEMCrypto_SUCCESS, sts);
sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox)); sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox));
if (good) { if (good) {
@@ -1884,7 +1964,6 @@ TEST_F(DISABLED_TestKeybox, BadCRCKeybox) {
InstallKeybox(keybox, false); InstallKeybox(keybox, false);
sts = OEMCrypto_IsKeyboxValid(); sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
InstallKeybox(kDefaultKeybox, true);
} }
TEST_F(DISABLED_TestKeybox, BadMagicKeybox) { TEST_F(DISABLED_TestKeybox, BadMagicKeybox) {
@@ -1894,7 +1973,6 @@ TEST_F(DISABLED_TestKeybox, BadMagicKeybox) {
InstallKeybox(keybox, false); InstallKeybox(keybox, false);
sts = OEMCrypto_IsKeyboxValid(); sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts); ASSERT_EQ(OEMCrypto_ERROR_BAD_MAGIC, sts);
InstallKeybox(kDefaultKeybox, true);
} }
TEST_F(DISABLED_TestKeybox, BadDataKeybox) { TEST_F(DISABLED_TestKeybox, BadDataKeybox) {
@@ -1904,11 +1982,9 @@ TEST_F(DISABLED_TestKeybox, BadDataKeybox) {
InstallKeybox(keybox, false); InstallKeybox(keybox, false);
sts = OEMCrypto_IsKeyboxValid(); sts = OEMCrypto_IsKeyboxValid();
ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts); ASSERT_EQ(OEMCrypto_ERROR_BAD_CRC, sts);
InstallKeybox(kDefaultKeybox, true);
} }
TEST_F(DISABLED_TestKeybox, GenerateSignature) { TEST_F(DISABLED_TestKeybox, GenerateSignature) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -1942,7 +2018,6 @@ TEST_F(DISABLED_TestKeybox, GenerateSignature) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyNoNonce) { TEST_F(DISABLED_TestKeybox, LoadKeyNoNonce) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -1952,7 +2027,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyNoNonce) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithNonce) { TEST_F(DISABLED_TestKeybox, LoadKeyWithNonce) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -1962,7 +2036,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithNonce) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithNoMAC) { TEST_F(DISABLED_TestKeybox, LoadKeyWithNoMAC) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2000,7 +2073,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithNoMAC) {
of all the pointers. It should reject a message if the pointer does of all the pointers. It should reject a message if the pointer does
not point into the message buffer */ not point into the message buffer */
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange1) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange1) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2019,7 +2091,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange1) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange2) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange2) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2038,7 +2109,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange2) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange3) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange3) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2057,7 +2127,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange3) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange4) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange4) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2077,7 +2146,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange4) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange5) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange5) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2095,7 +2163,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange5) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange6) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange6) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2116,7 +2183,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange6) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange7) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange7) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2137,7 +2203,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadRange7) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadNonce) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadNonce) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2152,7 +2217,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadNonce) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithRepeatNonce) { TEST_F(DISABLED_TestKeybox, LoadKeyWithRepeatNonce) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2176,7 +2240,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithRepeatNonce) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeyWithBadVerification) { TEST_F(DISABLED_TestKeybox, LoadKeyWithBadVerification) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2193,7 +2256,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeyWithBadVerification) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeysBadSignature) { TEST_F(DISABLED_TestKeybox, LoadKeysBadSignature) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2208,7 +2270,6 @@ TEST_F(DISABLED_TestKeybox, LoadKeysBadSignature) {
} }
TEST_F(DISABLED_TestKeybox, LoadKeysWithNoDerivedKeys) { TEST_F(DISABLED_TestKeybox, LoadKeysWithNoDerivedKeys) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
// s.GenerateDerivedKeys(); // s.GenerateDerivedKeys();
@@ -2226,7 +2287,6 @@ class DISABLED_DecryptWithHDCP : public DISABLED_TestKeybox,
public: public:
void DecryptWithHDCP(OEMCrypto_HDCP_Capability version) { void DecryptWithHDCP(OEMCrypto_HDCP_Capability version) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
OEMCrypto_HDCP_Capability current, maximum; OEMCrypto_HDCP_Capability current, maximum;
sts = OEMCrypto_GetHDCPCapability(&current, &maximum); sts = OEMCrypto_GetHDCPCapability(&current, &maximum);
ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(OEMCrypto_SUCCESS, sts);
@@ -2286,7 +2346,7 @@ TEST_P(DISABLED_RefreshKeyTest, RefreshWithNonce) {
s.RefreshTestKeys(num_keys_, wvoec_mock::kControlNonceEnabled, nonce, true); s.RefreshTestKeys(num_keys_, wvoec_mock::kControlNonceEnabled, nonce, true);
} }
TEST_P(DISABLED_RefreshKeyTest, RefresNoNonce) { TEST_P(DISABLED_RefreshKeyTest, RefreshNoNonce) {
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2339,7 +2399,6 @@ INSTANTIATE_TEST_CASE_P(TestRefreshEachKeys, DISABLED_RefreshKeyTest,
// //
TEST_F(DISABLED_TestKeybox, Decrypt) { TEST_F(DISABLED_TestKeybox, Decrypt) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2352,7 +2411,6 @@ TEST_F(DISABLED_TestKeybox, Decrypt) {
TEST_F(DISABLED_TestKeybox, DecryptZeroDuration) { TEST_F(DISABLED_TestKeybox, DecryptZeroDuration) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2365,7 +2423,6 @@ TEST_F(DISABLED_TestKeybox, DecryptZeroDuration) {
TEST_F(DISABLED_TestKeybox, DecryptWithOffset) { TEST_F(DISABLED_TestKeybox, DecryptWithOffset) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2460,7 +2517,6 @@ vector<uint8_t> EncryptCTR(const vector<uint8_t>& key,
TEST_F(DISABLED_TestKeybox, DecryptWithNearWrap) { TEST_F(DISABLED_TestKeybox, DecryptWithNearWrap) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2511,7 +2567,6 @@ TEST_F(DISABLED_TestKeybox, DecryptWithNearWrap) {
TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) { TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2558,7 +2613,6 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencrypted) {
TEST_F(DISABLED_TestKeybox, DecryptUnencryptedNoKey) { TEST_F(DISABLED_TestKeybox, DecryptUnencryptedNoKey) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
@@ -2597,7 +2651,6 @@ TEST_F(DISABLED_TestKeybox, DecryptUnencryptedNoKey) {
TEST_F(DISABLED_TestKeybox, DecryptSecureToClear) { TEST_F(DISABLED_TestKeybox, DecryptSecureToClear) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2611,7 +2664,6 @@ TEST_F(DISABLED_TestKeybox, DecryptSecureToClear) {
TEST_F(DISABLED_TestKeybox, KeyDuration) { TEST_F(DISABLED_TestKeybox, KeyDuration) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2657,7 +2709,6 @@ TEST_F(DISABLED_TestKeybox, ValidateRSATestKeys) {
} }
TEST_F(DISABLED_TestKeybox, CertificateProvision) { TEST_F(DISABLED_TestKeybox, CertificateProvision) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2674,7 +2725,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvision) {
} }
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange1) { TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange1) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2702,7 +2752,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange1) {
} }
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange2) { TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange2) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2732,7 +2781,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange2) {
} }
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange3) { TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange3) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2763,7 +2811,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRange3) {
} }
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadSignature) { TEST_F(DISABLED_TestKeybox, CertificateProvisionBadSignature) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2792,7 +2839,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadSignature) {
} }
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadNonce) { TEST_F(DISABLED_TestKeybox, CertificateProvisionBadNonce) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2821,7 +2867,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadNonce) {
} }
TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRSAKey) { TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRSAKey) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -2851,7 +2896,6 @@ TEST_F(DISABLED_TestKeybox, CertificateProvisionBadRSAKey) {
TEST_F(DISABLED_TestKeybox, LoadWrappedRSAKey) { TEST_F(DISABLED_TestKeybox, LoadWrappedRSAKey) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
std::vector<uint8_t> wrapped_rsa_key; std::vector<uint8_t> wrapped_rsa_key;
CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true); CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true);
@@ -2864,7 +2908,6 @@ TEST_F(DISABLED_TestKeybox, LoadWrappedRSAKey) {
TEST_F(DISABLED_TestKeybox, RSASignature) { TEST_F(DISABLED_TestKeybox, RSASignature) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
std::vector<uint8_t> wrapped_rsa_key; std::vector<uint8_t> wrapped_rsa_key;
CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true); CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true);
@@ -2902,7 +2945,6 @@ TEST_F(DISABLED_TestKeybox, RSASignature) {
} }
TEST_F(DISABLED_TestKeybox, LoadRSASessionKey) { TEST_F(DISABLED_TestKeybox, LoadRSASessionKey) {
InstallKeybox(kDefaultKeybox, true);
std::vector<uint8_t> wrapped_rsa_key; std::vector<uint8_t> wrapped_rsa_key;
CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true); CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true);
Session s; Session s;
@@ -2912,7 +2954,6 @@ TEST_F(DISABLED_TestKeybox, LoadRSASessionKey) {
TEST_F(DISABLED_TestKeybox, CertificateDecrypt) { TEST_F(DISABLED_TestKeybox, CertificateDecrypt) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
std::vector<uint8_t> wrapped_rsa_key; std::vector<uint8_t> wrapped_rsa_key;
CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true); CreateWrappedRSAKey(&wrapped_rsa_key, kSign_RSASSA_PSS, true);
Session s; Session s;
@@ -3006,7 +3047,6 @@ class DISABLED_AlternateRSAAlgorithms : public DISABLED_TestKeybox {
} }
void LoadWithAllowedSchemes(uint32_t schemes, bool force) { void LoadWithAllowedSchemes(uint32_t schemes, bool force) {
InstallKeybox(kDefaultKeybox, true);
CreateWrappedRSAKey(&wrapped_rsa_key_, schemes, force); CreateWrappedRSAKey(&wrapped_rsa_key_, schemes, force);
key_loaded_ = (wrapped_rsa_key_.size() > 0); key_loaded_ = (wrapped_rsa_key_.size() > 0);
} }
@@ -3080,6 +3120,7 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
void BuildRSAKey() { void BuildRSAKey() {
vector<uint8_t> field_n = vector<uint8_t> field_n =
encode(0x02, wvcdm::a2b_hex( encode(0x02, wvcdm::a2b_hex(
"00"
"df271fd25f8644496b0c81be4bd50297" "df271fd25f8644496b0c81be4bd50297"
"ef099b002a6fd67727eb449cea566ed6" "ef099b002a6fd67727eb449cea566ed6"
"a3981a71312a141cabc9815c1209e320" "a3981a71312a141cabc9815c1209e320"
@@ -3099,6 +3140,7 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
vector<uint8_t> field_e = encode(0x02, wvcdm::a2b_hex("010001")); vector<uint8_t> field_e = encode(0x02, wvcdm::a2b_hex("010001"));
vector<uint8_t> field_d = vector<uint8_t> field_d =
encode(0x02, wvcdm::a2b_hex( encode(0x02, wvcdm::a2b_hex(
"00"
"5bd910257830dce17520b03441a51a8c" "5bd910257830dce17520b03441a51a8c"
"ab94020ac6ecc252c808f3743c95b7c8" "ab94020ac6ecc252c808f3743c95b7c8"
"3b8c8af1a5014346ebc4242cdfb5d718" "3b8c8af1a5014346ebc4242cdfb5d718"
@@ -3117,6 +3159,7 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
"d4c6591c807defd71ab06866bb5e7745")); "d4c6591c807defd71ab06866bb5e7745"));
vector<uint8_t> field_p = vector<uint8_t> field_p =
encode(0x02, wvcdm::a2b_hex( encode(0x02, wvcdm::a2b_hex(
"00"
"f44f5e4246391f482b2f5296e3602eb3" "f44f5e4246391f482b2f5296e3602eb3"
"4aa136427710f7c0416d403fd69d4b29" "4aa136427710f7c0416d403fd69d4b29"
"130cfebef34e885abdb1a8a0a5f0e9b5" "130cfebef34e885abdb1a8a0a5f0e9b5"
@@ -3127,6 +3170,7 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
"bd9efc123d9c54b6705590d006cfcf3f")); "bd9efc123d9c54b6705590d006cfcf3f"));
vector<uint8_t> field_q = vector<uint8_t> field_q =
encode(0x02, wvcdm::a2b_hex( encode(0x02, wvcdm::a2b_hex(
"00"
"e9d49841e0e0a6ad0d517857133e36dc" "e9d49841e0e0a6ad0d517857133e36dc"
"72c1bdd90f9174b52e26570f373640f1" "72c1bdd90f9174b52e26570f373640f1"
"c185e7ea8e2ed7f1e4ebb951f70a5802" "c185e7ea8e2ed7f1e4ebb951f70a5802"
@@ -3137,6 +3181,7 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
"9eec1cf85e80aba079b2e6106b97bced")); "9eec1cf85e80aba079b2e6106b97bced"));
vector<uint8_t> field_exp1 = vector<uint8_t> field_exp1 =
encode(0x02, wvcdm::a2b_hex( encode(0x02, wvcdm::a2b_hex(
"00"
"ed102acdb26871534d1c414ecad9a4d7" "ed102acdb26871534d1c414ecad9a4d7"
"32fe95b10eea370da62f05de2c393b1a" "32fe95b10eea370da62f05de2c393b1a"
"633303ea741b6b3269c97f704b352702" "633303ea741b6b3269c97f704b352702"
@@ -3147,6 +3192,7 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
"0dcbbc9b528f64a01706e05b0b91106f")); "0dcbbc9b528f64a01706e05b0b91106f"));
vector<uint8_t> field_exp2 = vector<uint8_t> field_exp2 =
encode(0x02, wvcdm::a2b_hex( encode(0x02, wvcdm::a2b_hex(
"00"
"6827924a85e88b55ba00f8219128bd37" "6827924a85e88b55ba00f8219128bd37"
"24c6b7d1dfe5629ef197925fecaff5ed" "24c6b7d1dfe5629ef197925fecaff5ed"
"b9cdf3a7befd8ea2e8dd3707138b3ff8" "b9cdf3a7befd8ea2e8dd3707138b3ff8"
@@ -3157,6 +3203,7 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
"eec82d7f5458ec19e71b90eeef7dff61")); "eec82d7f5458ec19e71b90eeef7dff61"));
vector<uint8_t> field_invq = vector<uint8_t> field_invq =
encode(0x02, wvcdm::a2b_hex( encode(0x02, wvcdm::a2b_hex(
"00"
"57b73888d183a99a6307422277551a3d" "57b73888d183a99a6307422277551a3d"
"9e18adf06a91e8b55ceffef9077c8496" "9e18adf06a91e8b55ceffef9077c8496"
"948ecb3b16b78155cb2a3a57c119d379" "948ecb3b16b78155cb2a3a57c119d379"
@@ -3295,7 +3342,6 @@ class DISABLED_AlternateRSAKey : public DISABLED_TestKeybox {
void LoadWithAllowedSchemes(uint32_t schemes, bool force) { void LoadWithAllowedSchemes(uint32_t schemes, bool force) {
BuildRSAKey(); BuildRSAKey();
InstallKeybox(kDefaultKeybox, true);
CreateWrappedRSAKey(&wrapped_rsa_key_, schemes, force, &encoded_key_[0], CreateWrappedRSAKey(&wrapped_rsa_key_, schemes, force, &encoded_key_[0],
encoded_key_.size()); encoded_key_.size());
key_loaded_ = (wrapped_rsa_key_.size() > 0); key_loaded_ = (wrapped_rsa_key_.size() > 0);
@@ -4072,7 +4118,6 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox {
void BadEncrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, void BadEncrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm,
size_t buffer_length) { size_t buffer_length) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4094,7 +4139,6 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox {
void BadDecrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm, void BadDecrypt(unsigned int key_index, OEMCrypto_Algorithm algorithm,
size_t buffer_length) { size_t buffer_length) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4115,7 +4159,6 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox {
void BadSign(unsigned int key_index, OEMCrypto_Algorithm algorithm) { void BadSign(unsigned int key_index, OEMCrypto_Algorithm algorithm) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4139,7 +4182,6 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox {
void BadVerify(unsigned int key_index, OEMCrypto_Algorithm algorithm, void BadVerify(unsigned int key_index, OEMCrypto_Algorithm algorithm,
size_t signature_size, bool alter_data) { size_t signature_size, bool alter_data) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4162,7 +4204,6 @@ class DISABLED_GenericDRMTest : public DISABLED_TestKeybox {
}; };
TEST_F(DISABLED_GenericDRMTest, GenericKeyLoad) { TEST_F(DISABLED_GenericDRMTest, GenericKeyLoad) {
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4173,7 +4214,6 @@ TEST_F(DISABLED_GenericDRMTest, GenericKeyLoad) {
TEST_F(DISABLED_GenericDRMTest, GenericKeyEncrypt) { TEST_F(DISABLED_GenericDRMTest, GenericKeyEncrypt) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4204,7 +4244,6 @@ TEST_F(DISABLED_GenericDRMTest, GenericKeyBadEncrypt) {
TEST_F(DISABLED_GenericDRMTest, GenericKeyDecrypt) { TEST_F(DISABLED_GenericDRMTest, GenericKeyDecrypt) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4226,7 +4265,6 @@ TEST_F(DISABLED_GenericDRMTest, GenericKeyDecrypt) {
TEST_F(DISABLED_GenericDRMTest, GenericSecureToClear) { TEST_F(DISABLED_GenericDRMTest, GenericSecureToClear) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4258,7 +4296,6 @@ TEST_F(DISABLED_GenericDRMTest, GenericKeyBadDecrypt) {
TEST_F(DISABLED_GenericDRMTest, GenericKeySign) { TEST_F(DISABLED_GenericDRMTest, GenericKeySign) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4295,7 +4332,6 @@ TEST_F(DISABLED_GenericDRMTest, GenericKeyBadSign) {
TEST_F(DISABLED_GenericDRMTest, GenericKeyVerify) { TEST_F(DISABLED_GenericDRMTest, GenericKeyVerify) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4327,7 +4363,6 @@ TEST_F(DISABLED_GenericDRMTest, GenericKeyBadVerify) {
TEST_F(DISABLED_GenericDRMTest, KeyDurationEncrypt) { TEST_F(DISABLED_GenericDRMTest, KeyDurationEncrypt) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4364,7 +4399,6 @@ TEST_F(DISABLED_GenericDRMTest, KeyDurationEncrypt) {
TEST_F(DISABLED_GenericDRMTest, KeyDurationDecrypt) { TEST_F(DISABLED_GenericDRMTest, KeyDurationDecrypt) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4399,7 +4433,6 @@ TEST_F(DISABLED_GenericDRMTest, KeyDurationDecrypt) {
TEST_F(DISABLED_GenericDRMTest, KeyDurationSign) { TEST_F(DISABLED_GenericDRMTest, KeyDurationSign) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();
@@ -4438,7 +4471,6 @@ TEST_F(DISABLED_GenericDRMTest, KeyDurationSign) {
TEST_F(DISABLED_GenericDRMTest, KeyDurationVerify) { TEST_F(DISABLED_GenericDRMTest, KeyDurationVerify) {
OEMCryptoResult sts; OEMCryptoResult sts;
InstallKeybox(kDefaultKeybox, true);
Session s; Session s;
s.open(); s.open();
s.GenerateDerivedKeys(); s.GenerateDerivedKeys();

View File

@@ -14,7 +14,7 @@
'oemcrypto_v8%': 'false', 'oemcrypto_v8%': 'false',
'certificate_provision%': 'false', 'certificate_provision%': 'false',
'force_use_of_secure_buffers%': 'false', 'force_use_of_secure_buffers%': 'false',
'disable_privacy_crypto%': 'false', 'privacy_crypto_impl%': 'openssl',
# There are three protobuf configurations: # There are three protobuf configurations:
# #

View File

@@ -3,14 +3,15 @@
'target_defaults': { 'target_defaults': {
'type': 'static_library', 'type': 'static_library',
'include_dirs': [ 'include_dirs': [
'../../third_party/gmock', 'gmock',
'../../third_party/gmock/include', 'gmock/include',
'../../third_party/gmock/gtest/include', 'gmock/gtest',
'gmock/gtest/include',
], ],
'direct_dependent_settings': { 'direct_dependent_settings': {
'include_dirs': [ 'include_dirs': [
'../../third_party/gmock/include', 'gmock/include',
'../../third_party/gmock/gtest/include', 'gmock/gtest/include',
], ],
}, },
}, },
@@ -18,13 +19,19 @@
{ {
'target_name': 'gmock', 'target_name': 'gmock',
'sources': [ 'sources': [
'../../third_party/gmock/src/gmock-all.cc', 'gmock/src/gmock-all.cc',
], ],
}, },
{ {
'target_name': 'gmock_main', 'target_name': 'gmock_main',
'sources': [ 'sources': [
'../../third_party/gmock/src/gmock_main.cc', 'gmock/src/gmock_main.cc',
],
},
{
'target_name': 'gtest',
'sources': [
'gmock/gtest/src/gtest-all.cc',
], ],
}, },
], ],

View File

@@ -14,6 +14,13 @@ needs a different format of build script, and it needs to build against the OS'
modified version of gTest, rather than the packaged version. modified version of gTest, rather than the packaged version.
Local Modifications: Local Modifications:
--Tue Jun 3, 2014 (joeyparrish)--
Removed DOS newlines for the sake of the git-to-piper bridge. Piper converts
newlines automatically to unix style. If this change is not made in git, the
bridge detects differences and attempts to sync to piper, even when nothing
has changed in git.
--Mon Feb 4, 2014 (juce)-- --Mon Feb 4, 2014 (juce)--
Upgraded to gMock 1.7.0. This is required for compatibility with the upgrade Upgraded to gMock 1.7.0. This is required for compatibility with the upgrade
@@ -59,4 +66,4 @@ Excluded the following files from being copied to Android repo:
make/ make/
msvc/ msvc/
fused-src/ fused-src/
gtest/ gtest/