diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp index 4b4b693e..5959ca15 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_device_properties_mod.cpp @@ -514,6 +514,8 @@ class AndroidModifiableCryptoEngine : public CryptoEngine { return false; } + virtual int nonce_flood_count() { GetOption("nonce_flood_count", 20); } + virtual void adjust_destination(OEMCrypto_DestBufferDesc *out_description, size_t data_length, uint8_t subsample_flags) { if (out_description->type != OEMCrypto_BufferType_Secure) return; diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 791c771f..340b7ea1 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -146,6 +146,9 @@ class CryptoEngine { virtual bool srm_blacklisted_device_attached() { return false; } + // Rate limit for nonce generation. Default to 20 nonce/second. + virtual int nonce_flood_count() { return 20; } + // Set destination pointer based on the output destination description. OEMCryptoResult SetDestination(OEMCrypto_DestBufferDesc* out_description, size_t data_length, uint8_t subsample_flags); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index ad1dd824..611c6c3e 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,13 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateDerivedKeys( return OEMCrypto_SUCCESS; } +static const uint64_t one_second = 1000000ull; +static uint64_t TimeStamp(void) { + struct timeval tv; + gettimeofday(&tv,NULL); + return tv.tv_sec * one_second + tv.tv_usec; +} + extern "C" OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, uint32_t* nonce) { if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { @@ -213,12 +221,15 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, } // Prevent nonce flood. - static time_t last_nonce_time = 0; - static int nonce_count = 0; - time_t now = time(NULL); - if (now == last_nonce_time) { + uint64_t now = TimeStamp(); + static uint64_t last_nonce_time = now; + // For testing, we set nonce_flood_count to 1. Since count is initialized to + // 1, the very first nonce after initialization is counted as a flood. + static int nonce_count = 1; + + if (now - last_nonce_time < one_second) { nonce_count++; - if (nonce_count > 20) { + if (nonce_count > crypto_engine->nonce_flood_count()) { LOGE("[OEMCrypto_GenerateNonce(): Nonce Flood detected]"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 469cdd95..0a7f85e1 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -1197,15 +1197,17 @@ void Session::VerifyPST(const Test_PST_Report& expected) { char* pst_ptr = reinterpret_cast(computed.pst()); std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length()); ASSERT_EQ(expected.pst, computed_pst); - EXPECT_NEAR(expected.seconds_since_license_received, + time_t now = time(NULL); + int64_t age = now - expected.time_created; // How old is this report. + EXPECT_NEAR(expected.seconds_since_license_received + age, computed.seconds_since_license_received(), kTimeTolerance); // Decrypt times only valid on licenses that have been active. if (expected.status == kActive || expected.status == kInactiveUsed) { - EXPECT_NEAR(expected.seconds_since_first_decrypt, + EXPECT_NEAR(expected.seconds_since_first_decrypt + age, computed.seconds_since_first_decrypt(), kUsageTableTimeTolerance); - EXPECT_NEAR(expected.seconds_since_last_decrypt, + EXPECT_NEAR(expected.seconds_since_last_decrypt + age, computed.seconds_since_last_decrypt(), kUsageTableTimeTolerance); } diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index c4b35f44..d1428b70 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -94,13 +94,14 @@ struct RSAPrivateKeyMessage { struct Test_PST_Report { Test_PST_Report(const std::string& pst_in, OEMCrypto_Usage_Entry_Status status_in) - : status(status_in), pst(pst_in) {} + : status(status_in), pst(pst_in), time_created(time(NULL)) {} OEMCrypto_Usage_Entry_Status status; int64_t seconds_since_license_received; int64_t seconds_since_first_decrypt; int64_t seconds_since_last_decrypt; std::string pst; + time_t time_created; }; struct EntitledContentKeyData { diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index c6a22f9e..194b61e7 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -4935,6 +4935,7 @@ TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + time_t loaded = time(NULL); // Offline license with new mac keys should fail. Session s2; @@ -4961,7 +4962,10 @@ TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); - ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateVerifyReport(pst, kUnused, + loaded, // license loaded. + 0, 0)); // first and last decrypt } // An offline license should not load on the first call if the nonce is bad.