From fec3e87167f23da6693e250ba00f0e8dfed9752e Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Sat, 15 Mar 2014 14:44:05 -0700 Subject: [PATCH] Prevent Nonce Flood From the Widevine CDM repository: https://widevine-internal-review.googlesource.com/#/c/9182/ This CL adds a test to verify that at most 20 nonces may be created in one second. This should prevent the replay attack that an appplication could do by generating large quantities of nonces until it finds a repeat. I've also updated the Level 3 and reference implementations. This feature is required for OEMCrypto version 9. Change-Id: Ia86323133810fcbbd79d7bb27bd5a004d7c87314 --- .../oemcrypto/mock/src/oemcrypto_mock.cpp | 17 ++++++ .../oemcrypto/test/oemcrypto_test.cpp | 58 ++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 1e233514..c2e32c4d 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "log.h" #include "oemcrypto_engine_mock.h" @@ -139,11 +140,27 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, return OEMCrypto_ERROR_INVALID_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) { + nonce_count++; + if (nonce_count > 20) { + LOGE("[OEMCrypto_GenerateNonce(): Nonce Flood detected]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + } else { + nonce_count = 1; + last_nonce_time = now; + } + uint32_t nonce_value; uint8_t* nonce_string = reinterpret_cast(&nonce_value); // Generate 4 bytes of random data if (!RAND_bytes(nonce_string, 4)) { + LOGE("[OEMCrypto_GenerateNonce(): Random bytes failure]"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } session_ctx->AddNonce(nonce_value); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 135bf7f0..f3d29fe3 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -905,9 +906,17 @@ class Session { } } - void GenerateNonce(uint32_t* nonce) { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_GenerateNonce(session_id(), nonce)); + void GenerateNonce(uint32_t* nonce, int* error_counter = NULL) { + if (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(session_id(), nonce)) { + return; + } + if (error_counter) { + (*error_counter)++; + } else { + sleep(1); // wait a second, then try again. + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_GenerateNonce(session_id(), nonce)); + } } void FillDefaultContext(vector* mac_context, @@ -1729,6 +1738,49 @@ TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { testTearDown(); } +TEST_F(OEMCryptoClientTest, PreventNonceFlood) { + Session& s = createSession("ONE"); + testSetUp(); + s.open(); + int error_counter = 0; + uint32_t nonce; + // More than 20 nonces should generate an error. + // To allow for some slop, we actually test for more than 40. + for (int i = 0; i < 60; i++) { + s.GenerateNonce(&nonce, &error_counter); + } + ASSERT_LE(20, error_counter); + error_counter = 0; + sleep(2); // After a pause, we should be able to regenerate nonces. + s.GenerateNonce(&nonce, &error_counter); + ASSERT_EQ(0, error_counter); + s.close(); + testTearDown(); +} + +// Prevent a nonce flood even if each nonce is in a different session. +TEST_F(OEMCryptoClientTest, PreventNonceFlood2) { + Session& s = createSession("ONE"); + testSetUp(); + int error_counter = 0; + uint32_t nonce; + // More than 20 nonces should generate an error. + // To allow for some slop, we actually test for more than 40. + for (int i = 0; i < 60; i++) { + s.open(); + s.GenerateNonce(&nonce, &error_counter); + s.close(); + } + ASSERT_LE(20, error_counter); + error_counter = 0; + sleep(2); // After a pause, we should be able to regenerate nonces. + s.open(); + s.GenerateNonce(&nonce, &error_counter); + s.close(); + ASSERT_EQ(0, error_counter); + testTearDown(); +} + TEST_F(OEMCryptoClientTest, GenerateDerivedKeys) { Session& s = createSession("ONE"); testSetUp();