// 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_license_test.h" #include "platform.h" #include "test_sleep.h" using ::testing::Range; namespace wvoec { /// @addtogroup license /// @{ // Function to test APIs that expect a buffer length as input // by passing huge buffer lengths up to end_buffer_length and test that the API // doesn't crash. void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, size_t start_buffer_length, size_t end_buffer_length, bool check_status) { OEMCryptoResult sts = OEMCrypto_SUCCESS; for (size_t buffer_length = start_buffer_length; buffer_length < end_buffer_length && (sts == OEMCrypto_SUCCESS || sts == OEMCrypto_ERROR_SHORT_BUFFER || !check_status); buffer_length *= 2) { sts = f(buffer_length); if (check_status && sts != OEMCrypto_SUCCESS && sts != OEMCrypto_ERROR_SHORT_BUFFER) { LOGI("Test exits huge buffer loop for length:%zu, status:%d", buffer_length, sts); } } } // Function to test APIs that expect a buffer length as input // by passing huge buffer lengths up to kHugeInputBufferLength and test that // the API doesn't crash. void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status) { TestHugeLengthDoesNotCrashAPI(f, 1, kHugeInputBufferLength, check_status); } // This test verifies that OEMCrypto can load the total number of keys required // for the reported resource level. void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session) { const size_t max_total_keys = GetResourceValue(kMaxTotalKeys); ASSERT_LE(num_keys_per_session, kMaxNumKeys) << "Update test constants."; std::vector> sessions; std::vector> licenses; size_t total_keys = 0; for (size_t i = 0; total_keys < max_total_keys; i++) { sessions.push_back(std::unique_ptr(new Session())); licenses.push_back(std::unique_ptr( new LicenseRoundTrip(sessions[i].get()))); const size_t num_keys = std::min(max_total_keys - total_keys, num_keys_per_session); licenses[i]->set_num_keys(static_cast(num_keys)); total_keys += num_keys; ASSERT_NO_FATAL_FAILURE(sessions[i]->open()); ASSERT_NO_FATAL_FAILURE(util->InstallTestDrmKey(sessions[i].get())); ASSERT_NO_FATAL_FAILURE(licenses[i]->SignAndVerifyRequest()); } for (size_t i = 0; i < licenses.size(); i++) { ASSERT_NO_FATAL_FAILURE(licenses[i]->CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(licenses[i]->EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, licenses[i]->LoadResponse()); } constexpr bool kSelectKeyFirst = true; for (size_t i = 0; i < licenses.size(); i++) { for (size_t key_index = 0; key_index < licenses[i]->num_keys(); key_index++) { ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR( kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); } } // Second call to decrypt for each session. for (size_t i = 0; i < licenses.size(); i++) { for (size_t key_index = 0; key_index < licenses[i]->num_keys(); key_index++) { ASSERT_NO_FATAL_FAILURE(sessions[i]->TestDecryptCTR( kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); } } } TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryPrepareLicenseRequestForHugeRequestMessageLength) { TestPrepareLicenseRequestForHugeBufferLengths( [](size_t message_size, LicenseRoundTrip* license_messages) { license_messages->set_message_size(message_size); }, kCheckStatus); } TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryPrepareLicenseRequestForHugeCoreMessageLength) { TestPrepareLicenseRequestForHugeBufferLengths( [](size_t core_message_size, LicenseRoundTrip* license_messages) { license_messages->set_core_message_size(core_message_size); }, kCheckStatus); } TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryPrepareLicenseRequestForHugeSignatureLength) { // There is a limit of signature length that gets validated. Hence not // checking status as we would like to test it for all possible signature // lengths. TestPrepareLicenseRequestForHugeBufferLengths( [](size_t length, LicenseRoundTrip* license_messages) { license_messages->set_request_signature_size(length); }, !kCheckStatus); } // Verify that a license may be signed. TEST_P(OEMCryptoLicenseTest, SignLicenseRequest) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); } // Verify that a large license request may be signed. TEST_P(OEMCryptoLicenseTest, SignLargeLicenseRequest) { const size_t max_size = GetResourceValue(kLargeMessageSize); license_messages_.set_message_size(max_size); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); } // Verify that a license may be loaded without a nonce. TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(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(true, OEMCrypto_SUCCESS)); } // Verify that a preloaded license may be loaded without first signing the // request. This test is important for the preloaded licenses used by ATSC and // CAS. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) { if (license_api_version_ > global_features.api_version) { // We should not attempt to preload a license with an API higher than that // of OEMCrypto. license_api_version_ = global_features.api_version; license_messages_.set_api_version(license_api_version_); } license_messages_.set_control(0); // Notice that we do not call SignAndVerifyRequest -- we do not need a request // in order to generate a response for a preloaded license. // The test code uses the core request to create the core response. license_messages_.core_request().api_major_version = global_features.api_version; license_messages_.core_request().api_minor_version = 0; ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // Load license in a different session, which did not create the request. Session session2; ASSERT_NO_FATAL_FAILURE(session2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session2)); ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2)); ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS)); } // Verify that a license may be reloaded without a nonce, but with a nonzero // rental duration. In order to start the rental clock, we sign a placeholder // license instead. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequestRentalDuration) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(0); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // It is not recommended for a license without a nonce to have a nonzero // rental duration. But there are content providers that have licenses with // this policy. license_messages_.core_response().timer_limits.rental_duration_seconds = kDuration; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // Load license in a different session, which did not create the request. Session session2; ASSERT_NO_FATAL_FAILURE(session2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session2)); // However, in order to start the rental clock, we have to sign something. So // we will sign a placeholder license request. LicenseRoundTrip dummy_license(&session2); ASSERT_NO_FATAL_FAILURE(dummy_license.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2)); ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS)); } // Verify that a license may be loaded with a nonce. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) { 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(true, OEMCrypto_SUCCESS)); } // Verify that a second license may not be loaded in a session. TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonceTwiceAPI16) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(0); license_messages_.skip_nonce_check(); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); // A second load, should NOT succeed. ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); } // Verify that a second license may not be loaded in a session. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonceTwiceAPI16) { 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()); // A second load, should NOT succeed. ASSERT_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); } // This tests load license with an 8k license response. TEST_P(OEMCryptoLicenseTest, LoadKeyLargeBuffer) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); const size_t max_size = GetResourceValue(kLargeMessageSize); license_messages_.set_message_size(max_size); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } //---------------------------------------------------------------------------// //---------------------------------------------------------------------------// // Each of the following LoadKeyWithBadRange_* tests is similar. They verify // that OEMCrypto_LoadLicense checks the range of all the pointers. It should // reject a message if the pointer does not point into the message buffer. //---------------------------------------------------------------------------// //---------------------------------------------------------------------------// TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().enc_mac_keys.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_enc_mac_keys_iv) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().enc_mac_keys_iv.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_id) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[0].key_id.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[1].key_data.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_data_iv) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[1].key_data_iv.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[2].key_control.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_key_control_iv) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().key_array[2].key_control_iv.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadRange_pst) { license_messages_.set_control(wvoec::kControlNonceOrEntry); license_messages_.set_pst("my_pst"); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // See the comment in LicenseRoundTrip::LoadResponse for why we increment by // the message size. license_messages_.core_response().pst.offset += sizeof(license_messages_.response_data()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // If we have a pst, then we need a usage entry. ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } //---------------------------------------------------------------------------// //---------------------------------------------------------------------------// // Test that LoadKeys fails when a key is loaded with no key control block. TEST_P(OEMCryptoLicenseTest, LoadKeyWithNullKeyControl) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_response().key_array[2].key_control.offset = 0; license_messages_.core_response().key_array[2].key_control.length = 0; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when a key's nonce is wrong. TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce ^= 42; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when the core message's nonce is wrong. TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce2) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().nonce ^= 42; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when the core message's session is wrong. TEST_F(OEMCryptoLicenseTestAPI16, LoadKeyWithBadNonce3) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().session_id++; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // Verify that LoadKeys fails when an attempt is made to use a nonce twice. TEST_P(OEMCryptoLicenseTest, LoadKeyWithRepeatNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); const uint32_t nonce = session_.nonce(); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); // This is the first attempt. It should succeed. ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); // Now, open a new session and try to load a license with the same nonce. session_.close(); ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session_)); license_messages_.skip_nonce_check(); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); // Repeat the nonce. license_messages_.core_request().nonce = nonce; for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce = htonl(nonce); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // This tests that a nonce cannot be used in new session. This is similar to // the previous test, but does not use the nonce in the first session. The nonce // should be tied to a session, so generating a nonce in the first session and // then using it in the second session should fail. TEST_P(OEMCryptoLicenseTest, LoadKeyNonceReopenSession) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); uint32_t nonce = session_.nonce(); // Do not use the nonce now. Close session and use it after re-opening. ASSERT_NO_FATAL_FAILURE(session_.close()); // Actually, this isn't the same session. OEMCrypto opens a new session, but // we are guarding against the possibility that it re-uses the session data // and might not clear out the nonce correctly. ASSERT_NO_FATAL_FAILURE(session_.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session_)); license_messages_.skip_nonce_check(); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().nonce = nonce; for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce = htonl(nonce); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // This tests that a nonce cannot be used in wrong session. This is similar to // the previous test, except we do not close session 1 before we open session 2. TEST_P(OEMCryptoLicenseTest, LoadKeyNonceWrongSession) { // First, open a session and generate a nonce. We don't use the nonce in this // session. Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s2)); ASSERT_NO_FATAL_FAILURE(s2.GenerateNonce()); uint32_t nonce = s2.nonce(); // Do not use the nonce. Also, leave the session open. We want to make sure // that session_ and s2 do NOT share a nonce. This is different from // the LoadKeyNonceReopenSession in that we do not close s1. ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.core_request().nonce = nonce; for (unsigned int i = 0; i < license_messages_.num_keys(); i++) license_messages_.response_data().keys[i].control.nonce = htonl(nonce); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_INVALID_NONCE, license_messages_.LoadResponse()); } // LoadKeys should fail if the key control block as a bad verification string. TEST_P(OEMCryptoLicenseTest, LoadKeyWithBadVerification) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); license_messages_.response_data().keys[1].control.verification[2] = 'Z'; ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // This test verifies that LoadKeys still works when the message is not aligned // in memory on a word (2 or 4 byte) boundary. TEST_P(OEMCryptoLicenseTest, LoadKeyUnalignedMessageAPI16) { license_messages_.skip_request_hash(); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); std::vector buffer(1, '0'); // A string of 1 byte long. size_t offset = buffer.size(); ASSERT_EQ(1u, offset); // We assume that vectors are allocated on as a small chunk of data that is // aligned on a word boundary. I.e. we assume buffer is word aligned. Next, // we append the message to buffer after the single padding byte. buffer.insert(buffer.end(), license_messages_.encrypted_response_buffer().begin(), license_messages_.encrypted_response_buffer().end()); // Thus, buffer[offset] is NOT word aligned. const uint8_t* unaligned_message = &buffer[offset]; const std::vector context = session_.GetDefaultContext(); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadLicense( session_.session_id(), context.data(), context.size(), session_.enc_session_key().data(), session_.enc_session_key().size(), unaligned_message, license_messages_.encrypted_response_buffer().size(), license_messages_.serialized_core_message().size(), license_messages_.response_signature().data(), license_messages_.response_signature().size())); } // Verifies that a session can't reload a license without being closed and // reopened. TEST_P(OEMCryptoLicenseTest, LoadLicenseAgainFailureAPI16) { 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_EQ(OEMCrypto_ERROR_LICENSE_RELOAD, license_messages_.LoadResponse()); } TEST_P(OEMCryptoLicenseTest, LoadKeysBadSignatureAPI16) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); license_messages_.response_signature()[0] ^= 42; ASSERT_EQ(OEMCrypto_ERROR_SIGNATURE_FAILURE, license_messages_.LoadResponse()); } // LoadKeys should fail if we try to load keys with no keys. TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(0); license_messages_.set_num_keys(0); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } // Like the previous test, except we ask for a nonce first. TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeyWithNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_num_keys(0); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } /// @} /// @addtogroup security /// @{ // Following two tests will test huge values for num bytes clear, num bytes // encrypted, input data length and clear address, clear address_length. TEST_P(OEMCryptoLicenseTest, OEMCryptoMemoryDecryptCENCForHugeNumBytesClearAndBuffers) { TestDecryptCENCForHugeBufferLengths( [](size_t message_size, OEMCrypto_SampleDescription* sample_description) { OEMCrypto_SubSampleDescription* sub_samples = const_cast( sample_description->subsamples); sub_samples[0].num_bytes_clear = sub_samples[0].num_bytes_clear + message_size; }, !kCheckStatus); } TEST_P(OEMCryptoLicenseTest, DecryptCENCForNumBytesClearPlusEncryptedOverflowsSize) { LoadLicense(); vector key_handle; GetKeyHandleIntoVector(session_.session_id(), session_.license().keys[0].key_id, session_.license().keys[0].key_id_length, OEMCrypto_CipherMode_CENC, key_handle); size_t input_buffer_size = 1; vector in_buffer(input_buffer_size); vector out_buffer(in_buffer.size()); OEMCrypto_SampleDescription sample_description; OEMCrypto_SubSampleDescription subsample_description; GenerateSimpleSampleDescription(in_buffer, out_buffer, &sample_description, &subsample_description); OEMCrypto_SubSampleDescription* sub_samples = const_cast( sample_description.subsamples); // If Decrypt cenc API does not check for overflow on clear + encrypted // addition operation. This will result in 1 which will match with input data // length, which causes validation to pass. sub_samples[0].num_bytes_clear = 2; sub_samples[0].num_bytes_encrypted = ~0; // Create the pattern description (always 0,0 for CTR) OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; // Try to decrypt the data ASSERT_NE(OEMCrypto_SUCCESS, OEMCrypto_DecryptCENC(key_handle.data(), key_handle.size(), &sample_description, 1, &pattern)); } TEST_P(OEMCryptoLicenseTest, OEMCryptoMemoryDecryptCENCForHugeNumBytesEncryptedAndBuffers) { TestDecryptCENCForHugeBufferLengths( [](size_t message_size, OEMCrypto_SampleDescription* sample_description) { OEMCrypto_SubSampleDescription* sub_samples = const_cast( sample_description->subsamples); sub_samples[0].num_bytes_encrypted = sub_samples[0].num_bytes_encrypted + message_size; }, !kCheckStatus); } TEST_P(OEMCryptoLicenseTest, OEMCryptoMemoryDecryptCENCForHugeSecureHandleLength) { TestDecryptCENCForHugeBufferLengths( [](size_t message_size, OEMCrypto_SampleDescription* sample_description) { OEMCrypto_SubSampleDescription* sub_samples = const_cast( sample_description->subsamples); // TestDecryptCENCForHugeBufferLengths alloctes huge secure handle // buffer. sample_description->buffers.output_descriptor.type = OEMCrypto_BufferType_Secure; sub_samples[0].num_bytes_clear = sub_samples[0].num_bytes_clear + message_size; }, !kCheckStatus); } TEST_P(OEMCryptoLicenseTest, OEMCryptoMemoryDecryptCENCForOutOfRangeNumBytesClear) { TestDecryptCENCForOutOfRangeOffsetsAndLengths( [](OEMCrypto_SampleDescription* sample_description) { OEMCrypto_SubSampleDescription* sub_samples = const_cast( sample_description->subsamples); sub_samples[0].num_bytes_clear = sub_samples[0].num_bytes_clear + 1; }, !kDecryptCENCSecureBuffer); } TEST_P(OEMCryptoLicenseTest, OEMCryptoMemoryDecryptCENCForOutOfRangeNumBytesEncryptedAPI16) { TestDecryptCENCForOutOfRangeOffsetsAndLengths( [](OEMCrypto_SampleDescription* sample_description) { OEMCrypto_SubSampleDescription* sub_samples = const_cast( sample_description->subsamples); sub_samples[0].num_bytes_encrypted = sub_samples[0].num_bytes_encrypted + 1; }, !kDecryptCENCSecureBuffer); } TEST_P(OEMCryptoLicenseTest, OEMCryptoMemoryDecryptCENCForOutOfRangeSecureBufferOffset) { TestDecryptCENCForOutOfRangeOffsetsAndLengths( [](OEMCrypto_SampleDescription* sample_description) { sample_description->buffers.output_descriptor.type = OEMCrypto_BufferType_Secure; sample_description->buffers.output_descriptor.buffer.secure.offset = sample_description->buffers.output_descriptor.buffer.secure .secure_buffer_length + 1; }, kDecryptCENCSecureBuffer); } // After loading keys, we should be able to query the key control block. If we // attempt to query a key that has not been loaded, the error should be // NO_CONTENT_KEY. TEST_P(OEMCryptoLicenseTest, QueryKeyControl) { 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()); // Note: successful cases are tested in VerifyTestKeys. KeyControlBlock block; size_t size = sizeof(block) - 1; OEMCryptoResult sts = OEMCrypto_QueryKeyControl( session_.session_id(), license_messages_.response_data().keys[0].key_id, license_messages_.response_data().keys[0].key_id_length, reinterpret_cast(&block), &size); if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { return; } ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); const char* key_id = "no_key"; size = sizeof(block); ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, OEMCrypto_QueryKeyControl( session_.session_id(), reinterpret_cast(key_id), strlen(key_id), reinterpret_cast(&block), &size)); } // This case tests against the issue where certain 16.4.x SDK versions return a // clear key control block (KCB) in the license response. An OEMCrypto v17.1+ // implementation should be able to handle the clear KCB in the 16.4.x response // and load the license correctly. TEST_F(OEMCryptoSessionTests, ClearKcbAPI17) { if (wvoec::global_features.api_version < 17) { GTEST_SKIP() << "Test for versions 17 and up only."; } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s)); LicenseRoundTrip license_messages(&s); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); // Set odk version in the license response to be 16.4 oemcrypto_core_message::features::CoreMessageFeatures features = {}; features.maximum_major_version = 16; features.maximum_minor_version = 4; constexpr bool kForceClearKcb = true; ASSERT_NO_FATAL_FAILURE( license_messages.EncryptAndSignResponseWithCoreMessageFeatures( features, kForceClearKcb)); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); KeyControlBlock block; size_t size = sizeof(block); OEMCryptoResult sts = OEMCrypto_QueryKeyControl( s.session_id(), license_messages.response_data().keys[0].key_id, license_messages.response_data().keys[0].key_id_length, reinterpret_cast(&block), &size); if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { return; } ASSERT_EQ(OEMCrypto_SUCCESS, sts); } // If the device says it supports anti-rollback in the hardware, then it should // accept a key control block with the anti-rollback hardware bit set. // Otherwise, it should reject that key control block. TEST_P(OEMCryptoLicenseTest, AntiRollbackHardwareRequired) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); license_messages_.set_control(wvoec::kControlRequireAntiRollbackHardware); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); OEMCryptoResult sts = license_messages_.LoadResponse(); if (OEMCrypto_IsAntiRollbackHwPresent()) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } else { ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, sts); } } // This test verifies that OEMCrypto can load the number of keys required for // the reported resource level. TEST_P(OEMCryptoLicenseTest, MinimumKeys) { const size_t num_keys = GetResourceValue(kMaxKeysPerSession); ASSERT_LE(num_keys, kMaxNumKeys) << "Test constants need updating."; license_messages_.set_num_keys(static_cast(num_keys)); 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()); constexpr bool kSelectKeyFirst = true; for (size_t key_index = 0; key_index < num_keys; key_index++) { ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(kSelectKeyFirst, OEMCrypto_SUCCESS, key_index)); } } // This test verifies that OEMCrypto can load the total number of keys required // for the reported resource level. This maximizes keys per session. TEST_P(OEMCryptoLicenseTest, MaxTotalKeysPerSession) { const size_t max_num_keys = GetResourceValue(kMaxKeysPerSession); TestMaxKeys(this, max_num_keys); } // This test verifies that OEMCrypto can load the total number of keys required // for the reported resource level. This maximizes number of sessions. TEST_P(OEMCryptoLicenseTest, MaxTotalKeysManySessions) { const size_t max_total_keys = GetResourceValue(kMaxTotalKeys); const size_t max_sessions = GetResourceValue(kMaxConcurrentSession); const size_t max_num_keys = max_total_keys / max_sessions + 1; TestMaxKeys(this, max_num_keys); } // This test verifies that the minimum patch level can be required. The device // should accept a key control block with the current patch level, and it should // reject any key control blocks with a future patch level. TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { uint8_t patch_level = OEMCrypto_Security_Patch_Level(); printf(" Current Patch Level: %u.\n", patch_level); { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s)); LicenseRoundTrip license_messages(&s); license_messages.set_control(patch_level << wvoec::kControlSecurityPatchLevelShift); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); EXPECT_EQ(global_features.api_version, license_messages.api_version()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } // Reject any future patch levels. if (patch_level < 0x3F) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s)); LicenseRoundTrip license_messages(&s); license_messages.set_control((patch_level + 1) << wvoec::kControlSecurityPatchLevelShift); ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_ERROR_UNKNOWN_FAILURE, license_messages.LoadResponse()); } // Accept an old patch level. if (patch_level > 0) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&s)); LicenseRoundTrip license_messages(&s); license_messages.set_control((patch_level - 1) << wvoec::kControlSecurityPatchLevelShift); 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()); } } // // Load, Refresh Keys Test // // Refresh keys should work if the license uses a nonce. TEST_P(OEMCryptoRefreshTest, RefreshWithNonce) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } // Refresh keys should work if the license does not use a nonce. TEST_P(OEMCryptoRefreshTest, RefreshNoNonce) { license_messages_.set_control(0); LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } // Refresh keys should NOT work if a license has not been loaded. TEST_P(OEMCryptoRefreshTestAPI16, RefreshNoLicense) { Session s; s.open(); constexpr size_t message_size = kMaxCoreMessage + 42; std::vector data(message_size); for (size_t i = 0; i < data.size(); i++) data[i] = i & 0xFF; size_t gen_signature_length = 0; size_t core_message_length = 0; OEMCryptoResult sts = OEMCrypto_PrepAndSignRenewalRequest( s.session_id(), data.data(), data.size(), &core_message_length, nullptr, &gen_signature_length); ASSERT_LT(core_message_length, message_size); if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { vector gen_signature(gen_signature_length); sts = OEMCrypto_PrepAndSignRenewalRequest( s.session_id(), data.data(), data.size(), &core_message_length, gen_signature.data(), &gen_signature_length); } ASSERT_NE(OEMCrypto_SUCCESS, sts); } // Refresh keys should fail if the nonce is not in the session. TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadNonce) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); renewal_messages.core_request().nonce ^= 42; LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE); } // Refresh keys should fail if the session_id does not match the license. TEST_P(OEMCryptoRefreshTestAPI16, RefreshBadSessionID) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); renewal_messages.core_request().session_id += 1; LoadRenewal(&renewal_messages, OEMCrypto_ERROR_INVALID_NONCE); } // Refresh keys should handle the maximum message size. TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) { LoadLicense(); RenewalRoundTrip renewal_messages(&license_messages_); const size_t max_size = GetResourceValue(kLargeMessageSize); renewal_messages.set_message_size(max_size); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); } // This situation would occur if an app only uses one key in the license. When // that happens, GetKeyHandle would be called before the first decrypt, and then // would not need to be called again, even if the license is refreshed. TEST_P(OEMCryptoRefreshTest, RefreshWithNoSelectKey) { LoadLicense(); // Call select key before the refresh. No calls below to TestDecryptCTR with // select key set to true. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true)); // This should still be valid key, even if the refresh failed, because this // is before the original license duration. wvutil::TestSleep::Sleep(kShortSleep); ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false)); // This should be after duration of the original license, but before the // expiration of the refresh message. This should fail until we have loaded // the renewal. wvutil::TestSleep::Sleep(kShortSleep + kLongSleep); ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); // After we've loaded the renewal, decrypt should succeed again. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false)); } // Test that playback clock is correctly started and that the license can be // renewed. TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadSuccess) { license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load; timer_limits_.rental_duration_seconds = kDuration; // 2 seconds. timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds. // First version to support Renew on Load. constexpr uint32_t kFeatureVersion = 18; // Loading the license should start the playback clock. LoadLicense(); // Sleep until just after rental window is over. wvutil::TestSleep::Sleep(kDuration + kShortSleep); if (license_api_version_ < kFeatureVersion || global_features.api_version < kFeatureVersion) { // If the feature is not supported, then we expect failure because the // playback clock was not started and we are outside the rental window. ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(true, OEMCrypto_ERROR_KEY_EXPIRED)); return; } else { // If the feature is supported, we expect decrypt to work because we are // still within the initial renewal window, and the playback clock should // have started. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS)); } // This is after the initial renewal duration, so we expect failure before // loading the renewal. wvutil::TestSleep::Sleep(kShortSleep + kLongSleep); ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); RenewalRoundTrip renewal_messages(&license_messages_); MakeRenewalRequest(&renewal_messages); LoadRenewal(&renewal_messages, OEMCrypto_SUCCESS); // After we've loaded the renewal, decrypt should succeed again. ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(false)); } TEST_P(OEMCryptoRefreshTest, RenewLicenseLoadOutsideRentalDuration) { license_messages_.core_response().renewal_delay_base = OEMCrypto_License_Load; timer_limits_.rental_duration_seconds = kDuration; // 2 seconds. timer_limits_.initial_renewal_duration_seconds = kLongDuration; // 5 seconds. // Sleep until just after rental window is over. wvutil::TestSleep::Sleep(kDuration + kShortSleep); // Loading the license should start the playback clock. LoadLicense(); // If the license is loaded after the rental duration window, we expect // failure. ASSERT_NO_FATAL_FAILURE( session_.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoRefreshTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); // These tests only work when the license has a core message. INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16, Range(kCoreMessagesAPI, kCurrentAPI + 1)); /// @} } // namespace wvoec