Allow the integrity test to be run on demand.

Change-Id: If45a98427516c5a26f2048adb8f8d0415417dcf8
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/51987
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/crypto_test.cc b/crypto/crypto_test.cc
index 228aaf2..caccba5 100644
--- a/crypto/crypto_test.cc
+++ b/crypto/crypto_test.cc
@@ -152,3 +152,9 @@
   EXPECT_FALSE(FIPS_query_algorithm_status("FakeEncrypt"));
   EXPECT_FALSE(FIPS_query_algorithm_status(""));
 }
+
+#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 1219bc7..faff6c4 100644
--- a/crypto/fipsmodule/bcm.c
+++ b/crypto/fipsmodule/bcm.c
@@ -169,6 +169,23 @@
 #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
+
+  if (!boringssl_self_test_startup()) {
+    goto err;
+  }
+
+  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;
 
@@ -198,14 +215,14 @@
   const EVP_MD *const kHashFunction = EVP_sha256();
   if (!boringssl_self_test_sha256() ||
       !boringssl_self_test_hmac_sha256()) {
-    goto err;
+    return 0;
   }
 #else
   uint8_t result[SHA512_DIGEST_LENGTH];
   const EVP_MD *const kHashFunction = EVP_sha512();
   if (!boringssl_self_test_sha512() ||
       !boringssl_self_test_hmac_sha256()) {
-    goto err;
+    return 0;
   }
 #endif
 
@@ -216,7 +233,7 @@
   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);
@@ -236,7 +253,7 @@
   if (!HMAC_Final(&hmac_ctx, result, &result_len) ||
       result_len != sizeof(result)) {
     fprintf(stderr, "HMAC failed.\n");
-    goto err;
+    return 0;
   }
   HMAC_CTX_cleanse(&hmac_ctx); // FIPS 140-3, AS05.10.
 
@@ -244,22 +261,14 @@
 
   if (!check_test(expected, result, sizeof(result), "FIPS integrity test")) {
 #if !defined(BORINGSSL_FIPS_BREAK_TESTS)
-    goto err;
+    return 0;
 #endif
   }
 
   OPENSSL_cleanse(result, sizeof(result)); // FIPS 140-3, AS05.10.
-#endif  // OPENSSL_ASAN
-
-  if (!boringssl_self_test_startup()) {
-    goto err;
-  }
-
-  return;
-
-err:
-  BORINGSSL_FIPS_abort();
+  return 1;
 }
+#endif  // OPENSSL_ASAN
 
 void BORINGSSL_FIPS_abort(void) {
   for (;;) {
diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h
index 15db776..117b347 100644
--- a/include/openssl/crypto.h
+++ b/include/openssl/crypto.h
@@ -59,6 +59,12 @@
 // 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.