Sync the definition of WidevinePssh data
Merge from Widevine repo of http://go/wvgerrit/43202 Sync the definition of WidevinePssh data with the latest in support of entitlement keys. bug: 73297961 Fix or remove sublicense support. Test: tested as part of http://go/ag/4674759 Change-Id: Ia9faf82732854a705b4b14430169ce4c8ecbcfcd
This commit is contained in:
@@ -34,7 +34,8 @@ class InitializationData {
|
||||
// TODO(jfore): Perhaps this should be a generic structure with the ids for
|
||||
// any type of licensing?
|
||||
std::vector<video_widevine::SubLicense> ExtractSublicenseKeys() const;
|
||||
std::vector<video_widevine::WrappedKey> ExtractWrappedKeys() const;
|
||||
std::vector<video_widevine::WidevinePsshData_EntitledKey> ExtractWrappedKeys()
|
||||
const;
|
||||
|
||||
private:
|
||||
bool SelectWidevinePssh(const CdmInitData& init_data,
|
||||
|
||||
@@ -89,21 +89,14 @@ InitializationData::InitializationData(const std::string& type,
|
||||
std::vector<video_widevine::SubLicense>
|
||||
InitializationData::ExtractSublicenseKeys() const {
|
||||
std::vector<video_widevine::SubLicense> keys;
|
||||
WidevinePsshData cenc_header;
|
||||
if (!is_cenc_ || !cenc_header.ParseFromString(data_) ||
|
||||
cenc_header.sub_licenses().size() == 0)
|
||||
return keys;
|
||||
|
||||
keys.reserve(cenc_header.sub_licenses().size());
|
||||
for (int i = 0; i < cenc_header.sub_licenses().size(); ++i) {
|
||||
keys.push_back(cenc_header.sub_licenses(i));
|
||||
}
|
||||
// TODO(jfore): The pssh has changed in ways that are not compatible with
|
||||
//sublicenses. Restructure or remove sublicense support including this method.
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::vector<video_widevine::WrappedKey> InitializationData::ExtractWrappedKeys()
|
||||
const {
|
||||
std::vector<video_widevine::WrappedKey> keys;
|
||||
std::vector<video_widevine::WidevinePsshData_EntitledKey>
|
||||
InitializationData::ExtractWrappedKeys() const {
|
||||
std::vector<video_widevine::WidevinePsshData_EntitledKey> keys;
|
||||
WidevinePsshData cenc_header;
|
||||
if (!is_cenc_ || !cenc_header.ParseFromString(data_) ||
|
||||
cenc_header.entitled_keys().size() == 0)
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "clock.h"
|
||||
#include "cdm_session.h"
|
||||
#include "client_identification.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_key.h"
|
||||
#include "crypto_session.h"
|
||||
#include "device_files.h"
|
||||
@@ -39,6 +39,7 @@ using video_widevine::ClientIdentification_NameValue;
|
||||
using video_widevine::DrmDeviceCertificate;
|
||||
using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::License;
|
||||
using video_widevine::License_KeyContainer;
|
||||
using video_widevine::LicenseError;
|
||||
using video_widevine::LicenseIdentification;
|
||||
using video_widevine::LicenseRequest;
|
||||
@@ -46,7 +47,6 @@ using video_widevine::LicenseRequest_ContentIdentification;
|
||||
using video_widevine::LicenseRequest_ContentIdentification_CencDeprecated;
|
||||
using video_widevine::LicenseRequest_ContentIdentification_ExistingLicense;
|
||||
using video_widevine::LicenseRequest_ContentIdentification_WebmDeprecated;
|
||||
using video_widevine::License_KeyContainer;
|
||||
using video_widevine::SignedDrmDeviceCertificate;
|
||||
using video_widevine::SignedMessage;
|
||||
|
||||
@@ -237,11 +237,11 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
|
||||
|
||||
CdmLicense::~CdmLicense() {}
|
||||
|
||||
bool CdmLicense::Init(
|
||||
const std::string& client_token, CdmClientTokenType client_token_type,
|
||||
const std::string& device_id, bool use_privacy_mode,
|
||||
const std::string& signed_service_certificate, CryptoSession* session,
|
||||
PolicyEngine* policy_engine) {
|
||||
bool CdmLicense::Init(const std::string& client_token,
|
||||
CdmClientTokenType client_token_type,
|
||||
const std::string& device_id, bool use_privacy_mode,
|
||||
const std::string& signed_service_certificate,
|
||||
CryptoSession* session, PolicyEngine* policy_engine) {
|
||||
if (clock_.get() == NULL) {
|
||||
LOGE("CdmLicense::Init: clock parameter not provided");
|
||||
return false;
|
||||
@@ -321,18 +321,19 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
// If privacy mode and no service certificate, depending on platform
|
||||
// configuration, request service certificate or declare error
|
||||
if (use_privacy_mode_ && !service_certificate_.has_certificate()) {
|
||||
|
||||
if (!Properties::allow_service_certificate_requests()) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - "
|
||||
"no service certificate.");
|
||||
LOGE(
|
||||
"CdmLicense::PrepareKeyRequest: failure with privacy mode - "
|
||||
"no service certificate.");
|
||||
return PRIVACY_MODE_ERROR_1;
|
||||
}
|
||||
|
||||
stored_init_data_.reset(new InitializationData(init_data));
|
||||
|
||||
if (!ServiceCertificate::GetRequest(signed_request)) {
|
||||
LOGE("CdmLicense::PrepareKeyRequest: failed to prepare a service "
|
||||
"certificated request");
|
||||
LOGE(
|
||||
"CdmLicense::PrepareKeyRequest: failed to prepare a service "
|
||||
"certificated request");
|
||||
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
|
||||
@@ -452,8 +453,9 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
|
||||
if (renew_with_client_id_) {
|
||||
if (use_privacy_mode_ && !service_certificate_.has_certificate()) {
|
||||
LOGE("CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - "
|
||||
"no service certificate.");
|
||||
LOGE(
|
||||
"CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - "
|
||||
"no service certificate.");
|
||||
return PRIVACY_MODE_ERROR_2;
|
||||
}
|
||||
}
|
||||
@@ -573,13 +575,11 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
return INVALID_LICENSE_RESPONSE;
|
||||
}
|
||||
|
||||
if (use_privacy_mode_ &&
|
||||
Properties::allow_service_certificate_requests() &&
|
||||
if (use_privacy_mode_ && Properties::allow_service_certificate_requests() &&
|
||||
signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) {
|
||||
std::string signed_certificate;
|
||||
CdmResponseType status =
|
||||
ServiceCertificate::ParseResponse(license_response,
|
||||
&signed_certificate);
|
||||
CdmResponseType status = ServiceCertificate::ParseResponse(
|
||||
license_response, &signed_certificate);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
status = service_certificate_.Init(signed_certificate);
|
||||
@@ -591,7 +591,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
|
||||
if (signed_response.type() != SignedMessage::LICENSE) {
|
||||
LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d",
|
||||
signed_response.type());
|
||||
signed_response.type());
|
||||
return INVALID_LICENSE_TYPE;
|
||||
}
|
||||
|
||||
@@ -641,8 +641,6 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
if (key_array.empty()) {
|
||||
key_array = ExtractContentKeys(license);
|
||||
key_type = kLicenseKeyTypeContent;
|
||||
} else if (wrapped_keys_.empty()) {
|
||||
key_array.clear();
|
||||
}
|
||||
if (key_array.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
|
||||
@@ -659,8 +657,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
if (license.id().has_provider_session_token())
|
||||
provider_session_token_ = license.id().provider_session_token();
|
||||
|
||||
LOGV("provider_session_token: %s", provider_session_token_.empty() ?
|
||||
"N/A" : provider_session_token_.c_str());
|
||||
LOGV("provider_session_token: %s", provider_session_token_.empty()
|
||||
? "N/A"
|
||||
: provider_session_token_.c_str());
|
||||
|
||||
if (license.policy().has_renewal_server_url()) {
|
||||
server_url_ = license.policy().renewal_server_url();
|
||||
@@ -697,7 +696,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
|
||||
SignedMessage signed_response;
|
||||
if (!signed_response.ParseFromString(license_response)) {
|
||||
LOGE("CdmLicense::HandleKeyUpdateResponse: Unable to parse signed message");
|
||||
LOGE(
|
||||
"CdmLicense::HandleKeyUpdateResponse: Unable to parse signed "
|
||||
"message");
|
||||
return LICENSE_RESPONSE_PARSE_ERROR_2;
|
||||
}
|
||||
|
||||
@@ -871,7 +872,8 @@ bool CdmLicense::RestoreLicenseForRelease(
|
||||
|
||||
if (signed_request.type() != SignedMessage::LICENSE_REQUEST) {
|
||||
LOGE(
|
||||
"CdmLicense::RestoreLicenseForRelease: license request type: expected "
|
||||
"CdmLicense::RestoreLicenseForRelease: license request type: "
|
||||
"expected "
|
||||
"= %d, actual = %d",
|
||||
SignedMessage::LICENSE_REQUEST, signed_request.type());
|
||||
return false;
|
||||
@@ -955,14 +957,17 @@ bool CdmLicense::ExtractProviderSessionToken(
|
||||
}
|
||||
|
||||
if (signed_response.type() != SignedMessage::LICENSE) {
|
||||
LOGE("CdmLicense::ExtractProviderSessionToken: unrecognized signed message "
|
||||
"type: %d", signed_response.type());
|
||||
return false;
|
||||
LOGE(
|
||||
"CdmLicense::ExtractProviderSessionToken: unrecognized signed message "
|
||||
"type: %d",
|
||||
signed_response.type());
|
||||
return false;
|
||||
}
|
||||
|
||||
License license;
|
||||
if (!license.ParseFromString(signed_response.msg())) {
|
||||
LOGE("CdmLicense::ExtractProviderSessionToken: unable to parse license "
|
||||
LOGE(
|
||||
"CdmLicense::ExtractProviderSessionToken: unable to parse license "
|
||||
"response");
|
||||
return false;
|
||||
}
|
||||
@@ -999,7 +1004,6 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||
|
||||
CdmResponseType CdmLicense::PrepareClientId(
|
||||
const CdmAppParameterMap& app_parameters, LicenseRequest* license_request) {
|
||||
|
||||
wvcdm::ClientIdentification id;
|
||||
CdmResponseType status = id.Init(client_token_, device_id_, crypto_session_);
|
||||
if (status != NO_ERROR) return status;
|
||||
@@ -1100,8 +1104,8 @@ CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
|
||||
const std::string& mac_key_iv, const std::string& mac_key,
|
||||
const std::vector<CryptoKey>& key_array,
|
||||
const video_widevine::License& license) {
|
||||
if (key_array.empty() || wrapped_keys_.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyResponse : No content keys.");
|
||||
if (key_array.empty()) {
|
||||
LOGE("CdmLicense::HandleKeyResponse : No entitlement keys.");
|
||||
return NO_CONTENT_KEY;
|
||||
}
|
||||
CdmResponseType resp = crypto_session_->LoadKeys(
|
||||
|
||||
@@ -743,61 +743,47 @@ message SubLicense {
|
||||
optional bytes group_id = 13;
|
||||
}
|
||||
|
||||
// Container for keys which are wrapped using an entitlement key from a master
|
||||
// license.
|
||||
message WrappedKey {
|
||||
// ID of the wrapped key. Required.
|
||||
optional bytes key_id = 1;
|
||||
// ID of wrapping key. Required.
|
||||
optional bytes wrapping_key_id = 2;
|
||||
// IV used to wrap the key. Required.
|
||||
optional bytes wrapping_iv = 3;
|
||||
// Encrypted entitled key. Wrapped with the entitlement key and IV, using
|
||||
// AES-256-CBC with PKCS#7 padding. Required.
|
||||
optional bytes wrapped_key = 4;
|
||||
}
|
||||
|
||||
message WidevinePsshData {
|
||||
// Superceded by protection_scheme.
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
AESCTR = 1;
|
||||
};
|
||||
enum Type {
|
||||
SINGLE = 0; // Single PSSH to be used to retrieve content keys.
|
||||
ENTITLEMENT = 1; // Primary PSSH used to retrieve entitlement keys.
|
||||
ENTITLED_KEY = 2; // Secondary PSSH containing entitled key(s).
|
||||
}
|
||||
|
||||
optional Algorithm algorithm = 1 [deprecated = true];
|
||||
message EntitledKey {
|
||||
// ID of entitlement key used for wrapping |key|.
|
||||
optional bytes entitlement_key_id = 1;
|
||||
// ID of the entitled key.
|
||||
optional bytes key_id = 2;
|
||||
// Wrapped key. Required.
|
||||
optional bytes key = 3;
|
||||
// IV used for wrapping |key|. Required.
|
||||
optional bytes iv = 4;
|
||||
}
|
||||
|
||||
// Key IDentifier(s). This field is mutually exclusive with content_id, below.
|
||||
// Only One or the other, but at least one must be present.
|
||||
// Entitlement or content key IDs. Can onnly present in SINGLE or ENTITLEMENT
|
||||
// PSSHs. May be repeated to facilitate delivery of multiple keys in a
|
||||
// single license. Cannot be used in conjunction with content_id or
|
||||
// group_ids, which are the preferred mechanism.
|
||||
repeated bytes key_ids = 2;
|
||||
|
||||
// Content provider name.
|
||||
optional string provider = 3 [deprecated = true];
|
||||
|
||||
// A content identifier, specified by content provider.
|
||||
// This field is mutually exclusive with key_ids, above. Only
|
||||
// one or the other, but at least one must be present.
|
||||
// Content identifier which may map to multiple entitlement or content key
|
||||
// IDs to facilitate the delivery of multiple keys in a single license.
|
||||
// Cannot be present in conjunction with key_ids, but if used must be in all
|
||||
// PSSHs.
|
||||
optional bytes content_id = 4;
|
||||
|
||||
// Track type. Acceptable values are SD, HD and AUDIO. Used to differentiate
|
||||
// content keys used by an asset.
|
||||
// No longer adding track_type to the PSSH since the Widevine license server
|
||||
// will return keys for all allowed track types in a single license.
|
||||
optional string track_type = 5 [deprecated = true];
|
||||
|
||||
// The name of a registered policy to be used for this asset.
|
||||
optional string policy = 6 [deprecated=true];
|
||||
|
||||
// Crypto period index, for media using key rotation.
|
||||
// Crypto period index, for media using key rotation. Always corresponds to
|
||||
// The content key period. This means that if using entitlement licensing
|
||||
// the ENTITLED_KEY PSSHs will have sequential crypto_period_index's, whereas
|
||||
// the ENTITELEMENT PSSHs will have gaps in the sequence. Required if doing
|
||||
// key rotation.
|
||||
optional uint32 crypto_period_index = 7;
|
||||
|
||||
// Optional protected context for group content. The grouped_license is a
|
||||
// serialized SignedMessage.
|
||||
optional bytes grouped_license = 8 [deprecated = true];
|
||||
|
||||
// Protection scheme identifying the encryption algorithm. The protection
|
||||
// scheme is represented as a uint32 value. The uint32 contains 4 bytes each
|
||||
// representing a single ascii character in one of the 4CC protection scheme
|
||||
// values. To be soon deprecated in favor of signaling from content.
|
||||
// values. To be deprecated in favor of signaling from content.
|
||||
// 'cenc' (AES-CTR) protection_scheme = 0x63656E63,
|
||||
// 'cbc1' (AES-CBC) protection_scheme = 0x63626331,
|
||||
// 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73,
|
||||
@@ -808,18 +794,35 @@ message WidevinePsshData {
|
||||
// of each crypto period in seconds.
|
||||
optional uint32 crypto_period_seconds = 10;
|
||||
|
||||
// Required when using content keys that are embedded in content.
|
||||
repeated SubLicense sub_licenses = 11;
|
||||
// Type of PSSH. Required if not SINGLE.
|
||||
optional Type type = 11 [default = SINGLE];
|
||||
|
||||
// IDs of the groups to which the content belongs. A group is a set of
|
||||
// content IDs. A particular piece of content may belong to multiple groups.
|
||||
repeated bytes group_ids = 12;
|
||||
// Key sequence for Widevine-managed keys. Optional.
|
||||
optional uint32 key_sequence = 12;
|
||||
|
||||
// Group identifiers for all groups to which the content belongs. This can
|
||||
// be used to deliver licenses to unlock multiple titles / channels.
|
||||
// Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, and
|
||||
// not in conjunction with key_ids.
|
||||
repeated bytes group_ids = 13;
|
||||
|
||||
// Copy/copies of the content key used to decrypt the media stream in which
|
||||
// the PSSH box is embedded, each wrapped with a different entitlement key.
|
||||
// May be repeated if using group entitlement keys. Optional, used for content
|
||||
// key rotation.
|
||||
repeated WrappedKey entitled_keys = 13;
|
||||
// May also contain sub-licenses to support devices with OEMCrypto 13 or
|
||||
// older. May be repeated if using group entitlement keys. Present only in
|
||||
// PSSHs of type ENTITLED_KEY.
|
||||
repeated EntitledKey entitled_keys = 14;
|
||||
|
||||
//////////////////////////// Deprecated Fields ////////////////////////////
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
AESCTR = 1;
|
||||
};
|
||||
optional Algorithm algorithm = 1 [deprecated = true];
|
||||
optional string provider = 3 [deprecated = true];
|
||||
optional string track_type = 5 [deprecated = true];
|
||||
optional string policy = 6 [deprecated = true];
|
||||
optional bytes grouped_license = 8 [deprecated = true];
|
||||
}
|
||||
|
||||
// Signed device certificate definition.
|
||||
|
||||
@@ -546,7 +546,9 @@ TEST_F(InitializationDataTest, HandlesMultipleWidevinePsshs) {
|
||||
EXPECT_EQ(kEntitledKeysWidevinePsshBoxData, entitled_init_data.data());
|
||||
}
|
||||
|
||||
TEST_F(InitializationDataTest, ExtractSubLicense) {
|
||||
// TODO(jfore): The pssh has changed in ways that are not compatible with
|
||||
//sublicenses. Restructure or remove sublicense support including this test.
|
||||
TEST_F(InitializationDataTest, DISABLED_ExtractSubLicense) {
|
||||
InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, kSubLicensePsshBox);
|
||||
ASSERT_FALSE(init_data.IsEmpty());
|
||||
std::vector<video_widevine::SubLicense> keys =
|
||||
|
||||
@@ -413,7 +413,9 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) {
|
||||
EXPECT_EQ(kNonce, license_request.key_control_nonce());
|
||||
}
|
||||
|
||||
TEST_F(SubLicenseTest, VerifySubSessionData) {
|
||||
// TODO(jfore): The pssh has changed in ways that are not compatible with
|
||||
//sublicenses. Restructure or remove sublicense support including this test.
|
||||
TEST_F(SubLicenseTest, DISABLED_VerifySubSessionData) {
|
||||
bool usage_information_support = true;
|
||||
CryptoSession::HdcpCapability current_hdcp_version = HDCP_NO_DIGITAL_OUTPUT;
|
||||
CryptoSession::HdcpCapability max_hdcp_version = HDCP_V2_1;
|
||||
|
||||
Reference in New Issue
Block a user