Files
android/libwvdrmengine/oemcrypto/test/oemcrypto_decrypt_test.cpp
John W. Bruce 5a4a8fdede Remove OEMCryptoLicenseTest.RejectCbc1API16
This test should have been removed in v17, when we allowed this pattern
to be used with cbcs. Although we can't start enforcing the correct
behavior until v20 now, we can remove enforcement of the incorrect
behavior.

Bug: 356173926
Merged from https://widevine-internal-review.googlesource.com/204832

Merged from https://widevine-internal-review.googlesource.com/204870

Change-Id: Idc6e3109286daabb83874d52ad3abaff5e14badb
2024-08-21 05:10:09 +00:00

662 lines
27 KiB
C++

// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine
// License Agreement.
//
#include "oemcrypto_decrypt_test.h"
#include "test_sleep.h"
using ::testing::Combine;
using ::testing::Range;
using ::testing::Values;
namespace wvoec {
// Cannot decrypt without first getting a key handle.
TEST_P(OEMCryptoLicenseTest, FailDecryptWithoutGettingAHandle) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// Cannot decrypt with an old key handle.
TEST_P(OEMCryptoLicenseTest, FailDecryptWithOldKeyHandle) {
Session donor_session;
LicenseRoundTrip license_messages2(&donor_session);
ASSERT_NO_FATAL_FAILURE(donor_session.open());
ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&donor_session));
ASSERT_NO_FATAL_FAILURE(license_messages2.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages2.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages2.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages2.LoadResponse());
ASSERT_NO_FATAL_FAILURE(donor_session.TestDecryptCTR());
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
// Inject the donor session's key handle into |session_| and then close the
// donor, which should render the handle invalid.
session_.key_handle() = donor_session.key_handle();
donor_session.close();
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// GetKeyHandle should fail if we attempt to select a key that has not been
// loaded. Also, the error should be NO_CONTENT_KEY. This test should pass for
// v15 devices, except that the exact error code was not specified until v16.
TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
const char* key_id = "no_key";
vector<uint8_t> key_handle;
OEMCryptoResult sts = GetKeyHandleIntoVector(
session_.session_id(), reinterpret_cast<const uint8_t*>(key_id),
strlen(key_id), OEMCrypto_CipherMode_CENC, key_handle);
if (sts != OEMCrypto_SUCCESS) {
EXPECT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, sts);
} else {
// Delayed error code. If select key was a success, then we should
// eventually see the error when we decrypt.
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
// Generate test data
for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256;
// Create the pattern description (always 0,0 for CTR)
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(),
&sample_description, 1, &pattern);
EXPECT_EQ(sts, OEMCrypto_ERROR_NO_CONTENT_KEY);
}
}
// 'cens' mode is no longer supported in v16
TEST_P(OEMCryptoLicenseTest, RejectCensAPI16) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
vector<uint8_t> key_handle;
OEMCryptoResult sts;
sts = GetKeyHandleIntoVector(session_.session_id(),
session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length,
OEMCrypto_CipherMode_CENC, key_handle);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
// Create a non-zero pattern to indicate this is 'cens'
OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(),
&sample_description, 1, &pattern);
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
}
TEST_P(OEMCryptoLicenseTest, RejectCbcsWithBlockOffset) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
vector<uint8_t> key_handle;
OEMCryptoResult sts;
sts = GetKeyHandleIntoVector(session_.session_id(),
session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length,
OEMCrypto_CipherMode_CBCS, key_handle);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
subsample_description.block_offset = 5; // Any value 1-15 will do.
// Create a non-zero pattern to indicate this is 'cbcs'.
OEMCrypto_CENCEncryptPatternDesc pattern = {1, 9};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(),
&sample_description, 1, &pattern);
EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts);
}
TEST_P(OEMCryptoLicenseTest, RejectOversizedBlockOffset) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
vector<uint8_t> key_handle;
OEMCryptoResult sts;
sts = GetKeyHandleIntoVector(session_.session_id(),
session_.license().keys[0].key_id,
session_.license().keys[0].key_id_length,
OEMCrypto_CipherMode_CENC, key_handle);
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
vector<uint8_t> in_buffer(256);
vector<uint8_t> out_buffer(in_buffer.size());
OEMCrypto_SampleDescription sample_description;
OEMCrypto_SubSampleDescription subsample_description;
GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description,
&subsample_description);
subsample_description.block_offset = 0xFF; // Anything 16+
// Create a zero pattern to indicate this is 'cenc'.
OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0};
// Try to decrypt the data
sts = OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(),
&sample_description, 1, &pattern);
EXPECT_NE(OEMCrypto_SUCCESS, sts);
// Try again with the minimum invalid value
subsample_description.block_offset = 16;
sts = OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(),
&sample_description, 1, &pattern);
EXPECT_NE(OEMCrypto_SUCCESS, sts);
}
TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) {
// Test parameterized by HDCP version.
DecryptWithHDCP(static_cast<OEMCrypto_HDCP_Capability>(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP,
Range(1, 6));
// If the license does not allow a hash, then we should not compute one.
TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) {
uint32_t hash_type = OEMCrypto_SupportsDecryptHash();
// If hash is not supported, or is vendor defined, don't try to test it.
if (hash_type != OEMCrypto_CRC_Clear_Buffer) return;
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
uint32_t frame_number = 1;
const uint32_t crc32 = 42;
// It is OK to set the hash before loading the keys
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_SetDecryptHash(session_.session_id(),
frame_number, crc32));
// It is OK to select the key and decrypt.
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR());
// But the error code should be bad.
ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE,
OEMCrypto_GetHashErrorCode(session_.session_id(), &frame_number));
}
// This test verifies OEMCrypto_SetDecryptHash for out of range frame number.
TEST_P(OEMCryptoLicenseTest, DecryptHashForOutOfRangeFrameNumber) {
const uint32_t frame_number = kHugeRandomNumber;
const uint32_t crc32 = 42;
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_SetDecryptHash(session_.session_id(), frame_number, crc32));
}
//
// Decrypt Tests -- these test Decrypt CTR mode only.
//
TEST_P(OEMCryptoLicenseTest, Decrypt) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR());
}
// Verify that a zero duration means infinite license duration.
TEST_P(OEMCryptoLicenseTest, DecryptZeroDuration) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = 0;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR());
}
TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) {
// This subsample size is larger than a few encrypt/skip patterns. Most
// test cases use a pattern length of 160, so we'll run through at least two
// full patterns if we have more than 320 -- round up to 400.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 400},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// When the pattern length is 10 blocks, there is a discrepancy between the
// HLS and the CENC standards for samples of size 160*N+16, for N = 1, 2, 3...
// We require the CENC standard for OEMCrypto, and let a layer above us break
// samples into pieces if they wish to use the HLS standard.
TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 160 + 16},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Test that a single block can be decrypted.
TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 16},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests the ability to decrypt multiple subsamples with no offset.
// There is no offset within the block, used by CTR mode.
TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{25, 160},
{50, 256},
{25, 160},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests an offset into the block for the second encrypted subsample.
// This should only work for CTR mode, for CBC mode an error is expected in
// the decrypt step.
// If this test fails for CTR mode, then it is probably handling the
// block_offset incorrectly.
TEST_P(OEMCryptoSessionTestsDecryptTests, EvenOffset) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{25, 8},
{25, 32},
{25, 50},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
// CTR Mode is self-inverse -- i.e. We can pick the encrypted data and
// compute the unencrypted data. By picking the encrypted data to be all 0,
// it is easier to re-encrypt the data and debug problems. Similarly, we
// pick an iv = 0.
memset(initial_iv_, 0, KEY_IV_SIZE);
TestSample& sample = samples_[0]; // There is only one sample in this test
sample.truth_buffer.assign(sample.description.buffers.input_data_length, 0);
ASSERT_NO_FATAL_FAILURE(EncryptData());
if (decrypt_inplace_) {
const size_t total_size = sample.description.buffers.input_data_length;
// In case of decrypt_inplace_, encrypted_buffer contains padded bytes
// which is used for buffer overrun validation. Do not copy the padded
// bytes to truth_buffer.
sample.truth_buffer.assign(sample.encrypted_buffer.begin(),
sample.encrypted_buffer.begin() + total_size);
} else {
sample.truth_buffer =
sample.encrypted_buffer; // truth_buffer_ = encrypted zero buffer.
}
// Run EncryptData to re-encrypt this buffer. For CTR mode, we should get
// back to zeros.
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// If the EvenOffset test passes, but this one doesn't, then DecryptCENC might
// be using the wrong definition of block offset. Adding the block offset to
// the block boundary should give you the beginning of the encrypted data.
// This should only work for CTR mode, for CBC mode, the block offset must be
// 0, so an error is expected in the decrypt step.
// Another way to view the block offset is with the formula:
// block_boundary + block_offset = beginning of subsample.
TEST_P(OEMCryptoSessionTestsDecryptTests, OddOffset) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{10, 50},
{10, 75},
{10, 75},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests that the algorithm used to increment the counter for
// AES-CTR mode is correct. There are two possible implementations:
// 1) increment the counter as if it were a 128 bit number,
// 2) increment the low 64 bits as a 64 bit number and leave the high bits
// alone.
// For CENC, the algorithm we should use is the second one. OpenSSL defaults to
// the first. If this test is not passing, you should look at the way you
// increment the counter. Look at the example code in ctr128_inc64 above.
// If you start with an IV of 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE, after you
// increment twice, you should get 0xFFFFFFFFFFFFFFFF0000000000000000.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) {
memcpy(initial_iv_,
wvutil::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE").data(),
KEY_IV_SIZE);
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 256},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests the case where an encrypted sample is not an even number of
// blocks. For CTR mode, the partial block is encrypted. For CBC mode the
// partial block should be a copy of the clear data.
TEST_P(OEMCryptoSessionTestsDecryptTests, PartialBlock) {
// Note: for more complete test coverage, we want a sample size that is in
// the encrypted range for some tests, e.g. (3,7), and in the skip range for
// other tests, e.g. (7, 3). 3*16 < 50 and 7*16 > 50.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 50},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Based on the resource rating, OEMCrypto should be able to handle the maximum
// amount of data that can be passed to it. This is the lesser of:
//
// 1) The maximum total sample size
// 2) The maximum number of subsamples multiplied by the maximum subsample size
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSampleAPI16) {
const size_t max_sample_size = GetResourceValue(kMaxSampleSize);
const size_t max_subsample_size = GetResourceValue(kMaxSubsampleSize);
const size_t max_num_subsamples = GetResourceValue(kMaxNumberSubsamples);
// The +1 on this line ensures that, even in cases where max_sample_size is
// not evenly divisible by max_num_subsamples and thus the division gets
// truncated, (max_num_subsamples * subsample_size) will be greater than
// max_sample_size.
const size_t subsample_size =
std::min(max_sample_size / max_num_subsamples + 1, max_subsample_size);
size_t bytes_remaining = max_sample_size;
std::vector<SubsampleSize> subsample_sizes;
while (bytes_remaining > 0 && subsample_sizes.size() < max_num_subsamples) {
const size_t this_subsample_size =
std::min(subsample_size, bytes_remaining);
const size_t clear_size = this_subsample_size / 2;
const size_t encrypted_size = this_subsample_size - clear_size;
subsample_sizes.push_back({clear_size, encrypted_size});
bytes_remaining -= this_subsample_size;
}
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes(std::move(subsample_sizes)));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryCheckDecryptCENCStatusForHugeNumberOfSubSamples) {
size_t number_of_subsamples = 10000;
std::vector<SubsampleSize> subsample_sizes;
while (number_of_subsamples-- > 0) {
subsample_sizes.push_back({100, 100});
}
SetSubsampleSizes(std::move(subsample_sizes));
LoadLicense();
MakeBuffers();
EncryptData();
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
OEMCryptoResult result =
OEMCrypto_DecryptCENC(key_handle_.data(), key_handle_.size(),
sample_descriptions.data(), 1, &pattern_);
LOGD("Large number of subsamples test has return code %d", result);
}
TEST_P(OEMCryptoSessionTestsDecryptTests,
OEMCryptoMemoryCheckDecryptCENCStatusForHugeSubSample) {
std::vector<SubsampleSize> subsample_sizes;
subsample_sizes.push_back({100000, 100000});
SetSubsampleSizes(std::move(subsample_sizes));
LoadLicense();
MakeBuffers();
EncryptData();
// Build an array of just the sample descriptions.
std::vector<OEMCrypto_SampleDescription> sample_descriptions;
sample_descriptions.reserve(samples_.size());
for (TestSample& sample : samples_) {
// This must be deferred until this point in case the test modifies the
// buffer before testing decrypt.
sample.description.buffers.input_data = sample.encrypted_buffer.data();
// Append to the description array.
sample_descriptions.push_back(sample.description);
}
OEMCryptoResult result =
OEMCrypto_DecryptCENC(key_handle_.data(), key_handle_.size(),
sample_descriptions.data(), 1, &pattern_);
LOGD("Large subsample test has return code %d", result);
}
// Based on the resource rating, OEMCrypto should be able to handle the maximum
// subsample size.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) {
const size_t max = GetResourceValue(kMaxSubsampleSize);
const size_t half_max = max / 2;
// This test assumes that the maximum sample size is always more than three
// times the maximum subsample size.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{max, 0},
{0, max},
{half_max, max - half_max},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptZeroSizeSubSample) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{10, 10},
{0, 0},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(DecryptCENC());
}
// There are probably no frames this small, but we should handle them anyway.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{5, 5},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Test the case where there is only a clear subsample and no encrypted
// subsample.
TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) {
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{256, 0},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests the ability to decrypt multiple samples at once.
TEST_P(OEMCryptoSessionTestsDecryptTests, MultipleSamples) {
ASSERT_NO_FATAL_FAILURE(SetSampleSizes({
{
{52, 160},
{25, 256},
{25, 320},
},
{
{300, 64},
{50, 160},
{2, 160},
{24, 160},
{128, 256},
},
{
{70, 320},
{160, 160},
},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// This tests that calling OEMCrypto_Idle and OEMCrypto_Wake once or multiple
// times doesn't break anything.
TEST_P(OEMCryptoSessionTestsDecryptTests, IdleAndWake) {
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
}
// This tests that after an idle and a wake, decryption can continue in an
// open session.
TEST_P(OEMCryptoSessionTestsDecryptTests, ContinueDecryptionAfterIdleAndWake) {
// This subsample size is larger than a few encrypt/skip patterns. Most
// test cases use a pattern length of 160, so we'll run through at least two
// full patterns if we have more than 320 -- round up to 400.
ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({
{0, 400},
}));
ASSERT_NO_FATAL_FAILURE(LoadLicense());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
FreeSecureBuffers();
// Set state to idle then wake again and try to reencrypt/decrypt
ASSERT_NO_FATAL_FAILURE(
OEMCrypto_Idle(OEMCrypto_IdleState::OEMCrypto_CpuSuspend, 0));
ASSERT_NO_FATAL_FAILURE(OEMCrypto_Wake());
ASSERT_NO_FATAL_FAILURE(MakeBuffers());
ASSERT_NO_FATAL_FAILURE(EncryptData());
ASSERT_NO_FATAL_FAILURE(TestDecryptCENC());
}
// Used to construct a specific pattern.
constexpr OEMCrypto_CENCEncryptPatternDesc MakePattern(size_t encrypt,
size_t skip) {
return {encrypt, skip};
}
INSTANTIATE_TEST_SUITE_P(
CTRTests, OEMCryptoSessionTestsDecryptTests,
Combine(Values(MakePattern(0, 0)), Values(OEMCrypto_CipherMode_CENC),
::testing::ValuesIn(global_features.GetOutputTypes())));
// Decrypt in place for CBC tests was only required in v13.
INSTANTIATE_TEST_SUITE_P(
CBCTestsAPI14, OEMCryptoSessionTestsDecryptTests,
Combine(
Values(MakePattern(3, 7), MakePattern(9, 1),
// HLS edge cases. We should follow the CENC spec, not HLS spec.
MakePattern(1, 9), MakePattern(1, 0),
// AV1 patterns not already covered above.
MakePattern(5, 5), MakePattern(10, 0)),
Values(OEMCrypto_CipherMode_CBCS),
::testing::ValuesIn(global_features.GetOutputTypes())));
// A request to decrypt data to a clear buffer when the key control block
// requires a secure data path.
TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.set_control(wvoec::kControlObserveDataPath |
wvoec::kControlDataPathSecure);
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE));
}
// Test that key duration is honored.
TEST_P(OEMCryptoLicenseTest, KeyDuration) {
ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest());
license_messages_.core_response()
.timer_limits.total_playback_duration_seconds = kDuration;
ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse());
ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse());
ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse());
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS));
wvutil::TestSleep::Sleep(kShortSleep); // Should still be valid key.
ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false, OEMCrypto_SUCCESS));
wvutil::TestSleep::Sleep(kLongSleep); // Should be expired key.
ASSERT_NO_FATAL_FAILURE(
session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED));
ASSERT_NO_FATAL_FAILURE(session_.TestGetKeyHandleExpired(0));
}
INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseTest,
Range<uint32_t>(kCurrentAPI - 2, kCurrentAPI + 1));
} // namespace wvoec