V18.4.0 CAS plugin
Note that this version does not have Widevine Provisioning 4.0 support. It is only suitable for device upgrades. A new patch with provisioning 4.0 support will be made later.
This commit is contained in:
128
plugin/include/cas_events.h
Normal file
128
plugin/include/cas_events.h
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef CAS_EVENTS_H
|
||||
#define CAS_EVENTS_H
|
||||
|
||||
#define PROVISIONING_EVENT_START 1000
|
||||
#define LICENSING_EVENT_START 2000
|
||||
#define CAS_SESSION_EVENT_START 3000
|
||||
#define CAS_QUERY_EVENT_START 4000
|
||||
#define CAS_ERROR_EVENT_START 5000
|
||||
#define CAS_PARENTAL_CONTROL_EVENT_START 6000
|
||||
#define CAS_FINGERPRINTING_EVENT_START 6100
|
||||
#define CAS_SERVICE_BLOCKING_EVENT_START 6200
|
||||
#define CAS_TEST_EVENT_START 10000
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN = 0,
|
||||
|
||||
INDIVIDUALIZATION_REQUEST = PROVISIONING_EVENT_START,
|
||||
INDIVIDUALIZATION_RESPONSE,
|
||||
INDIVIDUALIZATION_COMPLETE,
|
||||
|
||||
LICENSE_REQUEST = LICENSING_EVENT_START,
|
||||
LICENSE_RESPONSE,
|
||||
CAS_ERROR_DEPRECATED,
|
||||
LICENSE_RENEWAL_REQUEST,
|
||||
LICENSE_RENEWAL_RESPONSE,
|
||||
LICENSE_RENEWAL_URL,
|
||||
LICENSE_CAS_READY,
|
||||
LICENSE_CAS_RENEWAL_READY,
|
||||
LICENSE_REMOVAL,
|
||||
LICENSE_REMOVED,
|
||||
ASSIGN_LICENSE_ID,
|
||||
LICENSE_ID_ASSIGNED,
|
||||
LICENSE_NEW_EXPIRY_TIME,
|
||||
MULTI_CONTENT_LICENSE_INFO,
|
||||
GROUP_LICENSE_INFO,
|
||||
LICENSE_ENTITLEMENT_PERIOD_UPDATE_REQUEST,
|
||||
LICENSE_ENTITLEMENT_PERIOD_UPDATE_RESPONSE,
|
||||
LICENSE_ENTITLEMENT_PERIOD_UPDATED,
|
||||
|
||||
// TODO(jfore): Evaluate removing this event in favor of return status codes
|
||||
// from
|
||||
// frameworks/av/media/libstagefright/include/media/stagefright/MediaErrors.h
|
||||
CAS_ERROR = CAS_ERROR_EVENT_START,
|
||||
|
||||
CAS_SESSION_ID = CAS_SESSION_EVENT_START,
|
||||
SET_CAS_SOC_ID,
|
||||
SET_CAS_SOC_DATA,
|
||||
|
||||
UNIQUE_ID = CAS_QUERY_EVENT_START,
|
||||
QUERY_UNIQUE_ID,
|
||||
WV_CAS_PLUGIN_VERSION,
|
||||
QUERY_WV_CAS_PLUGIN_VERSION,
|
||||
|
||||
SET_PARENTAL_CONTROL_AGE = CAS_PARENTAL_CONTROL_EVENT_START,
|
||||
DEPRECATED_PARENTAL_CONTROL_AGE_UPDATED,
|
||||
ACCESS_DENIED_BY_PARENTAL_CONTROL,
|
||||
AGE_RESTRICTION_UPDATED,
|
||||
|
||||
// The content of FINGERPRINTING_INFO events follows TLV (Type (1 byte) -
|
||||
// Length (2 bytes) - Value) format. See FingerprintingFieldType for possible
|
||||
// types. A FINGERPRINTING_INFO event contains {one or more CHANNEL, one
|
||||
// CONTROL}.
|
||||
FINGERPRINTING_INFO = CAS_FINGERPRINTING_EVENT_START,
|
||||
// Fingerprinting control info for a session. The content of the event follows
|
||||
// TLV (Type (1 byte) - Length (2 bytes) - Value) format. See
|
||||
// SessionFingerprintingFieldType for possible types. It will contain {one
|
||||
// FINGERPRINTING_CONTROL}.
|
||||
SESSION_FINGERPRINTING_INFO,
|
||||
|
||||
// The content of SERVICE_BLOCKING_INFO events follows TLV (Type (1 byte) -
|
||||
// Length (2 bytes) - Value) format. See ServiceBlockingFieldType for possible
|
||||
// types. A SERVICE_BLOCKING_INFO event contains {one or more CHANNEL, one or
|
||||
// more DEVICE_GROUP, zero or one START_TIME_SECONDS, one END_TIME_SECONDS}.
|
||||
SERVICE_BLOCKING_INFO = CAS_SERVICE_BLOCKING_EVENT_START,
|
||||
// Service blocking device group for a session. The content of the event
|
||||
// follows TLV (Type (1 byte) - Length (2 bytes) - Value) format. See
|
||||
// SessionServiceBlockingFieldType for possible types. It will contain {one or
|
||||
// more SERVICE_BLOCKING_DEVICE_GROUP}.
|
||||
SESSION_SERVICE_BLOCKING_INFO,
|
||||
|
||||
TEST_FOR_ECHO =
|
||||
CAS_TEST_EVENT_START, // Request an ECHO response to test events passing.
|
||||
ECHO, // Respond to TEST_FOR_ECHO.
|
||||
} CasEventId;
|
||||
|
||||
// Types used inside an FINGERPRINTING_INFO event.
|
||||
typedef enum {
|
||||
FINGERPRINTING_CHANNEL = 0,
|
||||
FINGERPRINTING_CONTROL,
|
||||
} FingerprintingFieldType;
|
||||
|
||||
// Types used inside an SERVICE_BLOCKING_INFO event.
|
||||
typedef enum {
|
||||
SERVICE_BLOCKING_CHANNEL = 0,
|
||||
SERVICE_BLOCKING_DEVICE_GROUP,
|
||||
// Epoch time in seconds. Missing of this field or a value of 0 means
|
||||
// immediate start.
|
||||
SERVICE_BLOCKING_START_TIME_SECONDS,
|
||||
SERVICE_BLOCKING_END_TIME_SECONDS, // Epoch time in seconds.
|
||||
} ServiceBlockingFieldType;
|
||||
|
||||
// Types used inside an SESSION_FINGERPRINTING_CONTROL event.
|
||||
typedef enum {
|
||||
SESSION_FINGERPRINTING_CONTROL = 0,
|
||||
} SessionFingerprintingFieldType;
|
||||
|
||||
// Types used inside an SESSION_SERVICE_BLOCKING_GROUPS event.
|
||||
typedef enum {
|
||||
SESSION_SERVICE_BLOCKING_DEVICE_GROUP = 0,
|
||||
} SessionServiceBlockingFieldType;
|
||||
|
||||
// Types used inside a MULTI_CONTENT_LICENSE_INFO event.
|
||||
typedef enum {
|
||||
MULTI_CONTENT_LICENSE_ID = 0,
|
||||
MULTI_CONTENT_LICENSE_CONTENT_ID,
|
||||
} MultiContentLicenseFieldType;
|
||||
|
||||
// Types used inside a GROUP_LICENSE_INFO event.
|
||||
typedef enum {
|
||||
GROUP_LICENSE_ID = 0,
|
||||
GROUP_LICENSE_GROUP_ID,
|
||||
} GroupLicenseFieldType;
|
||||
|
||||
#endif // CAS_EVENTS_H
|
||||
184
plugin/include/cas_license.h
Normal file
184
plugin/include/cas_license.h
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef CAS_LICENSE_H
|
||||
#define CAS_LICENSE_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "cas_status.h"
|
||||
#include "crypto_session.h"
|
||||
#include "policy_engine.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// CasLicense implements the core functionality needed to interact with service
|
||||
// to obtain and manage entitlements.
|
||||
class CasLicense : public wvutil::TimerHandler, public wvcas::CasEventListener {
|
||||
public:
|
||||
CasLicense() {}
|
||||
virtual ~CasLicense() {}
|
||||
// Initialize CasLicense with a |crypto_session|. |listener| may be null.
|
||||
virtual CasStatus initialize(std::shared_ptr<CryptoSession> crypto_session,
|
||||
CasEventListener* listener);
|
||||
|
||||
// Generate a request to obtain a device certificate for requesting
|
||||
// entitlements. The generated message is set in |provisioning_request|.
|
||||
virtual CasStatus GenerateDeviceProvisioningRequest(
|
||||
std::string* provisioning_request) const;
|
||||
|
||||
// Process a server response containing a device certificate for use in
|
||||
// requesting entitlements. The contained certificate data will be extracted
|
||||
// and wrapped for storage. The public key cert will be returned in
|
||||
// |device_certificate|. The private key information will be wrapped by the
|
||||
// crypto session and returned in |wrapped_rsa_key|.
|
||||
// A secure binary file image containing the device cert is returned in
|
||||
// |device_file| if not nullptr. This file is suitable for storage on a device
|
||||
virtual CasStatus HandleDeviceProvisioningResponse(
|
||||
const std::string& signed_provisioning_response,
|
||||
std::string* device_certificate, std::string* wrapped_rsa_key,
|
||||
std::string* device_file) const;
|
||||
|
||||
// Generate a request to obtain an EMM (Entitlement Management Message) to
|
||||
// use to enable processing of ECM(s) (Encryption Management Message).
|
||||
// |init_data| is widevine metadata about the stream needed in the request.
|
||||
// |wrapped_rsa_key| and |signed_license_request| are the device certificate
|
||||
// obtained by HandleDeviceProvisioningResponse.
|
||||
virtual CasStatus GenerateEntitlementRequest(
|
||||
const std::string& init_data, const std::string& device_certificate,
|
||||
const std::string& wrapped_rsa_key, LicenseType license_type,
|
||||
std::string* signed_license_request);
|
||||
|
||||
// Restores a stored license making the keys available for use.
|
||||
virtual CasStatus HandleStoredLicense(const std::string& wrapped_rsa_key,
|
||||
const std::string& license_file);
|
||||
|
||||
// Process a server response containing a EMM for use in the processing of
|
||||
// ECM(s).
|
||||
// If |device_file| is not nullptr and the license policy allows a license to
|
||||
// be stored |device_file| is populated with the bytes of the license secured
|
||||
// for storage.
|
||||
virtual CasStatus HandleEntitlementResponse(
|
||||
const std::string& entitlement_response, std::string* device_file);
|
||||
|
||||
// Process a previously stored device |certificate| and make it available
|
||||
// for use in an EMM request.
|
||||
virtual CasStatus HandleStoredDrmCert(const std::string& certificate,
|
||||
std::string* device_certificate,
|
||||
std::string* wrapped_rsa_key);
|
||||
|
||||
// Generate an entitlement renewal request message in
|
||||
// |signed_renewal_request|.
|
||||
virtual CasStatus GenerateEntitlementRenewalRequest(
|
||||
const std::string& device_certificate,
|
||||
std::string* signed_renewal_request);
|
||||
|
||||
// Process a server response containing a EMM renewal. If |device_file| is not
|
||||
// nullptr and the license policy allows a license renewal to be stored
|
||||
// |device_file| is populated with the bytes of the license secured for
|
||||
// storage.
|
||||
virtual CasStatus HandleEntitlementRenewalResponse(
|
||||
const std::string& renewal_response, std::string* device_file);
|
||||
|
||||
// Query the license to see if a key is usable.
|
||||
virtual bool CanDecryptContent(const KeyId& key_id) const;
|
||||
|
||||
// Update the license after handling license remove. Plugin is disabled to
|
||||
// playback stream, store and renew license.
|
||||
virtual void UpdateLicenseForLicenseRemove();
|
||||
|
||||
// Query the license to see if storage is allowed.
|
||||
virtual bool CanStoreLicense() const;
|
||||
|
||||
// Returns the group id specified in the license. Group id is expected to be
|
||||
// non-empty if the license is MULTI_CONTENT_LICENSE or GROUP_LICENSE; and
|
||||
// empty if the license is SINGLE_CONTENT_LICENSE_DEFAULT.
|
||||
virtual std::string GetGroupId() const;
|
||||
|
||||
// If the license is MULTI_CONTENT_LICENSE, the returned vector contains all
|
||||
// content ids that the license is for. Returns empty if the license if not
|
||||
// MULTI_CONTENT_LICENSE.
|
||||
virtual std::vector<std::string> GetContentIdList() const;
|
||||
|
||||
// Returns true if the license is MULTI_CONTENT_LICENSE, and false otherwise.
|
||||
virtual bool IsMultiContentLicense() const;
|
||||
|
||||
// Returns true if the license is GROUP_LICENSE, and false otherwise.
|
||||
virtual bool IsGroupLicense() const;
|
||||
|
||||
// Policy timer implentation.
|
||||
void OnTimerEvent() override;
|
||||
|
||||
// Event listener implementation.
|
||||
void OnSessionRenewalNeeded() override;
|
||||
|
||||
void OnSessionKeysChange(const KeyStatusMap& keys_status,
|
||||
bool has_new_usable_key) override;
|
||||
|
||||
void OnExpirationUpdate(int64_t new_expiry_time_seconds) override;
|
||||
|
||||
void OnLicenseExpiration() override;
|
||||
|
||||
void OnNewRenewalServerUrl(const std::string& renewal_server_url) override;
|
||||
|
||||
void OnAgeRestrictionUpdated(const WvCasSessionId& sessionId,
|
||||
uint8_t ecm_age_restriction) override;
|
||||
void OnFingerprintingUpdated(const CasData& fingerprinting) override;
|
||||
void OnServiceBlockingUpdated(const CasData& service_blocking) override;
|
||||
|
||||
void OnSessionFingerprintingUpdated(const WvCasSessionId& sessionId,
|
||||
const CasData& fingerprinting) override;
|
||||
|
||||
void OnSessionServiceBlockingUpdated(
|
||||
const WvCasSessionId& sessionId,
|
||||
const CasData& service_blocking) override;
|
||||
|
||||
void OnEntitlementPeriodUpdateNeeded(
|
||||
const std::string& signed_license_request) override;
|
||||
|
||||
// Query to see if the license is expired.
|
||||
virtual bool IsExpired() const;
|
||||
|
||||
// Notify the license that playback decryption has begun.
|
||||
virtual void BeginDecryption();
|
||||
|
||||
// Returns NoError if a valid entitlement period index exists in
|
||||
// |license_file|. The index will be assigned to |entitlement_period_index|.
|
||||
static CasStatus GetEntitlementPeriodIndexFromStoredLicense(
|
||||
const std::string& license_file, uint32_t& entitlement_period_index);
|
||||
|
||||
CasLicense(const CasLicense&) = delete;
|
||||
CasLicense& operator=(const CasLicense&) = delete;
|
||||
|
||||
private:
|
||||
CasStatus GenerateDeviceProvisioningRequestWithKeybox(
|
||||
std::string* provisioning_request) const;
|
||||
CasStatus GenerateDeviceProvisioningRequestWithOEMCert() const;
|
||||
CasStatus InstallLicense(const std::string& session_key,
|
||||
const std::string& serialized_license,
|
||||
const std::string& core_message,
|
||||
const std::string& signature);
|
||||
CasStatus InstallLicenseRenewal(const std::string& serialized_license,
|
||||
const std::string& core_message,
|
||||
const std::string& signature);
|
||||
|
||||
virtual std::unique_ptr<PolicyEngine> GetPolicyEngine();
|
||||
|
||||
std::unique_ptr<PolicyEngine> policy_engine_;
|
||||
std::shared_ptr<CryptoSession> crypto_session_;
|
||||
CasEventListener* event_listener_ = nullptr;
|
||||
video_widevine::License license_;
|
||||
std::string emm_request_;
|
||||
std::string emm_response_;
|
||||
std::string renewal_request_;
|
||||
std::string renewal_response_;
|
||||
std::string init_data_;
|
||||
bool is_renewal_in_license_file_ = false;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // CAS_LICENSE_H
|
||||
25
plugin/include/cas_media_id.h
Normal file
25
plugin/include/cas_media_id.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef CAS_MEDIA_ID_H
|
||||
#define CAS_MEDIA_ID_H
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
class CasMediaId {
|
||||
protected:
|
||||
CasMediaId() {}
|
||||
|
||||
public:
|
||||
CasMediaId(const CasMediaId&) = delete;
|
||||
CasMediaId& operator=(const CasMediaId&) = delete;
|
||||
virtual ~CasMediaId() {}
|
||||
static std::unique_ptr<CasMediaId> create();
|
||||
virtual CasStatus initialize(const std::string& init_data) = 0;
|
||||
virtual const std::string content_id() = 0;
|
||||
virtual const std::string provider_id() = 0;
|
||||
virtual bool is_entitlement_rotation_enabled() { return false; }
|
||||
virtual uint32_t entitlement_period_index() = 0;
|
||||
virtual std::string get_init_data() = 0;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // CAS_MEDIA_ID_H
|
||||
53
plugin/include/cas_status.h
Normal file
53
plugin/include/cas_status.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef CAS_STATUS_H
|
||||
#define CAS_STATUS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// TODO(jfore): Add more detailed error status codes.
|
||||
enum class CasStatusCode : int32_t {
|
||||
kUnknownError = 0,
|
||||
kNoError = 1,
|
||||
kCryptoInterfaceError = 2,
|
||||
kCryptoSessionError = 3,
|
||||
kCasLicenseError = 4,
|
||||
kIndividualizationError = 5,
|
||||
kInvalidParameter = 6,
|
||||
kDecryptionError = 7,
|
||||
kKeyNotFound = 8,
|
||||
kSessionNotFound = 9,
|
||||
kUnknownLicenseType = 10,
|
||||
kLicenseFileParseError = 11,
|
||||
kInvalidLicenseFile = 12,
|
||||
kInvalidPesData = 13,
|
||||
kDeferedEcmProcessing = 14,
|
||||
kAccessDeniedByParentalControl = 15,
|
||||
kUnknownEvent = 16,
|
||||
kOEMCryptoVersionMismatch = 17,
|
||||
};
|
||||
|
||||
class CasStatus {
|
||||
public:
|
||||
CasStatus(CasStatusCode status = CasStatusCode::kNoError,
|
||||
const std::string& err_string = std::string())
|
||||
: status_(status), err_string_(err_string) {}
|
||||
static CasStatus OkStatus() { return CasStatus(); }
|
||||
virtual ~CasStatus() {}
|
||||
|
||||
virtual CasStatusCode status_code() const { return status_; }
|
||||
virtual const std::string& error_string() const { return err_string_; }
|
||||
virtual bool ok() const { return status_ == CasStatusCode::kNoError; }
|
||||
|
||||
private:
|
||||
CasStatusCode status_;
|
||||
std::string err_string_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // CAS_STATUS_H
|
||||
116
plugin/include/cas_types.h
Normal file
116
plugin/include/cas_types.h
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef CAS_TYPES_H
|
||||
#define CAS_TYPES_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
static const int kCryptoSessionErrorStart = 1000;
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
typedef std::vector<uint8_t> WvCasSessionId;
|
||||
|
||||
enum class LicenseType {
|
||||
kStreaming = 0,
|
||||
kOffline = 1,
|
||||
// License type decision is left to the provider.
|
||||
kAutomatic = 2,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ProvisioningError = 0, // Device cannot be provisioned.
|
||||
DrmCertificate = 1, // Device has baked in DRM certificate
|
||||
// (level 3 only)
|
||||
Keybox = 2, // Device has factory installed unique keybox.
|
||||
OEMCertificate = 3 // Device has factory installed OEM certificate.
|
||||
} CasProvisioningMethod;
|
||||
|
||||
enum class CryptoMode {
|
||||
kInvalid = -1,
|
||||
kAesCBC = 0,
|
||||
kAesCTR = 1,
|
||||
kDvbCsa2 = 2,
|
||||
kDvbCsa3 = 3,
|
||||
kAesOFB = 4,
|
||||
kAesSCTE = 5,
|
||||
kAesECB = 6,
|
||||
};
|
||||
|
||||
enum KeyStatus {
|
||||
kKeyStatusKeyUnknown,
|
||||
kKeyStatusUsable,
|
||||
kKeyStatusExpired,
|
||||
kKeyStatusOutputNotAllowed,
|
||||
kKeyStatusPending,
|
||||
kKeyStatusInternalError,
|
||||
};
|
||||
|
||||
enum SecurityLevel {
|
||||
kSecurityLevelUninitialized,
|
||||
kSecurityLevelL1,
|
||||
kSecurityLevelL2,
|
||||
kSecurityLevelL3,
|
||||
kSecurityLevelUnknown
|
||||
};
|
||||
|
||||
typedef std::vector<uint8_t> CasEcm;
|
||||
typedef std::vector<uint8_t> CasEmm;
|
||||
typedef std::vector<uint8_t> CasData;
|
||||
typedef std::string KeyId;
|
||||
typedef std::map<KeyId, KeyStatus> KeyStatusMap;
|
||||
|
||||
// Listener for events from the policy engine.
|
||||
class CasEventListener {
|
||||
public:
|
||||
CasEventListener() {}
|
||||
virtual ~CasEventListener() {}
|
||||
|
||||
// Notify listeners that a license renewal is needed.
|
||||
virtual void OnSessionRenewalNeeded() = 0;
|
||||
|
||||
// Notify listeners that the keys have changed state.
|
||||
virtual void OnSessionKeysChange(const KeyStatusMap& keys_status,
|
||||
bool has_new_usable_key) = 0;
|
||||
|
||||
// |new_expiry_time_seconds| of 0 means "never expires".
|
||||
virtual void OnExpirationUpdate(int64_t new_expiry_time_seconds) = 0;
|
||||
|
||||
// Notify listeners of the current renewal url.
|
||||
virtual void OnNewRenewalServerUrl(const std::string& renewal_server_url) = 0;
|
||||
|
||||
// Notify listeners of current license is expired.
|
||||
virtual void OnLicenseExpiration() = 0;
|
||||
|
||||
// Notify listeners of new age restriction value in processed ECM.
|
||||
virtual void OnAgeRestrictionUpdated(const WvCasSessionId& sessionId,
|
||||
uint8_t ecm_age_restriction) = 0;
|
||||
|
||||
// Notifies listeners of new session fingerprinting info.
|
||||
virtual void OnSessionFingerprintingUpdated(
|
||||
const WvCasSessionId& sessionId, const CasData& fingerprinting) = 0;
|
||||
|
||||
// Notifies listeners of new session service blocking info.
|
||||
virtual void OnSessionServiceBlockingUpdated(
|
||||
const WvCasSessionId& sessionId, const CasData& service_blocking) = 0;
|
||||
|
||||
// Notifies listeners of new fingerprinting info.
|
||||
virtual void OnFingerprintingUpdated(const CasData& fingerprinting) = 0;
|
||||
|
||||
// Notifies listeners of new service blocking info.
|
||||
virtual void OnServiceBlockingUpdated(const CasData& service_blocking) = 0;
|
||||
|
||||
virtual void OnEntitlementPeriodUpdateNeeded(
|
||||
const std::string& signed_license_request) = 0;
|
||||
|
||||
CasEventListener(const CasEventListener&) = delete;
|
||||
CasEventListener& operator=(const CasEventListener&) = delete;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // CAS_TYPES_H
|
||||
21
plugin/include/cas_util.h
Normal file
21
plugin/include/cas_util.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef CAS_UTIL_H_
|
||||
#define CAS_UTIL_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#if __cplusplus >= 201402L || \
|
||||
(defined __cpp_lib_make_unique && __cpp_lib_make_unique >= 201304L) || \
|
||||
(defined(_MSC_VER) && _MSC_VER >= 1900)
|
||||
using std::make_unique;
|
||||
#else
|
||||
template <typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args) {
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CAS_UTIL_H_
|
||||
310
plugin/include/crypto_session.h
Normal file
310
plugin/include/crypto_session.h
Normal file
@@ -0,0 +1,310 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef CRYPTO_SESSION_H
|
||||
#define CRYPTO_SESSION_H
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
#include "cas_status.h"
|
||||
#include "cas_types.h"
|
||||
#include "oemcrypto_interface.h"
|
||||
#include "rw_lock.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
struct KeySlot {
|
||||
std::vector<uint8_t> key_id;
|
||||
std::vector<uint8_t> entitlement_key_id;
|
||||
std::vector<uint8_t> wrapped_key;
|
||||
std::vector<uint8_t> wrapped_key_iv;
|
||||
std::vector<uint8_t> content_iv;
|
||||
CryptoMode cipher_mode;
|
||||
};
|
||||
|
||||
struct SubSample {
|
||||
uint32_t num_bytes_of_clear;
|
||||
uint32_t num_bytes_of_encrypted;
|
||||
};
|
||||
|
||||
typedef OEMCrypto_HDCP_Capability HdcpCapability;
|
||||
|
||||
class CryptoLock {
|
||||
public:
|
||||
CryptoLock(){};
|
||||
// These methods should be used to take the various CryptoSession mutexes in
|
||||
// preference to taking the mutexes directly.
|
||||
//
|
||||
// A lock should be taken on the Static Field Mutex before accessing any of
|
||||
// CryptoSession's non-atomic static fields. It can be taken as a reader or as
|
||||
// a writer, depending on how you will be accessing the static fields.
|
||||
//
|
||||
// Before calling into OEMCrypto, code must take locks on the OEMCrypto Mutex
|
||||
// and/or the OEMCrypto Session Mutex. Which of them should be taken and how
|
||||
// depends on the OEMCrypto function being called; consult the OEMCrypto
|
||||
// specification's threading guarantees before making any calls. The OEMCrypto
|
||||
// specification defines several classes of functions for the purposes of
|
||||
// parallelism. The methods below lock the OEMCrypto Mutex and OEMCrypto
|
||||
// Session Mutex in the correct order and manner to fulfill the guarantees in
|
||||
// the specification.
|
||||
//
|
||||
// For this function class... | ...use this locking method
|
||||
// ------------------------------+---------------------------
|
||||
// Initialization & Termination | WithOecWriteLock()
|
||||
// Property | WithOecReadLock()
|
||||
// Session Initialization | WithOecWriteLock()
|
||||
// Usage Table | WithOecWriteLock()
|
||||
// Session | WithOecSessionLock()
|
||||
//
|
||||
// Note that accessing |key_session_| often accesses the OEMCrypto session, so
|
||||
// WithOecSessionLock() should be used before accessing |key_session_| as
|
||||
// well.
|
||||
//
|
||||
// If a function needs to take a lock on both the Static Field Mutex and some
|
||||
// of the OEMCrypto mutexes simultaneously, it must *always* lock the Static
|
||||
// Field Mutex before the OEMCrypto mutexes.
|
||||
//
|
||||
// In general, all locks should only be held for the minimum time necessary
|
||||
// (e.g. a lock on the OEMCrypto mutexes should only be held for the duration
|
||||
// of a single call into OEMCrypto) unless there is a compelling argument
|
||||
// otherwise, such as making two calls into OEMCrypto immediately after each
|
||||
// other.
|
||||
template <class Func>
|
||||
static auto WithStaticFieldWriteLock(const char* tag, Func body)
|
||||
-> decltype(body());
|
||||
template <class Func>
|
||||
static auto WithStaticFieldReadLock(const char* tag, Func body)
|
||||
-> decltype(body());
|
||||
template <class Func>
|
||||
static auto WithOecWriteLock(const char* tag, Func body) -> decltype(body());
|
||||
template <class Func>
|
||||
static auto WithOecReadLock(const char* tag, Func body) -> decltype(body());
|
||||
template <class Func>
|
||||
auto WithOecSessionLock(const char* tag, Func body) -> decltype(body());
|
||||
|
||||
private:
|
||||
// The locking methods above should be used in preference to taking these
|
||||
// mutexes directly. If code takes these manually and needs to take more
|
||||
// than one, it must *always* take them in the order they are defined here.
|
||||
static wvutil::shared_mutex static_field_mutex_;
|
||||
static wvutil::shared_mutex oem_crypto_mutex_;
|
||||
std::mutex oem_crypto_session_mutex_;
|
||||
};
|
||||
|
||||
class CryptoInterface {
|
||||
CryptoInterface();
|
||||
|
||||
public:
|
||||
virtual ~CryptoInterface();
|
||||
|
||||
virtual OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session);
|
||||
virtual OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session);
|
||||
virtual OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod();
|
||||
virtual OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
|
||||
size_t* keyDataLength);
|
||||
virtual uint32_t OEMCrypto_SupportedCertificates();
|
||||
virtual OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
||||
uint32_t* nonce);
|
||||
virtual OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
|
||||
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
|
||||
uint32_t enc_key_context_length);
|
||||
virtual OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
|
||||
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
||||
size_t* core_message_size, uint8_t* signature, size_t* signature_length);
|
||||
virtual OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest(
|
||||
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
||||
size_t* core_message_size, uint8_t* signature, size_t* signature_length);
|
||||
virtual OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest(
|
||||
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
||||
size_t* core_message_size, uint8_t* signature, size_t* signature_length);
|
||||
virtual OEMCryptoResult OEMCrypto_LoadProvisioning(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
size_t core_message_length, const uint8_t* signature,
|
||||
size_t signature_length, uint8_t* wrapped_private_key,
|
||||
size_t* wrapped_private_key_length);
|
||||
virtual OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
|
||||
OEMCrypto_SESSION session, uint8_t* public_cert,
|
||||
size_t* public_cert_length);
|
||||
virtual OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(
|
||||
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
|
||||
const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length);
|
||||
virtual OEMCryptoResult OEMCrypto_GenerateRSASignature(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length,
|
||||
RSA_Padding_Scheme padding_scheme);
|
||||
virtual OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
OEMCrypto_SESSION session, const uint8_t* enc_session_key,
|
||||
size_t enc_session_key_length, const uint8_t* mac_key_context,
|
||||
size_t mac_key_context_length, const uint8_t* enc_key_context,
|
||||
size_t enc_key_context_length);
|
||||
virtual OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
size_t core_message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
virtual OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
size_t core_message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length);
|
||||
virtual OEMCryptoResult OEMCrypto_LoadCasECMKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const OEMCrypto_EntitledContentKeyObject* even_key,
|
||||
const OEMCrypto_EntitledContentKeyObject* odd_key);
|
||||
virtual OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* max);
|
||||
virtual OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
|
||||
size_t* idLength);
|
||||
virtual const char* OEMCrypto_SecurityLevel();
|
||||
virtual OEMCryptoResult OEMCrypto_CreateEntitledKeySession(
|
||||
OEMCrypto_SESSION session, OEMCrypto_SESSION* entitled_key_session_id);
|
||||
virtual OEMCryptoResult OEMCrypto_RemoveEntitledKeySession(
|
||||
OEMCrypto_SESSION entitled_key_session_id);
|
||||
virtual OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession(
|
||||
OEMCrypto_SESSION key_sid, OEMCrypto_SESSION oec_sid);
|
||||
virtual uint32_t OEMCrypto_APIVersion();
|
||||
virtual OEMCryptoResult OEMCrypto_GetOEMKeyToken(
|
||||
OEMCrypto_SESSION key_session, uint8_t* key_token,
|
||||
size_t* key_token_length);
|
||||
virtual OEMCryptoResult OEMCrypto_GetSignatureHashAlgorithm(
|
||||
OEMCrypto_SESSION session, OEMCrypto_SignatureHashAlgorithm* algorithm);
|
||||
|
||||
// This is the factory method used to enable the oemcrypto interface.
|
||||
static OEMCryptoResult create(std::unique_ptr<CryptoInterface>* init) {
|
||||
// This is *the* oemcrypto interface used in the normal running. There is
|
||||
// only one and there is not need to destroy it.
|
||||
static OEMCryptoInterface oemcrypto_interface;
|
||||
return create_internal(&oemcrypto_interface, init);
|
||||
}
|
||||
|
||||
// This initializer factory method is templated to allow tests to pass in
|
||||
// a mocked OEMCryptoInterface. The caller retains ownership of
|
||||
// |oemcrypto_interface|.
|
||||
template <typename CI>
|
||||
static OEMCryptoResult create(std::unique_ptr<CryptoInterface>* init,
|
||||
CI* oemcrypto_interface) {
|
||||
return create_internal(oemcrypto_interface, init);
|
||||
}
|
||||
|
||||
CryptoInterface(const CryptoInterface&) = delete;
|
||||
CryptoInterface& operator=(const CryptoInterface&) = delete;
|
||||
|
||||
private:
|
||||
static OEMCryptoResult create_internal(
|
||||
OEMCryptoInterface* oemcrypto_interface,
|
||||
std::unique_ptr<CryptoInterface>* init);
|
||||
|
||||
static bool initialized_;
|
||||
static int session_count_;
|
||||
static std::unique_ptr<CryptoLock> lock_;
|
||||
OEMCryptoInterface* oemcrypto_interface_;
|
||||
};
|
||||
|
||||
class SupportedCertificates {
|
||||
public:
|
||||
explicit SupportedCertificates(uint32_t supported) : supported_(supported) {}
|
||||
bool rsa_2048bit() { return OEMCrypto_Supports_RSA_2048bit & supported_; }
|
||||
bool rsa_3072bit() { return OEMCrypto_Supports_RSA_3072bit & supported_; }
|
||||
bool rsa_CASTbit() { return OEMCrypto_Supports_RSA_CAST & supported_; }
|
||||
|
||||
private:
|
||||
uint32_t supported_;
|
||||
};
|
||||
|
||||
// CryptoSession implements the core methods need to interface with OEMCrypto.
|
||||
class CryptoSession {
|
||||
public:
|
||||
explicit CryptoSession();
|
||||
virtual ~CryptoSession();
|
||||
virtual CasStatus initialize();
|
||||
virtual CasStatus reset();
|
||||
virtual CasStatus close();
|
||||
virtual CasProvisioningMethod provisioning_method();
|
||||
virtual CasStatus GetKeyData(uint8_t* keyData, size_t* keyDataLength);
|
||||
virtual SupportedCertificates supported_certificates();
|
||||
virtual CasStatus GenerateNonce(uint32_t* nonce);
|
||||
virtual CasStatus GenerateDerivedKeys(const uint8_t* mac_key_context,
|
||||
uint32_t mac_key_context_length,
|
||||
const uint8_t* enc_key_context,
|
||||
uint32_t enc_key_context_length);
|
||||
virtual CasStatus PrepareAndSignLicenseRequest(
|
||||
const std::string& message, std::string* core_message,
|
||||
std::string* signature, bool& should_specify_algorithm,
|
||||
OEMCrypto_SignatureHashAlgorithm& algorithm);
|
||||
virtual CasStatus PrepareAndSignRenewalRequest(const std::string& message,
|
||||
std::string* core_message,
|
||||
std::string* signature);
|
||||
virtual CasStatus PrepareAndSignProvisioningRequest(
|
||||
const std::string& message, std::string* core_message,
|
||||
std::string* signature, bool& should_specify_algorithm,
|
||||
OEMCrypto_SignatureHashAlgorithm& algorithm);
|
||||
virtual CasStatus LoadProvisioning(const std::string& signed_message,
|
||||
const std::string& core_message,
|
||||
const std::string& signature,
|
||||
std::string* wrapped_private_key);
|
||||
virtual CasStatus GetOEMPublicCertificate(uint8_t* public_cert,
|
||||
size_t* public_cert_length);
|
||||
virtual CasStatus LoadDeviceRSAKey(const uint8_t* wrapped_rsa_key,
|
||||
size_t wrapped_rsa_key_length);
|
||||
virtual CasStatus GenerateRSASignature(const uint8_t* message,
|
||||
size_t message_length,
|
||||
uint8_t* signature,
|
||||
size_t* signature_length,
|
||||
RSA_Padding_Scheme padding_scheme);
|
||||
virtual CasStatus DeriveKeysFromSessionKey(const uint8_t* enc_session_key,
|
||||
size_t enc_session_key_length,
|
||||
const uint8_t* mac_key_context,
|
||||
size_t mac_key_context_length,
|
||||
const uint8_t* enc_key_context,
|
||||
size_t enc_key_context_length);
|
||||
virtual CasStatus LoadLicense(const std::string& signed_message,
|
||||
const std::string& core_message,
|
||||
const std::string& signature);
|
||||
virtual CasStatus LoadRenewal(const std::string& signed_message,
|
||||
const std::string& core_message,
|
||||
const std::string& signature);
|
||||
// LoadCasECMKeys loads the ecm keys into the crypto library making them
|
||||
// available for use.
|
||||
// |odd_key| - if not null, contains control word data.
|
||||
// |even_key| - if not null, contains control word data.
|
||||
virtual CasStatus LoadCasECMKeys(OEMCrypto_SESSION session,
|
||||
const KeySlot* even_key,
|
||||
const KeySlot* odd_key);
|
||||
virtual bool GetHdcpCapabilities(HdcpCapability* current,
|
||||
HdcpCapability* max);
|
||||
virtual CasStatus GetDeviceID(std::string* buffer);
|
||||
virtual const char* SecurityLevel();
|
||||
virtual CasStatus CreateEntitledKeySession(
|
||||
OEMCrypto_SESSION* entitled_key_session_id);
|
||||
virtual CasStatus RemoveEntitledKeySession(
|
||||
OEMCrypto_SESSION entitled_key_session_id);
|
||||
virtual CasStatus ReassociateEntitledKeySession(
|
||||
OEMCrypto_SESSION entitled_key_session_id);
|
||||
virtual CasStatus APIVersion(uint32_t* api_version);
|
||||
virtual CasStatus GetOEMKeyToken(OEMCrypto_SESSION entitled_key_session_id,
|
||||
std::vector<uint8_t>& token);
|
||||
|
||||
CryptoSession(const CryptoSession&) = delete;
|
||||
CryptoSession& operator=(const CryptoSession&) = delete;
|
||||
|
||||
private:
|
||||
virtual OEMCryptoResult getCryptoInterface(
|
||||
std::unique_ptr<CryptoInterface>* interface);
|
||||
|
||||
// TODO(jfore, widevine-eng): Merge CryptoInterface into CryptoSession and
|
||||
// drop this shared pointer.
|
||||
std::unique_ptr<CryptoInterface> crypto_interface_;
|
||||
OEMCrypto_SESSION session_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // CRYPTO_SESSION_H
|
||||
59
plugin/include/ecm_parser.h
Normal file
59
plugin/include/ecm_parser.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef ECM_PARSER_H
|
||||
#define ECM_PARSER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "cas_types.h"
|
||||
#include "media_cas.pb.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
enum class KeySlotId { kEvenKeySlot, kOddKeySlot };
|
||||
|
||||
// EcmParser allows random access to the fields of an ECM.
|
||||
class EcmParser {
|
||||
public:
|
||||
EcmParser() = default;
|
||||
virtual ~EcmParser() {}
|
||||
|
||||
// The EcmParser factory method.
|
||||
// Validates the ecm. If validations is successful returns true and constructs
|
||||
// an EcmParser in |parser| using |ecm|.
|
||||
static std::unique_ptr<EcmParser> Create(const CasEcm& ecm);
|
||||
|
||||
// Accessor methods.
|
||||
virtual uint8_t version() const = 0;
|
||||
virtual CryptoMode crypto_mode() const = 0;
|
||||
virtual bool rotation_enabled() const = 0;
|
||||
virtual size_t content_iv_size() const = 0;
|
||||
virtual uint8_t age_restriction() const = 0;
|
||||
virtual std::vector<uint8_t> entitlement_key_id(KeySlotId id) const = 0;
|
||||
virtual std::vector<uint8_t> content_key_id(KeySlotId id) const = 0;
|
||||
virtual std::vector<uint8_t> wrapped_key_data(KeySlotId id) const = 0;
|
||||
virtual std::vector<uint8_t> wrapped_key_iv(KeySlotId id) const = 0;
|
||||
virtual std::vector<uint8_t> content_iv(KeySlotId id) const = 0;
|
||||
|
||||
// Process group content keys instead of the normal content keys.
|
||||
virtual bool set_group_id(const std::string& group_id) = 0;
|
||||
|
||||
virtual bool has_fingerprinting() const = 0;
|
||||
virtual video_widevine::Fingerprinting fingerprinting() const = 0;
|
||||
virtual bool has_service_blocking() const = 0;
|
||||
virtual video_widevine::ServiceBlocking service_blocking() const = 0;
|
||||
// The serialized payload that the signature is calculated on.
|
||||
virtual std::string ecm_serialized_payload() const = 0;
|
||||
virtual std::string signature() const = 0;
|
||||
|
||||
virtual bool is_entitlement_rotation_enabled() const = 0;
|
||||
virtual uint32_t entitlement_period_index() const = 0;
|
||||
virtual uint32_t entitlement_rotation_window_left() const = 0;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // ECM_PARSER_H
|
||||
85
plugin/include/ecm_parser_v2.h
Normal file
85
plugin/include/ecm_parser_v2.h
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef ECM_PARSER_V2_H
|
||||
#define ECM_PARSER_V2_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "cas_types.h"
|
||||
#include "ecm_parser.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
struct EcmKeyData;
|
||||
|
||||
// EcmParserV2 allows random access to the fields of an ECM version 2 and under.
|
||||
// It should be initialized via EcmParser factory create only.
|
||||
class EcmParserV2 : public EcmParser {
|
||||
public:
|
||||
~EcmParserV2() override = default;
|
||||
EcmParserV2(const EcmParserV2&) = delete;
|
||||
EcmParserV2& operator=(const EcmParserV2&) = delete;
|
||||
|
||||
// The EcmParserV2 factory method.
|
||||
// |ecm| must be Widevine ECM v2 or under without section header.
|
||||
// Validates the ecm. The only validation performed is to ensure that the ecm
|
||||
// passed in is large enough to hold a single key entry. If validations is
|
||||
// successful returns true and constructs an EcmParserV2 in |parser| using
|
||||
// |ecm|.
|
||||
static bool create(const CasEcm& cas_ecm,
|
||||
std::unique_ptr<EcmParserV2>* parser);
|
||||
|
||||
// Accessor methods.
|
||||
uint8_t version() const override;
|
||||
CryptoMode crypto_mode() const override;
|
||||
bool rotation_enabled() const override;
|
||||
size_t content_iv_size() const override;
|
||||
uint8_t age_restriction() const override;
|
||||
std::vector<uint8_t> entitlement_key_id(KeySlotId id) const override;
|
||||
std::vector<uint8_t> content_key_id(KeySlotId id) const override;
|
||||
std::vector<uint8_t> wrapped_key_data(KeySlotId id) const override;
|
||||
std::vector<uint8_t> wrapped_key_iv(KeySlotId id) const override;
|
||||
std::vector<uint8_t> content_iv(KeySlotId id) const override;
|
||||
|
||||
// Group keys not supported in v2.
|
||||
bool set_group_id(const std::string& group_id) override {
|
||||
return group_id.empty();
|
||||
};
|
||||
|
||||
// ECM v2 or under does not have these fields.
|
||||
bool has_fingerprinting() const override { return false; }
|
||||
video_widevine::Fingerprinting fingerprinting() const override {
|
||||
video_widevine::Fingerprinting fingerprinting;
|
||||
return fingerprinting;
|
||||
}
|
||||
bool has_service_blocking() const override { return false; };
|
||||
video_widevine::ServiceBlocking service_blocking() const override {
|
||||
video_widevine::ServiceBlocking service_blocking;
|
||||
return service_blocking;
|
||||
}
|
||||
std::string ecm_serialized_payload() const override { return ""; }
|
||||
std::string signature() const override { return ""; }
|
||||
|
||||
bool is_entitlement_rotation_enabled() const override { return false; }
|
||||
uint32_t entitlement_period_index() const override { return 0; }
|
||||
uint32_t entitlement_rotation_window_left() const override { return 0; }
|
||||
|
||||
private:
|
||||
// Constructs an EcmParserV2 using |ecm|.
|
||||
explicit EcmParserV2(const CasEcm& ecm);
|
||||
|
||||
size_t key_data_size() const;
|
||||
// Returns false if the ecm used to construct the object is not a valid size.
|
||||
// TODO(jfore): Add validation using the version field.
|
||||
bool is_valid_size() const;
|
||||
const EcmKeyData* key_slot_data(KeySlotId id) const;
|
||||
|
||||
CasEcm ecm_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // ECM_PARSER_V2_H
|
||||
68
plugin/include/ecm_parser_v3.h
Normal file
68
plugin/include/ecm_parser_v3.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef ECM_PARSER_V3_H
|
||||
#define ECM_PARSER_V3_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "cas_types.h"
|
||||
#include "ecm_parser.h"
|
||||
#include "media_cas.pb.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// EcmParser allows random access to the fields of an ECM.
|
||||
class EcmParserV3 : public EcmParser {
|
||||
public:
|
||||
~EcmParserV3() override = default;
|
||||
EcmParserV3(const EcmParserV3&) = delete;
|
||||
EcmParserV3& operator=(const EcmParserV3&) = delete;
|
||||
|
||||
// The EcmParserV3 factory method.
|
||||
// |ecm| must be Widevine ECM v3 (or higher if compatible) without section
|
||||
// header. Validates the ecm. If validations is successful returns an
|
||||
// EcmParserV3, otherwise an nullptr.
|
||||
static std::unique_ptr<EcmParserV3> Create(const CasEcm& ecm);
|
||||
|
||||
// Accessor methods.
|
||||
uint8_t version() const override;
|
||||
CryptoMode crypto_mode() const override;
|
||||
bool rotation_enabled() const override;
|
||||
size_t content_iv_size() const override;
|
||||
uint8_t age_restriction() const override;
|
||||
std::vector<uint8_t> entitlement_key_id(KeySlotId id) const override;
|
||||
std::vector<uint8_t> content_key_id(KeySlotId id) const override;
|
||||
std::vector<uint8_t> wrapped_key_data(KeySlotId id) const override;
|
||||
std::vector<uint8_t> wrapped_key_iv(KeySlotId id) const override;
|
||||
std::vector<uint8_t> content_iv(KeySlotId id) const override;
|
||||
|
||||
bool set_group_id(const std::string& group_id) override;
|
||||
|
||||
bool has_fingerprinting() const override;
|
||||
video_widevine::Fingerprinting fingerprinting() const override;
|
||||
bool has_service_blocking() const override;
|
||||
video_widevine::ServiceBlocking service_blocking() const override;
|
||||
// The serialized payload that the signature is calculated on.
|
||||
std::string ecm_serialized_payload() const override;
|
||||
std::string signature() const override;
|
||||
|
||||
bool is_entitlement_rotation_enabled() const override;
|
||||
uint32_t entitlement_period_index() const override;
|
||||
uint32_t entitlement_rotation_window_left() const override;
|
||||
|
||||
private:
|
||||
// Constructs an EcmParserV3 using |ecm|.
|
||||
EcmParserV3(video_widevine::SignedEcmPayload signed_ecm_payload,
|
||||
video_widevine::EcmPayload ecm_payload);
|
||||
video_widevine::SignedEcmPayload signed_ecm_payload_;
|
||||
video_widevine::EcmPayload ecm_payload_;
|
||||
video_widevine::EcmKeyData even_key_data_;
|
||||
video_widevine::EcmKeyData odd_key_data_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // ECM_PARSER_V3_H
|
||||
49
plugin/include/emm_parser.h
Normal file
49
plugin/include/emm_parser.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef EMM_PARSER_H
|
||||
#define EMM_PARSER_H
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "cas_types.h"
|
||||
#include "media_cas.pb.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
using video_widevine::EmmPayload;
|
||||
|
||||
class EmmParser {
|
||||
public:
|
||||
EmmParser(const EmmParser&) = delete;
|
||||
EmmParser& operator=(const EmmParser&) = delete;
|
||||
virtual ~EmmParser() = default;
|
||||
|
||||
// The EmmParser factory method.
|
||||
// The methods validates the passed in |emm|. If validation is successful, it
|
||||
// constructs and returns an EmmParser. Otherwise, nullptr is returned.
|
||||
static std::unique_ptr<const EmmParser> Create(const CasEmm& emm);
|
||||
|
||||
// Accessor methods.
|
||||
virtual uint64_t timestamp() const { return timestamp_; }
|
||||
virtual std::string signature() const { return signature_; }
|
||||
virtual EmmPayload emm_payload() const { return emm_payload_; };
|
||||
|
||||
protected:
|
||||
// Called by the factory create and unit test.
|
||||
EmmParser() = default;
|
||||
|
||||
private:
|
||||
bool Parse(int start_index, const CasEmm& emm);
|
||||
|
||||
uint8_t version_;
|
||||
uint64_t timestamp_;
|
||||
EmmPayload emm_payload_;
|
||||
std::string signature_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // EMM_PARSER_H
|
||||
210
plugin/include/license_key_status.h
Normal file
210
plugin/include/license_key_status.h
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_CAS_LICENSE_KEY_STATUS_H_
|
||||
#define WIDEVINE_CAS_LICENSE_KEY_STATUS_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "crypto_session.h"
|
||||
#include "license_protocol.pb.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
class LicenseKeyStatus;
|
||||
|
||||
using video_widevine::WidevinePsshData_EntitledKey;
|
||||
|
||||
// Policy engine HDCP enforcement
|
||||
static constexpr uint32_t HDCP_UNSPECIFIED_VIDEO_RESOLUTION = 0;
|
||||
static constexpr int64_t HDCP_DEVICE_CHECK_INTERVAL = 10;
|
||||
|
||||
enum KeySecurityLevel {
|
||||
kKeySecurityLevelUnset,
|
||||
kSoftwareSecureCrypto,
|
||||
kSoftwareSecureDecode,
|
||||
kHardwareSecureCrypto,
|
||||
kHardwareSecureDecode,
|
||||
kHardwareSecureAll,
|
||||
kKeySecurityLevelUnknown,
|
||||
};
|
||||
|
||||
class KeyAllowedUsage {
|
||||
public:
|
||||
KeyAllowedUsage() { Clear(); }
|
||||
|
||||
bool Valid() const { return valid_; }
|
||||
void SetValid() { valid_ = true; }
|
||||
|
||||
void Clear() {
|
||||
decrypt_to_clear_buffer = false;
|
||||
decrypt_to_secure_buffer = false;
|
||||
generic_encrypt = false;
|
||||
generic_decrypt = false;
|
||||
generic_sign = false;
|
||||
generic_verify = false;
|
||||
key_security_level_ = kKeySecurityLevelUnset;
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
bool Equals(const KeyAllowedUsage& other) {
|
||||
if (!valid_ || !other.Valid() ||
|
||||
decrypt_to_clear_buffer != other.decrypt_to_clear_buffer ||
|
||||
decrypt_to_secure_buffer != other.decrypt_to_secure_buffer ||
|
||||
generic_encrypt != other.generic_encrypt ||
|
||||
generic_decrypt != other.generic_decrypt ||
|
||||
generic_sign != other.generic_sign ||
|
||||
generic_verify != other.generic_verify ||
|
||||
key_security_level_ != other.key_security_level_) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool decrypt_to_clear_buffer;
|
||||
bool decrypt_to_secure_buffer;
|
||||
bool generic_encrypt;
|
||||
bool generic_decrypt;
|
||||
bool generic_sign;
|
||||
bool generic_verify;
|
||||
KeySecurityLevel key_security_level_;
|
||||
|
||||
private:
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
// Holds all content and operator session keys for a session.
|
||||
class LicenseKeys {
|
||||
public:
|
||||
LicenseKeys() {}
|
||||
virtual ~LicenseKeys() { Clear(); }
|
||||
|
||||
virtual bool Empty() { return key_statuses_.empty(); }
|
||||
|
||||
// Returns true if the key is a content key (not an operator session key)
|
||||
virtual bool IsContentKey(const KeyId& key_id);
|
||||
|
||||
// Returns true if the key is currently usable for content decryption.
|
||||
virtual bool CanDecryptContent(const KeyId& key_id);
|
||||
|
||||
// Returns the allowed usages for a key.
|
||||
virtual bool GetAllowedUsage(const KeyId& key_id,
|
||||
KeyAllowedUsage* allowed_usage);
|
||||
|
||||
// Applies a new status to each content key.
|
||||
// Returns true if any statuses changed, and sets new_usable_keys to
|
||||
// true if the status changes resulted in keys becoming usable.
|
||||
virtual bool ApplyStatusChange(KeyStatus new_status, bool* new_usable_keys);
|
||||
|
||||
// Returns current KeyStatus for the given key.
|
||||
// Returns kKeyStatusKeyUnknown if key_id not found.
|
||||
virtual KeyStatus GetKeyStatus(const KeyId& key_id);
|
||||
|
||||
// Populates a KeyStatusMap with the current content keys.
|
||||
virtual void ExtractKeyStatuses(KeyStatusMap* content_keys);
|
||||
|
||||
// Determines whether the specified key can be used under the current
|
||||
// resolution and/or hdcp constraints. If no constraints have been applied
|
||||
// to the key, returns true.
|
||||
virtual bool MeetsConstraints(const KeyId& key_id);
|
||||
|
||||
// Applies a resolution and/or hdcp change to each key, updating their
|
||||
// useability under their constraints.
|
||||
virtual void ApplyConstraints(uint32_t new_resolution,
|
||||
HdcpCapability new_hdcp_level);
|
||||
|
||||
// Extracts the keys from a license and makes them available for
|
||||
// querying usage and constraint settings.
|
||||
virtual void SetFromLicense(const video_widevine::License& license);
|
||||
|
||||
// Sets the keys from the input entitled key data.
|
||||
virtual void SetEntitledKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& keys);
|
||||
|
||||
LicenseKeys(const LicenseKeys&) = delete;
|
||||
LicenseKeys& operator=(const LicenseKeys&) = delete;
|
||||
|
||||
private:
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
typedef std::map<KeyId, LicenseKeyStatus*>::const_iterator
|
||||
LicenseKeyStatusIterator;
|
||||
|
||||
void Clear();
|
||||
|
||||
bool is_initialized_;
|
||||
// |key_statuses_| can hold either content key statuses, or entitlement key
|
||||
// statuses.
|
||||
std::map<KeyId, LicenseKeyStatus*> key_statuses_;
|
||||
// |content_keyid_to_entitlement_key_id_| maps a content key id to an
|
||||
// entitlement_key_id. The resulting key id can be used to obtain the current
|
||||
// key status from |key_statuses_| when using entitlement key licensing.
|
||||
std::map<KeyId, KeyId> content_keyid_to_entitlement_key_id_;
|
||||
};
|
||||
|
||||
// Holds the current license status of a key.
|
||||
class LicenseKeyStatus {
|
||||
friend class LicenseKeys;
|
||||
|
||||
public:
|
||||
// Returns true if the key is a content key (not an operator session key)
|
||||
virtual bool IsContentKey() { return is_content_key_; }
|
||||
|
||||
// Returns true if the key is currently usable for content decryption
|
||||
virtual bool CanDecryptContent();
|
||||
|
||||
// Returns the usages allowed for this key.
|
||||
virtual bool GetAllowedUsage(KeyAllowedUsage* allowed_usage);
|
||||
|
||||
// Returns the current status of the key.
|
||||
virtual KeyStatus GetKeyStatus() const { return key_status_; }
|
||||
|
||||
// Applies a new status to this key.
|
||||
// Returns true if the status changed, and sets new_usable_keys to
|
||||
// true if the status changes resulted in the key becoming usable.
|
||||
virtual bool ApplyStatusChange(KeyStatus new_status, bool* new_usable_keys);
|
||||
|
||||
// Returns the current constraint status of this key. The result
|
||||
// may change due to calls to ApplyConstraints().
|
||||
// Note: this will return true until the first call to ApplyConstraints().
|
||||
virtual bool MeetsConstraints() const { return meets_constraints_; }
|
||||
|
||||
// Applies the given changes in resolution or HDCP settings.
|
||||
virtual void ApplyConstraints(uint32_t new_resolution,
|
||||
HdcpCapability new_hdcp_level);
|
||||
|
||||
LicenseKeyStatus(const LicenseKeyStatus&) = delete;
|
||||
LicenseKeyStatus& operator=(const LicenseKeyStatus&) = delete;
|
||||
|
||||
protected:
|
||||
typedef ::video_widevine::License::KeyContainer KeyContainer;
|
||||
typedef KeyContainer::OperatorSessionKeyPermissions
|
||||
OperatorSessionKeyPermissions;
|
||||
typedef KeyContainer::OutputProtection OutputProtection;
|
||||
typedef KeyContainer::VideoResolutionConstraint VideoResolutionConstraint;
|
||||
typedef ::google::protobuf::RepeatedPtrField<VideoResolutionConstraint>
|
||||
ConstraintList;
|
||||
|
||||
LicenseKeyStatus(const KeyContainer& key);
|
||||
|
||||
virtual ~LicenseKeyStatus() {}
|
||||
|
||||
private:
|
||||
void ParseContentKey(const KeyContainer& key);
|
||||
void ParseOperatorSessionKey(const KeyContainer& key);
|
||||
|
||||
bool HasConstraints() { return is_content_key_ && constraints_.size() != 0; }
|
||||
|
||||
void SetConstraints(const ConstraintList& constraints);
|
||||
|
||||
bool is_content_key_;
|
||||
KeyStatus key_status_;
|
||||
bool meets_constraints_;
|
||||
KeyAllowedUsage allowed_usage_;
|
||||
HdcpCapability default_hdcp_level_;
|
||||
ConstraintList constraints_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // WIDEVINE_CAS_LICENSE_KEY_STATUS_H_
|
||||
138
plugin/include/oemcrypto_interface.h
Normal file
138
plugin/include/oemcrypto_interface.h
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef OEMCRYPTO_INTERFACE_H
|
||||
#define OEMCRYPTO_INTERFACE_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENC.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// InputStreamParams mirrors the parameters in OEMCrypto API. The
|
||||
// purpose is to allow OEMCrypto_Descramble to be mocked. OEMCrypto_Descramble
|
||||
// takes 11 parameters as of API V15. GoogleMock allows a maximum of 10.
|
||||
struct InputStreamParams {
|
||||
const uint8_t* data_addr;
|
||||
size_t data_length;
|
||||
bool is_encrypted;
|
||||
|
||||
InputStreamParams(){};
|
||||
InputStreamParams(const uint8_t* data_addr, size_t data_length,
|
||||
bool is_encrypted)
|
||||
: data_addr(data_addr),
|
||||
data_length(data_length),
|
||||
is_encrypted(is_encrypted) {}
|
||||
};
|
||||
|
||||
// Calls to oemcrypto are called via this object. The purpose of this object is
|
||||
// to allow OEMCrypto to be mocked. The implementation of this object only wraps
|
||||
// OEMCrypto methods adding limited additional functionality. Added
|
||||
// functionality is limited to adapt the input parameters to the oemcrypto api.
|
||||
// Method signatures in this class can only have a maximum of 10 parameters to
|
||||
// maintain compatibility with googlemock.
|
||||
class OEMCryptoInterface {
|
||||
public:
|
||||
OEMCryptoInterface();
|
||||
virtual ~OEMCryptoInterface();
|
||||
|
||||
virtual OEMCryptoResult OEMCrypto_Initialize(void);
|
||||
virtual OEMCryptoResult OEMCrypto_Terminate(void);
|
||||
virtual OEMCryptoResult OEMCrypto_OpenSession(
|
||||
OEMCrypto_SESSION* session) const;
|
||||
virtual OEMCryptoResult OEMCrypto_CloseSession(
|
||||
OEMCrypto_SESSION session) const;
|
||||
virtual OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod() const;
|
||||
virtual OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* keyData,
|
||||
size_t* keyDataLength) const;
|
||||
virtual uint32_t OEMCrypto_SupportedCertificates() const;
|
||||
virtual OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session,
|
||||
uint32_t* nonce) const;
|
||||
virtual OEMCryptoResult OEMCrypto_GenerateDerivedKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* mac_key_context,
|
||||
uint32_t mac_key_context_length, const uint8_t* enc_key_context,
|
||||
uint32_t enc_key_context_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest(
|
||||
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
||||
size_t* core_message_size, uint8_t* signature,
|
||||
size_t* signature_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest(
|
||||
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
||||
size_t* core_message_size, uint8_t* signature,
|
||||
size_t* signature_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest(
|
||||
OEMCrypto_SESSION session, uint8_t* message, size_t message_length,
|
||||
size_t* core_message_size, uint8_t* signature,
|
||||
size_t* signature_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_LoadProvisioning(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
size_t core_message_length, const uint8_t* signature,
|
||||
size_t signature_length, uint8_t* wrapped_private_key,
|
||||
size_t* wrapped_private_key_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(
|
||||
OEMCrypto_SESSION session, uint8_t* public_cert,
|
||||
size_t* public_cert_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(
|
||||
OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type,
|
||||
const uint8_t* wrapped_rsa_key, size_t wrapped_rsa_key_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_GenerateRSASignature(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
uint8_t* signature, size_t* signature_length,
|
||||
RSA_Padding_Scheme padding_scheme) const;
|
||||
virtual OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey(
|
||||
OEMCrypto_SESSION session, const uint8_t* enc_session_key,
|
||||
size_t enc_session_key_length, const uint8_t* mac_key_context,
|
||||
size_t mac_key_context_length, const uint8_t* enc_key_context,
|
||||
size_t enc_key_context_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
size_t core_message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session,
|
||||
const uint8_t* message,
|
||||
size_t message_length,
|
||||
size_t core_message_length,
|
||||
const uint8_t* signature,
|
||||
size_t signature_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_LoadCasECMKeys(
|
||||
OEMCrypto_SESSION session, const uint8_t* message, size_t message_length,
|
||||
const OEMCrypto_EntitledContentKeyObject* even_key,
|
||||
const OEMCrypto_EntitledContentKeyObject* odd_key) const;
|
||||
virtual OEMCryptoResult OEMCrypto_GetHDCPCapability(
|
||||
OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* max) const;
|
||||
virtual OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* deviceID,
|
||||
size_t* idLength) const;
|
||||
virtual OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer,
|
||||
size_t length) const;
|
||||
virtual const char* OEMCrypto_SecurityLevel() const;
|
||||
virtual OEMCryptoResult OEMCrypto_CreateEntitledKeySession(
|
||||
OEMCrypto_SESSION oec_session, OEMCrypto_SESSION* key_session) const;
|
||||
virtual OEMCryptoResult OEMCrypto_RemoveEntitledKeySession(
|
||||
OEMCrypto_SESSION key_session) const;
|
||||
virtual OEMCryptoResult OEMCrypto_ReassociateEntitledKeySession(
|
||||
OEMCrypto_SESSION key_session, OEMCrypto_SESSION oec_session) const;
|
||||
virtual uint32_t OEMCrypto_APIVersion() const;
|
||||
virtual OEMCryptoResult OEMCrypto_GetOEMKeyToken(
|
||||
OEMCrypto_SESSION key_session, uint8_t* key_token,
|
||||
size_t* key_token_length) const;
|
||||
virtual OEMCryptoResult OEMCrypto_GetSignatureHashAlgorithm(
|
||||
OEMCrypto_SESSION session,
|
||||
OEMCrypto_SignatureHashAlgorithm* algorithm) const;
|
||||
|
||||
OEMCryptoInterface(const OEMCryptoInterface&) = delete;
|
||||
OEMCryptoInterface& operator=(const OEMCryptoInterface&) = delete;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // OEMCRYPTO_INTERFACE_H
|
||||
214
plugin/include/policy_engine.h
Normal file
214
plugin/include/policy_engine.h
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_CAS_POLICY_ENGINE_H_
|
||||
#define WIDEVINE_CAS_POLICY_ENGINE_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "cas_util.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "license_key_status.h"
|
||||
#include "license_protocol.pb.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// This acts as an oracle that basically says "Yes(true) you may still decrypt
|
||||
// or no(false) you may not decrypt this data anymore."
|
||||
// TODO(jfore): Just pass in a function object? OnTimerEvent can be a
|
||||
class PolicyEngine : public wvutil::TimerHandler {
|
||||
typedef enum {
|
||||
kLicenseStateInitial,
|
||||
kLicenseStatePending, // if license is issued for sometime in the future
|
||||
kLicenseStateCanPlay,
|
||||
kLicenseStateNeedRenewal,
|
||||
kLicenseStateWaitingLicenseUpdate,
|
||||
kLicenseStateExpired
|
||||
} LicenseState;
|
||||
|
||||
public:
|
||||
// The default constructor.
|
||||
PolicyEngine() {}
|
||||
virtual ~PolicyEngine() {}
|
||||
|
||||
virtual void initialize(std::shared_ptr<CryptoSession> crypto_session,
|
||||
CasEventListener* event_listener) {
|
||||
license_keys_ = CreateLicenseKeys();
|
||||
clock_ = CreateClock();
|
||||
event_listener_ = event_listener;
|
||||
crypto_session_ = crypto_session;
|
||||
}
|
||||
|
||||
// The value returned should be taken as a hint rather than an absolute
|
||||
// status. It is computed during the last call to either SetLicense/
|
||||
// UpdateLicense/OnTimerEvent/BeginDecryption and may be out of sync
|
||||
// depending on the amount of time elapsed. The current decryption
|
||||
// status is not calculated to avoid overhead in the decryption path.
|
||||
virtual bool CanDecryptContent(const KeyId& key_id) const;
|
||||
|
||||
// The value returned indicates if a license renewal is allowed under the
|
||||
// current policy.
|
||||
virtual bool CanRenew() const { return policy_.can_renew(); }
|
||||
|
||||
// The value returned indicates if the license is persisted to non-volatile
|
||||
// storage for offline use.
|
||||
virtual bool CanPersist() const { return policy_.can_persist(); }
|
||||
|
||||
// The value returned indicates whether or not the client id should be
|
||||
// included in renewal requests.
|
||||
virtual bool always_include_client_id() const {
|
||||
return policy_.always_include_client_id();
|
||||
}
|
||||
|
||||
// The value returned is the current license id.
|
||||
virtual const video_widevine::LicenseIdentification& license_id() const {
|
||||
return license_id_;
|
||||
}
|
||||
|
||||
virtual const std::string& renewal_server_url() const {
|
||||
return policy_.renewal_server_url();
|
||||
}
|
||||
|
||||
// SetLicense is used in handling the initial license response. It stores
|
||||
// an exact copy of the policy information stored in the license.
|
||||
// The license state transitions to kLicenseStateCanPlay if the license
|
||||
// permits playback.
|
||||
virtual void SetLicense(const video_widevine::License& license);
|
||||
|
||||
// UpdateLicense is used in handling a license response for a renewal request.
|
||||
// The response may only contain any policy fields that have changed. In this
|
||||
// case an exact copy is not what we want to happen. We also will receive an
|
||||
// updated license_start_time from the server. The license will transition to
|
||||
// kLicenseStateCanPlay if the license permits playback.
|
||||
virtual void UpdateLicense(const video_widevine::License& license);
|
||||
|
||||
// Call this on first decrypt to set the start of playback.
|
||||
virtual void BeginDecryption(void);
|
||||
|
||||
// OnTimerEvent is called when a timer fires. It notifies the Policy Engine
|
||||
// that the timer has fired and dispatches the relevant events through
|
||||
// |event_listener_|.
|
||||
virtual void OnTimerEvent() override;
|
||||
|
||||
// Used to update the currently loaded entitled content keys.
|
||||
virtual void SetEntitledLicenseKeys(
|
||||
const std::vector<WidevinePsshData_EntitledKey>& entitled_keys);
|
||||
|
||||
// Used to query if the current license state is expired.
|
||||
virtual bool IsExpired() const {
|
||||
return license_state_ == kLicenseStateExpired;
|
||||
}
|
||||
|
||||
// for offline save and restore
|
||||
int64_t GetPlaybackStartTime() const { return playback_start_time_; }
|
||||
int64_t GetLastPlaybackTime() const { return last_playback_time_; }
|
||||
int64_t GetGracePeriodEndTime() const { return grace_period_end_time_; }
|
||||
void RestorePlaybackTimes(int64_t playback_start_time,
|
||||
int64_t last_playback_time,
|
||||
int64_t grace_period_end_time);
|
||||
|
||||
PolicyEngine(const PolicyEngine&) = delete;
|
||||
PolicyEngine& operator=(const PolicyEngine&) = delete;
|
||||
|
||||
private:
|
||||
// Notifies updates in keys information and fire OnKeysChange event if
|
||||
// key changes.
|
||||
void NotifyKeysChange(KeyStatus new_status);
|
||||
|
||||
void NotifyLicenseExpired(LicenseState key_status);
|
||||
|
||||
bool HasLicenseOrPlaybackDurationExpired(int64_t current_time);
|
||||
|
||||
// Notifies updates in expiry time and fire OnExpirationUpdate event if
|
||||
// expiry time changes.
|
||||
void NotifyExpirationUpdate(int64_t current_time);
|
||||
|
||||
// Notify listeners of the current renewal url.
|
||||
void NotifyRenewalServerUpdate();
|
||||
|
||||
// Guard against clock rollbacks
|
||||
int64_t GetCurrentTime();
|
||||
|
||||
LicenseState license_state_ = kLicenseStateInitial;
|
||||
|
||||
void CheckDeviceHdcpStatus();
|
||||
|
||||
// Gets the clock time that the license expires based on whether we have
|
||||
// started playing. This takes into account GetHardLicenseExpiryTime.
|
||||
int64_t GetExpiryTime(int64_t current_time,
|
||||
bool ignore_soft_enforce_playback_duration);
|
||||
|
||||
void CheckDeviceHdcpStatusOnTimer(int64_t current_time);
|
||||
|
||||
bool HasPlaybackStarted(int64_t current_time) {
|
||||
if (playback_start_time_ == 0) return false;
|
||||
|
||||
const int64_t playback_time = current_time - playback_start_time_;
|
||||
return playback_time >= policy_.play_start_grace_period_seconds();
|
||||
}
|
||||
|
||||
// Gets the clock time that the rental duration will expire, using the license
|
||||
// duration if one is not present.
|
||||
int64_t GetRentalExpiryTime();
|
||||
|
||||
// Gets the clock time that the license expires. This is the hard limit that
|
||||
// all license types must obey at all times.
|
||||
int64_t GetHardLicenseExpiryTime();
|
||||
|
||||
bool HasRenewalDelayExpired(int64_t current_time);
|
||||
|
||||
bool HasRenewalRetryIntervalExpired(int64_t current_time);
|
||||
|
||||
void UpdateRenewalRequest(int64_t current_time);
|
||||
|
||||
virtual std::unique_ptr<wvcas::LicenseKeys> CreateLicenseKeys();
|
||||
|
||||
virtual std::unique_ptr<wvutil::Clock> CreateClock();
|
||||
|
||||
// This is the current policy information for this license. This gets updated
|
||||
// as license renewals occur.
|
||||
video_widevine::License::Policy policy_;
|
||||
|
||||
// This is the license id field from server response. This data gets passed
|
||||
// back to the server in each renewal request. When we get a renewal response
|
||||
// from the license server we will get an updated id field.
|
||||
video_widevine::LicenseIdentification license_id_;
|
||||
|
||||
// The server returns the license start time in the license/license renewal
|
||||
// response based off the request time sent by the client in the
|
||||
// license request/renewal
|
||||
int64_t license_start_time_ = 0;
|
||||
int64_t playback_start_time_ = 0;
|
||||
int64_t last_playback_time_ = 0;
|
||||
int64_t grace_period_end_time_ = 0;
|
||||
bool last_expiry_time_set_ = false;
|
||||
bool was_expired_on_load_ = false;
|
||||
|
||||
// This is used as a reference point for policy management. This value
|
||||
// represents an offset from license_start_time_. This is used to
|
||||
// calculate the time where renewal retries should occur.
|
||||
int64_t next_renewal_time_ = 0;
|
||||
|
||||
CasEventListener* event_listener_ = nullptr;
|
||||
|
||||
// Keys associated with license - holds allowed usage, usage constraints,
|
||||
// and current status (KeyStatus)
|
||||
std::unique_ptr<LicenseKeys> license_keys_;
|
||||
std::unique_ptr<wvutil::Clock> clock_;
|
||||
// to assist in clock rollback checks
|
||||
int64_t last_recorded_current_time_ = 0;
|
||||
int64_t last_expiry_time_ = 0;
|
||||
int64_t next_device_check_ = 0;
|
||||
std::shared_ptr<wvcas::CryptoSession> crypto_session_;
|
||||
uint32_t current_resolution_ = 0;
|
||||
std::string renewal_server_url_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // WIDEVINE_CAS_POLICY_ENGINE_H_
|
||||
210
plugin/include/widevine_cas_api.h
Normal file
210
plugin/include/widevine_cas_api.h
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_CAS_API_H
|
||||
#define WIDEVINE_CAS_API_H
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "cas_license.h"
|
||||
#include "cas_media_id.h"
|
||||
#include "cas_status.h"
|
||||
#include "cas_types.h"
|
||||
#include "crypto_session.h"
|
||||
#include "ecm_parser.h"
|
||||
#include "emm_parser.h"
|
||||
#include "file_store.h"
|
||||
#include "media_cas.pb.h"
|
||||
#include "timer.h"
|
||||
#include "widevine_cas_session.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// TODO(jfore): Fix the function name inconsistency in this class. These
|
||||
// functions were migrated from the android plugin api implementation. They
|
||||
// should not follow Android's style.
|
||||
class WidevineCas : public wvutil::TimerHandler {
|
||||
public:
|
||||
WidevineCas() {}
|
||||
virtual ~WidevineCas() {}
|
||||
|
||||
virtual CasStatus initialize(CasEventListener* event_listener);
|
||||
|
||||
// Open a session for descrambling a program, or one or more elementary
|
||||
// streams.
|
||||
virtual CasStatus openSession(WvCasSessionId* sessionId);
|
||||
|
||||
// Close a previously opened session.
|
||||
virtual CasStatus closeSession(const WvCasSessionId& sessionId);
|
||||
|
||||
// Process an EMM which may contain fingerprinting and service blocking info.
|
||||
virtual CasStatus processEmm(const CasEmm& emm);
|
||||
|
||||
// Process an ECM from the ECM stream for this session’s elementary
|
||||
// stream.
|
||||
virtual CasStatus processEcm(const WvCasSessionId& sessionId,
|
||||
const CasEcm& ecm);
|
||||
|
||||
// Generates a device provisioning request message in |provisioning_request|.
|
||||
virtual CasStatus generateDeviceProvisioningRequest(
|
||||
std::string* provisioning_request);
|
||||
|
||||
// Processes a |response| to provisioning request.
|
||||
virtual CasStatus handleProvisioningResponse(const std::string& response);
|
||||
|
||||
// Generates an entitlement license request in |entitlement_request| for the
|
||||
// media described in |init_data|.
|
||||
virtual CasStatus generateEntitlementRequest(const std::string& init_data,
|
||||
std::string* entitlement_request,
|
||||
std::string& license_id);
|
||||
|
||||
// Processes the entitlement |response| to a entitlement license request.
|
||||
// |license_id| is the id of the license installed. Can be used to select
|
||||
// which license to install.
|
||||
// |multi_content_license_info| contains the message that can be sent to the
|
||||
// app if the installed license is a multi content license.
|
||||
// |group_license_info| contains the message that can be sent to the app if
|
||||
// the installed license is a group license.
|
||||
virtual CasStatus handleEntitlementResponse(
|
||||
const std::string& response, std::string& license_id,
|
||||
std::string& multi_content_license_info, std::string& group_license_info);
|
||||
|
||||
// Generates an entitlement license request in |entitlement_request| for the
|
||||
// media described in |init_data|.
|
||||
virtual CasStatus generateEntitlementRenewalRequest(
|
||||
std::string* entitlement_renewal_request);
|
||||
|
||||
// Processes the entitlement renewal |response| to a entitlement license
|
||||
// request.
|
||||
virtual CasStatus handleEntitlementRenewalResponse(
|
||||
const std::string& response, std::string& license_id);
|
||||
|
||||
// Generates an entitlement license request in a new crypto session, and send
|
||||
// the license request as an event to the app.
|
||||
virtual CasStatus generateEntitlementPeriodUpdateRequest(
|
||||
const std::string& init_data);
|
||||
|
||||
// Processes the license |response| to switch the current license to this
|
||||
// new one.
|
||||
virtual CasStatus handleEntitlementPeriodUpdateResponse(
|
||||
const std::string& response, std::string& license_id);
|
||||
|
||||
// Returns true if the device has been provisioned with a device certificate.
|
||||
virtual bool is_provisioned() const;
|
||||
|
||||
// Processes the CAS |private_data| from a CAT table. If successful a
|
||||
// serialized pssh data is retured in |init_data|.
|
||||
virtual CasStatus ProcessCAPrivateData(const CasData& private_data,
|
||||
std::string* init_data);
|
||||
|
||||
// Processes the CAS |private_data| from a PMT table. If successful a
|
||||
// serialized pssh data is retured in |init_data|. The CA private data can be
|
||||
// unique to the ecm session indicated by |session_id|.
|
||||
virtual CasStatus ProcessSessionCAPrivateData(
|
||||
const WvCasSessionId& session_id, const CasData& private_data,
|
||||
std::string* init_data);
|
||||
// Returns the device unique identifier.
|
||||
virtual CasStatus GetUniqueID(std::string* buffer);
|
||||
|
||||
// Set the minimum age required to process ECM.
|
||||
virtual CasStatus HandleSetParentalControlAge(const CasData& data);
|
||||
|
||||
// Remove the license file given the filename user provides.
|
||||
virtual CasStatus RemoveLicense(const std::string& file_name);
|
||||
|
||||
// Record the license id that user provides. This license id will be used to
|
||||
// select license if multiple licenses exist.
|
||||
virtual CasStatus RecordLicenseId(const std::string& license_id);
|
||||
|
||||
void OnTimerEvent() override;
|
||||
|
||||
// Stops the timer thread. Called by CAS plugin destructor to avoid race.
|
||||
void StopTimer();
|
||||
|
||||
private:
|
||||
virtual CasStatus HandleStoredDrmCert(const std::string& certificate);
|
||||
virtual CasStatus HandleProcessEcm(const WvCasSessionId& sessionId,
|
||||
const CasEcm& ecm);
|
||||
virtual CasStatus HandleDeferredECMs();
|
||||
// Extracts the entitlement rotation period index from ECM if specified, and
|
||||
// store it. The function should be called before any license request and the
|
||||
// extracted index will be included in the license request.
|
||||
virtual void TryExtractEntitlementPeriodIndex(const CasEcm& ecm);
|
||||
// Returns true if an offline license with |filename| is successfully loaded.
|
||||
virtual bool TryReuseStoredLicense(const std::string& filename);
|
||||
// Check if a new license is needed due to entitlement period changes. If so,
|
||||
// it will call generateEntitlementPeriodUpdateRequest().
|
||||
void CheckEntitlementPeriodUpdate(uint32_t period_index,
|
||||
uint32_t window_left);
|
||||
|
||||
virtual std::shared_ptr<CryptoSession> getCryptoSession();
|
||||
virtual std::unique_ptr<CasLicense> getCasLicense();
|
||||
virtual std::unique_ptr<wvutil::FileSystem> getFileSystem();
|
||||
virtual std::shared_ptr<WidevineCasSession> newCasSession();
|
||||
virtual std::unique_ptr<EcmParser> getEcmParser(const CasEcm& ecm) const;
|
||||
|
||||
// Creates an EmmParser. Marked as virtual for easier unit test.
|
||||
virtual std::unique_ptr<const EmmParser> getEmmParser(
|
||||
const CasEmm& emm) const;
|
||||
std::vector<uint8_t> GenerateFingerprintingEventMessage(
|
||||
const video_widevine::Fingerprinting& fingerprinting) const;
|
||||
std::vector<uint8_t> GenerateServiceBlockingEventMessage(
|
||||
const video_widevine::ServiceBlocking& service_blocking) const;
|
||||
|
||||
// The CryptoSession will be shared by the all cas sessions. It is also needed
|
||||
// by the cas api to generate EMM requests.
|
||||
std::shared_ptr<CryptoSession> crypto_session_;
|
||||
std::unique_ptr<CasLicense> cas_license_;
|
||||
std::unique_ptr<wvutil::FileSystem> file_system_;
|
||||
std::string device_certificate_;
|
||||
std::string wrapped_rsa_key_;
|
||||
CasEventListener* event_listener_ = nullptr;
|
||||
std::mutex lock_;
|
||||
wvutil::Timer policy_timer_;
|
||||
LicenseType license_type_;
|
||||
std::unique_ptr<CasMediaId> media_id_;
|
||||
// Sometimes delays in receiving a license or the format in which the content
|
||||
// is encoded my result in ecms being processed before a valid license has
|
||||
// been loaded. In this cas |has_license_| will be false and the ecm will be
|
||||
// stored in |deferred_ecms_|. Once a license has been loaded, the stored ecms
|
||||
// are processed to set the current content keys.
|
||||
std::map<WvCasSessionId, const CasEcm> deferred_ecms_;
|
||||
// The value |has_license_| will be false when the plugin is created. Once a
|
||||
// license is loaded, |has_license_| will be set to true.
|
||||
bool has_license_ = false;
|
||||
// The age_restriction field in ECM must be greater or equal to
|
||||
// |parental_control_min_age|. Otherwise, ECM will stop being processed.
|
||||
uint parental_control_age_ = 0;
|
||||
// The requested_license_id helps to indicate which license file current
|
||||
// content will use if multiple licenses exist.
|
||||
std::string requested_license_id_;
|
||||
// The current in use license_id.
|
||||
std::string license_id_;
|
||||
// The group id of a Group license. Empty if the license is not a Group
|
||||
// license (multi content license is not a group license). Used in processECM
|
||||
// to select group keys that can be decrypted by the license.
|
||||
std::string license_group_id_;
|
||||
// Fingerprinting events sent in processing last ECM/EMM. Used to avoid
|
||||
// sending a same event again.
|
||||
std::set<CasData> last_fingerprinting_events_;
|
||||
// Service blocking events sent in processing last ECM/EMM. Used to avoid
|
||||
// sending a same event again.
|
||||
std::set<CasData> last_service_blocking_events_;
|
||||
// Indicates if |entitlement_period_index_| below is valid or not.
|
||||
bool is_entitlement_rotation_enabled_ = false;
|
||||
// The entitlement period index in the last received ECM.
|
||||
uint32_t entitlement_period_index_;
|
||||
|
||||
// |next_*| used to handle entitlement key rotation. They will be moved to
|
||||
// normal ones once the license switch completed.
|
||||
std::shared_ptr<CryptoSession> next_crypto_session_;
|
||||
std::unique_ptr<CasLicense> next_cas_license_;
|
||||
std::unique_ptr<CasMediaId> next_media_id_;
|
||||
}; // namespace wvcas
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // WIDEVINE_CAS_API_H
|
||||
113
plugin/include/widevine_cas_session.h
Normal file
113
plugin/include/widevine_cas_session.h
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_CAS_SESSION_H
|
||||
#define WIDEVINE_CAS_SESSION_H
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "cas_types.h"
|
||||
#include "crypto_session.h"
|
||||
#include "ecm_parser.h"
|
||||
#include "media_cas.pb.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
class WidevineCasSession;
|
||||
typedef std::shared_ptr<WidevineCasSession> CasSessionPtr;
|
||||
|
||||
class CasKeySlotData {
|
||||
public:
|
||||
CasKeySlotData() {}
|
||||
~CasKeySlotData() {}
|
||||
|
||||
KeySlot& operator[](KeySlotId slot_id);
|
||||
const KeySlot& operator[](KeySlotId slot_id) const;
|
||||
|
||||
private:
|
||||
KeySlot keys_[2]; // Odd and even key slots.
|
||||
};
|
||||
|
||||
enum class ScramblingControl {
|
||||
kScrambling_Unscrambled = 0,
|
||||
kScrambling_Reserved = 1,
|
||||
kScrambling_EvenKey = 2,
|
||||
kScrambling_OddKey = 3,
|
||||
};
|
||||
|
||||
// WidevineCasSession represents an encryption context for a single ECM key
|
||||
// stream. It processes ECMs for the stream and maintains the key information.
|
||||
class WidevineCasSession {
|
||||
public:
|
||||
WidevineCasSession() {}
|
||||
virtual ~WidevineCasSession();
|
||||
|
||||
CasStatus initialize(std::shared_ptr<CryptoSession> crypto_session,
|
||||
CasEventListener* event_listener,
|
||||
WvCasSessionId* session_id);
|
||||
|
||||
CasStatus resetCryptoSession(std::shared_ptr<CryptoSession> crypto_session);
|
||||
|
||||
// Process an ecm and extract the key slot data. Extracted data will be used
|
||||
// to update |current_ecm_| and |entitlement_key_id_| and |keys_|.
|
||||
// |parental_control_age| (if non-zero) must be greater or equal to the
|
||||
// age_restriction field specified in |ecm|. Otherwise, ECM will not be
|
||||
// processed and error will be returned.
|
||||
// |license_group_id| if non empty, processEcm will decrypt content keys that
|
||||
// are specified by |license_group_id|.
|
||||
virtual CasStatus processEcm(const CasEcm& ecm, uint8_t parental_control_age,
|
||||
const std::string& license_group_id);
|
||||
|
||||
// Returns the security level retrieved from OEMCrypto.
|
||||
const char* securityLevel();
|
||||
|
||||
// Returns current ecm age restriction value.
|
||||
uint8_t GetEcmAgeRestriction() const { return ecm_age_restriction_; }
|
||||
// Returns the entitlement period index specified in the last received ECM.
|
||||
uint32_t GetEntitlementPeriodIndex() const {
|
||||
return entitlement_period_index_;
|
||||
}
|
||||
// Returns the entitlement rotation window left value specified in the last
|
||||
// received ECM.
|
||||
uint32_t GetEntitlementRotationWindowLeft() const {
|
||||
return entitlement_rotation_window_left_;
|
||||
}
|
||||
|
||||
WidevineCasSession(const WidevineCasSession&) = delete;
|
||||
WidevineCasSession& operator=(const WidevineCasSession&) = delete;
|
||||
|
||||
private:
|
||||
// Creates an EcmParser.
|
||||
virtual std::unique_ptr<EcmParser> getEcmParser(const CasEcm& ecm) const;
|
||||
|
||||
CasKeySlotData keys_; // Odd and even key slots.
|
||||
std::string entitlement_key_id_;
|
||||
std::mutex crypto_lock_;
|
||||
CasEcm current_ecm_;
|
||||
uint8_t ecm_age_restriction_ = 0;
|
||||
std::shared_ptr<CryptoSession> crypto_session_;
|
||||
// Id of the entitled key session in OEMCrypto associated with this session.
|
||||
uint32_t key_session_id_;
|
||||
// This is the session id returned to the app. It is actually the OEM key
|
||||
// token.
|
||||
std::vector<uint8_t> external_key_session_id_;
|
||||
CasEventListener* event_listener_ = nullptr;
|
||||
// Fingerprinting events sent in processing last ECM/EMM. Used to avoid
|
||||
// sending a same event again.
|
||||
std::vector<uint8_t> last_fingerprinting_message_;
|
||||
// Service blocking events sent in processing last ECM/EMM. Used to avoid
|
||||
// sending a same event again.
|
||||
std::vector<uint8_t> last_service_blocking_message_;
|
||||
// The entitlement period index in the last received ECM.
|
||||
uint32_t entitlement_period_index_;
|
||||
// The entitlement rotation window left in the last received ECM.
|
||||
uint32_t entitlement_rotation_window_left_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // WIDEVINE_CAS_SESSION_H
|
||||
54
plugin/include/widevine_cas_session_map.h
Normal file
54
plugin/include/widevine_cas_session_map.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_CAS_SESSION_MAP_H
|
||||
#define WIDEVINE_CAS_SESSION_MAP_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "cas_types.h"
|
||||
#include "widevine_cas_session.h"
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
typedef std::map<WvCasSessionId, CasSessionPtr> CasSessionMap;
|
||||
|
||||
// WidevineCasSessionMap is a singleton. It used as a shared resource used by
|
||||
// both cas and descrambler plugins. Cas sessions are created by the cas plugin,
|
||||
// and can be *discovered* by descrambler plugins.
|
||||
class WidevineCasSessionMap {
|
||||
public:
|
||||
virtual ~WidevineCasSessionMap() {}
|
||||
|
||||
// Adds a new Widevine cas session to the map.
|
||||
// Returns true if the session is successfully added to the map, false
|
||||
// otherwise.
|
||||
bool AddSession(const WvCasSessionId& cas_session_id, CasSessionPtr session);
|
||||
// Obtain a shared pointer to a cas session. If the session does not exist in
|
||||
// the map, the returned pointer == nullptr.
|
||||
CasSessionPtr GetSession(const WvCasSessionId& cas_session_id) const;
|
||||
// Remove an entry in the map.
|
||||
void RemoveSession(const WvCasSessionId& cas_session_id);
|
||||
// Retrieves all the session ids.
|
||||
std::vector<CasSessionPtr> GetAllSessions() const;
|
||||
|
||||
// Returns a reference to the map.
|
||||
static WidevineCasSessionMap& instance();
|
||||
|
||||
WidevineCasSessionMap(const WidevineCasSessionMap&) = delete;
|
||||
WidevineCasSessionMap& operator=(const WidevineCasSessionMap&) = delete;
|
||||
|
||||
private:
|
||||
WidevineCasSessionMap() {}
|
||||
CasSessionMap map_;
|
||||
mutable std::mutex lock_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // WIDEVINE_CAS_SESSION_MAP_H
|
||||
59
plugin/include/widevine_media_cas.h
Normal file
59
plugin/include/widevine_media_cas.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_MEDIA_CAS_H
|
||||
#define WIDEVINE_MEDIA_CAS_H
|
||||
|
||||
#include "media/cas/CasAPI.h"
|
||||
|
||||
using android::CasFactory;
|
||||
using android::CasPlugin;
|
||||
using android::CasPluginCallback;
|
||||
using android::CasPluginCallbackExt;
|
||||
using android::CasPluginDescriptor;
|
||||
using android::status_t;
|
||||
using android::String8;
|
||||
|
||||
extern "C" {
|
||||
android::CasFactory* createCasFactory();
|
||||
}
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// WidevineCasFactory implements the android::CasFactory interface.
|
||||
class WidevineCasFactory : public CasFactory {
|
||||
public:
|
||||
// Implements android::CasFactory* createCasFactory(). This method is used
|
||||
// by the MediaCas framework to construct our factory.
|
||||
static WidevineCasFactory* createCasFactory();
|
||||
~WidevineCasFactory() override {}
|
||||
|
||||
// Test the input |CA_system_id| and return true if it is supported.
|
||||
// We only support the Widevine CA ID.
|
||||
bool isSystemIdSupported(int32_t CA_system_id) const override;
|
||||
|
||||
// Add a descriptor to the vector |descriptors| for each CA ID we support.
|
||||
// We only support the Widevine CA ID.
|
||||
status_t queryPlugins(
|
||||
std::vector<CasPluginDescriptor>* descriptors) const override;
|
||||
|
||||
// Construct an instance of our CAS API plugin.
|
||||
status_t createPlugin(int32_t CA_system_id, void* appData,
|
||||
CasPluginCallback callback,
|
||||
CasPlugin** plugin) override;
|
||||
|
||||
// Construct a new extend instance of a CasPlugin given a CA_system_id
|
||||
status_t createPlugin(int32_t CA_system_id, void* appData,
|
||||
CasPluginCallbackExt callback,
|
||||
CasPlugin** plugin) override;
|
||||
|
||||
private:
|
||||
WidevineCasFactory() {}
|
||||
WidevineCasFactory(const WidevineCasFactory&);
|
||||
WidevineCasFactory& operator=(const WidevineCasFactory&);
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // WIDEVINE_MEDIA_CAS_H
|
||||
154
plugin/include/widevine_media_cas_plugin.h
Normal file
154
plugin/include/widevine_media_cas_plugin.h
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
||||
// source code may only be used and distributed under the Widevine Master
|
||||
// License Agreement.
|
||||
|
||||
#ifndef WIDEVINE_MEDIA_CAS_PLUGIN
|
||||
#define WIDEVINE_MEDIA_CAS_PLUGIN
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "media/cas/CasAPI.h"
|
||||
#include "widevine_cas_api.h"
|
||||
|
||||
using android::CasPlugin;
|
||||
using android::CasPluginCallback;
|
||||
using android::CasPluginCallbackExt;
|
||||
using android::CasPluginStatusCallback;
|
||||
using android::CasSessionId;
|
||||
using android::status_t;
|
||||
using android::String8;
|
||||
|
||||
namespace wvcas {
|
||||
|
||||
// WidevineCasPlugin inplements the android::CasPlugin interface.
|
||||
class WidevineCasPlugin : public CasPlugin, public CasEventListener {
|
||||
public:
|
||||
// Constructs a new WidevineCasPlugin. The |callback| is a method to transmit
|
||||
// events to the user application. To receive these events, a MediaCas
|
||||
// application must implement The MediaCas.EventListener java interface and
|
||||
// call MediaCas.setEventListener. More information can be found in the
|
||||
// MediaCas platform api documentation.
|
||||
WidevineCasPlugin(void* appData, CasPluginCallback callback);
|
||||
WidevineCasPlugin(void* appData, CasPluginCallbackExt callback);
|
||||
~WidevineCasPlugin() override;
|
||||
|
||||
status_t initialize();
|
||||
|
||||
// Provide a callback to report plugin status.
|
||||
status_t setStatusCallback(CasPluginStatusCallback callback) override;
|
||||
|
||||
// Provide the CA private data from a CA_descriptor in the conditional
|
||||
// access table.
|
||||
status_t setPrivateData(const CasData& privateData) override;
|
||||
|
||||
// Open a session for descrambling a program, or one or more elementary
|
||||
// streams.
|
||||
status_t openSession(CasSessionId* sessionId) override;
|
||||
|
||||
// Open a session with intend and mode for descrambling a program, or one
|
||||
// or more elementary streams.
|
||||
status_t openSession(uint32_t intent, uint32_t mode,
|
||||
CasSessionId* sessionId) override;
|
||||
|
||||
// Close a previously opened session.
|
||||
status_t closeSession(const CasSessionId& sessionId) override;
|
||||
|
||||
// Provide the CA private data from a CA_descriptor in the program map
|
||||
// table.
|
||||
status_t setSessionPrivateData(const CasSessionId& sessionId,
|
||||
const CasData& privateData) override;
|
||||
|
||||
// Process an ECM from the ECM stream for this session’s elementary stream.
|
||||
status_t processEcm(const CasSessionId& sessionId,
|
||||
const CasEcm& ecm) override;
|
||||
|
||||
// Process an in-band EMM from the EMM stream.
|
||||
status_t processEmm(const CasEmm& emm) override;
|
||||
|
||||
// Deliver an event to the CasPlugin. The format of the event is specific
|
||||
// to the CA scheme and is opaque to the framework.
|
||||
status_t sendEvent(int32_t event, int32_t arg,
|
||||
const CasData& eventData) override;
|
||||
|
||||
// Deliver an session event to the CasPlugin. The format of the event is
|
||||
// specific to the CA scheme and is opaque to the framework.
|
||||
status_t sendSessionEvent(const CasSessionId& sessionId, int32_t event,
|
||||
int32_t arg, const CasData& eventData) override;
|
||||
|
||||
// Native implementation of the MediaCas Java API provision method.
|
||||
status_t provision(const String8& provisionString) override;
|
||||
|
||||
// Native implementation of the MediaCas Java API refreshEntitlements method.
|
||||
status_t refreshEntitlements(int32_t refreshType,
|
||||
const CasData& refreshData) override;
|
||||
|
||||
WidevineCasPlugin(const WidevineCasPlugin&) = delete;
|
||||
WidevineCasPlugin& operator=(const WidevineCasPlugin&) = delete;
|
||||
|
||||
protected:
|
||||
// For unit test only.
|
||||
virtual void SetWidevineCasApi(
|
||||
std::unique_ptr<WidevineCas> widevine_cas_api) {
|
||||
widevine_cas_api_ = std::move(widevine_cas_api);
|
||||
}
|
||||
WidevineCasPlugin(){};
|
||||
|
||||
private:
|
||||
// |sessionId| is nullptr if the event is not a session event.
|
||||
CasStatus processEvent(int32_t event, int32_t arg, const CasData& eventData,
|
||||
const CasSessionId* sessionId);
|
||||
CasStatus HandleIndividualizationResponse(const CasData& response);
|
||||
CasStatus HandleEntitlementResponse(const CasData& response);
|
||||
status_t requestLicense(const std::string& init_data);
|
||||
CasStatus HandleEntitlementRenewalResponse(const CasData& response);
|
||||
CasStatus HandleUniqueIDQuery();
|
||||
CasStatus HandleSetParentalControlAge(const CasData& data);
|
||||
CasStatus HandleLicenseRemoval(const CasData& license_id);
|
||||
CasStatus HandleAssignLicenseID(const CasData& license_id);
|
||||
CasStatus HandlePluginVersionQuery();
|
||||
CasStatus HandleEntitlementPeriodUpdateResponse(const CasData& response);
|
||||
|
||||
// Returns true if the device has been provisioned with a device certificate.
|
||||
bool is_provisioned() const;
|
||||
// Event listener implementation
|
||||
void OnSessionRenewalNeeded() override;
|
||||
void OnSessionKeysChange(const KeyStatusMap& keys_status,
|
||||
bool has_new_usable_key) override;
|
||||
// |new_expiry_time_seconds| of 0 means "never expires".
|
||||
void OnExpirationUpdate(int64_t new_expiry_time_seconds) override;
|
||||
void OnNewRenewalServerUrl(const std::string& renewal_server_url) override;
|
||||
void OnLicenseExpiration() override;
|
||||
void OnAgeRestrictionUpdated(const WvCasSessionId& sessionId,
|
||||
uint8_t ecm_age_restriction) override;
|
||||
void OnSessionFingerprintingUpdated(const WvCasSessionId& sessionId,
|
||||
const CasData& fingerprinting) override;
|
||||
void OnSessionServiceBlockingUpdated(
|
||||
const WvCasSessionId& sessionId,
|
||||
const CasData& service_blocking) override;
|
||||
void OnFingerprintingUpdated(const CasData& fingerprinting) override;
|
||||
void OnServiceBlockingUpdated(const CasData& service_blocking) override;
|
||||
void OnEntitlementPeriodUpdateNeeded(
|
||||
const std::string& signed_license_request) override;
|
||||
|
||||
// Choose to use |callback_| or |callback_ext_| to send back information.
|
||||
// |sessionId| is ignored if |callback_ext_| is null,
|
||||
virtual void CallBack(void* appData, int32_t event, int32_t arg,
|
||||
uint8_t* data, size_t size,
|
||||
const CasSessionId* sessionId) const;
|
||||
|
||||
void* app_data_;
|
||||
CasPluginCallback callback_;
|
||||
CasPluginCallbackExt callback_ext_;
|
||||
// If provision is called with a non-empty string,
|
||||
// it is taken as a PSSH that overrides data in CA descripor.
|
||||
// Otherwise, first CA descriptor available to the plugin
|
||||
// is used to build a PSSH, and others are discarded.
|
||||
bool is_emm_request_sent_ = false;
|
||||
// This is always the serialized PSSH data.
|
||||
std::string init_data_;
|
||||
std::unique_ptr<WidevineCas> widevine_cas_api_;
|
||||
};
|
||||
|
||||
} // namespace wvcas
|
||||
|
||||
#endif // WIDEVINE_MEDIA_CAS_PLUGIN
|
||||
Reference in New Issue
Block a user