Source release v3.1.0
This commit is contained in:
18
cdm/cdm.gyp
18
cdm/cdm.gyp
@@ -7,7 +7,7 @@
|
||||
{
|
||||
'variables': {
|
||||
# Override if you intend to link against a different OEMCrypto API version.
|
||||
'oemcrypto_version%': 10,
|
||||
'oemcrypto_version%': 11,
|
||||
|
||||
# Override if you can't depend on OpenSSL for privacy features.
|
||||
# If set to 'dummy', privacy mode in the CDM will fail.
|
||||
@@ -84,6 +84,7 @@
|
||||
'include_dirs': [
|
||||
'../core/include',
|
||||
'../oemcrypto/include',
|
||||
'../third_party/jsmn',
|
||||
'../third_party/stringencoders/src',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
@@ -104,10 +105,10 @@
|
||||
'../core/include/device_files.h',
|
||||
'../core/include/file_store.h',
|
||||
'../core/include/initialization_data.h',
|
||||
'../core/include/license_key_status.h',
|
||||
'../core/include/license.h',
|
||||
'../core/include/lock.h',
|
||||
'../core/include/log.h',
|
||||
'../core/include/max_res_engine.h',
|
||||
'../core/include/oemcrypto_adapter.h',
|
||||
'../core/include/policy_engine.h',
|
||||
'../core/include/privacy_crypto.h',
|
||||
@@ -124,13 +125,18 @@
|
||||
'../core/src/crypto_session.cpp',
|
||||
'../core/src/device_files.cpp',
|
||||
'../core/src/initialization_data.cpp',
|
||||
'../core/src/license_key_status.cpp',
|
||||
'../core/src/license.cpp',
|
||||
'../core/src/max_res_engine.cpp',
|
||||
'../core/src/oemcrypto_adapter_static.cpp',
|
||||
'../core/src/policy_engine.cpp',
|
||||
'../core/src/privacy_crypto_<(privacy_crypto_impl).cpp',
|
||||
'../core/src/properties.cpp',
|
||||
'../core/src/string_conversions.cpp',
|
||||
'../third_party/jsmn/jsmn.h',
|
||||
'../third_party/jsmn/jsmn.c',
|
||||
'../third_party/stringencoders/src/modp_b64_data.h',
|
||||
'../third_party/stringencoders/src/modp_b64.cpp',
|
||||
'../third_party/stringencoders/src/modp_b64.h',
|
||||
'../third_party/stringencoders/src/modp_b64w_data.h',
|
||||
'../third_party/stringencoders/src/modp_b64w.cpp',
|
||||
'../third_party/stringencoders/src/modp_b64w.h',
|
||||
@@ -148,6 +154,12 @@
|
||||
'../core/src/oemcrypto_adapter_static_v10.cpp',
|
||||
],
|
||||
}],
|
||||
['oemcrypto_version < 11', {
|
||||
'sources': [
|
||||
# Include APIs introduced in v10.
|
||||
'../core/src/oemcrypto_adapter_static_v11.cpp',
|
||||
],
|
||||
}],
|
||||
['privacy_crypto_impl=="openssl"', {
|
||||
'conditions': [
|
||||
['openssl_config == "target"', {
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
'../core/test/cdm_session_unittest.cpp',
|
||||
'../core/test/config_test_env.cpp',
|
||||
'../core/test/device_files_unittest.cpp',
|
||||
'../core/test/generic_crypto_unittest.cpp',
|
||||
'../core/test/http_socket.cpp',
|
||||
'../core/test/initialization_data_unittest.cpp',
|
||||
'../core/test/license_request.cpp',
|
||||
'../core/test/license_unittest.cpp',
|
||||
'../core/test/max_res_engine_unittest.cpp',
|
||||
'../core/test/license_keys_unittest.cpp',
|
||||
'../core/test/policy_engine_unittest.cpp',
|
||||
'../core/test/policy_engine_constraints_unittest.cpp',
|
||||
'../core/test/test_printers.cpp',
|
||||
'../core/test/url_request.cpp',
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
// Based on the EME draft spec from 2015 November 20.
|
||||
// https://rawgit.com/w3c/encrypted-media/1dab9e5/index.html
|
||||
// Based on the EME draft spec from 2016 June 10.
|
||||
// http://www.w3.org/TR/2016/WD-encrypted-media-20160610/"
|
||||
#ifndef WVCDM_CDM_CDM_H_
|
||||
#define WVCDM_CDM_CDM_H_
|
||||
|
||||
@@ -58,7 +58,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
kLicenseRequest = 0,
|
||||
kLicenseRenewal = 1,
|
||||
kLicenseRelease = 2,
|
||||
kIndividualizationRequest = 3,
|
||||
kIndividualizationRequest = 3, // Not used. Direct Individualization
|
||||
// is used instead of App-Assisted
|
||||
} MessageType;
|
||||
|
||||
typedef enum {
|
||||
@@ -77,6 +78,10 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
kQuotaExceeded = 8,
|
||||
kRangeError = 9,
|
||||
|
||||
// The action could not be completed yet but has been scheduled to be done
|
||||
// later. A call to |event_listener.onDeferredComplete| will be made once
|
||||
// the action is complete.
|
||||
kDeferred = 99998,
|
||||
// This covers errors that we do not expect (see logs for details):
|
||||
kUnexpectedError = 99999,
|
||||
} Status;
|
||||
@@ -86,8 +91,18 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
kCenc = 0,
|
||||
kKeyIds = 1, // NOTE: not supported by Widevine at this time
|
||||
kWebM = 2,
|
||||
|
||||
// This type is not defined by EME but is supported by Widevine
|
||||
kHls = 10000,
|
||||
} InitDataType;
|
||||
|
||||
// These are the crypto schemes supported by CENC 3.0.
|
||||
typedef enum {
|
||||
kClear = 0,
|
||||
kAesCtr = 1, // AES-CTR, for use with cenc and cens modes
|
||||
kAesCbc = 2, // AES-CBC, for use with cbc1 and cbcs modes
|
||||
} EncryptionScheme;
|
||||
|
||||
// These are key statuses defined by EME.
|
||||
typedef enum {
|
||||
kUsable = 0,
|
||||
@@ -99,6 +114,19 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
kReleased = 5,
|
||||
} KeyStatus;
|
||||
|
||||
// Permissible usages for a key. Returned as a set of flags; multiple
|
||||
// flags may be set. The specific settings are defined in the license
|
||||
// and the OEMCrypto Key Control Block. The CDM uses settings in the
|
||||
// license to derive these flags.
|
||||
typedef uint32_t KeyAllowedUsageFlags;
|
||||
static const KeyAllowedUsageFlags kAllowNone = 0;
|
||||
static const KeyAllowedUsageFlags kAllowDecryptToClearBuffer = 1;
|
||||
static const KeyAllowedUsageFlags kAllowDecryptToSecureBuffer = 2;
|
||||
static const KeyAllowedUsageFlags kAllowGenericEncrypt = 4;
|
||||
static const KeyAllowedUsageFlags kAllowGenericDecrypt = 8;
|
||||
static const KeyAllowedUsageFlags kAllowGenericSign = 16;
|
||||
static const KeyAllowedUsageFlags kAllowGenericSignatureVerify = 32;
|
||||
|
||||
// These are defined by Widevine. The CDM can be configured to decrypt in
|
||||
// three modes (dependent on OEMCrypto support).
|
||||
typedef enum {
|
||||
@@ -137,20 +165,6 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
// See Cdm::createSession().
|
||||
class IEventListener {
|
||||
public:
|
||||
// A URL to be added to a renewal request message.
|
||||
// This call will immediately precede the onMessage() call.
|
||||
// Do not override this call if the URL is not needed.
|
||||
//
|
||||
// WARNING: this call exists temporarily to allow interoperation with
|
||||
// older versions of Chromium and the prefixed EME API. This call will
|
||||
// be removed in a future release. Therefore: (1) Do not use this call
|
||||
// unless you are certain that it is needed on your platform for your
|
||||
// application, and (2) If it is needed, figure how move to a new version
|
||||
// of Chromium and the unprefixed EME API as soon as possible.
|
||||
// TODO: Remove this call (see b/24776024).
|
||||
virtual void onMessageUrl(const std::string& session_id,
|
||||
const std::string& server_url) {}
|
||||
|
||||
// A message (license request, renewal, etc.) to be dispatched to the
|
||||
// application's license server.
|
||||
// The response, if successful, should be provided back to the CDM via a
|
||||
@@ -165,14 +179,30 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
// A remove() operation has been completed.
|
||||
virtual void onRemoveComplete(const std::string& session_id) = 0;
|
||||
|
||||
// Called when a deferred action has completed.
|
||||
virtual void onDeferredComplete(const std::string& session_id,
|
||||
Status result) = 0;
|
||||
|
||||
// Called when the CDM requires a new device certificate
|
||||
virtual void onDirectIndividualizationRequest(
|
||||
const std::string& session_id, const std::string& request) = 0;
|
||||
|
||||
protected:
|
||||
IEventListener() {}
|
||||
virtual ~IEventListener() {}
|
||||
};
|
||||
|
||||
// A storage interface provided by the application, independent of CDM
|
||||
// instances.
|
||||
// See Cdm::initialize().
|
||||
// A storage interface provided by the application. This defines the "origin"
|
||||
// that the CDM will operate in by the files it can access. Passing different
|
||||
// IStorage instances to Cdm::create will cause those CDM instances to be in
|
||||
// different "origins" as defined by the IStorage instance. For example,
|
||||
// different IStorage instances could be tied to different folders for
|
||||
// different origins.
|
||||
//
|
||||
// It is important for multi-origin hosts to verify the application's origin.
|
||||
// This ensures that the application does not access files from another
|
||||
// origin.
|
||||
//
|
||||
// NOTE: It is important for users of your application to be able to clear
|
||||
// stored data. Also, browsers or other multi-application systems should
|
||||
// store data separately per-app or per-origin.
|
||||
@@ -258,35 +288,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
std::string build_info;
|
||||
};
|
||||
|
||||
// Device certificate request information.
|
||||
// The structure is passed by the application to the library in as an output
|
||||
// parameter to Cdm::initialize().
|
||||
// All fields are filled in by the library to instruct the application to
|
||||
// handle device certificate requests, if needed.
|
||||
struct DeviceCertificateRequest {
|
||||
// If false, the library is ready to create and/or load sessions.
|
||||
// If true, a device certificate is needed first.
|
||||
// Sessions cannot be created or loaded until the device certificate has
|
||||
// been provisioned.
|
||||
bool needed;
|
||||
|
||||
// If |needed| is true, this string contains the URL that must be used to
|
||||
// provision a device certificate. The request must be a POST.
|
||||
std::string url;
|
||||
|
||||
// If |needed| is true, the response from the above-described HTTP POST
|
||||
// must be provided as an argument to this method.
|
||||
// Returns kSuccess if the provisioning was successful.
|
||||
// Any other return value means the provisioning failed and the CDM cannot
|
||||
// be used yet.
|
||||
Status acceptReply(const std::string& reply);
|
||||
};
|
||||
|
||||
// Initialize the CDM library and provide access to platform services.
|
||||
// All platform interfaces are required.
|
||||
// The |device_certificate_request| parameter will be filled in by
|
||||
// initialize().
|
||||
// See documentation for DeviceCertificateRequest for more information.
|
||||
// Logging is controlled by |verbosity|.
|
||||
// Must be called and must return kSuccess before create() is called.
|
||||
static Status initialize(
|
||||
@@ -295,7 +298,6 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
IStorage* storage,
|
||||
IClock* clock,
|
||||
ITimer* timer,
|
||||
DeviceCertificateRequest* device_certificate_request,
|
||||
LogLevel verbosity);
|
||||
|
||||
// Query the CDM library version.
|
||||
@@ -306,6 +308,9 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
// instance may be constructed.
|
||||
// The CDM may notify of events at any time via the provided |listener|,
|
||||
// which may not be NULL.
|
||||
// |storage| defines the storage to use for this instance. This can be used
|
||||
// to provide per-origin storage. Passing NULL will use the storage passed
|
||||
// to initialize().
|
||||
// If |privacy_mode| is true, server certificates are required and will be
|
||||
// used to encrypt messages to the license server.
|
||||
// By using server certificates to encrypt communication with the license
|
||||
@@ -315,6 +320,7 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
// This is particularly useful for browser environments, but is recommended
|
||||
// for use whenever possible.
|
||||
static Cdm* create(IEventListener* listener,
|
||||
IStorage* storage,
|
||||
bool privacy_mode);
|
||||
|
||||
virtual ~Cdm() {}
|
||||
@@ -360,6 +366,18 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
virtual Status getKeyStatuses(const std::string& session_id,
|
||||
KeyStatusMap* key_statuses) = 0;
|
||||
|
||||
// Gets the permitted usage for a specific key by ID.
|
||||
virtual Status getKeyAllowedUsages(const std::string& session_id,
|
||||
const std::string& key_id,
|
||||
KeyAllowedUsageFlags* usage_flags) = 0;
|
||||
|
||||
// Gets the permitted usage for a specific key by ID.
|
||||
// Search for key across all known sessions. If there are keys in separate
|
||||
// sessions that match the given key_id, return kTypeError unless all such
|
||||
// keys have identical Allowed Usage settings.
|
||||
virtual Status getKeyAllowedUsages(const std::string& key_id,
|
||||
KeyAllowedUsageFlags* usage_flags) = 0;
|
||||
|
||||
// Indicates that the application no longer needs the session and the CDM
|
||||
// should release any resources associated with it and close it.
|
||||
// Does not generate release messages for persistent sessions.
|
||||
@@ -373,6 +391,28 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
// session is fully removed.
|
||||
virtual Status remove(const std::string& session_id) = 0;
|
||||
|
||||
// Describes a repeating pattern as defined by the CENC 3.0 standard. A
|
||||
// CENC 3.0 pattern consists of a number of encrypted blocks followed by a
|
||||
// number of clear blocks, after which it repeats.
|
||||
struct Pattern {
|
||||
public:
|
||||
Pattern()
|
||||
: encrypted_blocks(0),
|
||||
clear_blocks(0) {}
|
||||
|
||||
Pattern(uint32_t encrypt, uint32_t clear)
|
||||
: encrypted_blocks(encrypt),
|
||||
clear_blocks(clear) {}
|
||||
|
||||
// The number of crypto blocks that are encrypted and therefore need to be
|
||||
// decrypted.
|
||||
uint32_t encrypted_blocks;
|
||||
|
||||
// The number of crypto blocks that are not encrypted and therefore should
|
||||
// be skipped when doing decryption.
|
||||
uint32_t clear_blocks;
|
||||
};
|
||||
|
||||
struct InputBuffer {
|
||||
public:
|
||||
InputBuffer()
|
||||
@@ -380,10 +420,11 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
key_id_length(0),
|
||||
iv(NULL),
|
||||
iv_length(0),
|
||||
pattern(),
|
||||
data(NULL),
|
||||
data_length(0),
|
||||
block_offset(0),
|
||||
is_encrypted(true),
|
||||
encryption_scheme(kAesCtr),
|
||||
is_video(true),
|
||||
first_subsample(true),
|
||||
last_subsample(true) {}
|
||||
@@ -395,18 +436,34 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
const uint8_t* iv;
|
||||
uint32_t iv_length;
|
||||
|
||||
// Describes the repeating pattern with which the content was encrypted. If
|
||||
// left at its default value of (0,0), patterns will be disabled. Should
|
||||
// only be changed for content that uses patterns, such as for CENC 3.0
|
||||
// "cens" and "cbcs" content or for HLS content.
|
||||
Pattern pattern;
|
||||
|
||||
// This pointer and length describe the data to be decrypted. This data
|
||||
// should be ready to be decrypted with no further processing. If the data
|
||||
// is coming from a format that requires processing before decryption, that
|
||||
// processing needs to happen before the data is passed in here. For
|
||||
// example, content coming from HLS will need to have its extra start code
|
||||
// emulation prevention removed before it is passed to Widevine.
|
||||
const uint8_t* data;
|
||||
uint32_t data_length;
|
||||
|
||||
// |data|'s offset within its 16-byte AES block, used for CENC subsamples.
|
||||
// Should start at 0 for each sample, then go up by |data_length| (mod 16)
|
||||
// after the |is_encrypted| part of each subsample.
|
||||
// |data|'s offset within its 16-byte AES block. Only used for encrypted
|
||||
// subsamples from content using CENC standards before 3.0 or the
|
||||
// equivalent mode in CENC 3.0, "cenc" mode. Should always be 0 in CENC 3.0
|
||||
// modes "cens," "cbc1," and "cbcs," as well as for HLS content. When used,
|
||||
// it should start at 0 for each sample, then go up by |data_length| (mod
|
||||
// 16) after the |is_encrypted| part of each subsample.
|
||||
uint32_t block_offset;
|
||||
|
||||
// If false, copies the input data directly to the output buffer. Used for
|
||||
// secure output types, where the output buffer cannot be directly accessed
|
||||
// above the CDM.
|
||||
bool is_encrypted;
|
||||
// Specifies the encryption scheme, if any, to be used to decrypt the data.
|
||||
// When set to kClear, decryption will copy the input data directly to the
|
||||
// output buffer. This is necessary for secure output types, where the
|
||||
// output buffer cannot be directly accessed above the CDM.
|
||||
EncryptionScheme encryption_scheme;
|
||||
|
||||
// Used by secure output type kDirectRender, where the secure hardware must
|
||||
// decode and render the decrypted content:
|
||||
@@ -473,6 +530,44 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
// Clears all the values in the custom app settings. See setAppParameter().
|
||||
virtual Status clearAppParameters() = 0;
|
||||
|
||||
// Generic crypto - functions for applying crypto operations to
|
||||
// app-level data (outside the content stream).
|
||||
|
||||
typedef enum {
|
||||
kEncryptionAlgorithmUnknown,
|
||||
kEncryptionAlgorithmAesCbc128,
|
||||
} GenericEncryptionAlgorithmType;
|
||||
|
||||
typedef enum {
|
||||
kSigningAlgorithmUnknown,
|
||||
kSigningAlgorithmHmacSha256
|
||||
} GenericSigningAlgorithmType;
|
||||
|
||||
// Encrypts a buffer of app-level data.
|
||||
virtual Status genericEncrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) = 0;
|
||||
|
||||
// Decrypts a buffer of app-level data.
|
||||
virtual Status genericDecrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) = 0;
|
||||
|
||||
// Signs a buffer of app-level data.
|
||||
virtual Status genericSign(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
std::string* signature) = 0;
|
||||
|
||||
// Verifies the signature on a buffer of app-level data.
|
||||
// Returns kSuccess if signature is verified, otherwise returns kDecryptError.
|
||||
virtual Status genericVerify(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
const std::string& signature) = 0;
|
||||
|
||||
protected:
|
||||
Cdm() {}
|
||||
};
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Widevine CE CDM Version
|
||||
#define CDM_VERSION "v3.0.5-0-g897db53-ce"
|
||||
#define CDM_VERSION "v3.1.0-0-g63dfeca-ce"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
# Does not include the test runner main.
|
||||
{
|
||||
'sources': [
|
||||
'../oemcrypto/test/oec_device_features.cpp',
|
||||
'../oemcrypto/test/oec_session_util.cpp',
|
||||
'../oemcrypto/test/oemcrypto_test.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
|
||||
655
cdm/src/cdm.cpp
655
cdm/src/cdm.cpp
@@ -38,14 +38,14 @@ struct HostType {
|
||||
Cdm::IStorage* storage;
|
||||
Cdm::IClock* clock;
|
||||
Cdm::ITimer* timer;
|
||||
CdmEngine* provisioning_engine;
|
||||
FileSystem* file_system;
|
||||
bool initialized;
|
||||
|
||||
HostType()
|
||||
: storage(NULL),
|
||||
clock(NULL),
|
||||
timer(NULL),
|
||||
provisioning_engine(NULL),
|
||||
file_system(NULL),
|
||||
initialized(false) {}
|
||||
} host;
|
||||
|
||||
@@ -109,8 +109,7 @@ class PropertySet : public CdmClientPropertySet {
|
||||
class CdmImpl : public Cdm,
|
||||
public WvCdmEventListener {
|
||||
public:
|
||||
CdmImpl(IEventListener* listener,
|
||||
bool privacy_mode);
|
||||
CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode);
|
||||
|
||||
virtual ~CdmImpl();
|
||||
|
||||
@@ -135,6 +134,13 @@ class CdmImpl : public Cdm,
|
||||
virtual Status getKeyStatuses(const std::string& session_id,
|
||||
KeyStatusMap* key_statuses) OVERRIDE;
|
||||
|
||||
virtual Status getKeyAllowedUsages(
|
||||
const std::string& session_id, const std::string& key_id,
|
||||
KeyAllowedUsageFlags* usage_flags) OVERRIDE;
|
||||
|
||||
virtual Status getKeyAllowedUsages(
|
||||
const std::string& key_id, KeyAllowedUsageFlags* usage_flags) OVERRIDE;
|
||||
|
||||
virtual Status setAppParameter(const std::string& key,
|
||||
const std::string& value) OVERRIDE;
|
||||
|
||||
@@ -152,6 +158,25 @@ class CdmImpl : public Cdm,
|
||||
virtual Status decrypt(const InputBuffer& input,
|
||||
const OutputBuffer& output) OVERRIDE;
|
||||
|
||||
virtual Status genericEncrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
GenericEncryptionAlgorithmType algorithm,
|
||||
std::string* out_buffer) OVERRIDE;
|
||||
virtual Status genericDecrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
GenericEncryptionAlgorithmType algorithm,
|
||||
std::string* out_buffer) OVERRIDE;
|
||||
virtual Status genericSign(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
std::string* signature) OVERRIDE;
|
||||
virtual Status genericVerify(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
const std::string& signature) OVERRIDE;
|
||||
|
||||
// ITimerClient:
|
||||
virtual void onTimerExpired(void* context) OVERRIDE;
|
||||
|
||||
@@ -166,9 +191,17 @@ class CdmImpl : public Cdm,
|
||||
int64_t new_expiry_time_seconds) OVERRIDE;
|
||||
|
||||
private:
|
||||
KeyAllowedUsageFlags KeyAllowedFlags(const CdmKeyAllowedUsage& usages);
|
||||
bool SendProvisioningRequest(const std::string& session_id);
|
||||
CdmEncryptionAlgorithm ConvertEncryptionAlgorithm(
|
||||
GenericEncryptionAlgorithmType algorithm);
|
||||
CdmSigningAlgorithm ConvertSigningAlgorithm(
|
||||
GenericSigningAlgorithmType algorithm);
|
||||
|
||||
IEventListener* listener_;
|
||||
bool policy_timer_enabled_;
|
||||
|
||||
FileSystem file_system_;
|
||||
CdmEngine cdm_engine_;
|
||||
PropertySet property_set_;
|
||||
CdmAppParameterMap app_parameters_;
|
||||
@@ -184,13 +217,33 @@ class CdmImpl : public Cdm,
|
||||
type((SessionType)-1),
|
||||
expiration(0) {}
|
||||
};
|
||||
struct UnprovisionedSessionMetadata {
|
||||
SessionType type;
|
||||
std::string init_data;
|
||||
InitDataType init_data_type;
|
||||
bool has_init_data;
|
||||
bool is_load;
|
||||
|
||||
UnprovisionedSessionMetadata()
|
||||
: type((SessionType)-1),
|
||||
init_data_type((InitDataType)-1),
|
||||
has_init_data(false),
|
||||
is_load(false) {}
|
||||
};
|
||||
typedef std::map<std::string, UnprovisionedSessionMetadata> UnprovisionedMap;
|
||||
|
||||
std::map<std::string, SessionMetadata> sessions_;
|
||||
UnprovisionedMap unprovisioned_sessions_;
|
||||
bool provision_request_sent_;
|
||||
};
|
||||
|
||||
CdmImpl::CdmImpl(IEventListener* listener,
|
||||
bool privacy_mode)
|
||||
CdmImpl::CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode)
|
||||
: listener_(listener),
|
||||
policy_timer_enabled_(false) {
|
||||
policy_timer_enabled_(false),
|
||||
file_system_("", storage),
|
||||
cdm_engine_(&file_system_),
|
||||
provision_request_sent_(false) {
|
||||
assert(NULL != listener_);
|
||||
property_set_.set_use_privacy_mode(privacy_mode);
|
||||
}
|
||||
|
||||
@@ -220,7 +273,7 @@ Cdm::Status CdmImpl::setServerCertificate(const std::string& certificate) {
|
||||
|
||||
Cdm::Status CdmImpl::createSession(SessionType session_type,
|
||||
std::string* session_id) {
|
||||
if (!session_id) {
|
||||
if (NULL == session_id) {
|
||||
LOGE("Missing session ID pointer.");
|
||||
return kTypeError;
|
||||
}
|
||||
@@ -238,17 +291,17 @@ Cdm::Status CdmImpl::createSession(SessionType session_type,
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
std::string empty_origin;
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, empty_origin, this,
|
||||
NULL, session_id);
|
||||
"com.widevine.alpha", &property_set_, this, session_id);
|
||||
switch (result) {
|
||||
case NO_ERROR:
|
||||
sessions_[*session_id].type = session_type;
|
||||
return kSuccess;
|
||||
case NEED_PROVISIONING:
|
||||
LOGE("A device certificate is needed.");
|
||||
return kNeedsDeviceCertificate;
|
||||
// The session does not exist in |cdm_engine_| yet, will be added in
|
||||
// generateRequest.
|
||||
unprovisioned_sessions_[*session_id].type = session_type;
|
||||
return kSuccess;
|
||||
default:
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
@@ -259,8 +312,43 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
InitDataType init_data_type,
|
||||
const std::string& init_data) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
UnprovisionedMap::iterator session_iter =
|
||||
unprovisioned_sessions_.find(session_id);
|
||||
if (session_iter == unprovisioned_sessions_.end()) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
|
||||
if (cdm_engine_.IsProvisioned(kSecurityLevelL1)) {
|
||||
// We are provisioned now, create the session and generate the request
|
||||
// like normal.
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, session_id, this);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
sessions_[session_id].type = unprovisioned_sessions_[session_id].type;
|
||||
unprovisioned_sessions_.erase(session_iter);
|
||||
} else {
|
||||
// We are not provisioned yet, save the init data and send a provisioning
|
||||
// request if needed.
|
||||
if (unprovisioned_sessions_[session_id].has_init_data) {
|
||||
LOGE("Request already generated: %s", session_id.c_str());
|
||||
return kInvalidState;
|
||||
}
|
||||
|
||||
unprovisioned_sessions_[session_id].has_init_data = true;
|
||||
unprovisioned_sessions_[session_id].init_data_type = init_data_type;
|
||||
unprovisioned_sessions_[session_id].init_data = init_data;
|
||||
|
||||
if (provision_request_sent_)
|
||||
return kDeferred;
|
||||
|
||||
if (!SendProvisioningRequest(session_id))
|
||||
return kUnexpectedError;
|
||||
return kSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
if (sessions_[session_id].callable) {
|
||||
@@ -296,6 +384,9 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
case kWebM:
|
||||
init_data_type_name = WEBM_INIT_DATA_FORMAT;
|
||||
break;
|
||||
case kHls:
|
||||
init_data_type_name = HLS_INIT_DATA_FORMAT;
|
||||
break;
|
||||
default:
|
||||
LOGE("Invalid init data type: %d", init_data_type);
|
||||
return kTypeError;
|
||||
@@ -315,14 +406,11 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
std::string key_request;
|
||||
CdmKeyRequestType key_request_type;
|
||||
std::string ignored_server_url;
|
||||
CdmKeyRequest key_request;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, init_data_obj,
|
||||
license_type, app_parameters_, &key_request, &key_request_type,
|
||||
&ignored_server_url, NULL);
|
||||
license_type, app_parameters_, &key_request);
|
||||
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
@@ -330,7 +418,9 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
}
|
||||
|
||||
sessions_[session_id].callable = true;
|
||||
assert(key_request_type == kKeyRequestTypeInitial);
|
||||
assert(key_request.type == kKeyRequestTypeInitial);
|
||||
|
||||
MessageType message_type;
|
||||
if (property_set_.use_privacy_mode() &&
|
||||
property_set_.service_certificate().empty()) {
|
||||
// We can deduce that this is a server cert request, even though CdmEgine
|
||||
@@ -339,10 +429,12 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
// The EME editor has clarified that this was a misinterpretation of the
|
||||
// spec, and that this should also be kLicenseRequest.
|
||||
LOGI("A server certificate request has been generated.");
|
||||
message_type = kLicenseRequest;
|
||||
} else {
|
||||
LOGI("A license request has been generated.");
|
||||
message_type = kLicenseRequest;
|
||||
}
|
||||
listener_->onMessage(session_id, kLicenseRequest, key_request);
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
@@ -357,23 +449,28 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
|
||||
std::string empty_origin;
|
||||
std::string ignored_session_id_output;
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, empty_origin, this,
|
||||
&session_id, &ignored_session_id_output);
|
||||
"com.widevine.alpha", &property_set_, session_id, this);
|
||||
switch (result) {
|
||||
case NO_ERROR:
|
||||
break;
|
||||
case NEED_PROVISIONING:
|
||||
LOGE("A device certificate is needed.");
|
||||
return kNeedsDeviceCertificate;
|
||||
unprovisioned_sessions_[session_id].is_load = true;
|
||||
if (provision_request_sent_)
|
||||
return kDeferred;
|
||||
|
||||
// Send a provisioning request right away. Then we will load the session
|
||||
// again in |update|.
|
||||
if (!SendProvisioningRequest(session_id))
|
||||
return kUnexpectedError;
|
||||
|
||||
return kSuccess;
|
||||
default:
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
DeviceFiles f;
|
||||
DeviceFiles f(&file_system_);
|
||||
if (!f.Init(kSecurityLevelUnknown)) {
|
||||
LOGE("Unexpected error, failed to init DeviceFiles");
|
||||
return kUnexpectedError;
|
||||
@@ -381,8 +478,8 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
|
||||
if (!f.LicenseExists(session_id)) {
|
||||
// This might be a usage record session which needs to be loaded.
|
||||
CdmKeyMessage release_message;
|
||||
result = cdm_engine_.LoadUsageSession(session_id, &release_message);
|
||||
CdmKeyMessage ignored_release_message;
|
||||
result = cdm_engine_.LoadUsageSession(session_id, &ignored_release_message);
|
||||
if (result == LOAD_USAGE_INFO_MISSING) {
|
||||
LOGE("Unable to load license: %s", session_id.c_str());
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
@@ -393,10 +490,6 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
LOGI("A usage record release has been generated.");
|
||||
MessageType message_type = kLicenseRelease;
|
||||
listener_->onMessage(session_id, message_type, release_message);
|
||||
|
||||
sessions_[session_id].type = kPersistentUsageRecord;
|
||||
sessions_[session_id].callable = true;
|
||||
return kSuccess;
|
||||
@@ -405,24 +498,7 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
result = cdm_engine_.RestoreKey(session_id, session_id);
|
||||
if (result == GET_RELEASED_LICENSE_ERROR) {
|
||||
// This was partially removed already.
|
||||
// The EME spec states that we should send a release message right away.
|
||||
InitializationData empty_initialization_data;
|
||||
CdmKeyMessage key_request;
|
||||
std::string ignored_server_url;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_initialization_data,
|
||||
kLicenseTypeRelease, app_parameters_, &key_request, NULL,
|
||||
&ignored_server_url, NULL);
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
LOGI("A license release has been generated.");
|
||||
MessageType message_type = kLicenseRelease;
|
||||
listener_->onMessage(session_id, message_type, key_request);
|
||||
// The EME spec states that we should be able to load it, but not use it.
|
||||
} else if (result != KEY_ADDED) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
@@ -435,6 +511,64 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
|
||||
Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
const std::string& response) {
|
||||
if (provision_request_sent_) {
|
||||
std::string ignored_cert;
|
||||
std::string ignored_wrapped_key;
|
||||
|
||||
provision_request_sent_ = false;
|
||||
CdmResponseType result = cdm_engine_.HandleProvisioningResponse(
|
||||
response, &ignored_cert, &ignored_wrapped_key);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
// We are now provisioned, we need to recreate the unprovisioned sessions.
|
||||
Cdm::Status ret = kSuccess;
|
||||
for (UnprovisionedMap::iterator it = unprovisioned_sessions_.begin();
|
||||
it != unprovisioned_sessions_.end();) {
|
||||
if (it->second.is_load) {
|
||||
Cdm::Status load_status = load(it->first);
|
||||
if (it->first != session_id)
|
||||
listener_->onDeferredComplete(it->first, load_status);
|
||||
else if (load_status != kSuccess)
|
||||
ret = load_status;
|
||||
unprovisioned_sessions_.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
result = cdm_engine_.OpenSession("com.widevine.alpha", &property_set_,
|
||||
it->first, this);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
if (it->first == session_id)
|
||||
ret = kUnexpectedError;
|
||||
else
|
||||
listener_->onDeferredComplete(it->first, kUnexpectedError);
|
||||
// We failed to create the session. Keep it in the unprovisioned
|
||||
// sessions map so that another call to generateRequest will try to
|
||||
// create the session again.
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
sessions_[it->first].type = it->second.type;
|
||||
|
||||
// Call generate request again for the session.
|
||||
if (it->second.has_init_data) {
|
||||
Cdm::Status generate_status = generateRequest(
|
||||
it->first, it->second.init_data_type, it->second.init_data);
|
||||
if (it->first != session_id)
|
||||
listener_->onDeferredComplete(it->first, generate_status);
|
||||
else if (generate_status != kSuccess)
|
||||
// Still erase the item because the session exists in |cdm_engine_|.
|
||||
ret = generate_status;
|
||||
}
|
||||
unprovisioned_sessions_.erase(it++);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
@@ -467,14 +601,11 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
// The underlying session in CdmEngine has stored a copy of the original
|
||||
// init data, so we can use an empty one this time.
|
||||
InitializationData empty_init_data;
|
||||
std::string key_request;
|
||||
CdmKeyRequestType key_request_type;
|
||||
std::string ignored_server_url;
|
||||
CdmKeyRequest key_request;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_init_data, kLicenseTypeDeferred,
|
||||
app_parameters_, &key_request, &key_request_type,
|
||||
&ignored_server_url, NULL);
|
||||
app_parameters_, &key_request);
|
||||
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
@@ -482,9 +613,9 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
}
|
||||
|
||||
LOGI("A deferred license request has been generated.");
|
||||
assert(key_request_type == kKeyRequestTypeInitial);
|
||||
assert(key_request.type == kKeyRequestTypeInitial);
|
||||
MessageType message_type = kLicenseRequest;
|
||||
listener_->onMessage(session_id, message_type, key_request);
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
return kSuccess;
|
||||
} else if (result == OFFLINE_LICENSE_PROHIBITED) {
|
||||
LOGE("A temporary session cannot be used for a persistent license.");
|
||||
@@ -518,6 +649,10 @@ Cdm::Status CdmImpl::getExpiration(const std::string& session_id,
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
if (NULL == expiration) {
|
||||
LOGE("Missing pointer to expiration result.");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
*expiration = sessions_[session_id].expiration;
|
||||
return kSuccess;
|
||||
@@ -529,11 +664,69 @@ Cdm::Status CdmImpl::getKeyStatuses(const std::string& session_id,
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
if (NULL == key_statuses) {
|
||||
LOGE("Missing pointer to KeyStatusMap result.");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
*key_statuses = sessions_[session_id].key_statuses;
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& session_id,
|
||||
const std::string& key_id,
|
||||
KeyAllowedUsageFlags* usage_flags) {
|
||||
if (!cdm_engine_.IsOpenSession(session_id)) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
if (NULL == usage_flags) {
|
||||
LOGE("Missing pointer to KeyAllowedUsageFlags result.");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
CdmKeyAllowedUsage usage_for_key;
|
||||
CdmResponseType result = cdm_engine_.QueryKeyAllowedUsage(session_id,
|
||||
key_id,
|
||||
&usage_for_key);
|
||||
if (result != NO_ERROR) {
|
||||
if (result == KEY_NOT_FOUND_1) {
|
||||
return kNoKey;
|
||||
} else {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
}
|
||||
|
||||
*usage_flags = KeyAllowedFlags(usage_for_key);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& key_id,
|
||||
KeyAllowedUsageFlags* usage_flags) {
|
||||
if (NULL == usage_flags) {
|
||||
LOGE("Missing pointer to KeyAllowedUsageFlags result.");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
CdmKeyAllowedUsage usage_for_key;
|
||||
CdmResponseType result = cdm_engine_.QueryKeyAllowedUsage(key_id,
|
||||
&usage_for_key);
|
||||
if (result != NO_ERROR) {
|
||||
if (result == KEY_NOT_FOUND_1 || result == KEY_NOT_FOUND_2) {
|
||||
return kNoKey;
|
||||
} else if (result == KEY_CONFLICT_1) {
|
||||
return kTypeError;
|
||||
} else {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
}
|
||||
|
||||
*usage_flags = KeyAllowedFlags(usage_for_key);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::setAppParameter(const std::string& key,
|
||||
const std::string& value) {
|
||||
if (key.empty()) {
|
||||
@@ -602,8 +795,7 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
|
||||
}
|
||||
|
||||
InitializationData empty_initialization_data;
|
||||
CdmKeyMessage key_request;
|
||||
std::string ignored_server_url;
|
||||
CdmKeyRequest key_request;
|
||||
|
||||
// Mark all keys as released ahead of generating the release request.
|
||||
// When released, cdm_engine_ will mark all keys as expired, which we will
|
||||
@@ -615,8 +807,7 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_initialization_data,
|
||||
kLicenseTypeRelease, app_parameters_, &key_request, NULL,
|
||||
&ignored_server_url, NULL);
|
||||
kLicenseTypeRelease, app_parameters_, &key_request);
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
@@ -625,13 +816,14 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
|
||||
|
||||
LOGI("A license release has been generated.");
|
||||
MessageType message_type = kLicenseRelease;
|
||||
listener_->onMessage(session_id, message_type, key_request);
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
|
||||
const OutputBuffer& output) {
|
||||
if (input.is_encrypted && input.iv_length != 16) {
|
||||
const bool is_encrypted = (input.encryption_scheme != kClear);
|
||||
if (is_encrypted && input.iv_length != 16) {
|
||||
LOGE("The IV must be 16 bytes long.");
|
||||
return kTypeError;
|
||||
}
|
||||
@@ -646,8 +838,13 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
|
||||
std::vector<uint8_t> iv(input.iv, input.iv + input.iv_length);
|
||||
|
||||
CdmDecryptionParameters parameters;
|
||||
parameters.is_encrypted = input.is_encrypted;
|
||||
parameters.is_encrypted = is_encrypted;
|
||||
parameters.is_secure = output.is_secure;
|
||||
if (input.encryption_scheme == kAesCtr) {
|
||||
parameters.cipher_mode = kCipherModeCtr;
|
||||
} else if (input.encryption_scheme == kAesCbc) {
|
||||
parameters.cipher_mode = kCipherModeCbc;
|
||||
}
|
||||
parameters.key_id = &key_id;
|
||||
parameters.encrypt_buffer = input.data;
|
||||
parameters.encrypt_length = input.data_length;
|
||||
@@ -660,6 +857,8 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
|
||||
(input.first_subsample ? OEMCrypto_FirstSubsample : 0) |
|
||||
(input.last_subsample ? OEMCrypto_LastSubsample : 0);
|
||||
parameters.is_video = input.is_video;
|
||||
parameters.pattern_descriptor.encrypt_blocks = input.pattern.encrypted_blocks;
|
||||
parameters.pattern_descriptor.skip_blocks = input.pattern.clear_blocks;
|
||||
|
||||
CdmSessionId empty_session_id;
|
||||
CdmResponseType result = cdm_engine_.Decrypt(empty_session_id, parameters);
|
||||
@@ -675,6 +874,118 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
|
||||
return kDecryptError;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::genericEncrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) {
|
||||
|
||||
CdmEncryptionAlgorithm cdm_algorithm = ConvertEncryptionAlgorithm(algorithm);
|
||||
if (cdm_algorithm == wvcdm::kEncryptionAlgorithmUnknown) {
|
||||
LOGE("Unrecognized encryption algorithm: %d.", cdm_algorithm);
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenericEncrypt(
|
||||
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
|
||||
if (result == NO_ERROR) {
|
||||
return kSuccess;
|
||||
}
|
||||
if (result == SESSION_NOT_FOUND_13) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
if (result == KEY_NOT_FOUND_3 || result == KEY_ERROR_1) {
|
||||
LOGE("Key Error: %s", session_id.c_str());
|
||||
return kNoKey;
|
||||
}
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::genericDecrypt(
|
||||
const std::string& session_id, const std::string& in_buffer,
|
||||
const std::string& key_id, const std::string& iv,
|
||||
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) {
|
||||
|
||||
CdmEncryptionAlgorithm cdm_algorithm = ConvertEncryptionAlgorithm(algorithm);
|
||||
if (cdm_algorithm == wvcdm::kEncryptionAlgorithmUnknown) {
|
||||
LOGE("Unrecognized encryption algorithm: %d.", cdm_algorithm);
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenericDecrypt(
|
||||
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
|
||||
if (result == NO_ERROR) {
|
||||
return kSuccess;
|
||||
}
|
||||
if (result == SESSION_NOT_FOUND_14) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
if (result == KEY_NOT_FOUND_4 || result == KEY_ERROR_2) {
|
||||
LOGE("Key Error: %s", session_id.c_str());
|
||||
return kNoKey;
|
||||
}
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::genericSign(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
std::string* signature) {
|
||||
|
||||
CdmSigningAlgorithm cdm_algorithm = ConvertSigningAlgorithm(algorithm);
|
||||
if (cdm_algorithm == wvcdm::kSigningAlgorithmUnknown) {
|
||||
LOGE("Unrecognized signing algorithm: %d.", cdm_algorithm);
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenericSign(
|
||||
session_id, message, key_id, cdm_algorithm, signature);
|
||||
if (result == NO_ERROR) {
|
||||
return kSuccess;
|
||||
}
|
||||
if (result == SESSION_NOT_FOUND_15) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
if (result == KEY_NOT_FOUND_5 || result == KEY_ERROR_3) {
|
||||
LOGE("Key Error: %s", session_id.c_str());
|
||||
return kNoKey;
|
||||
}
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::genericVerify(
|
||||
const std::string& session_id, const std::string& message,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
const std::string& signature) {
|
||||
|
||||
CdmSigningAlgorithm cdm_algorithm = ConvertSigningAlgorithm(algorithm);
|
||||
if (cdm_algorithm == wvcdm::kSigningAlgorithmUnknown) {
|
||||
LOGE("Unrecognized signing algorithm: %d.", cdm_algorithm);
|
||||
return kNotSupported;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenericVerify(
|
||||
session_id, message, key_id, cdm_algorithm, signature);
|
||||
if (result == NO_ERROR) {
|
||||
return kSuccess;
|
||||
}
|
||||
if (result == SESSION_NOT_FOUND_16) {
|
||||
LOGE("No such session: %s", session_id.c_str());
|
||||
return kSessionNotFound;
|
||||
}
|
||||
if (result == KEY_NOT_FOUND_6 || result == KEY_ERROR_4) {
|
||||
LOGE("Key Error: %s", session_id.c_str());
|
||||
return kNoKey;
|
||||
}
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
void CdmImpl::onTimerExpired(void* context) {
|
||||
if (context == kPolicyTimerContext) {
|
||||
if (policy_timer_enabled_) {
|
||||
@@ -687,10 +998,9 @@ void CdmImpl::onTimerExpired(void* context) {
|
||||
}
|
||||
|
||||
void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
|
||||
CdmKeyMessage message;
|
||||
std::string server_url;
|
||||
CdmKeyRequest key_request;
|
||||
CdmResponseType result =
|
||||
cdm_engine_.GenerateRenewalRequest(session_id, &message, &server_url);
|
||||
cdm_engine_.GenerateRenewalRequest(session_id, &key_request);
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return;
|
||||
@@ -698,12 +1008,7 @@ void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
|
||||
|
||||
LOGI("A license renewal has been generated.");
|
||||
MessageType message_type = kLicenseRenewal;
|
||||
|
||||
// Post the server_url before providing the message.
|
||||
// For systems that still require the server URL,
|
||||
// the listener will add the URL to its renewal request.
|
||||
listener_->onMessageUrl(session_id, server_url);
|
||||
listener_->onMessage(session_id, message_type, message);
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
}
|
||||
|
||||
void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
|
||||
@@ -756,6 +1061,55 @@ void CdmImpl::OnExpirationUpdate(const CdmSessionId& session_id,
|
||||
}
|
||||
}
|
||||
|
||||
Cdm::KeyAllowedUsageFlags CdmImpl::KeyAllowedFlags(
|
||||
const CdmKeyAllowedUsage& usages) {
|
||||
KeyAllowedUsageFlags flags = kAllowNone;
|
||||
flags |= (usages.decrypt_to_clear_buffer) ? kAllowDecryptToClearBuffer : 0;
|
||||
flags |= (usages.decrypt_to_secure_buffer) ? kAllowDecryptToSecureBuffer : 0;
|
||||
flags |= (usages.generic_encrypt) ? kAllowGenericEncrypt : 0;
|
||||
flags |= (usages.generic_decrypt) ? kAllowGenericDecrypt : 0;
|
||||
flags |= (usages.generic_sign) ? kAllowGenericSign : 0;
|
||||
flags |= (usages.generic_verify) ? kAllowGenericSignatureVerify : 0;
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool CdmImpl::SendProvisioningRequest(const std::string& session_id) {
|
||||
// Generate a provisioning request.
|
||||
std::string empty_authority;
|
||||
std::string ignored_base_url;
|
||||
std::string signed_request;
|
||||
CdmResponseType result = cdm_engine_.GetProvisioningRequest(
|
||||
kCertificateWidevine, empty_authority, &signed_request,
|
||||
&ignored_base_url);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return false;
|
||||
}
|
||||
listener_->onDirectIndividualizationRequest(session_id, signed_request);
|
||||
provision_request_sent_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmEncryptionAlgorithm CdmImpl::ConvertEncryptionAlgorithm(
|
||||
GenericEncryptionAlgorithmType algorithm) {
|
||||
if (algorithm == Cdm::kEncryptionAlgorithmAesCbc128) {
|
||||
return wvcdm::kEncryptionAlgorithmAesCbc128;
|
||||
} else {
|
||||
LOGW("Unknown encryption algorithm: %d", algorithm);
|
||||
return wvcdm::kEncryptionAlgorithmUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
CdmSigningAlgorithm CdmImpl::ConvertSigningAlgorithm(
|
||||
GenericSigningAlgorithmType algorithm) {
|
||||
if (algorithm == Cdm::kSigningAlgorithmHmacSha256) {
|
||||
return wvcdm::kSigningAlgorithmHmacSha256;
|
||||
} else {
|
||||
LOGW("Unknown signing algorithm: %d", algorithm);
|
||||
return wvcdm::kSigningAlgorithmUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
bool VerifyL1() {
|
||||
CryptoSession cs;
|
||||
@@ -772,7 +1126,6 @@ Cdm::Status Cdm::initialize(
|
||||
IStorage* storage,
|
||||
IClock* clock,
|
||||
ITimer* timer,
|
||||
DeviceCertificateRequest* device_certificate_request,
|
||||
LogLevel verbosity) {
|
||||
// If you want to direct-render on L3, CryptoSession will pass that request
|
||||
// along to OEMCrypto. But if you want to use an opaque handle on L3,
|
||||
@@ -813,11 +1166,6 @@ Cdm::Status Cdm::initialize(
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
if (!device_certificate_request) {
|
||||
LOGE("Device certificate request pointer is required!");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
// Our enum values match those in core/include/log.h
|
||||
g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
|
||||
@@ -827,33 +1175,6 @@ Cdm::Status Cdm::initialize(
|
||||
host.storage = storage;
|
||||
host.clock = clock;
|
||||
host.timer = timer;
|
||||
|
||||
device_certificate_request->needed = false;
|
||||
|
||||
if (!host.provisioning_engine) {
|
||||
host.provisioning_engine = new CdmEngine();
|
||||
}
|
||||
bool has_cert = host.provisioning_engine->IsProvisioned(
|
||||
kSecurityLevelL1, "" /* origin */);
|
||||
|
||||
if (!has_cert) {
|
||||
device_certificate_request->needed = true;
|
||||
std::string empty_authority;
|
||||
std::string empty_origin;
|
||||
std::string base_url;
|
||||
std::string signed_request;
|
||||
CdmResponseType result = host.provisioning_engine->GetProvisioningRequest(
|
||||
kCertificateWidevine, empty_authority, empty_origin,
|
||||
&signed_request, &base_url);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
device_certificate_request->url = base_url;
|
||||
device_certificate_request->url.append("&signedRequest=");
|
||||
device_certificate_request->url.append(signed_request);
|
||||
}
|
||||
|
||||
host.initialized = true;
|
||||
return kSuccess;
|
||||
}
|
||||
@@ -865,6 +1186,7 @@ const char* Cdm::version() {
|
||||
|
||||
// static
|
||||
Cdm* Cdm::create(IEventListener* listener,
|
||||
IStorage* storage,
|
||||
bool privacy_mode) {
|
||||
if (!host.initialized) {
|
||||
LOGE("Not initialized!");
|
||||
@@ -874,28 +1196,10 @@ Cdm* Cdm::create(IEventListener* listener,
|
||||
LOGE("No listener!");
|
||||
return NULL;
|
||||
}
|
||||
return new CdmImpl(listener, privacy_mode);
|
||||
}
|
||||
if (!storage)
|
||||
storage = host.storage;
|
||||
|
||||
Cdm::Status Cdm::DeviceCertificateRequest::acceptReply(
|
||||
const std::string& reply) {
|
||||
if (!host.provisioning_engine) {
|
||||
LOGE("Provisioning reply received while not in a provisioning state!");
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
std::string empty_origin;
|
||||
std::string ignored_cert;
|
||||
std::string ignored_wrapped_key;
|
||||
|
||||
CdmResponseType result =
|
||||
host.provisioning_engine->HandleProvisioningResponse(
|
||||
empty_origin, reply, &ignored_cert, &ignored_wrapped_key);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
return new CdmImpl(listener, storage, privacy_mode);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
@@ -911,35 +1215,22 @@ int64_t Clock::GetCurrentTime() {
|
||||
|
||||
class File::Impl {
|
||||
public:
|
||||
Cdm::IStorage* storage;
|
||||
std::string name;
|
||||
bool read_only;
|
||||
bool truncate;
|
||||
};
|
||||
|
||||
File::File() : impl_(NULL) {}
|
||||
File::File(Impl* impl) : impl_(impl) {}
|
||||
|
||||
File::~File() {
|
||||
Close();
|
||||
}
|
||||
|
||||
bool File::Open(const std::string& file_path, int flags) {
|
||||
if (!(flags & kCreate) && !host.storage->exists(file_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
impl_ = new Impl;
|
||||
impl_->name = file_path;
|
||||
impl_->read_only = (flags & kReadOnly);
|
||||
impl_->truncate = (flags & kTruncate);
|
||||
return true;
|
||||
}
|
||||
File::~File() {}
|
||||
|
||||
ssize_t File::Read(char* buffer, size_t bytes) {
|
||||
if (!impl_) {
|
||||
return -1;
|
||||
}
|
||||
std::string data;
|
||||
if (!host.storage->read(impl_->name, &data)) {
|
||||
if (!impl_->storage->read(impl_->name, &data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -957,7 +1248,7 @@ ssize_t File::Write(const char* buffer, size_t bytes) {
|
||||
return -1;
|
||||
}
|
||||
std::string data(buffer, bytes);
|
||||
if (!host.storage->write(impl_->name, data)) {
|
||||
if (!impl_->storage->write(impl_->name, data)) {
|
||||
return -1;
|
||||
}
|
||||
return bytes;
|
||||
@@ -968,45 +1259,61 @@ void File::Close() {
|
||||
delete impl_;
|
||||
}
|
||||
impl_ = NULL;
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool File::Exists(const std::string& file_path) {
|
||||
// An empty path is the "base directory" for CE CDM's file storage.
|
||||
// Therefore, it should always be seen as existing.
|
||||
// If it ever does not exist, CdmEngine detects this as a "factory reset"
|
||||
// and wipes out all usage table data.
|
||||
return file_path.empty() || host.storage->exists(file_path);
|
||||
class FileSystem::Impl {
|
||||
public:
|
||||
widevine::Cdm::IStorage* storage;
|
||||
};
|
||||
|
||||
FileSystem::FileSystem() : FileSystem("", NULL) {}
|
||||
|
||||
FileSystem::FileSystem(const std::string& origin, void* extra_data)
|
||||
: impl_(new Impl), origin_(origin) {
|
||||
if (extra_data)
|
||||
impl_->storage = (widevine::Cdm::IStorage*)extra_data;
|
||||
else
|
||||
impl_->storage = host.storage;
|
||||
}
|
||||
|
||||
bool File::Remove(const std::string& file_path) {
|
||||
return host.storage->remove(file_path);
|
||||
FileSystem::~FileSystem() {
|
||||
delete impl_;
|
||||
impl_ = NULL;
|
||||
}
|
||||
|
||||
bool File::Copy(const std::string& old_path, const std::string& new_path) {
|
||||
std::string data;
|
||||
bool read_ok = host.storage->read(old_path, &data);
|
||||
if (!read_ok) return false;
|
||||
return host.storage->write(new_path, data);
|
||||
File* FileSystem::Open(const std::string& file_path, int flags) {
|
||||
if (!impl_ || (!(flags & kCreate) && !impl_->storage->exists(file_path))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
File::Impl* file_impl = new File::Impl;
|
||||
file_impl->storage = impl_->storage;
|
||||
file_impl->name = file_path;
|
||||
file_impl->read_only = (flags & kReadOnly);
|
||||
file_impl->truncate = (flags & kTruncate);
|
||||
return new File(file_impl);
|
||||
}
|
||||
|
||||
bool File::List(const std::string& path, std::vector<std::string>* files) {
|
||||
return false;
|
||||
bool FileSystem::Exists(const std::string& file_path) {
|
||||
if (!impl_)
|
||||
return false;
|
||||
|
||||
return file_path.empty() || impl_->storage->exists(file_path);
|
||||
}
|
||||
|
||||
bool File::CreateDirectory(const std::string dir_path) {
|
||||
return true;
|
||||
bool FileSystem::Remove(const std::string& file_path) {
|
||||
if (!impl_)
|
||||
return false;
|
||||
|
||||
return impl_->storage->remove(file_path);
|
||||
}
|
||||
|
||||
bool File::IsDirectory(const std::string& dir_path) {
|
||||
return false;
|
||||
}
|
||||
ssize_t FileSystem::FileSize(const std::string& file_path) {
|
||||
if (!impl_)
|
||||
return -1;
|
||||
|
||||
bool File::IsRegularFile(const std::string& file_path) {
|
||||
return host.storage->exists(file_path);
|
||||
}
|
||||
|
||||
ssize_t File::FileSize(const std::string& file_path) {
|
||||
return host.storage->size(file_path);
|
||||
return impl_->storage->size(file_path);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
#include "test_host.h"
|
||||
|
||||
#if defined(OEMCRYPTO_TESTS)
|
||||
# include "oemcrypto_test.h"
|
||||
# include "oec_device_features.h"
|
||||
#endif
|
||||
|
||||
using namespace widevine;
|
||||
@@ -68,13 +68,11 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
client_info.build_info = __DATE__;
|
||||
|
||||
Cdm::DeviceCertificateRequest cert_request;
|
||||
Cdm::Status status = Cdm::initialize(
|
||||
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host, &cert_request,
|
||||
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host,
|
||||
static_cast<Cdm::LogLevel>(verbosity));
|
||||
(void)status; // status is now used when assertions are turned off.
|
||||
assert(status == Cdm::kSuccess);
|
||||
assert(cert_request.needed == false);
|
||||
|
||||
#if defined(OEMCRYPTO_TESTS)
|
||||
// Set up the OEMCrypto test harness.
|
||||
|
||||
@@ -71,6 +71,7 @@ bool TestHost::exists(const std::string& name) {
|
||||
}
|
||||
|
||||
bool TestHost::remove(const std::string& name) {
|
||||
LOGD("remove: %s", name.c_str());
|
||||
files_.erase(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user