// 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)); } // SelectKey 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 key_handle; OEMCryptoResult sts = GetKeyHandleIntoVector( session_.session_id(), reinterpret_cast(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 in_buffer(256); vector 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 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 in_buffer(256); vector 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); } // 'cbc1' mode is no longer supported in v16 TEST_P(OEMCryptoLicenseTest, RejectCbc1API16) { 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 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 in_buffer(256); vector out_buffer(in_buffer.size()); OEMCrypto_SampleDescription sample_description; OEMCrypto_SubSampleDescription subsample_description; GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description, &subsample_description); // Create a zero pattern to indicate this is 'cbc1' 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(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 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 in_buffer(256); vector 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 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 in_buffer(256); vector 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(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; uint32_t hash = 42; // It is OK to set the hash before loading the keys ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_SetDecryptHash(session_.session_id(), frame_number, reinterpret_cast(&hash), sizeof(hash))); // 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) { uint32_t frame_number = kHugeRandomNumber; uint32_t hash = 42; ASSERT_NO_FATAL_FAILURE(OEMCrypto_SetDecryptHash( session_.session_id(), frame_number, reinterpret_cast(&hash), sizeof(hash))); } // // 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 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(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 subsample_sizes; while (number_of_subsamples-- > 0) { subsample_sizes.push_back({100, 100}); } SetSubsampleSizes(subsample_sizes); LoadLicense(); MakeBuffers(); EncryptData(); // Build an array of just the sample descriptions. std::vector 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 subsample_sizes; subsample_sizes.push_back({100000, 100000}); SetSubsampleSizes(subsample_sizes); LoadLicense(); MakeBuffers(); EncryptData(); // Build an array of just the sample descriptions. std::vector 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()); } // 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(kCurrentAPI - 2, kCurrentAPI + 1)); } // namespace wvoec