diff options
Diffstat (limited to 'security/nss/gtests/freebl_gtest/prng_kat_unittest.cc')
-rw-r--r-- | security/nss/gtests/freebl_gtest/prng_kat_unittest.cc | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/security/nss/gtests/freebl_gtest/prng_kat_unittest.cc b/security/nss/gtests/freebl_gtest/prng_kat_unittest.cc new file mode 100644 index 0000000000..caa2e1eba2 --- /dev/null +++ b/security/nss/gtests/freebl_gtest/prng_kat_unittest.cc @@ -0,0 +1,176 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "nspr.h" +#include "nss.h" +#include "ssl.h" + +#include <cstdlib> +#include <fstream> + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "util.h" + +#include "blapi.h" + +extern std::string g_source_dir; + +namespace nss_test { + +struct PRNGTestValues { + std::vector<uint8_t> entropy; + std::vector<uint8_t> nonce; + std::vector<uint8_t> personal; + std::vector<uint8_t> expected_result; + std::vector<uint8_t> additional_entropy; + std::vector<uint8_t> additional_input_reseed; + std::vector<std::vector<uint8_t>> additional_input; +}; + +bool contains(std::string& s, const char* to_find) { + return s.find(to_find) != std::string::npos; +} + +std::string trim(std::string str) { + std::string whitespace = " \t\r\n"; + const auto strBegin = str.find_first_not_of(whitespace); + if (strBegin == std::string::npos) { + return ""; + } + const auto strEnd = str.find_last_not_of(whitespace); + const auto strRange = strEnd - strBegin + 1; + return str.substr(strBegin, strRange); +} + +std::vector<uint8_t> read_option_s(std::string& s) { + size_t start = s.find("=") + 1; + assert(start > 0); + return hex_string_to_bytes(trim(s.substr(start, s.find("]", start)))); +} + +void print_bytes(std::vector<uint8_t> bytes, std::string name) { + std::cout << name << ": "; + for (auto b : bytes) { + std::cout << std::setfill('0') << std::setw(2) << std::hex + << static_cast<int>(b); + } + std::cout << std::endl; +} + +static std::vector<PRNGTestValues> ReadFile(const std::string file_name) { + std::vector<PRNGTestValues> test_vector; + std::ifstream infile(file_name); + EXPECT_FALSE(infile.fail()) << "kat file: " << file_name; + std::string line; + + // Variables holding the input for each test. + bool valid_option = false; + + // Read the file. + std::streampos pos; + while (std::getline(infile, line)) { + // We only implement SHA256. Skip all other tests. + if (contains(line, "[SHA-")) { + valid_option = contains(line, "[SHA-256]"); + } + if (!valid_option) { + continue; + } + + // We ignore the options and infer them from the test case. + + PRNGTestValues test; + if (line.find("COUNT =")) { + continue; + } + + // Read test input. + do { + pos = infile.tellg(); + std::getline(infile, line); + if (contains(line, "EntropyInput ")) { + test.entropy = read_option_s(line); + continue; + } + if (contains(line, "Nonce")) { + test.nonce = read_option_s(line); + continue; + } + if (contains(line, "PersonalizationString")) { + test.personal = read_option_s(line); + continue; + } + if (contains(line, "AdditionalInput ")) { + test.additional_input.push_back(read_option_s(line)); + continue; + } + if (contains(line, "EntropyInputReseed")) { + test.additional_entropy = read_option_s(line); + continue; + } + if (contains(line, "AdditionalInputReseed")) { + test.additional_input_reseed = read_option_s(line); + continue; + } + if (contains(line, "ReturnedBits")) { + test.expected_result = read_option_s(line); + continue; + } + } while (!infile.eof() && line.find("COUNT =") && line.find("[")); + + // Save test case. + test_vector.push_back(test); + test = {}; + infile.seekg(pos); + } + return test_vector; +} + +class PRNGTest : public ::testing::Test { + protected: + void SetUp() override { + test_vector_ = ReadFile(::g_source_dir + "/kat/Hash_DRBG.rsp"); + ASSERT_FALSE(test_vector_.empty()); + } + + void RunTest(PRNGTestValues& test) { + ASSERT_EQ(2U, test.additional_input.size()); + SECStatus rv = PRNGTEST_Instantiate_Kat( + test.entropy.data(), test.entropy.size(), test.nonce.data(), + test.nonce.size(), test.personal.data(), test.personal.size()); + ASSERT_EQ(SECSuccess, rv); + rv = PRNGTEST_Reseed(test.additional_entropy.data(), + test.additional_entropy.size(), + test.additional_input_reseed.data(), + test.additional_input_reseed.size()); + ASSERT_EQ(SECSuccess, rv); + + // Generate bytes. + uint8_t bytes[128]; + PRNGTEST_Generate(bytes, 128, test.additional_input[0].data(), + test.additional_input[0].size()); + PRNGTEST_Generate(bytes, 128, test.additional_input[1].data(), + test.additional_input[1].size()); + std::vector<uint8_t> result(bytes, bytes + 128); + if (result != test.expected_result) { + print_bytes(result, "result "); + print_bytes(test.expected_result, "expected"); + } + ASSERT_EQ(test.expected_result, result); + rv = PRNGTEST_Uninstantiate(); + ASSERT_EQ(SECSuccess, rv); + } + + protected: + std::vector<PRNGTestValues> test_vector_; +}; + +TEST_F(PRNGTest, HashDRBG) { + for (auto& v : test_vector_) { + RunTest(v); + } +} + +} // namespace nss_test |