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
This commit is contained in:
Fred Gylys-Colwell
2014-03-15 14:44:05 -07:00
parent 71e9cacfe2
commit fec3e87167
2 changed files with 72 additions and 3 deletions

View File

@@ -11,6 +11,7 @@
#include <iostream>
#include <cstring>
#include <stdio.h>
#include <time.h>
#include <string>
#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<uint8_t*>(&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);

View File

@@ -16,6 +16,7 @@
#include <openssl/sha.h>
#include <stdint.h>
#include <sys/types.h>
#include <time.h>
#include <algorithm>
#include <map>
#include <string>
@@ -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<uint8_t>* 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();