diff --git a/crypto/crypto_test.cc b/crypto/crypto_test.cc index 941233ea02..972078267b 100644 --- a/crypto/crypto_test.cc +++ b/crypto/crypto_test.cc @@ -89,3 +89,10 @@ TEST(CryptoTest, FIPSdownstreamPrecompilationFlag) { #endif } #endif // defined(BORINGSSL_FIPS) + +#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN) +TEST(Crypto, OnDemandIntegrityTest) { + BORINGSSL_integrity_test(); +} +#endif + diff --git a/crypto/fipsmodule/bcm.c b/crypto/fipsmodule/bcm.c index 263b58e05d..75f603462f 100644 --- a/crypto/fipsmodule/bcm.c +++ b/crypto/fipsmodule/bcm.c @@ -191,9 +191,29 @@ BORINGSSL_bcm_power_on_self_test(void) { goto err; } + // Per FIPS 140-3 we have to perform the CAST of the HMAC used for integrity + // check before the integrity check itself. So we first call the self-test + // before we calculate the hash of the module. + if (!boringssl_fips_self_test()) { + goto err; + } + #if !defined(OPENSSL_ASAN) // Integrity tests cannot run under ASAN because it involves reading the full // .text section, which triggers the global-buffer overflow detection. + if (!BORINGSSL_integrity_test()) { + goto err; + } +#endif // OPENSSL_ASAN + + return; + +err: + BORINGSSL_FIPS_abort(); +} + +#if !defined(OPENSSL_ASAN) +int BORINGSSL_integrity_test(void) { const uint8_t *const start = BORINGSSL_bcm_text_start; const uint8_t *const end = BORINGSSL_bcm_text_end; @@ -219,13 +239,6 @@ BORINGSSL_bcm_power_on_self_test(void) { assert_within(rodata_start, kP256Params, rodata_end); assert_within(rodata_start, kPKCS1SigPrefixes, rodata_end); - // Per FIPS 140-3 we have to perform the CAST of the HMAC used for integrity - // check before the integrity check itself. So we first call the self-test - // before we calculate the hash of the module. - if (!boringssl_fips_self_test()) { - goto err; - } - uint8_t result[SHA256_DIGEST_LENGTH]; const EVP_MD *const kHashFunction = EVP_sha256(); @@ -236,7 +249,7 @@ BORINGSSL_bcm_power_on_self_test(void) { if (!HMAC_Init_ex(&hmac_ctx, kHMACKey, sizeof(kHMACKey), kHashFunction, NULL /* no ENGINE */)) { fprintf(stderr, "HMAC_Init_ex failed.\n"); - goto err; + return 0; } BORINGSSL_maybe_set_module_text_permissions(PROT_READ | PROT_EXEC); @@ -256,27 +269,20 @@ BORINGSSL_bcm_power_on_self_test(void) { if (!HMAC_Final(&hmac_ctx, result, &result_len) || result_len != sizeof(result)) { fprintf(stderr, "HMAC failed.\n"); - goto err; + return 0; } HMAC_CTX_cleanup(&hmac_ctx); const uint8_t *expected = BORINGSSL_bcm_text_hash; if (!check_test(expected, result, sizeof(result), "FIPS integrity test")) { - goto err; - } - -#else - if (!BORINGSSL_self_test()) { - goto err; + return 0; } -#endif // OPENSSL_ASAN - return; - -err: - BORINGSSL_FIPS_abort(); + OPENSSL_cleanse(result, sizeof(result)); // FIPS 140-3, AS05.10. + return 1; } +#endif // OPENSSL_ASAN void BORINGSSL_FIPS_abort(void) { for (;;) { diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h index 93b1a9bb28..5ffd0ad0e9 100644 --- a/include/openssl/crypto.h +++ b/include/openssl/crypto.h @@ -59,6 +59,12 @@ OPENSSL_EXPORT int CRYPTO_has_asm(void); // success and zero on error. OPENSSL_EXPORT int BORINGSSL_self_test(void); +// BORINGSSL_integrity_test triggers the module's integrity test where the code +// and data of the module is matched against a hash injected at build time. It +// returns one on success or zero if there's a mismatch. This function only +// exists if the module was built in FIPS mode without ASAN. +OPENSSL_EXPORT int BORINGSSL_integrity_test(void); + // CRYPTO_pre_sandbox_init initializes the crypto library, pre-acquiring some // unusual resources to aid running in sandboxed environments. It is safe to // call this function multiple times and concurrently from multiple threads.