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:
Fred Gylys-Colwell
2018-07-01 19:41:58 -07:00
parent 6000f834a0
commit d36ab56575
6 changed files with 104 additions and 99 deletions

View File

@@ -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,

View File

@@ -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)

View File

@@ -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(

View File

@@ -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.

View File

@@ -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 =

View File

@@ -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;