This patch adds a suite of tests for OEMCrypto that verifying buffer overflow and off-by-one errors. The reference code has also been updated to pass these tests. The ODK library and the OEMCrypto API have not changed since the release of version 16.4.
188 lines
5.8 KiB
C++
188 lines
5.8 KiB
C++
// Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine License
|
|
// Agreement.
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "cdm_random.h"
|
|
|
|
namespace wvcdm {
|
|
|
|
namespace {
|
|
// Random data vector lengths.
|
|
constexpr size_t kVectorLength = 1024;
|
|
constexpr size_t kMaxRandomDataLength =
|
|
CdmRandomGenerator::kMaxRandomDataLength;
|
|
constexpr size_t kAboveMaxRandomDataLength = std::numeric_limits<size_t>::max();
|
|
|
|
constexpr size_t kRandomTrialCount = 100;
|
|
constexpr size_t kThreadCount = 16;
|
|
constexpr unsigned int kSeeds[] = {0, 1337, 1565904109, 776964657};
|
|
|
|
class CdmRandomGeneratorTest : public testing::TestWithParam<unsigned int> {};
|
|
} // namespace
|
|
|
|
// Checks that the class CdmRandomGenerator meets the requirements of
|
|
// UniformRandomBitGenerator.
|
|
// ref: https://en.cppreference.com/w/cpp/named_req/UniformRandomBitGenerator
|
|
TEST(CdmRandomGeneratorTest, UniformRandomBitGeneratorRequirements) {
|
|
// Let G represent class CdmRandomGenerator, and g represent an instance
|
|
// of CdmRandomGenerator.
|
|
// 1) G::result_type is an unsigned integer (unspecified precision).
|
|
EXPECT_TRUE(std::is_integral<CdmRandomGenerator::result_type>::value);
|
|
EXPECT_TRUE(std::is_unsigned<CdmRandomGenerator::result_type>::value);
|
|
// 2&3 a) G::min() and G::max() have the result type of G::result_type.
|
|
EXPECT_TRUE((std::is_same<CdmRandomGenerator::result_type,
|
|
decltype(CdmRandomGenerator::min())>::value));
|
|
EXPECT_TRUE((std::is_same<CdmRandomGenerator::result_type,
|
|
decltype(CdmRandomGenerator::max())>::value));
|
|
// 2&3 b) G::min() is strictly less than G::max().
|
|
EXPECT_LT(CdmRandomGenerator::min(), CdmRandomGenerator::max());
|
|
// 4 a) g() have the result type of G::result_type.
|
|
CdmRandomGenerator g;
|
|
EXPECT_TRUE(
|
|
(std::is_same<CdmRandomGenerator::result_type, decltype(g())>::value));
|
|
// 4 b) g() is within [G::min() G::max()]
|
|
std::vector<CdmRandomGenerator::result_type> values;
|
|
for (size_t i = 0; i < kRandomTrialCount; ++i) {
|
|
CdmRandomGenerator::result_type x = g();
|
|
EXPECT_LE(CdmRandomGenerator::min(), x);
|
|
EXPECT_GE(CdmRandomGenerator::max(), x);
|
|
values.push_back(x);
|
|
}
|
|
|
|
// Verify compilation.
|
|
|
|
// std::shuffle(RandomIt first, RandomIt last, URBG&& g) requires the
|
|
// class URBG to meet "UniformRandomBitGenerator" requirements. This
|
|
// will fail to compile if the requirements are not met.
|
|
std::shuffle(values.begin(), values.end(), CdmRandomGenerator());
|
|
}
|
|
|
|
TEST_P(CdmRandomGeneratorTest, AllMethods) {
|
|
const unsigned int seed = GetParam();
|
|
CdmRandomGenerator rng;
|
|
rng.Seed();
|
|
rng.Seed(seed);
|
|
|
|
rng.Rand();
|
|
rng();
|
|
|
|
rng.RandomInRange(1234, 1000000);
|
|
rng.RandomInRange(1000000);
|
|
|
|
rng.RandomData(kVectorLength);
|
|
|
|
rng.RandomBool();
|
|
}
|
|
|
|
TEST_P(CdmRandomGeneratorTest, RandomInRange) {
|
|
const unsigned int seed = GetParam();
|
|
CdmRandomGenerator rng(seed);
|
|
|
|
for (size_t i = 0; i < kRandomTrialCount; ++i) {
|
|
const int rand_int = rng.Rand();
|
|
EXPECT_GE(rand_int, 0);
|
|
EXPECT_LE(rand_int, RAND_MAX);
|
|
}
|
|
|
|
// Range size of 1.
|
|
const uint64_t rand_u64_1 = rng.RandomInRange(100, 100);
|
|
EXPECT_EQ(rand_u64_1, 100ul);
|
|
|
|
// Range size of 2.
|
|
const uint64_t rand_u64_2 = rng.RandomInRange(1234, 1235);
|
|
EXPECT_GE(rand_u64_2, 1234ul);
|
|
EXPECT_LE(rand_u64_2, 1235ul);
|
|
|
|
// Small range.
|
|
const uint64_t rand_u64_3 = rng.RandomInRange(10);
|
|
EXPECT_LE(rand_u64_3, 10ul);
|
|
|
|
// Max range, mainly checking that nothing crashes.
|
|
rng.RandomInRange(0, std::numeric_limits<uint64_t>::max());
|
|
|
|
// Invalid range representation. Should swap the bounds.
|
|
const uint64_t rand_u64_4 = rng.RandomInRange(1235, 1234);
|
|
EXPECT_GE(rand_u64_4, 1234ul);
|
|
EXPECT_LE(rand_u64_4, 1235ul);
|
|
}
|
|
|
|
TEST_P(CdmRandomGeneratorTest, RandomDataLength) {
|
|
const unsigned int seed = GetParam();
|
|
CdmRandomGenerator rng(seed);
|
|
|
|
const std::string empty_data = rng.RandomData(0);
|
|
EXPECT_EQ(empty_data.size(), 0ul);
|
|
|
|
const std::string data = rng.RandomData(kVectorLength);
|
|
EXPECT_EQ(data.size(), kVectorLength);
|
|
|
|
const std::string max_data = rng.RandomData(kMaxRandomDataLength);
|
|
EXPECT_EQ(max_data.size(), kMaxRandomDataLength);
|
|
|
|
// Requesting data above the maximum length will result in an error,
|
|
// returning an empty string.
|
|
const std::string error_data = rng.RandomData(kAboveMaxRandomDataLength);
|
|
EXPECT_EQ(error_data.size(), 0ul);
|
|
}
|
|
|
|
TEST_P(CdmRandomGeneratorTest, Reproducibility) {
|
|
const unsigned int seed = GetParam();
|
|
CdmRandomGenerator rng(seed);
|
|
const std::string random_data_1 = rng.RandomData(kVectorLength);
|
|
// Reset generator.
|
|
rng.Seed(seed);
|
|
const std::string random_data_2 = rng.RandomData(kVectorLength);
|
|
EXPECT_EQ(random_data_1, random_data_2);
|
|
}
|
|
|
|
TEST_P(CdmRandomGeneratorTest, ThreadSafety) {
|
|
const unsigned int seed = GetParam();
|
|
CdmRandomGenerator rng(seed);
|
|
bool barrier = true;
|
|
|
|
auto thread_job = [&]() {
|
|
while (barrier) {
|
|
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
|
}
|
|
for (size_t i = 0; i < kRandomTrialCount; ++i) {
|
|
rng.Rand();
|
|
}
|
|
};
|
|
|
|
std::vector<std::thread> threads;
|
|
for (size_t i = 0; i < kThreadCount; ++i) {
|
|
threads.push_back(std::thread(thread_job));
|
|
}
|
|
std::this_thread::sleep_for(std::chrono::microseconds(100));
|
|
barrier = false;
|
|
for (auto& thread : threads) {
|
|
thread.join();
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(VariousSeeds, CdmRandomGeneratorTest,
|
|
testing::ValuesIn(kSeeds));
|
|
|
|
TEST(CdmRandomTest, AllMethods) {
|
|
CdmRandom::Rand();
|
|
CdmRandom::RandomInRange(1234, 1000000);
|
|
CdmRandom::RandomInRange(1000000);
|
|
CdmRandom::RandomData(kVectorLength);
|
|
CdmRandom::RandomBool();
|
|
}
|
|
|
|
} // namespace wvcdm
|