Add self-test suppression flag file for Android FIPS builds.

FIPS IG 9.11 allows for a shared library to skip running self tests if
they have already run successfully for a given module and environment.
This change has Android FIPS builds read and write a flag file in
/dev/boringssl to implement this. The flag file is named after the hash
of the module to ensure specificity.

Change-Id: I5c4e7b6244831746e61c5f78f703b0b4fb0ddd10
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/37204
Reviewed-by: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/bcm.c b/crypto/fipsmodule/bcm.c
index 7666222..559ade3 100644
--- a/crypto/fipsmodule/bcm.c
+++ b/crypto/fipsmodule/bcm.c
@@ -109,6 +109,8 @@
 extern const uint8_t BORINGSSL_bcm_rodata_start[];
 extern const uint8_t BORINGSSL_bcm_rodata_end[];
 #endif
+#else
+static const uint8_t BORINGSSL_bcm_text_hash[SHA512_DIGEST_LENGTH] = {0};
 #endif
 
 static void __attribute__((constructor))
@@ -161,7 +163,7 @@
   }
 #endif
 
-  if (!BORINGSSL_self_test()) {
+  if (!BORINGSSL_self_test(BORINGSSL_bcm_text_hash)) {
     goto err;
   }
 
diff --git a/crypto/fipsmodule/self_check/self_check.c b/crypto/fipsmodule/self_check/self_check.c
index 1bbefa9..3d47e69 100644
--- a/crypto/fipsmodule/self_check/self_check.c
+++ b/crypto/fipsmodule/self_check/self_check.c
@@ -35,6 +35,15 @@
 // compile this.
 #if !defined(_MSC_VER)
 
+#if defined(BORINGSSL_FIPS) && defined(OPENSSL_ANDROID)
+// FIPS builds on Android will attempt to write flag files to
+// /dev/boringssl/selftest/ named after the module hash. If the flag file
+// exists, it's assumed that self-tests have already passed and thus do not need
+// to be repeated.
+#define BORINGSSL_FIPS_SELF_TEST_FLAG_FILE
+static const char kFlagPrefix[] = "/dev/boringssl/selftest/";
+#endif
+
 static void hexdump(const uint8_t *in, size_t len) {
   for (size_t i = 0; i < len; i++) {
     fprintf(stderr, "%02x", in[i]);
@@ -227,7 +236,30 @@
   return ec_key;
 }
 
-int BORINGSSL_self_test(void) {
+int BORINGSSL_self_test(
+    const uint8_t module_sha512_hash[SHA512_DIGEST_LENGTH]) {
+#if defined(BORINGSSL_FIPS_SELF_TEST_FLAG_FILE)
+  // Test whether the flag file exists.
+  char flag_path[sizeof(kFlagPrefix) + 2*SHA512_DIGEST_LENGTH];
+  memcpy(flag_path, kFlagPrefix, sizeof(kFlagPrefix) - 1);
+  static const char kHexTable[17] = "0123456789abcdef";
+  uint8_t module_hash_sum = 0;
+  for (size_t i = 0; i < SHA512_DIGEST_LENGTH; i++) {
+    module_hash_sum |= module_sha512_hash[i];
+    flag_path[sizeof(kFlagPrefix) - 1 + 2 * i] =
+        kHexTable[module_sha512_hash[i] >> 4];
+    flag_path[sizeof(kFlagPrefix) - 1 + 2 * i + 1] =
+        kHexTable[module_sha512_hash[i] & 15];
+  }
+  flag_path[sizeof(flag_path) - 1] = 0;
+
+  const int flag_path_valid = (module_hash_sum != 0);
+  if (flag_path_valid && access(flag_path, F_OK) == 0) {
+    // Flag file found. Skip self-tests.
+    return 1;
+  }
+#endif // BORINGSSL_FIPS_SELF_TEST_FLAG_FILE
+
   static const uint8_t kAESKey[16] = "BoringCrypto Key";
   static const uint8_t kAESIV[16] = {0};
   static const uint8_t kPlaintext[64] =
@@ -577,6 +609,16 @@
 
   ret = 1;
 
+#if defined(BORINGSSL_FIPS_SELF_TEST_FLAG_FILE)
+  // Tests were successful. Write flag file if requested.
+  if (flag_path_valid) {
+    const int fd = open(flag_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    if (fd >= 0) {
+      close(fd);
+    }
+  }
+#endif  // BORINGSSL_FIPS_SELF_TEST_FLAG_FILE
+
 err:
   EVP_AEAD_CTX_cleanup(&aead_ctx);
   RSA_free(rsa_key);
diff --git a/crypto/self_test.cc b/crypto/self_test.cc
index c20b5de..b0c769d 100644
--- a/crypto/self_test.cc
+++ b/crypto/self_test.cc
@@ -19,6 +19,7 @@
 
 TEST(SelfTests, KAT) {
 #if !defined(_MSC_VER)
-  EXPECT_TRUE(BORINGSSL_self_test());
+  const uint8_t zero_hash[SHA512_DIGEST_LENGTH] = {0};
+  EXPECT_TRUE(BORINGSSL_self_test(zero_hash));
 #endif
 }
diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h
index d35cf71..b9d8449 100644
--- a/include/openssl/crypto.h
+++ b/include/openssl/crypto.h
@@ -16,6 +16,7 @@
 #define OPENSSL_HEADER_CRYPTO_H
 
 #include <openssl/base.h>
+#include <openssl/sha.h>
 
 // Upstream OpenSSL defines |OPENSSL_malloc|, etc., in crypto.h rather than
 // mem.h.
@@ -58,9 +59,13 @@
 // which case it returns one.
 OPENSSL_EXPORT int FIPS_mode(void);
 
-// BORINGSSL_self_test triggers the FIPS KAT-based self tests. It returns one
-// on success and zero on error.
-OPENSSL_EXPORT int BORINGSSL_self_test(void);
+// BORINGSSL_self_test triggers the FIPS KAT-based self tests. It returns one on
+// success and zero on error. The argument is the integrity hash of the FIPS
+// module and may be used to check and write flag files to suppress duplicate
+// self-tests. If it is all zeros, no flag file will be checked nor written and
+// tests will always be run.
+OPENSSL_EXPORT int BORINGSSL_self_test(
+    const uint8_t module_sha512_hash[SHA512_DIGEST_LENGTH]);
 
 
 // Deprecated functions.