Combined Decrypt Calls

(This is a merge of http://go/wvgerrit/93829,
http://go/wvgerrit/93830, http://go/wvgerrit/93832,
http://go/wvgerrit/93833, and http://go/wvgerrit/93834 from the
Widevine repo.)

This implements the CDM code changes necessary to take advantage of
Combined Decrypt Calls on OEMCrypto v16. The result of this is that
WVCryptoPlugin is much lighter now because it can pass the full sample
down to the core in one call, but CryptoSession is heavier, as it now
has to handle more complex fallback logic when devices can't handle
multiple subsamples at once.

This patch also removes support for the 'cens' and 'cbc1' schema, which
are being dropped in OEMCrypto v16. This fixes an overflow in the code
for handling those schemas by removing it entirely.

This patch also fixes the "in chunks" legacy decrypt path to use larger
chunk sizes on devices with higher resource rating tiers.

Bug: 135285640
Bug: 123435824
Bug: 138584971
Bug: 139257871
Bug: 78289910
Bug: 149361893
Test: no new CE CDM Unit Test failures
Test: Google Play plays
Test: Netflix plays
Test: no new GTS failures
Change-Id: Ic4952c9fa3bc7fd5ed08698e88254380a7a18514
This commit is contained in:
John W. Bruce
2020-02-18 14:46:31 -08:00
parent 3708c4d53f
commit a62886b925
28 changed files with 1253 additions and 1260 deletions

View File

@@ -258,8 +258,9 @@ class CdmEngine {
// Decryption and key related methods
// Accept encrypted buffer and return decrypted data.
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters);
virtual CdmResponseType DecryptV16(
const CdmSessionId& session_id,
const CdmDecryptionParametersV16& parameters);
// Generic crypto operations - provides basic crypto operations that an
// application can use outside of content stream processing

View File

@@ -251,12 +251,17 @@ class CdmEngineMetricsImpl : public T {
return status;
}
CdmResponseType Decrypt(const CdmSessionId& session_id,
const CdmDecryptionParameters& parameters) override {
CdmResponseType DecryptV16(
const CdmSessionId& session_id,
const CdmDecryptionParametersV16& parameters) override {
size_t total_size = 0;
for (const CdmDecryptionSample& sample : parameters.samples) {
total_size += sample.encrypt_buffer_length;
}
CdmResponseType sts;
M_TIME(sts = T::Decrypt(session_id, parameters), metrics_,
cdm_engine_decrypt_, sts,
metrics::Pow2Bucket(parameters.encrypt_length));
M_TIME(sts = T::DecryptV16(session_id, parameters), metrics_,
cdm_engine_decrypt_, sts, metrics::Pow2Bucket(total_size));
return sts;
}

View File

@@ -107,7 +107,7 @@ class CdmSession {
virtual CdmResponseType QueryOemCryptoSessionId(CdmQueryMap* query_response);
// Decrypt() - Accept encrypted buffer and return decrypted data.
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
virtual CdmResponseType Decrypt(const CdmDecryptionParametersV16& parameters);
// License renewal
// GenerateRenewalRequest() - Construct valid renewal request for the current

View File

@@ -36,7 +36,6 @@ class ContentKeySession : public KeySession {
const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
CdmCipherMode* cipher_mode,
const std::string& srm_requirement) override;
OEMCryptoResult LoadEntitledContentKeys(
@@ -50,16 +49,15 @@ class ContentKeySession : public KeySession {
// Decrypt for ContentKeySession
OEMCryptoResult Decrypt(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc& buffer_descriptor, size_t additional_offset,
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) override;
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc& pattern) override;
protected:
virtual OEMCryptoResult LoadKeysAsLicenseType(
const std::string& message, const std::string& signature,
const std::string& mac_key_iv, const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token, CdmCipherMode* cipher_mode,
const std::string& provider_session_token,
const std::string& srm_requirement, OEMCrypto_LicenseType license_type);
CryptoSessionId oec_session_id_;

View File

@@ -156,7 +156,7 @@ class CryptoSession {
const std::string& wrapped_key);
// Media data path
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& params);
virtual CdmResponseType Decrypt(const CdmDecryptionParametersV16& params);
virtual bool IsAntiRollbackHwPresent();
@@ -300,6 +300,7 @@ class CryptoSession {
CdmResponseType GetSystemIdInternal(uint32_t* system_id);
CdmResponseType GenerateRsaSignature(const std::string& message,
std::string* signature);
bool GetMaxSubsampleRegionSize(size_t* max);
bool SetDestinationBufferType();
@@ -314,17 +315,24 @@ class CryptoSession {
CdmEncryptionAlgorithm algorithm);
size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm);
// These methods are used when a subsample exceeds the maximum buffer size
// that the device can handle.
OEMCryptoResult CopyBufferInChunks(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc buffer_descriptor);
OEMCryptoResult DecryptInChunks(
const CdmDecryptionParameters& params,
const OEMCrypto_DestBufferDesc& full_buffer_descriptor,
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor,
size_t max_chunk_size);
static void IncrementIV(uint64_t increase_by, std::vector<uint8_t>* iv_out);
// These methods fall back into each other in the order given, depending on
// how much data they were given and how much data OEMCrypto can accept in one
// call.
OEMCryptoResult DecryptMultipleSamples(
const std::vector<OEMCrypto_SampleDescription>& samples,
CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern);
OEMCryptoResult DecryptSample(
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern);
OEMCryptoResult LegacyDecrypt(
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern);
OEMCryptoResult LegacyCopyBufferInChunks(
const OEMCrypto_SampleDescription& sample, size_t max_chunk_size);
OEMCryptoResult LegacyDecryptInChunks(
const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode,
const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size);
// These methods should be used to take the various CryptoSession mutexes in
// preference to taking the mutexes directly.
@@ -420,7 +428,6 @@ class CryptoSession {
std::string request_id_;
static std::atomic<uint64_t> request_id_index_source_;
CdmCipherMode cipher_mode_;
uint32_t api_version_;
// In order to avoid creating a deadlock if instantiation needs to take any

View File

@@ -30,7 +30,6 @@ class EntitlementKeySession : public ContentKeySession {
const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
CdmCipherMode* cipher_mode,
const std::string& srm_requirement) override;
OEMCryptoResult LoadEntitledContentKeys(
const std::vector<CryptoKey>& keys) override;

View File

@@ -32,16 +32,14 @@ class KeySession {
const std::string& mac_key,
const std::vector<CryptoKey>& keys,
const std::string& provider_session_token,
CdmCipherMode* cipher_mode,
const std::string& srm_requirement) = 0;
virtual OEMCryptoResult LoadEntitledContentKeys(
const std::vector<CryptoKey>& keys) = 0;
virtual OEMCryptoResult SelectKey(const std::string& key_id,
CdmCipherMode cipher_mode) = 0;
virtual OEMCryptoResult Decrypt(
const CdmDecryptionParameters& params,
OEMCrypto_DestBufferDesc& buffer_descriptor, size_t additional_offset,
const OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0;
const OEMCrypto_SampleDescription* samples, size_t samples_length,
const OEMCrypto_CENCEncryptPatternDesc& pattern) = 0;
protected:
metrics::CryptoMetrics* metrics_;

View File

@@ -404,6 +404,10 @@ enum CdmResponseType {
CORE_MESSAGE_NOT_FOUND = 350,
LOAD_LICENSE_ERROR = 351,
LOAD_RENEWAL_ERROR = 352,
CANNOT_DECRYPT_ZERO_SAMPLES = 353,
CANNOT_DECRYPT_ZERO_SUBSAMPLES = 354,
SAMPLE_AND_SUBSAMPLE_SIZE_MISMATCH = 355,
INVALID_IV_SIZE = 356,
// Don't forget to add new values to
// * core/test/test_printers.cpp.
// * android/include/mapErrors-inl.h
@@ -581,11 +585,13 @@ class CdmKeyAllowedUsage {
bool valid_;
};
// For schemes that do not use pattern encryption (cenc and cbc1), encrypt
// and skip should be set to 0. For those that do (cens and cbcs), it is
// recommended that encrypt+skip bytes sum to 10 and for cbcs that a 1:9
// encrypt:skip ratio be used. See ISO/IEC DIS 23001-7, section 10.4.2 for
// more information.
// For schemes that do not use pattern encryption (cenc), encrypt and skip
// must be set to 0. For those that do (cbcs), it is recommended that
// encrypt+skip bytes sum to 10. See ISO/IEC DIS 23001-7, section 10.4.2 for
// more information. For Widevine, we often use the pattern (1,0) to represent
// "encrypt all blocks," but in content, the patterns (0,0) and (10,0) are more
// common. Since we are constrained to use (0,0) for "do not use pattern
// encryption" — as noted above — we commonly use (1,0) for this case instead.
struct CdmCencPatternEncryptionDescriptor {
size_t encrypt_blocks; // number of 16 byte blocks to decrypt
size_t skip_blocks; // number of 16 byte blocks to leave in clear
@@ -640,6 +646,119 @@ struct CdmDecryptionParameters {
is_video(true) {}
};
struct CdmDecryptionSubsample {
size_t clear_bytes;
size_t protected_bytes;
// TODO(b/149524614): These fields are not necessary except for
// backwards-compatibility while we are transitioning from the v15 API to the
// v16 API.
uint8_t flags;
size_t block_offset;
CdmDecryptionSubsample()
: clear_bytes(0), protected_bytes(0), flags(0), block_offset(0) {}
CdmDecryptionSubsample(size_t clear_param, size_t protected_param)
: clear_bytes(clear_param),
protected_bytes(protected_param),
flags(0),
block_offset(0) {}
};
struct CdmDecryptionSample {
const uint8_t* encrypt_buffer;
size_t encrypt_buffer_length;
void* decrypt_buffer;
size_t decrypt_buffer_size;
size_t decrypt_buffer_offset;
std::vector<CdmDecryptionSubsample> subsamples;
std::vector<uint8_t> iv;
CdmDecryptionSample()
: encrypt_buffer(nullptr),
encrypt_buffer_length(0),
decrypt_buffer(nullptr),
decrypt_buffer_size(0),
decrypt_buffer_offset(0),
subsamples(),
iv() {}
CdmDecryptionSample(const uint8_t* encrypt_buffer_param,
void* decrypt_buffer_param,
size_t decrypt_buffer_offset_param, size_t length,
const std::vector<uint8_t>& iv_param)
: encrypt_buffer(encrypt_buffer_param),
encrypt_buffer_length(length),
decrypt_buffer(decrypt_buffer_param),
decrypt_buffer_size(length),
decrypt_buffer_offset(decrypt_buffer_offset_param),
subsamples(),
iv(iv_param) {}
};
// TODO(b/149524614): This name is a temporary measure for
// backwards-compatibility while we are transitioning from the v15 API to the
// v16 API.
struct CdmDecryptionParametersV16 {
KeyId key_id;
std::vector<CdmDecryptionSample> samples;
bool is_secure;
CdmCipherMode cipher_mode;
bool is_video;
CdmCencPatternEncryptionDescriptor pattern;
// TODO(b/149524614): These field is not necessary except for
// backwards-compatibility while we are transitioning from the v15 API to the
// v16 API.
bool observe_legacy_fields;
CdmDecryptionParametersV16()
: key_id(),
samples(),
is_secure(true),
cipher_mode(kCipherModeCtr),
is_video(true),
pattern(),
observe_legacy_fields(false) {}
CdmDecryptionParametersV16(const KeyId& key_id_param)
: key_id(key_id_param),
samples(),
is_secure(true),
cipher_mode(kCipherModeCtr),
is_video(true),
pattern(),
observe_legacy_fields(false) {}
// TODO(b/149524614): This method is a temporary measure for
// backwards-compatibility while we are transitioning from the v15 API to the
// v16 API.
static CdmDecryptionParametersV16 from_v15(
const CdmDecryptionParameters& v15_params) {
CdmDecryptionParametersV16 new_params;
new_params.key_id = *(v15_params.key_id);
new_params.is_secure = v15_params.is_secure;
new_params.cipher_mode = v15_params.cipher_mode;
new_params.is_video = v15_params.is_video;
new_params.pattern = v15_params.pattern_descriptor;
new_params.observe_legacy_fields = true;
new_params.samples.emplace_back();
CdmDecryptionSample& sample = new_params.samples.back();
sample.encrypt_buffer = v15_params.encrypt_buffer;
sample.encrypt_buffer_length = v15_params.encrypt_length;
sample.decrypt_buffer = v15_params.decrypt_buffer;
sample.decrypt_buffer_size = v15_params.decrypt_buffer_length;
sample.decrypt_buffer_offset = v15_params.decrypt_buffer_offset;
sample.iv = *(v15_params.iv);
sample.subsamples.emplace_back();
CdmDecryptionSubsample& subsample = sample.subsamples.back();
if (v15_params.is_encrypted) {
subsample.protected_bytes = v15_params.encrypt_length;
} else {
subsample.clear_bytes = v15_params.encrypt_length;
}
subsample.flags = v15_params.subsample_flags;
subsample.block_offset = v15_params.block_offset;
return new_params;
}
};
struct CdmKeyRequest {
CdmKeyMessage message;
CdmKeyRequestType type;