Commit 8533ddd3 authored by Franziskus Kiefer's avatar Franziskus Kiefer

Bug 1345089 - add prng kat tests, r=ttaubert

Differential Revision: https://nss-review.dev.mozaws.net/D237

--HG--
extra : rebase_source : acfb8b83f674211a6495c0aa230c30ceca81dcc2
extra : amend_source : db848c7b99d077a9ab15fcca72c1000281035349
extra : histedit_source : f18c033c425051aa40fa98967cad794bc012c030%2Cf3e94a7be307767cbfd3465ec60dc1c9d72ff87b
parent bf0c68ca
......@@ -10,8 +10,12 @@
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
NSS_NoDB_Init(nullptr);
NSS_SetDomesticPolicy();
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
return 1;
}
if (NSS_SetDomesticPolicy() != SECSuccess) {
return 1;
}
int rv = RUN_ALL_TESTS();
if (NSS_Shutdown() != SECSuccess) {
......
......@@ -35,7 +35,28 @@
],
}],
],
}
},
{
'target_name': 'prng_gtest',
'type': 'executable',
'sources': [
'prng_kat_unittest.cc',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/lib/util/util.gyp:nssutil3',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
'<(DEPTH)/lib/nss/nss.gyp:nss_static',
'<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
'<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
'<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
'<(DEPTH)/lib/certdb/certdb.gyp:certdb',
'<(DEPTH)/lib/base/base.gyp:nssb',
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/pki/pki.gyp:nsspki',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl',
],
},
],
'target_defaults': {
'include_dirs': [
......
This diff is collapsed.
This diff is collapsed.
// 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>
#include <string>
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "blapi.h"
namespace nss_test {
typedef struct PRNGTestValuesStr {
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;
} PRNGTestValues;
std::vector<PRNGTestValues> test_vector;
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";
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> hex_string_to_bytes(std::string s) {
std::vector<uint8_t> bytes;
for (size_t i = 0; i < s.length() - 1; i += 2) {
bytes.push_back(std::stoul(s.substr(i, 2), nullptr, 16));
}
return bytes;
}
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 void ReadFile(const std::string file_name) {
std::ifstream infile(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);
}
}
class PRNGTest : public ::testing::TestWithParam<PRNGTestValues> {
protected:
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);
}
};
TEST_P(PRNGTest, HashDRBG) { RunTest(GetParam()); }
INSTANTIATE_TEST_CASE_P(NISTTestVector, PRNGTest,
::testing::ValuesIn(test_vector));
} // nss_test
int main(int argc, char** argv) {
if (argc < 2) {
std::cout << "usage: prng_gtest <.rsp file>" << std::endl;
return 1;
}
nss_test::ReadFile(argv[1]);
assert(!nss_test::test_vector.empty());
::testing::InitGoogleTest(&argc, argv);
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
return 1;
}
int rv = RUN_ALL_TESTS();
if (NSS_Shutdown() != SECSuccess) {
return 1;
}
return rv;
}
......@@ -31,12 +31,18 @@ int main(int argc, char** argv) {
}
}
NSS_Initialize(g_working_dir_path.c_str(), "", "", SECMOD_DB,
NSS_INIT_READONLY);
NSS_SetDomesticPolicy();
if (NSS_Initialize(g_working_dir_path.c_str(), "", "", SECMOD_DB,
NSS_INIT_READONLY) != SECSuccess) {
return 1;
}
if (NSS_SetDomesticPolicy() != SECSuccess) {
return 1;
}
int rv = RUN_ALL_TESTS();
NSS_Shutdown();
if (NSS_Shutdown() != SECSuccess) {
return 1;
}
nss_test::Poller::Shutdown();
......
......@@ -1469,6 +1469,12 @@ FIPS186Change_ReduceModQForDSA(const unsigned char *w,
const unsigned char *q,
unsigned char *xj);
/* To allow NIST KAT tests */
extern SECStatus
PRNGTEST_Instantiate_Kat(const PRUint8 *entropy, unsigned int entropy_len,
const PRUint8 *nonce, unsigned int nonce_len,
const PRUint8 *personal_string, unsigned int ps_len);
/*
* The following functions are for FIPS poweron self test and FIPS algorithm
* testing.
......
......@@ -99,7 +99,8 @@ struct RNGContextStr {
* RNG_RandomUpdate. */
PRUint8 additionalDataCache[PRNG_ADDITONAL_DATA_CACHE_SIZE];
PRUint32 additionalAvail;
PRBool isValid; /* false if RNG reaches an invalid state */
PRBool isValid; /* false if RNG reaches an invalid state */
PRBool isKatTest; /* true if running NIST PRNG KAT tests */
};
typedef struct RNGContextStr RNGContext;
......@@ -150,7 +151,7 @@ prng_Hash_df(PRUint8 *requested_bytes, unsigned int no_of_bytes_to_return,
}
/*
* Hash_DRBG Instantiate NIST SP 800-80 10.1.1.2
* Hash_DRBG Instantiate NIST SP 800-90 10.1.1.2
*
* NOTE: bytes & len are entropy || nonce || personalization_string. In
* normal operation, NSS calculates them all together in a single call.
......@@ -158,9 +159,11 @@ prng_Hash_df(PRUint8 *requested_bytes, unsigned int no_of_bytes_to_return,
static SECStatus
prng_instantiate(RNGContext *rng, const PRUint8 *bytes, unsigned int len)
{
if (len < PRNG_SEEDLEN) {
/* if the seedlen is to small, it's probably because we failed to get
* enough random data */
if (!rng->isKatTest && len < PRNG_SEEDLEN) {
/* If the seedlen is too small, it's probably because we failed to get
* enough random data.
* This is stricter than NIST SP800-90A requires. Don't enforce it for
* tests. */
PORT_SetError(SEC_ERROR_NEED_RANDOM);
return SECFailure;
}
......@@ -272,7 +275,7 @@ prng_reseed_test(RNGContext *rng, const PRUint8 *entropy,
#define PRNG_ADD_BITS_AND_CARRY(dest, dest_len, add, len, carry) \
PRNG_ADD_BITS(dest, dest_len, add, len, carry) \
PRNG_ADD_CARRY_ONLY(dest, dest_len - len, carry)
PRNG_ADD_CARRY_ONLY(dest, dest_len - len - 1, carry)
/*
* This function expands the internal state of the prng to fulfill any number
......@@ -445,6 +448,7 @@ rng_init(void)
/* the RNG is in a valid state */
globalrng->isValid = PR_TRUE;
globalrng->isKatTest = PR_FALSE;
/* fetch one random value so that we can populate rng->oldV for our
* continous random number test. */
......@@ -700,6 +704,17 @@ RNG_RNGShutdown(void)
* entropy we may have previously collected. */
RNGContext testContext;
SECStatus
PRNGTEST_Instantiate_Kat(const PRUint8 *entropy, unsigned int entropy_len,
const PRUint8 *nonce, unsigned int nonce_len,
const PRUint8 *personal_string, unsigned int ps_len)
{
testContext.isKatTest = PR_TRUE;
return PRNGTEST_Instantiate(entropy, entropy_len,
nonce, nonce_len,
personal_string, ps_len);
}
/*
* Test vector API. Use NIST SP 800-90 general interface so one of the
* other NIST SP 800-90 algorithms may be used in the future.
......
......@@ -178,10 +178,11 @@
'cmd/vfychain/vfychain.gyp:vfychain',
'cmd/vfyserv/vfyserv.gyp:vfyserv',
'gtests/der_gtest/der_gtest.gyp:der_gtest',
'gtests/freebl_gtest/freebl_gtest.gyp:prng_gtest',
'gtests/pk11_gtest/pk11_gtest.gyp:pk11_gtest',
'gtests/ssl_gtest/ssl_gtest.gyp:ssl_gtest',
'gtests/util_gtest/util_gtest.gyp:util_gtest',
'gtests/nss_bogo_shim/nss_bogo_shim.gyp:nss_bogo_shim'
'gtests/nss_bogo_shim/nss_bogo_shim.gyp:nss_bogo_shim',
],
'conditions': [
[ 'OS=="linux"', {
......
......@@ -55,7 +55,8 @@ gtest_start()
GTESTREPORT="$GTESTDIR/report.xml"
PARSED_REPORT="$GTESTDIR/report.parsed"
echo "executing $i"
${BINDIR}/$i -d "$GTESTDIR" --gtest_output=xml:"${GTESTREPORT}" \
${BINDIR}/$i "${SOURCE_DIR}/gtests/freebl_gtest/kat/Hash_DRBG.rsp" \
-d "$GTESTDIR" --gtest_output=xml:"${GTESTREPORT}" \
--gtest_filter="${GTESTFILTER-*}"
html_msg $? 0 "$i run successfully"
echo "test output dir: ${GTESTREPORT}"
......@@ -82,7 +83,8 @@ gtest_cleanup()
}
################## main #################################################
GTESTS="der_gtest pk11_gtest util_gtest"
GTESTS="prng_gtest der_gtest pk11_gtest util_gtest"
SOURCE_DIR="$PWD"/../..
gtest_init $0
gtest_start
gtest_cleanup
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment