Skip to content

Commit

Permalink
Add RSA and EC FIPS pairwise failure tests with the new callback (aws…
Browse files Browse the repository at this point in the history
…#954)

* Add RSA and EC FIPS pairwise failure tests with the new callback

* PR feedback: add additional snaity tests to PowerOnTests and cleanup other runtime test
  • Loading branch information
andrewhop authored Apr 21, 2023
1 parent a0670f3 commit a12e3e3
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 31 deletions.
103 changes: 73 additions & 30 deletions crypto/fips_callback_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#if defined(__ELF__) && defined(__GNUC__)

#include <gtest/gtest.h>
#include <openssl/ec_key.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <algorithm>
#include <list>
#include <string>
Expand Down Expand Up @@ -38,62 +40,62 @@ static bool message_in_errors(const string& expected_message) {
struct test_config {
string expected_failure_message;
int initial_failure_count;
int expected_failure_count;
};

// If hmac sha-256 is broken the integrity check can not be trusted to check
// itself and fails earlier
set<string> integrity_test_names = {"SHA-256", "HMAC-SHA-256"};
const test_config integrity_test_config = {
"BORINGSSL_integrity_test",
1,
2
};
vector<string> integrity_tests = {"SHA-256", "HMAC-SHA-256"};

// The fast tests run automatically at startup and will report a failure to
// the test callback immediately, and then again when BORINGSSL_self_test is called
const test_config fast_test_config = {
"boringssl_self_test_startup",
1,
2
};

// The lazy tests are not run at power up, only when called directly with
// BORINGSSL_self_test, therefore the callback should have been called once
set<string> lazy_test_names = {"ECDSA-sign", "ECDSA-verify", "FFDH", "RSA-sign", "RSA-verify", "Z-computation"};
const test_config lazy_test_config = {
"BORINGSSL_self_test",
0,
1
};

vector<string> lazy_tests = {"ECDSA-sign", "ECDSA-verify", "FFDH", "RSA-sign", "RSA-verify", "Z-computation"};
// The fast tests run automatically at startup and will report a failure to
// the test callback immediately, and then again when BORINGSSL_self_test is called
const test_config fast_test_config = {
"boringssl_self_test_startup",
1,
};

static test_config get_self_test_failure_config(char* broken_kat) {
if(integrity_test_names.find(broken_kat) != integrity_test_names.end()) {
return integrity_test_config;
} else if (lazy_test_names.find(broken_kat) != lazy_test_names.end()) {
return lazy_test_config;
} else {
return fast_test_config;
}
}

TEST(FIPSCallback, PowerOnTests) {
ASSERT_EQ(1, FIPS_mode());
// At this point the library has loaded, if a self test was broken
// AWS_LC_FIPS_Callback would have already been called. If this test
// wasn't broken the call count should be zero
char* broken_kat = getenv("FIPS_CALLBACK_TEST_POWER_ON_TEST_FAILURE");
if (broken_kat != nullptr) {
struct test_config config;
if(find(integrity_tests.begin(), integrity_tests.end(), broken_kat) != integrity_tests.end()) {
config = integrity_test_config;
} else if (find(lazy_tests.begin(), lazy_tests.end(), broken_kat) != lazy_tests.end()) {
config = lazy_test_config;
} else {
config = fast_test_config;
}
test_config config = get_self_test_failure_config(broken_kat);
// Fast tests will have already run and if they were broken our callback would
// have already been called
ASSERT_EQ(config.initial_failure_count, failure_count);

// Trigger lazy tests to run
ASSERT_EQ(0, BORINGSSL_self_test());
ASSERT_EQ(config.expected_failure_count, failure_count);
// BORINGSSL_self_test will re-run the fast tests and trigger the lazy tests.
ASSERT_FALSE(BORINGSSL_self_test());
ASSERT_EQ(config.initial_failure_count + 1, failure_count);
ASSERT_TRUE(message_in_errors(config.expected_failure_message));
} else {
// break-kat.go has not run and corrupted this test yet, everything should work
ASSERT_EQ(1, BORINGSSL_self_test());
ASSERT_TRUE(BORINGSSL_self_test());
ASSERT_EQ(0, failure_count);
ASSERT_EQ(1, FIPS_mode());
}
ASSERT_EQ(1, FIPS_mode());
}

TEST(FIPSCallback, DRBGRuntime) {
Expand All @@ -106,15 +108,56 @@ TEST(FIPSCallback, DRBGRuntime) {
uint8_t buf[10];
if (broken_runtime_test != nullptr && strcmp(broken_runtime_test, "CRNG" ) == 0) {
ASSERT_FALSE(RAND_bytes(buf, sizeof(buf)));
// TODO: make AWS_LC_FIPS_error update a new global state so FIPS_mode returns 0
ASSERT_EQ(1, FIPS_mode());
ASSERT_EQ(1, failure_count);
} else {
// BORINGSSL_FIPS_BREAK_TEST has not been set and everything should work
ASSERT_TRUE(RAND_bytes(buf, sizeof(buf)));
ASSERT_EQ(1, FIPS_mode());
ASSERT_EQ(0, failure_count);
}
ASSERT_EQ(1, FIPS_mode());
}

TEST(FIPSCallback, RSARuntimeTest) {
// At this point the library has loaded, if a self test was broken
// AWS_LC_FIPS_Callback would have already been called. If this test
// wasn't broken the call count should be zero
char*broken_runtime_test = getenv("BORINGSSL_FIPS_BREAK_TEST");
bssl::UniquePtr<RSA> rsa(RSA_new());
ASSERT_EQ(0, failure_count);
ASSERT_EQ(1, FIPS_mode());
if (broken_runtime_test != nullptr && (strcmp(broken_runtime_test, "RSA_PWCT" ) == 0 ||
strcmp(broken_runtime_test, "CRNG" ) == 0)) {
ASSERT_FALSE(RSA_generate_key_fips(rsa.get(), 2048, nullptr));
// RSA key generation can call the DRBG multiple times before failing we
// don't know how many times, but it should fail at least once.
ASSERT_NE(0, failure_count);
} else {
// BORINGSSL_FIPS_BREAK_TEST has not been set and everything should work
ASSERT_TRUE(RSA_generate_key_fips(rsa.get(), 2048, nullptr));
}
ASSERT_EQ(1, FIPS_mode());
}

TEST(FIPSCallback, ECDSARuntimeTest) {
// At this point the library has loaded, if a self test was broken
// AWS_LC_FIPS_Callback would have already been called. If this test
// wasn't broken the call count should be zero
char*broken_runtime_test = getenv("BORINGSSL_FIPS_BREAK_TEST");
bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
ASSERT_TRUE(key);
ASSERT_EQ(0, failure_count);
ASSERT_EQ(1, FIPS_mode());
if (broken_runtime_test != nullptr && (strcmp(broken_runtime_test, "ECDSA_PWCT" ) == 0 ||
strcmp(broken_runtime_test, "CRNG" ) == 0)) {
ASSERT_FALSE(EC_KEY_generate_key_fips(key.get()));
// EC key generation can call the DRBG multiple times before failing, we
// don't know how many times, but it should fail at least once.
ASSERT_NE(0, failure_count);
} else {
// BORINGSSL_FIPS_BREAK_TEST has not been set and everything should work
ASSERT_TRUE(EC_KEY_generate_key_fips(key.get()));
}
ASSERT_EQ(1, FIPS_mode());
}

#endif
4 changes: 3 additions & 1 deletion tests/ci/run_fips_callback_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ for kat in $KATS; do
unset FIPS_CALLBACK_TEST_POWER_ON_TEST_FAILURE
done

runtime_tests=("CRNG")
runtime_tests=("RSA_PWCT" "ECDSA_PWCT" "CRNG")
for runtime_test in "${runtime_tests[@]}"; do
# Tell our test what test is expected to fail
export FIPS_CALLBACK_TEST_RUNTIME_TEST_FAILURE="$runtime_test"
Expand All @@ -38,4 +38,6 @@ for runtime_test in "${runtime_tests[@]}"; do
# These tests will have side affects in the future (modifying the global FIPS state) and must be run in separate process
$original_test --gtest_filter=FIPSCallback.PowerOnTests
$original_test --gtest_filter=FIPSCallback.DRBGRuntime
$original_test --gtest_filter=FIPSCallback.RSARuntimeTest
$original_test --gtest_filter=FIPSCallback.ECDSARuntimeTest
done

0 comments on commit a12e3e3

Please sign in to comment.