diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 75c461dc..6c546cf6 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -1096,9 +1096,14 @@ void Session::InstallRSASessionTestKey(const vector& wrapped_rsa_key) { GenerateDerivedKeysFromSessionKey(); } -void Session::CreateNewUsageEntry() { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_)); +void Session::CreateNewUsageEntry(OEMCryptoResult* status) { + OEMCryptoResult result = + OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_); + if (status) { + *status = result; + return; + } + ASSERT_EQ(OEMCrypto_SUCCESS, result); } void Session::UpdateUsageEntry(std::vector* header_buffer) { diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index cea7d8dc..60c84ecf 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -290,7 +290,9 @@ class Session { // GenerateDerivedKeysFromSessionKey to install known encryption and mac keys. void InstallRSASessionTestKey(const vector& wrapped_rsa_key); // Creates a new usage entry, and keeps track of the index. - void CreateNewUsageEntry(); + // If status is null, we expect success, otherwise status is set to the + // return value. + void CreateNewUsageEntry(OEMCryptoResult *status = NULL); // Copy encrypted usage entry from other session, and then load it. // This session must already be open. void LoadUsageEntry(uint32_t index, const vector& buffer); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 4bb6969f..e42348fe 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -4684,66 +4685,6 @@ TEST_F(UsageTableTest, OnlineMissingEntry) { ASSERT_NO_FATAL_FAILURE(s.close()); } -TEST_F(UsageTableTest, TwoHundredEntries) { - Session s1; - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); - std::string pst1 = "pst saved"; - ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s1.get_nonce(), pst1)); - ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s1.CreateNewUsageEntry()); - ASSERT_EQ(0u, s1.usage_entry_number()); - time_t start = time(NULL); - ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s1.close()); - - // API says should hold at least 200 entries. Subtract one for s1's entry. - const size_t ENTRY_COUNT = 200 - 1; - vector sessions(ENTRY_COUNT); - for (size_t i = 0; i < ENTRY_COUNT; i++) { - ASSERT_NO_FATAL_FAILURE(sessions[i].open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); - std::string pst = "pst "; - char c1 = 'A' + (i/26); - char c2 = 'A' + (i%26); - pst = pst + c1 + c2; - ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( - 0, wvoec_mock::kControlNonceOrEntry, sessions[i].get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(sessions[i].CreateNewUsageEntry()); - ASSERT_EQ(sessions[i].usage_entry_number(), i + 1); - ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE( - sessions[i].UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(sessions[i].close()); - } - sleep(kShortSleep); - for (size_t i = 0; i < ENTRY_COUNT; i++) { - ASSERT_NO_FATAL_FAILURE(sessions[i].open()); - std::string pst = "pst "; - char c1 = 'A' + (i/26); - char c2 = 'A' + (i%26); - pst = pst + c1 + c2; - // Reuse license message created above. - ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); - ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)) - << "Failed to reload license " << i << " with pst = " << pst; - ASSERT_NO_FATAL_FAILURE( - sessions[i].UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(sessions[i].close()); - } - // Make sure s1's entry is still in the table. - ASSERT_NO_FATAL_FAILURE(s1.open()); - ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); - ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kUnused, start)); - ASSERT_NO_FATAL_FAILURE(s1.close()); -} - TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { std::string pst = "A PST"; uint32_t nonce = session_.get_nonce(); @@ -5418,6 +5359,95 @@ TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) { &data[0], data.size())); } +static std::string MakePST(size_t n) { + std::stringstream stream; + stream << "pst-" << n; + return stream.str(); +} + +TEST_F(UsageTableDefragTest, TwoHundredEntries) { + // OEMCrypto is required to store at least 200 entries in the usage table + // header, but it is allowed to store more. This test verifies that if we keep + // adding entries, the error indicates a resource limit. It then verifies + // that all of the successful entries are still valid after we throw out the + // last invalid entry. + const size_t ENTRY_COUNT = 2000; + vector sessions(ENTRY_COUNT); + size_t successful_count = 0; + for (size_t i = 0; i < ENTRY_COUNT; i++) { + if (i % 50 == 0) LOGD("Creating license %zd", i); + ASSERT_NO_FATAL_FAILURE(sessions[i].open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + std::string pst = MakePST(i); + ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, sessions[i].get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); + // We attempt to create a new usage table entry for this session. + OEMCryptoResult status; + ASSERT_NO_FATAL_FAILURE(sessions[i].CreateNewUsageEntry(&status)); + if (status == OEMCrypto_SUCCESS) { + ASSERT_EQ(sessions[i].usage_entry_number(), i); + ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)); + successful_count++; + } else { + // If we failed to create this many entries because of limited resources, + // then the error returned should be insufficient resources. + EXPECT_EQ(OEMCrypto_ERROR_INSUFFICIENT_RESOURCES, status) + << "Failed to create license " << i << " with pst = " << pst; + break; + } + ASSERT_NO_FATAL_FAILURE(sessions[i].close()); + } + LOGD("successful_count = %d", successful_count); + EXPECT_GE(successful_count, 200u); + sleep(kShortSleep); + // Now we will loop through each valid entry, and verify that we can still + // reload the license and perform a decrypt. + for (size_t i = 0; i < successful_count; i++) { + if (i % 50 == 0) LOGD("Reloading license %zd", i); + ASSERT_NO_FATAL_FAILURE(sessions[i].open()); + std::string pst = MakePST(i); + // Reuse license message created above. + ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)) + << "Failed to reload license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)) + << "Failed to update license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE(sessions[i].TestDecryptCTR()) + << "Failed to use license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)) + << "Failed to update license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE(sessions[i].close()); + } + // We also need to verify that a full table can be shrunk, and the remaining + // licenses still work. + size_t smaller_size = 10u; // 10 is smaller than 200. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(smaller_size)); + for (size_t i = 0; i < smaller_size; i++) { + ASSERT_NO_FATAL_FAILURE(sessions[i].open()); + std::string pst = MakePST(i); + // Reuse license message created above. + ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); + ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)) + << "Failed to reload license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)) + << "Failed to update license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE(sessions[i].TestDecryptCTR()) + << "Failed to use license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)) + << "Failed to update license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE(sessions[i].close()); + } +} + TEST_F(UsageTableTest, CopyOldEntries) { // First create three old entries. We open sessions first to force creation // of the mac keys.