prng_kat_unittest.cc (5061B)
1 // This Source Code Form is subject to the terms of the Mozilla Public 2 // License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 // You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 #include "nspr.h" 6 #include "nss.h" 7 #include "ssl.h" 8 9 #include <cstdlib> 10 #include <fstream> 11 12 #define GTEST_HAS_RTTI 0 13 #include "gtest/gtest.h" 14 #include "util.h" 15 16 #include "blapi.h" 17 18 extern std::string g_source_dir; 19 20 namespace nss_test { 21 22 struct PRNGTestValues { 23 std::vector<uint8_t> entropy; 24 std::vector<uint8_t> nonce; 25 std::vector<uint8_t> personal; 26 std::vector<uint8_t> expected_result; 27 std::vector<uint8_t> additional_entropy; 28 std::vector<uint8_t> additional_input_reseed; 29 std::vector<std::vector<uint8_t>> additional_input; 30 }; 31 32 bool contains(std::string& s, const char* to_find) { 33 return s.find(to_find) != std::string::npos; 34 } 35 36 std::string trim(std::string str) { 37 std::string whitespace = " \t\r\n"; 38 const auto strBegin = str.find_first_not_of(whitespace); 39 if (strBegin == std::string::npos) { 40 return ""; 41 } 42 const auto strEnd = str.find_last_not_of(whitespace); 43 const auto strRange = strEnd - strBegin + 1; 44 return str.substr(strBegin, strRange); 45 } 46 47 std::vector<uint8_t> read_option_s(std::string& s) { 48 size_t start = s.find("=") + 1; 49 assert(start > 0); 50 return hex_string_to_bytes(trim(s.substr(start, s.find("]", start)))); 51 } 52 53 void print_bytes(std::vector<uint8_t> bytes, std::string name) { 54 std::cout << name << ": "; 55 for (auto b : bytes) { 56 std::cout << std::setfill('0') << std::setw(2) << std::hex 57 << static_cast<int>(b); 58 } 59 std::cout << std::endl; 60 } 61 62 static std::vector<PRNGTestValues> ReadFile(const std::string file_name) { 63 std::vector<PRNGTestValues> test_vector; 64 std::ifstream infile(file_name); 65 EXPECT_FALSE(infile.fail()) << "kat file: " << file_name; 66 std::string line; 67 68 // Variables holding the input for each test. 69 bool valid_option = false; 70 71 // Read the file. 72 std::streampos pos; 73 while (std::getline(infile, line)) { 74 // We only implement SHA256. Skip all other tests. 75 if (contains(line, "[SHA-")) { 76 valid_option = contains(line, "[SHA-256]"); 77 } 78 if (!valid_option) { 79 continue; 80 } 81 82 // We ignore the options and infer them from the test case. 83 84 PRNGTestValues test; 85 if (line.find("COUNT =")) { 86 continue; 87 } 88 89 // Read test input. 90 do { 91 pos = infile.tellg(); 92 std::getline(infile, line); 93 if (contains(line, "EntropyInput ")) { 94 test.entropy = read_option_s(line); 95 continue; 96 } 97 if (contains(line, "Nonce")) { 98 test.nonce = read_option_s(line); 99 continue; 100 } 101 if (contains(line, "PersonalizationString")) { 102 test.personal = read_option_s(line); 103 continue; 104 } 105 if (contains(line, "AdditionalInput ")) { 106 test.additional_input.push_back(read_option_s(line)); 107 continue; 108 } 109 if (contains(line, "EntropyInputReseed")) { 110 test.additional_entropy = read_option_s(line); 111 continue; 112 } 113 if (contains(line, "AdditionalInputReseed")) { 114 test.additional_input_reseed = read_option_s(line); 115 continue; 116 } 117 if (contains(line, "ReturnedBits")) { 118 test.expected_result = read_option_s(line); 119 continue; 120 } 121 } while (!infile.eof() && line.find("COUNT =") && line.find("[")); 122 123 // Save test case. 124 test_vector.push_back(test); 125 test = {}; 126 infile.seekg(pos); 127 } 128 return test_vector; 129 } 130 131 class PRNGTest : public ::testing::Test { 132 protected: 133 void SetUp() override { 134 test_vector_ = ReadFile(::g_source_dir + "/kat/Hash_DRBG.rsp"); 135 ASSERT_FALSE(test_vector_.empty()); 136 } 137 138 void RunTest(PRNGTestValues& test) { 139 ASSERT_EQ(2U, test.additional_input.size()); 140 SECStatus rv = PRNGTEST_Instantiate_Kat( 141 test.entropy.data(), test.entropy.size(), test.nonce.data(), 142 test.nonce.size(), test.personal.data(), test.personal.size()); 143 ASSERT_EQ(SECSuccess, rv); 144 rv = PRNGTEST_Reseed(test.additional_entropy.data(), 145 test.additional_entropy.size(), 146 test.additional_input_reseed.data(), 147 test.additional_input_reseed.size()); 148 ASSERT_EQ(SECSuccess, rv); 149 150 // Generate bytes. 151 uint8_t bytes[128]; 152 PRNGTEST_Generate(bytes, 128, test.additional_input[0].data(), 153 test.additional_input[0].size()); 154 PRNGTEST_Generate(bytes, 128, test.additional_input[1].data(), 155 test.additional_input[1].size()); 156 std::vector<uint8_t> result(bytes, bytes + 128); 157 if (result != test.expected_result) { 158 print_bytes(result, "result "); 159 print_bytes(test.expected_result, "expected"); 160 } 161 ASSERT_EQ(test.expected_result, result); 162 rv = PRNGTEST_Uninstantiate(); 163 ASSERT_EQ(SECSuccess, rv); 164 } 165 166 protected: 167 std::vector<PRNGTestValues> test_vector_; 168 }; 169 170 TEST_F(PRNGTest, HashDRBG) { 171 for (auto& v : test_vector_) { 172 RunTest(v); 173 } 174 } 175 176 } // namespace nss_test