Source release 19.4.0
This commit is contained in:
@@ -54,6 +54,9 @@ class ClientIdentification {
|
||||
private:
|
||||
bool GetProvisioningTokenType(
|
||||
video_widevine::ClientIdentification::TokenType* token_type);
|
||||
bool GetProvisioning40TokenSignatureType(
|
||||
video_widevine::ClientIdentification::ClientCredentials::CredentialType*
|
||||
token_signature_type);
|
||||
|
||||
bool is_license_request_ = false;
|
||||
bool is_okp_request_ = false;
|
||||
|
||||
@@ -99,6 +99,13 @@ class CryptoSession {
|
||||
virtual CdmResponseType GetProvisioning40TokenType(
|
||||
OEMCrypto_BCCType* bcc_type);
|
||||
|
||||
virtual CdmResponseType GetProvisioning40TokenSignatureType(
|
||||
RequestedSecurityLevel requested_security_level,
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type);
|
||||
// Must be called after session is open.
|
||||
virtual CdmResponseType GetProvisioning40TokenSignatureType(
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type);
|
||||
|
||||
virtual CdmClientTokenType GetPreProvisionTokenType() {
|
||||
return pre_provision_token_type_;
|
||||
}
|
||||
@@ -213,6 +220,11 @@ class CryptoSession {
|
||||
std::string* additional_signature);
|
||||
virtual CdmResponseType GetBootCertificateChain(
|
||||
std::string* bcc, std::string* additional_signature);
|
||||
virtual CdmResponseType GetBootCertificateChainSignatureType(
|
||||
RequestedSecurityLevel requested_security_level,
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type);
|
||||
virtual CdmResponseType GetBootCertificateChainSignatureType(
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type);
|
||||
virtual CdmResponseType GetDeviceInformation(
|
||||
RequestedSecurityLevel requested_security_level,
|
||||
std::string* device_info);
|
||||
@@ -494,12 +506,12 @@ class CryptoSession {
|
||||
// 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());
|
||||
static auto WithStaticFieldWriteLock(const char* tag, Func body)
|
||||
-> decltype(body());
|
||||
|
||||
template <class Func>
|
||||
static auto WithStaticFieldReadLock(const char* tag,
|
||||
Func body) -> decltype(body());
|
||||
static auto WithStaticFieldReadLock(const char* tag, Func body)
|
||||
-> decltype(body());
|
||||
|
||||
template <class Func>
|
||||
static auto WithOecWriteLock(const char* tag, Func body) -> decltype(body());
|
||||
|
||||
@@ -62,39 +62,256 @@ class CdmLicense {
|
||||
|
||||
// == License Restoring API ==
|
||||
|
||||
// Restores an offline license for continued use.
|
||||
//
|
||||
// Parameters:
|
||||
// |client_token|
|
||||
// Client's DRM certificate used as the "token" when
|
||||
// authenticating the device and client with the license
|
||||
// server.
|
||||
// |license_request|
|
||||
// Original license request generated by this device.
|
||||
// Expected to be a serialized SignedMessage, with type
|
||||
// LICENSE_REQUEST.
|
||||
// |license_response|
|
||||
// Original license response generated by the license
|
||||
// server, associated with the |license_response|.
|
||||
// Expected to be a serialized SignedMessage, with type
|
||||
// LICENSE.
|
||||
// |license_renewal_response|
|
||||
// Last renewal response received from the renewal license
|
||||
// server.
|
||||
// Expected to be a serialized SignedMessage, with type
|
||||
// LICENSE.
|
||||
// |playback_start_time|, |last_playback_time|, |grace_period_end_time|
|
||||
// License timers from the CDM, not necessarily secure timers.
|
||||
//
|
||||
// Important Results:
|
||||
// NO_ERROR
|
||||
// Successfully restored license for playback.
|
||||
virtual CdmResponseType RestoreOfflineLicense(
|
||||
const std::string& client_token, const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response,
|
||||
const CdmKeyResponse& license_renewal_response,
|
||||
int64_t playback_start_time, int64_t last_playback_time,
|
||||
int64_t grace_period_end_time, CdmSession* cdm_session);
|
||||
|
||||
// Restores an offline license for release.
|
||||
//
|
||||
// License is loaded into OEMCrypto with the intention of
|
||||
// generating a release request.
|
||||
// Attempting playback will cause undefined behavior.
|
||||
//
|
||||
// Parameters:
|
||||
// |client_token|
|
||||
// Client's DRM certificate used as the "token" when
|
||||
// authenticating the device and client with the license
|
||||
// server.
|
||||
// |license_request|
|
||||
// Original license request generated by this device.
|
||||
// Expected to be a serialized SignedMessage, with type
|
||||
// LICENSE_REQUEST.
|
||||
// |license_response|
|
||||
// Original license response generated by the license
|
||||
// server, associated with the |license_response|.
|
||||
// Expected to be a serialized SignedMessage, with type
|
||||
// LICENSE.
|
||||
//
|
||||
// Important Results:
|
||||
// NO_ERROR
|
||||
// Successfully restored license for release.
|
||||
virtual CdmResponseType RestoreLicenseForRelease(
|
||||
const std::string& client_token, const CdmKeyMessage& license_request,
|
||||
const CdmKeyResponse& license_response);
|
||||
|
||||
// == Request/Response API ==
|
||||
|
||||
// Generates a license or service certificate request.
|
||||
//
|
||||
// If the license does not have a service certificate, then the
|
||||
// first time calling this method on a newly initialized instance
|
||||
// will generate a service certificate request.
|
||||
//
|
||||
// Parameters:
|
||||
// |init_data|
|
||||
// App provided initialization data. Should be of a supported
|
||||
// format.
|
||||
// |client_token|
|
||||
// Client's DRM certificate used as the "token" when
|
||||
// authenticating the device and client with the license
|
||||
// server.
|
||||
// |license_type|
|
||||
// Type of license to be requested, used in the generation
|
||||
// of the content ID.
|
||||
// |app_parameters|
|
||||
// Optional key-value pair of parameters to be inserted into
|
||||
// the client ID of the license request.
|
||||
// Note: Certain key's are reserved for Widevine use and
|
||||
// cannot be specified by the app.
|
||||
// |signed_request| (out)
|
||||
// Serialized license request to be sent to the server.
|
||||
// Only set if generating license is successful.
|
||||
// This message is a serialized SignedMessage, with type
|
||||
// NEW.
|
||||
// |server_url| (out)
|
||||
// Server URL, never specified by the CDM for new licenses.
|
||||
// Legacy field for older revisions.
|
||||
//
|
||||
// Important Results:
|
||||
// KEY_MESSAGE
|
||||
// Successfully generated a request.
|
||||
// PRIVACY_MODE_ERROR_1
|
||||
// Privacy mode is enabled, but no service certificate has
|
||||
// been provided, and requesting service certificates are
|
||||
// forbidden.
|
||||
// GENERATE_SIGNATURE_ERROR (legacy name)
|
||||
// OEMCrypto was unable to sign the request.
|
||||
virtual CdmResponseType PrepareKeyRequest(
|
||||
const InitializationData& init_data, const std::string& client_token,
|
||||
CdmLicenseType license_type, const CdmAppParameterMap& app_parameters,
|
||||
CdmKeyMessage* signed_request, std::string* server_url);
|
||||
|
||||
// Generates a license renewal or release request.
|
||||
//
|
||||
// For license renewals (|is_renewal| = true), will attempt to
|
||||
// generate a renewal request using the policies specified in the
|
||||
// original license. Playback will continue to be allowed so
|
||||
// long as the policy timers have not expired.
|
||||
//
|
||||
// For license release (|is_renewal| = false), will attempt to
|
||||
// generate a release request using the policies specified in the
|
||||
// original license. Playback will be halted, and not allowed to
|
||||
// resume. For licenses with a usage entry, OEMCrypto will enforce
|
||||
// playback halting, and a usage report will be generated to be
|
||||
// sent with the release.
|
||||
//
|
||||
// Parameters:
|
||||
// |is_renewal|
|
||||
// Flag to indicate whether the generated request is a
|
||||
// renewal (true) or a release (false).
|
||||
// |app_parameters|
|
||||
// Optional key-value pair of parameters to be inserted into
|
||||
// the client ID of the license request.
|
||||
// Note: Certain key's are reserved for Widevine use and
|
||||
// cannot be specified by the app.
|
||||
// |cdm_session|
|
||||
// Handle to the calling CdmSession to allow for usage
|
||||
// reporting.
|
||||
// |signed_request| (out)
|
||||
// Serialized renewal or release request to be sent to the
|
||||
// server. Only set if generating license is successful.
|
||||
// This message is a serialized SignedMessage, with type
|
||||
// RENEWAL or RELEASE.
|
||||
// |server_url| (out)
|
||||
// Server URL to be used for sending renewals. The value
|
||||
// provided is extracted from the license policy received
|
||||
// in the original license response.
|
||||
//
|
||||
// Important Results:
|
||||
// KEY_MESSAGE
|
||||
// Successfully generated a renewal or release request.
|
||||
// LICENSE_RENEWAL_PROHIBITED
|
||||
// License policy forbids license renewals.
|
||||
// GENERATE_SIGNATURE_ERROR (legacy name)
|
||||
// OEMCrypto was unable to sign the request.
|
||||
virtual CdmResponseType PrepareKeyUpdateRequest(
|
||||
bool is_renewal, const CdmAppParameterMap& app_parameters,
|
||||
CdmSession* cdm_session, CdmKeyMessage* signed_request,
|
||||
std::string* server_url);
|
||||
|
||||
// Parses and loads license or service certificate response,
|
||||
// or handles license errors.
|
||||
//
|
||||
// For license response, the content of the license is parsed;
|
||||
// extracting and verifying keys, initializing the license's
|
||||
// policy engine, and updating other policy rules. If license
|
||||
// contains entitlement keys, changes the license type
|
||||
// to entitlement.
|
||||
//
|
||||
// For service certificate response, the service certificate
|
||||
// is parsed and loaded into the |service_certificate_|
|
||||
// field for next call to generate request.
|
||||
//
|
||||
// For error response, parses the error message, producing logs
|
||||
// and returning an appropriate error.
|
||||
//
|
||||
// Parameters:
|
||||
// |license_response|
|
||||
// Serialized license response.
|
||||
// Expected to be a serialized SignedMessage, with type
|
||||
// LICENSE.
|
||||
//
|
||||
// Important Results:
|
||||
// KEY_ADDED
|
||||
// Successfully loaded license.
|
||||
// NEED_KEY
|
||||
// Successfully loaded service certificate, and signals
|
||||
// to the app that another request is required to generate
|
||||
// a license request.
|
||||
// NO_CONTENT_KEY
|
||||
// License was received, but did not contain any content
|
||||
// or operator session keys.
|
||||
// LOAD_LICENSE_ERROR
|
||||
// OEMCrypto rejected the license, see logs for details.
|
||||
virtual CdmResponseType HandleKeyResponse(
|
||||
bool is_restore, const CdmKeyResponse& license_response);
|
||||
const CdmKeyResponse& license_response) {
|
||||
return HandleKeyResponseInternal(/* is_restore = */ false,
|
||||
license_response);
|
||||
}
|
||||
|
||||
// Parses and loads renewal response, or handles license
|
||||
// errors.
|
||||
//
|
||||
// For license renewal response, the content of the renewal is
|
||||
// parsed; updating policy timers and other license policy
|
||||
// variables.
|
||||
//
|
||||
// For error response, parses the error message, producing logs
|
||||
// and returning an appropriate error.
|
||||
//
|
||||
// Parameters:
|
||||
// |license_response|
|
||||
// Serialized license renewal response.
|
||||
// Expected to be a serialized SignedMessage, with type
|
||||
// LICENSE.
|
||||
//
|
||||
// Important Results:
|
||||
// KEY_ADDED
|
||||
// Successfully loaded renewal.
|
||||
// NO_CONTENT_KEY
|
||||
// License was received, but did not contain any content
|
||||
// or operator session keys.
|
||||
// LOAD_LICENSE_ERROR
|
||||
// OEMCrypto rejected the license, see logs for details.
|
||||
virtual CdmResponseType HandleKeyUpdateResponse(
|
||||
bool is_renewal, bool is_restore, const CdmKeyResponse& license_response);
|
||||
bool is_renewal, const CdmKeyResponse& license_response) {
|
||||
return HandleKeyUpdateResponseInternal(is_renewal, /* is_restore = */ false,
|
||||
license_response);
|
||||
}
|
||||
|
||||
// Parses and loads new embedded keys.
|
||||
//
|
||||
// Updates the license's key session with new entitled keys
|
||||
// for which the license has entitlement keys for. Storing
|
||||
// those keys internally as new content keys.
|
||||
//
|
||||
// Used exclusively for entitlement licenses.
|
||||
//
|
||||
// Parameters:
|
||||
// |init_data|
|
||||
// Initialization data containing entitled keys within
|
||||
// the PSSH data.
|
||||
//
|
||||
// Important Results:
|
||||
// KEY_ADDED
|
||||
// Successfully loaded new entitled keys.
|
||||
virtual CdmResponseType HandleEmbeddedKeyData(
|
||||
const InitializationData& init_data);
|
||||
|
||||
// == Utilities ==
|
||||
|
||||
// Utility method for extracting the provider session token
|
||||
// from the license response.
|
||||
static bool ExtractProviderSessionToken(
|
||||
const CdmKeyResponse& license_response,
|
||||
std::string* provider_session_token);
|
||||
@@ -110,11 +327,22 @@ class CdmLicense {
|
||||
policy_engine_ = policy_engine;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
// Test Constructor.
|
||||
// CdmLicense takes ownership of the clock.
|
||||
CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock);
|
||||
|
||||
// Inserts an entitlement key ID from test data.
|
||||
void InsertEntitlementKeyIdForTest(const KeyId& entitlement_key_id) {
|
||||
entitlement_key_ids_.insert(entitlement_key_id);
|
||||
}
|
||||
|
||||
CdmResponseType HandleNewEntitledKeysForTest(
|
||||
const std::vector<PsshEntitledKey>& packaged_entitled_keys) {
|
||||
return HandleNewEntitledKeysInternal(packaged_entitled_keys);
|
||||
}
|
||||
|
||||
private:
|
||||
// == Internal Request/Response API ==
|
||||
|
||||
// Prepare to reload a key update message. Some special code is needed to work
|
||||
@@ -122,9 +350,25 @@ class CdmLicense {
|
||||
// TODO(b/166007195): Remove this.
|
||||
CdmResponseType PrepareKeyUpdateReload(CdmSession* cdm_session);
|
||||
|
||||
// Used internally to handle license responses for both newly acquired
|
||||
// licenses and for restoring of offline licenses.
|
||||
CdmResponseType HandleKeyResponseInternal(
|
||||
bool is_restore, const CdmKeyResponse& license_response);
|
||||
|
||||
// Used internally to handle renewal responses for both newly acquired
|
||||
// renewals and for restoring of offline licenses which had received
|
||||
// renewals.
|
||||
CdmResponseType HandleKeyUpdateResponseInternal(
|
||||
bool is_renewal, bool is_restore, const CdmKeyResponse& license_response);
|
||||
|
||||
// Used internally to handle license error responses for both
|
||||
// license requests and renewal requests. Maps the error code
|
||||
// from the license protocol to a CDM error code.
|
||||
CdmResponseType HandleKeyErrorResponse(
|
||||
const video_widevine::SignedMessage& signed_message);
|
||||
|
||||
// Used internally to load the content license into OEMCrypto
|
||||
// and update the license data if successful.
|
||||
CdmResponseType HandleContentKeyResponse(
|
||||
bool is_restore, const std::string& session_key, const std::string& msg,
|
||||
const std::string& core_message, const std::string& signature,
|
||||
@@ -142,7 +386,9 @@ class CdmLicense {
|
||||
const std::vector<CryptoKey>& license_keys,
|
||||
const video_widevine::License& license);
|
||||
|
||||
CdmResponseType HandleNewEntitledKeys(
|
||||
// Used internally to load the entitlement keys from
|
||||
// outside the license into the CryptoSession.
|
||||
CdmResponseType HandleNewEntitledKeysInternal(
|
||||
const std::vector<PsshEntitledKey>& packaged_entitled_keys);
|
||||
|
||||
// == Internal Utilities ==
|
||||
@@ -163,60 +409,116 @@ class CdmLicense {
|
||||
|
||||
// == Creation-time Variables ==
|
||||
|
||||
// CDM session ID associated with this license. CdmLicense should
|
||||
// only be associated with a single CDM session.
|
||||
const CdmSessionId session_id_;
|
||||
// Internal clock used to get REE system-time.
|
||||
// For production, uses the default Clock implementation;
|
||||
// for testing, uses a MockClock.
|
||||
std::unique_ptr<wvutil::Clock> clock_;
|
||||
|
||||
// == Initialization-time Variables ==
|
||||
|
||||
// Flag to indicate that the CdmLicense has been initialized
|
||||
// correctly via a call to Init().
|
||||
bool initialized_ = false;
|
||||
// License's crypto session, owned by the CdmSession which owns
|
||||
// this instance.
|
||||
CryptoSession* crypto_session_ = nullptr;
|
||||
// License's policy engine, owned by the CdmSession which owns
|
||||
// this instance.
|
||||
PolicyEngine* policy_engine_ = nullptr;
|
||||
|
||||
// Associated with ClientIdentification encryption
|
||||
// The flag |use_privacy_mode_| is used to determine whether
|
||||
// the client identification field should be encrypted when
|
||||
// generating license / renewal request.
|
||||
bool use_privacy_mode_ = false;
|
||||
// The service certificate used to encrypt client
|
||||
// identification. Must be initialized if |use_privacy_mode_|
|
||||
// is true.
|
||||
ServiceCertificate service_certificate_;
|
||||
|
||||
// Assume the latest, and downgrade later.
|
||||
// May be downgraded based on the OEMCrypto level, or the
|
||||
// license response.
|
||||
video_widevine::ProtocolVersion protocol_version_ =
|
||||
video_widevine::VERSION_2_2;
|
||||
|
||||
// == License Request/Response variables ==
|
||||
|
||||
// Device-side token used to authenticate the license request
|
||||
// with the license server.
|
||||
// Always contains a serialized DRM certificate associated
|
||||
// with the client.
|
||||
std::string client_token_;
|
||||
|
||||
// Contains the initialization data that was provided by the app
|
||||
// when making the first request.
|
||||
// Used for certain devices which may perform a service certificate
|
||||
// request. The app will only provided the license init data on
|
||||
// the first request, but may not provide initialization data on
|
||||
// the follow up request.
|
||||
std::unique_ptr<InitializationData> stored_init_data_;
|
||||
|
||||
// The nonce used in the original license request.
|
||||
uint32_t license_nonce_ = 0;
|
||||
|
||||
// Serialized LicenseRequest proto.
|
||||
// Either originates internally from a license request generated
|
||||
// since the opening of this session; or provided while restoring
|
||||
// of a license.
|
||||
CdmKeyMessage license_request_;
|
||||
|
||||
// For entitlement key licensing. This holds the keys from the init_data.
|
||||
// These keys are extracted from the PSSH when we generate a license request.
|
||||
// It is used to load content keys after we have received a license and
|
||||
// entitlement keys. It is also used in updating the key status info.
|
||||
// These are extracted from the PSSH from the initialization data.
|
||||
// They are used to load entitled content keys after the license
|
||||
// has been received. It is also used in updating the key status
|
||||
// info.
|
||||
// Used exclusively for entitlement key licensing.
|
||||
// These entitled keys are extracted at license request time.
|
||||
std::vector<PsshEntitledKey> request_entitled_keys_;
|
||||
|
||||
// Content-provider token for identifying a client's device across
|
||||
// multiple sessions/licenses.
|
||||
std::string provider_client_token_;
|
||||
// Content-provider token for identifying a client's session for
|
||||
// a single persistent license, and potentially across multiple
|
||||
// restores.
|
||||
std::string provider_session_token_;
|
||||
|
||||
// Flag to indicate that a renewal request requires a client ID.
|
||||
// This is specified in the license.
|
||||
bool renew_with_client_id_ = false;
|
||||
|
||||
// Server URL which the client app should use when requesting
|
||||
// a renewal.
|
||||
std::string renewal_server_url_;
|
||||
|
||||
// This is the latest version info extracted from the SignedMessage in
|
||||
// HandleKeyResponse
|
||||
// This is the latest version info extracted from the SignedMessage
|
||||
// when loading a received license.
|
||||
video_widevine::VersionInfo latest_service_version_;
|
||||
|
||||
// == License Life-Time Variables ==
|
||||
|
||||
// A license is assumed to be content type until the license
|
||||
// response changes that.
|
||||
CdmLicenseKeyType license_key_type_ = kLicenseKeyTypeContent;
|
||||
|
||||
// Flag to indicate that the license is offline.
|
||||
// A license is assumed to be streaming/online until a response is
|
||||
// received. This flag is determined by the license
|
||||
// type (OFFLINE) and the policy's "can persist" flag.
|
||||
bool is_offline_ = false;
|
||||
|
||||
// A list of content keys (either basic content keys, or entitled
|
||||
// content keys) which have been received.
|
||||
// Note: for entitlement licenses, this is updated when new entitled
|
||||
// content keys are received.
|
||||
std::set<KeyId> content_key_ids_;
|
||||
std::set<KeyId> entitlement_key_ids_;
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
friend class CdmLicenseTestPeer;
|
||||
#endif
|
||||
// A list of entitlement key IDs from the license. Used to filter
|
||||
// out the any entitled keys tied to an entitlement key which are
|
||||
// not specified in the license response.
|
||||
std::set<KeyId> entitlement_key_ids_;
|
||||
}; // class CdmLicense
|
||||
} // namespace wvcdm
|
||||
#endif // WVCDM_CORE_LICENSE_H_
|
||||
|
||||
@@ -97,5 +97,8 @@ OEMCryptoResult OEMCrypto_Generic_Verify(
|
||||
const OEMCrypto_SharedMemory* signature, size_t signature_length);
|
||||
OEMCryptoResult OEMCrypto_GetBCCType(RequestedSecurityLevel level,
|
||||
OEMCrypto_BCCType* bcc_type);
|
||||
OEMCryptoResult OEMCrypto_GetBCCSignatureType(
|
||||
RequestedSecurityLevel level,
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type);
|
||||
} // namespace wvcdm
|
||||
#endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_
|
||||
|
||||
@@ -125,6 +125,10 @@ static const std::string QUERY_KEY_PRODUCTION_READY = "ProductionReady";
|
||||
// Internal query key. Should not be exposed to Android apps.
|
||||
static const std::string QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN =
|
||||
"DebugBootCertificateChain";
|
||||
static const std::string QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE =
|
||||
"DebugBootCertificateChainSignature";
|
||||
static const std::string QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE_TYPE =
|
||||
"DebugBootCertificateChainSignatureType";
|
||||
static const std::string QUERY_KEY_DEVICE_INFORMATION = "DeviceInformation";
|
||||
|
||||
static const std::string QUERY_VALUE_TRUE = "True";
|
||||
|
||||
@@ -463,6 +463,7 @@ enum CdmResponseEnum : int32_t {
|
||||
GET_DEVICE_INFORMATION_ERROR = 398,
|
||||
GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR = 399,
|
||||
GET_TOKEN_FROM_EMBEDDED_CERT_ERROR = 400,
|
||||
GET_BCC_SIGNATURE_TYPE_ERROR = 401,
|
||||
// Don't forget to add new values to
|
||||
// * core/src/wv_cdm_types.cpp
|
||||
// * android/include/mapErrors-inl.h
|
||||
@@ -958,5 +959,6 @@ const char* BoolToString(bool value);
|
||||
|
||||
// Logging utilities for OEMCrypto types.
|
||||
const char* OemCryptoResultToString(OEMCryptoResult result);
|
||||
const char* OemCryptoBccSignatureTypeToString(OEMCrypto_BCCSignatureType type);
|
||||
} // namespace wvcdm
|
||||
#endif // WVCDM_CORE_WV_CDM_TYPES_H_
|
||||
|
||||
@@ -903,6 +903,46 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level,
|
||||
LOGE("Failed to extract BCC: status = %d", status.ToInt());
|
||||
return status;
|
||||
}
|
||||
if (query_token == QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE) {
|
||||
std::string bcc_unused;
|
||||
std::string signature;
|
||||
const CdmResponseType status = crypto_session->GetBootCertificateChain(
|
||||
security_level, &bcc_unused, &signature);
|
||||
if (status == NO_ERROR) {
|
||||
LOGV("BCC signature length: %zu", signature.size());
|
||||
*query_response = std::move(signature);
|
||||
return CdmResponseType(NO_ERROR);
|
||||
}
|
||||
if (status == NOT_IMPLEMENTED_ERROR ||
|
||||
status == PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR) {
|
||||
LOGD("BCC signature not available: %s", status.ToString().c_str());
|
||||
*query_response = QUERY_VALUE_NONE;
|
||||
return CdmResponseType(NO_ERROR);
|
||||
}
|
||||
LOGE("Failed to extract BCC signature: status = %s",
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
if (query_token == QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN_SIGNATURE_TYPE) {
|
||||
OEMCrypto_BCCSignatureType bcc_signature_type =
|
||||
OEMCrypto_BCCSigType_Unknown;
|
||||
const CdmResponseType status =
|
||||
crypto_session->GetBootCertificateChainSignatureType(
|
||||
security_level, &bcc_signature_type);
|
||||
if (status == NO_ERROR) {
|
||||
*query_response = OemCryptoBccSignatureTypeToString(bcc_signature_type);
|
||||
return CdmResponseType(NO_ERROR);
|
||||
}
|
||||
if (status == NOT_IMPLEMENTED_ERROR ||
|
||||
status == PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR) {
|
||||
LOGD("BCC signature type not available: %s", status.ToString().c_str());
|
||||
*query_response = QUERY_VALUE_NONE;
|
||||
return CdmResponseType(NO_ERROR);
|
||||
}
|
||||
LOGE("Failed to extract BCC signature type: status = %s",
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
if (query_token == QUERY_KEY_DEVICE_INFORMATION) {
|
||||
std::string device_info;
|
||||
const CdmResponseType status =
|
||||
|
||||
@@ -570,8 +570,7 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
if (sts != NO_ERROR) return sts;
|
||||
}
|
||||
}
|
||||
sts =
|
||||
license_parser_->HandleKeyResponse(/* is restore */ false, key_response);
|
||||
sts = license_parser_->HandleKeyResponse(key_response);
|
||||
|
||||
// Update the license sdk and service versions.
|
||||
const video_widevine::VersionInfo& version_info =
|
||||
@@ -779,8 +778,7 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
return CdmResponseType(NOT_INITIALIZED_ERROR);
|
||||
}
|
||||
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(
|
||||
/* is renewal */ true,
|
||||
/* is restore */ false, key_response);
|
||||
/* is renewal */ true, key_response);
|
||||
|
||||
// Record the timing on success.
|
||||
UpdateRequestLatencyTiming(sts);
|
||||
@@ -844,8 +842,7 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
return CdmResponseType(NOT_INITIALIZED_ERROR);
|
||||
}
|
||||
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(
|
||||
/* is renewal */ false,
|
||||
/* is restore */ false, key_response);
|
||||
/* is renewal */ false, key_response);
|
||||
// Record the timing on success.
|
||||
UpdateRequestLatencyTiming(sts);
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ bool IsPropertyKeyReserved(const std::string& prop_name) {
|
||||
// Protobuf generated classes.
|
||||
using ClientCapabilities =
|
||||
video_widevine::ClientIdentification::ClientCapabilities;
|
||||
using ClientCredentials =
|
||||
video_widevine::ClientIdentification::ClientCredentials;
|
||||
using AnalogOutputCapabilities = ClientCapabilities::AnalogOutputCapabilities;
|
||||
using video_widevine::ClientIdentification_NameValue;
|
||||
|
||||
@@ -141,7 +143,17 @@ CdmResponseType ClientIdentification::Prepare(
|
||||
}
|
||||
client_id->set_token(token);
|
||||
if (!additional_token.empty()) {
|
||||
// additional_token is only available for Provisioning 4.0 request, it
|
||||
// holds the BCC signature.
|
||||
client_id->mutable_device_credentials()->set_token(additional_token);
|
||||
ClientCredentials::CredentialType token_signature_type;
|
||||
if (!GetProvisioning40TokenSignatureType(&token_signature_type)) {
|
||||
client_id->mutable_device_credentials()->set_credential_type(
|
||||
ClientCredentials::CREDENTIAL_TYPE_UNKNOWN);
|
||||
} else {
|
||||
client_id->mutable_device_credentials()->set_credential_type(
|
||||
token_signature_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,4 +428,32 @@ bool ClientIdentification::GetProvisioningTokenType(
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientIdentification::GetProvisioning40TokenSignatureType(
|
||||
video_widevine::ClientIdentification::ClientCredentials::CredentialType*
|
||||
token_signature_type) {
|
||||
OEMCrypto_BCCSignatureType bcc_signature_type = OEMCrypto_BCCSigType_Unknown;
|
||||
const CdmResponseType status =
|
||||
crypto_session_->GetProvisioning40TokenSignatureType(&bcc_signature_type);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get provisioning token signature type: status = %s",
|
||||
status.ToString().c_str());
|
||||
return false;
|
||||
}
|
||||
switch (bcc_signature_type) {
|
||||
case OEMCrypto_BCCSigType_CBOR:
|
||||
*token_signature_type =
|
||||
ClientCredentials::CREDENTIAL_TYPE_BCC_SIGNATURE_CBOR;
|
||||
return true;
|
||||
case OEMCrypto_BCCSigType_PKCS7:
|
||||
*token_signature_type =
|
||||
ClientCredentials::CREDENTIAL_TYPE_BCC_SIGNATURE_PKCS7;
|
||||
return true;
|
||||
default:
|
||||
// shouldn't happen
|
||||
LOGE("Unexpected provisioning token signature type: %d",
|
||||
static_cast<int>(bcc_signature_type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -151,6 +151,7 @@ void AdvanceDestBuffer(OEMCrypto_DestBufferDesc* dest_buffer, size_t bytes) {
|
||||
switch (dest_buffer->type) {
|
||||
case OEMCrypto_BufferType_Clear:
|
||||
dest_buffer->buffer.clear.clear_buffer += bytes;
|
||||
dest_buffer->buffer.clear.clear_buffer_length -= bytes;
|
||||
return;
|
||||
|
||||
case OEMCrypto_BufferType_Secure:
|
||||
@@ -681,6 +682,26 @@ CdmResponseType CryptoSession::GetProvisioning40TokenType(
|
||||
"GetProvisioning40TokenType");
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetProvisioning40TokenSignatureType(
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type) {
|
||||
RETURN_IF_NOT_OPEN(CRYPTO_SESSION_NOT_OPEN);
|
||||
return GetProvisioning40TokenSignatureType(requested_security_level_,
|
||||
bcc_signature_type);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetProvisioning40TokenSignatureType(
|
||||
RequestedSecurityLevel requested_security_level,
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type) {
|
||||
RETURN_IF_NULL(bcc_signature_type, PARAMETER_NULL);
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
const OEMCryptoResult result = WithOecReadLock("GetBCCSignatureType", [&] {
|
||||
return OEMCrypto_GetBCCSignatureType(requested_security_level,
|
||||
bcc_signature_type);
|
||||
});
|
||||
return MapOEMCryptoResult(result, GET_BCC_SIGNATURE_TYPE_ERROR,
|
||||
"GetProvisioning40TokenSignatureType");
|
||||
}
|
||||
|
||||
CdmSecurityLevel CryptoSession::GetSecurityLevel() {
|
||||
LOGV("Getting security level");
|
||||
RETURN_IF_NOT_OPEN(kSecurityLevelUninitialized);
|
||||
@@ -1444,6 +1465,41 @@ CdmResponseType CryptoSession::GetBootCertificateChain(
|
||||
return CdmResponseType(NO_ERROR);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetBootCertificateChainSignatureType(
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type) {
|
||||
RETURN_IF_NOT_OPEN(CRYPTO_SESSION_NOT_OPEN);
|
||||
return GetBootCertificateChainSignatureType(requested_security_level_,
|
||||
bcc_signature_type);
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetBootCertificateChainSignatureType(
|
||||
RequestedSecurityLevel requested_security_level,
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type) {
|
||||
RETURN_IF_NULL(bcc_signature_type, PARAMETER_NULL);
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
if (GetSecurityLevel(requested_security_level) != kSecurityLevelL1) {
|
||||
LOGE("CDM only supports L1 provisioning40 token type");
|
||||
return CdmResponseType(NOT_IMPLEMENTED_ERROR);
|
||||
}
|
||||
CdmClientTokenType token_type = kClientTokenUninitialized;
|
||||
const CdmResponseType status =
|
||||
GetProvisioningMethod(requested_security_level, &token_type);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to get token type");
|
||||
return status;
|
||||
}
|
||||
if (token_type != kClientTokenBootCertChain) {
|
||||
return CdmResponseType(
|
||||
PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR);
|
||||
}
|
||||
const OEMCryptoResult result = WithOecReadLock("GetBCCSignatureType", [&] {
|
||||
return OEMCrypto_GetBCCSignatureType(requested_security_level,
|
||||
bcc_signature_type);
|
||||
});
|
||||
return MapOEMCryptoResult(result, UNKNOWN_CLIENT_TOKEN_TYPE,
|
||||
"GetBootCertificateChainSignatureType");
|
||||
}
|
||||
|
||||
CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate(
|
||||
RequestedSecurityLevel requested_security_level, std::string* token) {
|
||||
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
|
||||
@@ -3263,11 +3319,6 @@ OEMCryptoResult CryptoSession::DecryptSample(
|
||||
}
|
||||
|
||||
fake_sample.buffers.input_data_length = length;
|
||||
if (fake_sample.buffers.output_descriptor.type ==
|
||||
OEMCrypto_BufferType_Clear) {
|
||||
fake_sample.buffers.output_descriptor.buffer.clear
|
||||
.clear_buffer_length = length;
|
||||
}
|
||||
fake_sample.subsamples = &clear_subsample;
|
||||
fake_sample.subsamples_length = 1;
|
||||
|
||||
@@ -3295,11 +3346,6 @@ OEMCryptoResult CryptoSession::DecryptSample(
|
||||
}
|
||||
|
||||
fake_sample.buffers.input_data_length = length;
|
||||
if (fake_sample.buffers.output_descriptor.type ==
|
||||
OEMCrypto_BufferType_Clear) {
|
||||
fake_sample.buffers.output_descriptor.buffer.clear
|
||||
.clear_buffer_length = length;
|
||||
}
|
||||
fake_sample.subsamples = &encrypted_subsample;
|
||||
fake_sample.subsamples_length = 1;
|
||||
|
||||
@@ -3392,10 +3438,6 @@ OEMCryptoResult CryptoSession::LegacyCopyBufferInChunks(
|
||||
// Calculate the size of the next chunk.
|
||||
const size_t chunk_size = std::min(remaining_input_data, max_chunk_size);
|
||||
|
||||
if (output_descriptor.type == OEMCrypto_BufferType_Clear) {
|
||||
output_descriptor.buffer.clear.clear_buffer_length = chunk_size;
|
||||
}
|
||||
|
||||
// Re-add "last subsample" flag if this is the last subsample.
|
||||
if (chunk_size == remaining_input_data) {
|
||||
subsample_flags |= OEMCrypto_LastSubsample;
|
||||
@@ -3443,11 +3485,6 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks(
|
||||
// Calculate the size of the next chunk.
|
||||
const size_t chunk_size = std::min(remaining_input_data, max_chunk_size);
|
||||
fake_sample.buffers.input_data_length = chunk_size;
|
||||
if (fake_sample.buffers.output_descriptor.type ==
|
||||
OEMCrypto_BufferType_Clear) {
|
||||
fake_sample.buffers.output_descriptor.buffer.clear.clear_buffer_length =
|
||||
chunk_size;
|
||||
}
|
||||
if (is_protected) {
|
||||
fake_subsample.num_bytes_encrypted = chunk_size;
|
||||
} else {
|
||||
@@ -3573,40 +3610,40 @@ CdmResponseType CryptoSession::LoadOtaProvisioning(
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
auto CryptoSession::WithStaticFieldWriteLock(const char* tag,
|
||||
Func body) -> decltype(body()) {
|
||||
auto CryptoSession::WithStaticFieldWriteLock(const char* tag, Func body)
|
||||
-> decltype(body()) {
|
||||
LOGV("Static field write lock: %s", tag);
|
||||
std::unique_lock<wvutil::shared_mutex> auto_lock(static_field_mutex_);
|
||||
return body();
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
auto CryptoSession::WithStaticFieldReadLock(const char* tag,
|
||||
Func body) -> decltype(body()) {
|
||||
auto CryptoSession::WithStaticFieldReadLock(const char* tag, Func body)
|
||||
-> decltype(body()) {
|
||||
LOGV("Static field read lock: %s", tag);
|
||||
wvutil::shared_lock<wvutil::shared_mutex> auto_lock(static_field_mutex_);
|
||||
return body();
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
auto CryptoSession::WithOecWriteLock(const char* tag,
|
||||
Func body) -> decltype(body()) {
|
||||
auto CryptoSession::WithOecWriteLock(const char* tag, Func body)
|
||||
-> decltype(body()) {
|
||||
LOGV("OEMCrypto write lock: %s", tag);
|
||||
std::unique_lock<wvutil::shared_mutex> auto_lock(oem_crypto_mutex_);
|
||||
return body();
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
auto CryptoSession::WithOecReadLock(const char* tag,
|
||||
Func body) -> decltype(body()) {
|
||||
auto CryptoSession::WithOecReadLock(const char* tag, Func body)
|
||||
-> decltype(body()) {
|
||||
LOGV("OEMCrypto read lock: %s", tag);
|
||||
wvutil::shared_lock<wvutil::shared_mutex> auto_lock(oem_crypto_mutex_);
|
||||
return body();
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
auto CryptoSession::WithOecSessionLock(const char* tag,
|
||||
Func body) -> decltype(body()) {
|
||||
auto CryptoSession::WithOecSessionLock(const char* tag, Func body)
|
||||
-> decltype(body()) {
|
||||
LOGV("OEMCrypto session lock: %s", tag);
|
||||
wvutil::shared_lock<wvutil::shared_mutex> oec_auto_lock(oem_crypto_mutex_);
|
||||
std::unique_lock<std::mutex> session_auto_lock(oem_crypto_session_mutex_);
|
||||
|
||||
@@ -454,8 +454,7 @@ bool InitializationData::ConstructWidevineInitData(
|
||||
LOGV("Base64 decode of json data failed");
|
||||
return false;
|
||||
}
|
||||
std::string json_string((const char*)(&json_init_data[0]),
|
||||
json_init_data.size());
|
||||
const std::string json_string(json_init_data.begin(), json_init_data.end());
|
||||
|
||||
// Parse the Json string using jsmn
|
||||
jsmn_parser parser;
|
||||
@@ -513,12 +512,13 @@ bool InitializationData::ConstructWidevineInitData(
|
||||
break;
|
||||
case kContentIdState:
|
||||
if (tokens[i].type == JSMN_STRING) {
|
||||
std::string base64_content_id(json_string, tokens[i].start,
|
||||
tokens[i].end - tokens[i].start);
|
||||
std::vector<uint8_t> content_id_data =
|
||||
const std::string base64_content_id = json_string.substr(
|
||||
tokens[i].start, tokens[i].end - tokens[i].start);
|
||||
const std::vector<uint8_t> content_id_data =
|
||||
wvutil::Base64Decode(base64_content_id);
|
||||
content_id.assign(reinterpret_cast<const char*>(&content_id_data[0]),
|
||||
content_id_data.size());
|
||||
if (!content_id_data.empty()) {
|
||||
content_id.assign(content_id_data.begin(), content_id_data.end());
|
||||
}
|
||||
}
|
||||
state = kParseState;
|
||||
break;
|
||||
|
||||
@@ -43,7 +43,6 @@ using ContentIdentification =
|
||||
using video_widevine::HashAlgorithmProto;
|
||||
using video_widevine::License;
|
||||
using video_widevine::LicenseError;
|
||||
using video_widevine::LicenseIdentification;
|
||||
using video_widevine::LicenseRequest;
|
||||
using KeyContainer = video_widevine::License::KeyContainer;
|
||||
using video_widevine::SignedMessage;
|
||||
@@ -259,7 +258,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
}
|
||||
client_token_ = client_token;
|
||||
if (init_data.IsEmpty() && stored_init_data_) {
|
||||
InitializationData restored_init_data = *stored_init_data_;
|
||||
// In the event that the first call the PrepareKeyRequest()
|
||||
// was a service certificate request, |stored_init_data_|
|
||||
// was set. App may not provide the init data on the second
|
||||
// call.
|
||||
const InitializationData restored_init_data(std::move(*stored_init_data_));
|
||||
// Clear to prevent re-use.
|
||||
stored_init_data_.reset();
|
||||
return PrepareKeyRequest(restored_init_data, client_token, license_type,
|
||||
app_parameters, signed_request, server_url);
|
||||
@@ -306,10 +310,9 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
const std::string& request_id = crypto_session_->request_id();
|
||||
|
||||
LicenseRequest license_request;
|
||||
CdmResponseType status;
|
||||
status = PrepareClientId(app_parameters,
|
||||
/* provider_client_token = */ kEmptyString,
|
||||
&license_request);
|
||||
CdmResponseType status = PrepareClientId(
|
||||
app_parameters,
|
||||
/* provider_client_token = */ kEmptyString, &license_request);
|
||||
if (NO_ERROR != status) return status;
|
||||
|
||||
status =
|
||||
@@ -322,7 +325,6 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
// Get/set the nonce. This value will be reflected in the Key Control Block
|
||||
// of the license response.
|
||||
status = crypto_session_->GenerateNonce(&license_nonce_);
|
||||
|
||||
switch (status.code()) {
|
||||
case NO_ERROR:
|
||||
break;
|
||||
@@ -416,7 +418,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
LOGE("Output parameter |server_url| not provided");
|
||||
return CdmResponseType(INVALID_PARAMETERS_LIC_2);
|
||||
}
|
||||
|
||||
// If |is_renewal| is false, then this is a release request.
|
||||
if (is_renewal && !policy_engine_->CanRenew()) {
|
||||
LOGE("License renewal prohibited");
|
||||
return CdmResponseType(LICENSE_RENEWAL_PROHIBITED);
|
||||
@@ -430,24 +432,20 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
}
|
||||
|
||||
LicenseRequest license_request;
|
||||
if (is_renewal)
|
||||
license_request.set_type(LicenseRequest::RENEWAL);
|
||||
else
|
||||
license_request.set_type(LicenseRequest::RELEASE);
|
||||
|
||||
license_request.set_type(is_renewal ? LicenseRequest::RENEWAL
|
||||
: LicenseRequest::RELEASE);
|
||||
license_request.set_request_time(clock_->GetCurrentTime());
|
||||
license_request.set_protocol_version(protocol_version_);
|
||||
|
||||
if (renew_with_client_id_) {
|
||||
CdmResponseType status = PrepareClientId(
|
||||
const CdmResponseType status = PrepareClientId(
|
||||
app_parameters, provider_client_token_, &license_request);
|
||||
if (NO_ERROR != status) return status;
|
||||
}
|
||||
|
||||
ContentIdentification::ExistingLicense* current_license =
|
||||
license_request.mutable_content_id()->mutable_existing_license();
|
||||
const LicenseIdentification& license_id = policy_engine_->license_id();
|
||||
current_license->mutable_license_id()->CopyFrom(license_id);
|
||||
current_license->mutable_license_id()->CopyFrom(policy_engine_->license_id());
|
||||
|
||||
int64_t seconds_since_started = 0;
|
||||
int64_t seconds_since_last_played = 0;
|
||||
@@ -455,6 +453,8 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
CryptoSession::kUsageDurationsInvalid;
|
||||
if (!provider_session_token_.empty()) {
|
||||
if (!is_renewal) {
|
||||
// On release, must deactivate the usage entry
|
||||
// to prevent further playback.
|
||||
const CdmResponseType status =
|
||||
crypto_session_->DeactivateUsageInformation(provider_session_token_);
|
||||
if (NO_ERROR != status) return status;
|
||||
@@ -470,23 +470,31 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
const CdmResponseType status = crypto_session_->GenerateUsageReport(
|
||||
provider_session_token_, &usage_report, &usage_duration_status,
|
||||
&seconds_since_started, &seconds_since_last_played);
|
||||
if (!is_renewal) {
|
||||
if (NO_ERROR == status)
|
||||
current_license->set_session_usage_table_entry(usage_report);
|
||||
else
|
||||
return CdmResponseType(GENERATE_USAGE_REPORT_ERROR);
|
||||
if (status == NO_ERROR && !is_renewal) {
|
||||
current_license->set_session_usage_table_entry(usage_report);
|
||||
} else if (status != NO_ERROR && !is_renewal) {
|
||||
// Usage report is required for license release.
|
||||
return CdmResponseType(GENERATE_USAGE_REPORT_ERROR);
|
||||
} else if (status != NO_ERROR) { // && is_renewal
|
||||
// For renewals, failing to generate the usage report is
|
||||
// not a serious issue.
|
||||
LOGW("Failed to generate usage report, continuing without: status = %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (CryptoSession::kUsageDurationsValid != usage_duration_status) {
|
||||
if (policy_engine_->GetSecondsSinceStarted(&seconds_since_started) &&
|
||||
policy_engine_->GetSecondsSinceLastPlayed(&seconds_since_last_played))
|
||||
usage_duration_status = CryptoSession::kUsageDurationsValid;
|
||||
}
|
||||
|
||||
if (CryptoSession::kUsageDurationsValid == usage_duration_status) {
|
||||
// Set timers from usage report.
|
||||
current_license->set_seconds_since_started(seconds_since_started);
|
||||
current_license->set_seconds_since_last_played(seconds_since_last_played);
|
||||
} else if (policy_engine_->GetSecondsSinceStarted(&seconds_since_started) &&
|
||||
policy_engine_->GetSecondsSinceLastPlayed(
|
||||
&seconds_since_last_played)) {
|
||||
// Set timers from license policy engine.
|
||||
current_license->set_seconds_since_started(seconds_since_started);
|
||||
current_license->set_seconds_since_last_played(seconds_since_last_played);
|
||||
} else {
|
||||
LOGW("Failed to obtain license durations");
|
||||
}
|
||||
|
||||
// License request is complete. Serialize it.
|
||||
@@ -517,7 +525,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
return CdmResponseType(KEY_MESSAGE);
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
CdmResponseType CdmLicense::HandleKeyResponseInternal(
|
||||
bool is_restore, const CdmKeyResponse& license_response) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmLicense not initialized");
|
||||
@@ -580,27 +588,26 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
return CdmResponseType(SESSION_KEYS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Extract mac key
|
||||
std::string mac_key_iv;
|
||||
std::string mac_keys;
|
||||
for (int i = 0; i < license.key_size(); ++i) {
|
||||
if (license.key(i).type() == KeyContainer::SIGNING) {
|
||||
mac_key_iv.assign(license.key(i).iv());
|
||||
|
||||
// Strip off PKCS#5 padding
|
||||
mac_keys.assign(
|
||||
license.key(i).key().data(),
|
||||
std::min(kLicenseMacKeySize, license.key(i).key().size()));
|
||||
}
|
||||
// Verify signing key data and IV length.
|
||||
size_t signing_key_data_length = 0;
|
||||
size_t signing_key_iv_length = 0;
|
||||
for (const auto& key_container : license.key()) {
|
||||
if (key_container.type() != KeyContainer::SIGNING) continue;
|
||||
// To maintain backwards compatibility, in the case of multiple
|
||||
// signing keys, use the last one found.
|
||||
signing_key_data_length =
|
||||
std::min(kLicenseMacKeySize, key_container.key().size());
|
||||
signing_key_iv_length = key_container.iv().size();
|
||||
}
|
||||
if (license.policy().can_renew() ||
|
||||
(!mac_key_iv.empty() || !mac_keys.empty())) {
|
||||
if (mac_key_iv.size() != KEY_IV_SIZE ||
|
||||
mac_keys.size() != kLicenseMacKeySize) {
|
||||
(signing_key_data_length != 0 || signing_key_iv_length != 0)) {
|
||||
if (signing_key_iv_length != KEY_IV_SIZE ||
|
||||
signing_key_data_length != kLicenseMacKeySize) {
|
||||
LOGE(
|
||||
"MAC key/IV size error: expected = %zu/%zu, "
|
||||
"actual = %zu/%zu (key/iv)",
|
||||
kLicenseMacKeySize, KEY_IV_SIZE, mac_keys.size(), mac_key_iv.size());
|
||||
kLicenseMacKeySize, KEY_IV_SIZE, signing_key_data_length,
|
||||
signing_key_iv_length);
|
||||
return CdmResponseType(KEY_SIZE_ERROR_1);
|
||||
}
|
||||
}
|
||||
@@ -651,20 +658,17 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
crypto_session_->UseSecondaryKey(signed_response.using_secondary_key());
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
CdmResponseType resp(NO_CONTENT_KEY);
|
||||
if (kLicenseKeyTypeEntitlement == key_type) {
|
||||
resp = HandleEntitlementKeyResponse(
|
||||
return HandleEntitlementKeyResponse(
|
||||
is_restore, signed_response.session_key(), signed_message, core_message,
|
||||
signature, license_keys, license);
|
||||
} else if (kLicenseKeyTypeContent == key_type) {
|
||||
resp = HandleContentKeyResponse(is_restore, signed_response.session_key(),
|
||||
signed_message, core_message, signature,
|
||||
license_keys, license);
|
||||
}
|
||||
return resp;
|
||||
return HandleContentKeyResponse(is_restore, signed_response.session_key(),
|
||||
signed_message, core_message, signature,
|
||||
license_keys, license);
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
CdmResponseType CdmLicense::HandleKeyUpdateResponseInternal(
|
||||
bool is_renewal, bool is_restore, const CdmKeyResponse& license_response) {
|
||||
if (!initialized_) {
|
||||
LOGE("CdmLicense not initialized");
|
||||
@@ -681,15 +685,13 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
return CdmResponseType(LICENSE_RESPONSE_PARSE_ERROR_2);
|
||||
}
|
||||
|
||||
switch (signed_response.type()) {
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::ERROR_RESPONSE:
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
default:
|
||||
LOGE("Unrecognized signed message type: type = %d",
|
||||
static_cast<int>(signed_response.type()));
|
||||
return CdmResponseType(INVALID_LICENSE_TYPE);
|
||||
if (signed_response.type() == SignedMessage::ERROR_RESPONSE) {
|
||||
return HandleKeyErrorResponse(signed_response);
|
||||
}
|
||||
if (signed_response.type() != SignedMessage::LICENSE) {
|
||||
LOGE("Unrecognized signed message type: type = %d",
|
||||
static_cast<int>(signed_response.type()));
|
||||
return CdmResponseType(INVALID_LICENSE_TYPE);
|
||||
}
|
||||
|
||||
const std::string& signed_message = signed_response.msg();
|
||||
@@ -722,35 +724,35 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
}
|
||||
|
||||
if (!is_renewal) {
|
||||
if (!license.id().has_provider_session_token())
|
||||
return CdmResponseType(KEY_ADDED);
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
// For non-renewals, the response is assumed to be a release
|
||||
// response.
|
||||
if (license.id().has_provider_session_token()) {
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
}
|
||||
return CdmResponseType(KEY_ADDED);
|
||||
}
|
||||
|
||||
if (license.policy().has_renewal_server_url() &&
|
||||
license.policy().renewal_server_url().size() > 0) {
|
||||
!license.policy().renewal_server_url().empty()) {
|
||||
renewal_server_url_ = license.policy().renewal_server_url();
|
||||
}
|
||||
|
||||
// If the field is not set, it will default to false.
|
||||
// If |using_secondary_key| is not set, it will default to false.
|
||||
CdmResponseType status =
|
||||
crypto_session_->UseSecondaryKey(signed_response.using_secondary_key());
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
status =
|
||||
crypto_session_->LoadRenewal(signed_message, core_message, signature);
|
||||
if (status != KEY_ADDED) return status;
|
||||
|
||||
if (status == KEY_ADDED) {
|
||||
policy_engine_->UpdateLicense(license, is_restore);
|
||||
}
|
||||
|
||||
return status;
|
||||
policy_engine_->UpdateLicense(license, is_restore);
|
||||
return CdmResponseType(KEY_ADDED);
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleEmbeddedKeyData(
|
||||
const InitializationData& init_data) {
|
||||
return HandleNewEntitledKeys(init_data.ExtractWrappedKeys());
|
||||
return HandleNewEntitledKeysInternal(init_data.ExtractWrappedKeys());
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::RestoreOfflineLicense(
|
||||
@@ -794,16 +796,16 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
|
||||
}
|
||||
|
||||
CdmResponseType status =
|
||||
HandleKeyResponse(/* is_restore = */ true, license_response);
|
||||
HandleKeyResponseInternal(/* is_restore = */ true, license_response);
|
||||
|
||||
if (status != KEY_ADDED) return status;
|
||||
|
||||
if (!license_renewal_response.empty()) {
|
||||
status = PrepareKeyUpdateReload(cdm_session);
|
||||
if (status != KEY_MESSAGE && status != NO_ERROR) return status;
|
||||
status = HandleKeyUpdateResponse(/* is_renewal = */ true,
|
||||
/* is_restore = */ true,
|
||||
license_renewal_response);
|
||||
status = HandleKeyUpdateResponseInternal(/* is_renewal = */ true,
|
||||
/* is_restore = */ true,
|
||||
license_renewal_response);
|
||||
if (status != KEY_ADDED) return status;
|
||||
}
|
||||
|
||||
@@ -926,7 +928,7 @@ CdmResponseType CdmLicense::RestoreLicenseForRelease(
|
||||
|
||||
if (!license.id().has_provider_session_token()) {
|
||||
const CdmResponseType result =
|
||||
HandleKeyResponse(/* is_restore = */ false, license_response);
|
||||
HandleKeyResponseInternal(/* is_restore = */ false, license_response);
|
||||
return result == KEY_ADDED ? CdmResponseType(NO_ERROR) : result;
|
||||
}
|
||||
|
||||
@@ -1095,19 +1097,21 @@ CdmResponseType CdmLicense::HandleContentKeyResponse(
|
||||
LOGE("No content keys provided");
|
||||
return CdmResponseType(NO_CONTENT_KEY);
|
||||
}
|
||||
const CdmResponseType resp = crypto_session_->LoadLicense(
|
||||
const CdmResponseType status = crypto_session_->LoadLicense(
|
||||
protocol_version_ <= video_widevine::VERSION_2_1
|
||||
? license_request_
|
||||
: Sha512Hash(license_request_),
|
||||
session_key, msg, core_message, signature, kLicenseKeyTypeContent);
|
||||
if (KEY_ADDED == resp) {
|
||||
content_key_ids_.clear();
|
||||
for (const CryptoKey& key : license_keys) {
|
||||
content_key_ids_.insert(key.key_id());
|
||||
}
|
||||
policy_engine_->SetLicense(license, is_restore);
|
||||
if (status != KEY_ADDED) return status;
|
||||
|
||||
content_key_ids_.clear();
|
||||
for (const CryptoKey& key : license_keys) {
|
||||
// The CdmLicense handles content and operator session keys
|
||||
// the same.
|
||||
content_key_ids_.insert(key.key_id());
|
||||
}
|
||||
return resp;
|
||||
policy_engine_->SetLicense(license, is_restore);
|
||||
return CdmResponseType(KEY_ADDED);
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
|
||||
@@ -1119,27 +1123,26 @@ CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
|
||||
LOGE("No entitlement keys provided");
|
||||
return CdmResponseType(NO_CONTENT_KEY);
|
||||
}
|
||||
const CdmResponseType resp = crypto_session_->LoadLicense(
|
||||
const CdmResponseType status = crypto_session_->LoadLicense(
|
||||
protocol_version_ <= video_widevine::VERSION_2_1
|
||||
? license_request_
|
||||
: Sha512Hash(license_request_),
|
||||
session_key, msg, core_message, signature, kLicenseKeyTypeEntitlement);
|
||||
|
||||
if (KEY_ADDED != resp) {
|
||||
return resp;
|
||||
}
|
||||
if (status != KEY_ADDED) return status;
|
||||
|
||||
// Save the entitlement keys for future use to handle key changes,
|
||||
// and for call to HandleNewEntitledKeys().
|
||||
// and for call to HandleNewEntitledKeysInternal().
|
||||
for (const auto& key_container : license.key()) {
|
||||
if (key_container.type() != KeyContainer::ENTITLEMENT) continue;
|
||||
entitlement_key_ids_.insert(key_container.id());
|
||||
}
|
||||
policy_engine_->SetLicense(license, is_restore);
|
||||
return HandleNewEntitledKeys(request_entitled_keys_);
|
||||
// Now load any entitled keys that were in the PSSH provided
|
||||
// when generated the license request.
|
||||
return HandleNewEntitledKeysInternal(request_entitled_keys_);
|
||||
}
|
||||
|
||||
CdmResponseType CdmLicense::HandleNewEntitledKeys(
|
||||
CdmResponseType CdmLicense::HandleNewEntitledKeysInternal(
|
||||
const std::vector<PsshEntitledKey>& packaged_entitled_keys) {
|
||||
std::vector<CryptoKey> entitled_keys;
|
||||
entitled_keys.reserve(packaged_entitled_keys.size());
|
||||
@@ -1169,9 +1172,9 @@ CdmResponseType CdmLicense::HandleNewEntitledKeys(
|
||||
entitled_keys.push_back(std::move(entitled_key));
|
||||
}
|
||||
|
||||
const CdmResponseType resp =
|
||||
const CdmResponseType status =
|
||||
crypto_session_->LoadEntitledContentKeys(entitled_keys);
|
||||
if (resp != KEY_ADDED) return resp;
|
||||
if (status != KEY_ADDED) return status;
|
||||
|
||||
// Loaded entitled keys can be accessed like regular content keys
|
||||
// by the license.
|
||||
@@ -1201,5 +1204,4 @@ bool CdmLicense::SetTypeAndId(CdmLicenseType license_type,
|
||||
content_id->set_request_id(request_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -1192,8 +1192,22 @@ message ClientIdentification {
|
||||
}
|
||||
|
||||
message ClientCredentials {
|
||||
// Deprecated. Use credential_type instead.
|
||||
optional TokenType type = 1 [default = KEYBOX];
|
||||
optional bytes token = 2;
|
||||
|
||||
// Additional types of credentials that may be present in the client
|
||||
// identification.
|
||||
enum CredentialType {
|
||||
CREDENTIAL_TYPE_UNKNOWN = 0;
|
||||
// CBOR format used by the Provisioning 4.0 phase 3 uploading model.
|
||||
CREDENTIAL_TYPE_BCC_SIGNATURE_CBOR = 1;
|
||||
// PKCS7 format, used by Provisioning 4.0 signing model.
|
||||
CREDENTIAL_TYPE_BCC_SIGNATURE_PKCS7 = 2;
|
||||
}
|
||||
|
||||
// The type of the token.
|
||||
optional CredentialType credential_type = 3;
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
|
||||
@@ -224,6 +224,13 @@ OEMCryptoResult OEMCrypto_GetBCCType(RequestedSecurityLevel level,
|
||||
(void)level;
|
||||
return ::OEMCrypto_GetBCCType(bcc_type);
|
||||
}
|
||||
|
||||
OEMCryptoResult OEMCrypto_GetBCCSignatureType(
|
||||
RequestedSecurityLevel level,
|
||||
OEMCrypto_BCCSignatureType* bcc_signature_type) {
|
||||
(void)level;
|
||||
return ::OEMCrypto_GetBCCSignatureType(bcc_signature_type);
|
||||
}
|
||||
} // namespace wvcdm
|
||||
|
||||
// Provide default implementation of L3-only functions. WEAK allows them to be
|
||||
|
||||
@@ -889,6 +889,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) {
|
||||
return "SESSION_NOT_FOUND_GENERIC_CRYPTO";
|
||||
case SESSION_NOT_FOUND_24:
|
||||
return "SESSION_NOT_FOUND_24";
|
||||
case GET_BCC_SIGNATURE_TYPE_ERROR:
|
||||
return "GET_BCC_SIGNATURE_TYPE_ERROR";
|
||||
}
|
||||
return UnknownValueRep(cdm_response_enum);
|
||||
}
|
||||
@@ -1075,4 +1077,18 @@ const char* OemCryptoResultToString(OEMCryptoResult result) {
|
||||
return UnknownValueRep(result);
|
||||
}
|
||||
|
||||
const char* OemCryptoBccSignatureTypeToString(OEMCrypto_BCCSignatureType type) {
|
||||
switch (type) {
|
||||
case OEMCrypto_BCCSigType_Unknown:
|
||||
return "Unknown";
|
||||
case OEMCrypto_BCCSigType_CBOR:
|
||||
return "CBOR";
|
||||
case OEMCrypto_BCCSigType_PKCS7:
|
||||
return "PKCS7";
|
||||
case OEMCrypto_BCCSigType_Keybox:
|
||||
return "Keybox";
|
||||
}
|
||||
return UnknownValueRep(type);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -131,6 +131,7 @@ namespace wvcdm {
|
||||
using ::testing::_;
|
||||
using ::testing::ByMove;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Return;
|
||||
@@ -468,7 +469,7 @@ TEST_P(CertificateProvisioningTest, ProvisioningResponseSuccess) {
|
||||
.WillOnce(DoAll(SaveArg<0>(&stored_certificate), ReturnArg<1>()));
|
||||
|
||||
MockFileSystem file_system;
|
||||
EXPECT_CALL(file_system, Open(StrEq(wvutil::kLegacyCertificateFileName), _))
|
||||
EXPECT_CALL(file_system, Open(HasSubstr(wvutil::kLegacyCertificateFileName), _))
|
||||
.Times(1)
|
||||
.WillOnce(Return(ByMove(std::unique_ptr<File>(file))));
|
||||
|
||||
|
||||
@@ -145,8 +145,11 @@ TEST_F(CoreIntegrationTest, ProvisioningStableSpoidTest) {
|
||||
cdm_engine_.QueryStatus(kLevelDefault, QUERY_KEY_SECURITY_LEVEL, &level)
|
||||
.code());
|
||||
|
||||
ASSERT_TRUE(level == QUERY_VALUE_SECURITY_LEVEL_L1 ||
|
||||
level == QUERY_VALUE_SECURITY_LEVEL_L3)
|
||||
if (level == QUERY_VALUE_SECURITY_LEVEL_L3) {
|
||||
GTEST_SKIP(); // SPOID is not expected to be stable for L3
|
||||
}
|
||||
|
||||
ASSERT_TRUE(level == QUERY_VALUE_SECURITY_LEVEL_L1)
|
||||
<< "Unknown security level: " << level;
|
||||
|
||||
CdmSecurityLevel security_level = level == QUERY_VALUE_SECURITY_LEVEL_L1
|
||||
|
||||
@@ -1046,9 +1046,12 @@ class CdmUseCase_LicenseWithRenewal : public RenewalTest {
|
||||
initial_policy_.renewal_recovery_duration;
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
void SetUp() override final {
|
||||
RenewalTest::SetUp();
|
||||
if(Test::IsSkipped()) return;
|
||||
if (Test::IsSkipped()) return;
|
||||
if (!wvoec::global_features.usage_table) {
|
||||
GTEST_SKIP() << "Usage tables are not supported.";
|
||||
}
|
||||
const uint64_t next_renewal =
|
||||
start_of_playback_ + initial_policy_.renewal_delay;
|
||||
// Allow playback within the initial renewal window.
|
||||
@@ -1252,9 +1255,12 @@ class CdmUseCase_LicenseWithRenewalPlayback : public RenewalTest {
|
||||
initial_policy_.renewal_recovery_duration;
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
void SetUp() override final {
|
||||
RenewalTest::SetUp();
|
||||
if(Test::IsSkipped()) return;
|
||||
if (Test::IsSkipped()) return;
|
||||
if (!wvoec::global_features.usage_table) {
|
||||
GTEST_SKIP() << "Usage tables are not supported.";
|
||||
}
|
||||
uint64_t next_renewal = start_of_playback_ + initial_policy_.renewal_delay;
|
||||
// Allow playback within the initial renewal window.
|
||||
SleepUntil(start_of_playback_);
|
||||
@@ -1466,6 +1472,14 @@ class CdmUseCase_LimitedDurationLicense : public RenewalTest {
|
||||
renewal_load_time_ =
|
||||
start_of_playback_ + renewal_delay_ + renewal_recovery_ - 1;
|
||||
}
|
||||
|
||||
void SetUp() override final {
|
||||
RenewalTest::SetUp();
|
||||
if (!wvoec::global_features.usage_table) {
|
||||
GTEST_SKIP() << "Usage tables are not supported.";
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t renewal_delay_;
|
||||
uint64_t renewal_load_time_;
|
||||
uint64_t renewal_recovery_;
|
||||
@@ -1718,9 +1732,12 @@ class CdmUseCase_Heartbeat : public RenewalTest {
|
||||
initial_policy_.renewal_recovery_duration;
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
void SetUp() override final {
|
||||
RenewalTest::SetUp();
|
||||
if(Test::IsSkipped()) return;
|
||||
if (Test::IsSkipped()) return;
|
||||
if (!wvoec::global_features.usage_table) {
|
||||
GTEST_SKIP() << "Usage tables are not supported.";
|
||||
}
|
||||
const uint64_t next_renewal =
|
||||
start_of_playback_ + initial_policy_.renewal_delay;
|
||||
// Allow playback within the initial renewal window.
|
||||
@@ -1818,6 +1835,13 @@ class CdmUseCase_LicenseDuration : public CdmDurationTest {
|
||||
timer_limits_.soft_enforce_playback_duration = false;
|
||||
timer_limits_.total_playback_duration_seconds = 40u;
|
||||
}
|
||||
|
||||
void SetUp() override final {
|
||||
CdmDurationTest::SetUp();
|
||||
if (!wvoec::global_features.usage_table) {
|
||||
GTEST_SKIP() << "Usage tables are not supported.";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Playback within rental duration.
|
||||
@@ -1876,6 +1900,13 @@ class CdmUseCase_InfiniteRenewal : public RenewalTest {
|
||||
timer_limits_.rental_duration_seconds = 50u;
|
||||
timer_limits_.total_playback_duration_seconds = 0;
|
||||
}
|
||||
|
||||
void SetUp() override final {
|
||||
RenewalTest::SetUp();
|
||||
if (!wvoec::global_features.usage_table) {
|
||||
GTEST_SKIP() << "Usage tables are not supported.";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The renewal interval is infinite. We never need to load the renewal.
|
||||
@@ -1918,6 +1949,13 @@ class CdmUseCase_LicenseDurationWithRenewal : public RenewalTest {
|
||||
timer_limits_.rental_duration_seconds = 30u;
|
||||
timer_limits_.total_playback_duration_seconds = 0;
|
||||
}
|
||||
|
||||
void SetUp() override final {
|
||||
RenewalTest::SetUp();
|
||||
if (!wvoec::global_features.usage_table) {
|
||||
GTEST_SKIP() << "Usage tables are not supported.";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// If we do load the renewal, we may continue playback past original window.
|
||||
|
||||
@@ -869,4 +869,27 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
HlsAttributeVariant(kHlsAttributeListInvalidIv, HLS_IV_ATTRIBUTE,
|
||||
kHlsTestHexValueWithOddBytes, false)));
|
||||
|
||||
TEST_F(HlsParseTest, BadHlsData_InvalidContentId) {
|
||||
std::ostringstream hls_uri_json_stream;
|
||||
hls_uri_json_stream << "{";
|
||||
hls_uri_json_stream << "\"provider\": \"HlsParseTest.BadHlsData\", ";
|
||||
// Intentionally bad Base64 content ID.
|
||||
hls_uri_json_stream << "\"content_id\": \"$$$$\", ";
|
||||
hls_uri_json_stream << "\"key_ids\": [\"00000000000000000000000000000000\"]";
|
||||
hls_uri_json_stream << "}";
|
||||
const std::string hls_uri_json = hls_uri_json_stream.str();
|
||||
|
||||
std::ostringstream hls_stream;
|
||||
hls_stream << "#EXT-X-KEY:";
|
||||
hls_stream << "METHOD=AES-128,";
|
||||
hls_stream << "URI=\"data:text/plain;base64,"
|
||||
<< wvutil::Base64Encode(hls_uri_json) << "\",";
|
||||
hls_stream << "IV=0x00000000000000000000000000000000,";
|
||||
hls_stream << "KEYFORMAT=\"com.widevine\",";
|
||||
hls_stream << "KEYFORMATVERSIONS=\"1\"";
|
||||
const std::string hls_data = hls_stream.str();
|
||||
// std::cout << "HLS Data:" << std::endl << hls_data << std::endl;
|
||||
InitializationData init_data(HLS_INIT_DATA_FORMAT, hls_data);
|
||||
EXPECT_TRUE(init_data.is_hls());
|
||||
}
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "license_request.h"
|
||||
#include "message_dumper.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "properties.h"
|
||||
#include "test_base.h"
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -126,10 +127,27 @@ void LicenseHolder::GenerateAndPostReleaseRequest(
|
||||
const std::string init_data_string = MakePSSH(pssh);
|
||||
const InitializationData init_data(kCencMimeType, init_data_string);
|
||||
init_data.DumpToLogs();
|
||||
const CdmResponseType result = cdm_engine_->GenerateKeyRequest(
|
||||
session_id_, key_set_id_, init_data, kLicenseTypeRelease,
|
||||
empty_app_parameters, &request);
|
||||
|
||||
CdmSessionId session_id;
|
||||
CdmKeySetId key_set_id;
|
||||
CdmResponseType result;
|
||||
// For Android when key set IDs are used, the key set ID passed in should have
|
||||
// a value and the session ID should be empty.
|
||||
if (!Properties::AlwaysUseKeySetIds()) {
|
||||
key_set_id = key_set_id_;
|
||||
result = cdm_engine_->OpenKeySetSession(key_set_id_, nullptr, nullptr);
|
||||
ASSERT_EQ(NO_ERROR, result) << "Failed for " << content_id();
|
||||
// For CE CDM, we only need the session ID to be valid.
|
||||
} else {
|
||||
session_id = session_id_;
|
||||
}
|
||||
result = cdm_engine_->GenerateKeyRequest(session_id, key_set_id, init_data,
|
||||
kLicenseTypeRelease,
|
||||
empty_app_parameters, &request);
|
||||
ASSERT_EQ(KEY_MESSAGE, result) << "Failed for " << content_id();
|
||||
if (!Properties::AlwaysUseKeySetIds()) {
|
||||
cdm_engine_->CloseKeySetSession(key_set_id_);
|
||||
}
|
||||
if (config_.dump_golden_data()) {
|
||||
// TODO (b/295956275) vickymin: write DumpReleaseRequest function
|
||||
// MessageDumper::DumpReleaseRequest(request);
|
||||
|
||||
@@ -201,12 +201,15 @@ class CdmLicenseTestPeer : public CdmLicense {
|
||||
CdmLicenseTestPeer(const CdmSessionId& session_id, wvutil::Clock* clock)
|
||||
: CdmLicense(session_id, clock) {}
|
||||
|
||||
using CdmLicense::HandleNewEntitledKeys;
|
||||
CdmResponseType HandleNewEntitledKeys(
|
||||
const std::vector<PsshEntitledKey>& packaged_entitled_keys) {
|
||||
return HandleNewEntitledKeysForTest(packaged_entitled_keys);
|
||||
}
|
||||
|
||||
void set_entitlement_keys(const License& license) {
|
||||
void SetEntitlementKeys(const License& license) {
|
||||
for (const auto& key_container : license.key()) {
|
||||
if (key_container.type() != KeyContainer::ENTITLEMENT) continue;
|
||||
entitlement_key_ids_.insert(key_container.id());
|
||||
InsertEntitlementKeyIdForTest(key_container.id());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -608,10 +611,11 @@ TEST_P(CdmLicenseEntitledKeyTest, LoadsEntitledKeys) {
|
||||
// Set up the CdmLicense with the mocks and fake entitlement key
|
||||
ASSERT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate,
|
||||
crypto_session_.get(), policy_engine_.get()));
|
||||
cdm_license_->set_entitlement_keys(entitlement_license);
|
||||
cdm_license_->SetEntitlementKeys(entitlement_license);
|
||||
|
||||
// Call the function under test and check its return value
|
||||
CdmResponseType ret = cdm_license_->HandleNewEntitledKeys(entitled_keys);
|
||||
const CdmResponseType ret =
|
||||
cdm_license_->HandleNewEntitledKeys(entitled_keys);
|
||||
|
||||
if (variant.should_succeed) {
|
||||
EXPECT_EQ(KEY_ADDED, ret);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "license_holder.h"
|
||||
#include "log.h"
|
||||
#include "oec_device_features.h"
|
||||
#include "properties.h"
|
||||
#include "provisioning_holder.h"
|
||||
#include "test_base.h"
|
||||
#include "test_printers.h"
|
||||
@@ -193,13 +194,24 @@ TEST_F(CorePIGTest, LicenseRelease1) {
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
|
||||
EXPECT_EQ(NO_ERROR, holder.Decrypt(key_id));
|
||||
// For Android where AlwaysUseKeySetIds() is false, the CDM engine generates
|
||||
// a session separately. Thus, we close the session and only for CE CDM reopen
|
||||
// it for the license release.
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
if (Properties::AlwaysUseKeySetIds()) {
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.ReloadLicense());
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(holder.GenerateAndPostReleaseRequest(
|
||||
"CDM_UnlimitedStreaming_can_persist"));
|
||||
EXPECT_NE(NO_ERROR, holder.Decrypt(key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FetchRelease());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.LoadRelease());
|
||||
EXPECT_NE(NO_ERROR, holder.Decrypt(key_id));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
// For CE CDM, we can close the session after we have gotten the release.
|
||||
if (Properties::AlwaysUseKeySetIds()) {
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,11 +231,22 @@ TEST_F(CorePIGTest, LicenseRelease2) {
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FetchLicense());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.LoadLicense());
|
||||
wvutil::TestSleep::Sleep(10);
|
||||
// For Android where AlwaysUseKeySetIds() is false, the CDM engine generates
|
||||
// a session separately. Thus, we close the session and only for CE CDM reopen
|
||||
// it for the license release.
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
if (Properties::AlwaysUseKeySetIds()) {
|
||||
ASSERT_NO_FATAL_FAILURE(holder.OpenSession());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.ReloadLicense());
|
||||
}
|
||||
ASSERT_NO_FATAL_FAILURE(holder.GenerateAndPostReleaseRequest(
|
||||
"CDM_UnlimitedStreaming_can_persist"));
|
||||
ASSERT_NO_FATAL_FAILURE(holder.FetchRelease());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.LoadRelease());
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
// For CE CDM, we can close the session after we have gotten the release.
|
||||
if (Properties::AlwaysUseKeySetIds()) {
|
||||
ASSERT_NO_FATAL_FAILURE(holder.CloseSession());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CorePIGTest, CastReceiverProvisioningUsingCdm) {
|
||||
|
||||
Reference in New Issue
Block a user