Make FFDH self tests lazy.

Change-Id: I7ac046a2422d79b77a231ab65325402658144390
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/51566
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/dh/dh.c b/crypto/fipsmodule/dh/dh.c
index ab596e9..b59afc6 100644
--- a/crypto/fipsmodule/dh/dh.c
+++ b/crypto/fipsmodule/dh/dh.c
@@ -64,6 +64,7 @@
 #include <openssl/mem.h>
 #include <openssl/thread.h>
 
+#include "internal.h"
 #include "../../internal.h"
 #include "../bn/internal.h"
 
@@ -186,6 +187,8 @@
 }
 
 int DH_generate_key(DH *dh) {
+  boringssl_ensure_ffdh_self_test();
+
   int ok = 0;
   int generate_new_key = 0;
   BN_CTX *ctx = NULL;
@@ -322,7 +325,8 @@
   return ret;
 }
 
-int DH_compute_key_padded(unsigned char *out, const BIGNUM *peers_key, DH *dh) {
+int dh_compute_key_padded_no_self_test(unsigned char *out,
+                                       const BIGNUM *peers_key, DH *dh) {
   BN_CTX *ctx = BN_CTX_new();
   if (ctx == NULL) {
     return -1;
@@ -343,7 +347,15 @@
   return ret;
 }
 
+int DH_compute_key_padded(unsigned char *out, const BIGNUM *peers_key, DH *dh) {
+  boringssl_ensure_ffdh_self_test();
+
+  return dh_compute_key_padded_no_self_test(out, peers_key, dh);
+}
+
 int DH_compute_key(unsigned char *out, const BIGNUM *peers_key, DH *dh) {
+  boringssl_ensure_ffdh_self_test();
+
   BN_CTX *ctx = BN_CTX_new();
   if (ctx == NULL) {
     return -1;
diff --git a/crypto/fipsmodule/dh/internal.h b/crypto/fipsmodule/dh/internal.h
new file mode 100644
index 0000000..c40172d
--- /dev/null
+++ b/crypto/fipsmodule/dh/internal.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2022, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_FIPSMODULE_DH_INTERNAL_H
+#define OPENSSL_HEADER_CRYPTO_FIPSMODULE_DH_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// dh_compute_key_padded_no_self_test does the same as |DH_compute_key_padded|,
+// but doesn't try to run the self-test first. This is for use in the self tests
+// themselves, to prevent an infinite loop.
+int dh_compute_key_padded_no_self_test(unsigned char *out,
+                                       const BIGNUM *peers_key, DH *dh);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif  // OPENSSL_HEADER_CRYPTO_FIPSMODULE_DH_INTERNAL_H
diff --git a/crypto/fipsmodule/self_check/self_check.c b/crypto/fipsmodule/self_check/self_check.c
index 7191ca4..b7cd868 100644
--- a/crypto/fipsmodule/self_check/self_check.c
+++ b/crypto/fipsmodule/self_check/self_check.c
@@ -31,6 +31,7 @@
 #include <openssl/sha.h>
 
 #include "../../internal.h"
+#include "../dh/internal.h"
 #include "../ec/internal.h"
 #include "../ecdsa/internal.h"
 #include "../rand/internal.h"
@@ -534,40 +535,7 @@
   return ret;
 }
 
-#if defined(BORINGSSL_FIPS)
-
-static void run_self_test_rsa(void) {
-  if (!boringssl_self_test_rsa()) {
-    BORINGSSL_FIPS_abort();
-  }
-}
-
-DEFINE_STATIC_ONCE(g_self_test_once_rsa);
-
-void boringssl_ensure_rsa_self_test(void) {
-  CRYPTO_once(g_self_test_once_rsa_bss_get(), run_self_test_rsa);
-}
-
-static void run_self_test_ecc(void) {
-  if (!boringssl_self_test_ecc()) {
-    BORINGSSL_FIPS_abort();
-  }
-}
-
-DEFINE_STATIC_ONCE(g_self_test_once_ecc);
-
-void boringssl_ensure_ecc_self_test(void) {
-  CRYPTO_once(g_self_test_once_ecc_bss_get(), run_self_test_ecc);
-}
-
-#endif  // BORINGSSL_FIPS
-
-
-// Startup self tests.
-//
-// These tests are run at process start when in FIPS mode.
-
-static int boringssl_self_test_slow(void) {
+static int boringssl_self_test_ffdh(void) {
   int ret = 0;
   DH *dh = NULL;
   BIGNUM *ffdhe2048_value = NULL;
@@ -628,7 +596,8 @@
   dh = self_test_dh();
   uint8_t dh_out[sizeof(kDHOutput)];
   if (dh == NULL || ffdhe2048_value == NULL || sizeof(dh_out) != DH_size(dh) ||
-      DH_compute_key_padded(dh_out, ffdhe2048_value, dh) != sizeof(dh_out) ||
+      dh_compute_key_padded_no_self_test(dh_out, ffdhe2048_value, dh) !=
+          sizeof(dh_out) ||
       !check_test(kDHOutput, dh_out, sizeof(dh_out), "FFC DH")) {
     fprintf(stderr, "FFDH failed.\n");
     goto err;
@@ -643,6 +612,51 @@
   return ret;
 }
 
+#if defined(BORINGSSL_FIPS)
+
+static void run_self_test_rsa(void) {
+  if (!boringssl_self_test_rsa()) {
+    BORINGSSL_FIPS_abort();
+  }
+}
+
+DEFINE_STATIC_ONCE(g_self_test_once_rsa);
+
+void boringssl_ensure_rsa_self_test(void) {
+  CRYPTO_once(g_self_test_once_rsa_bss_get(), run_self_test_rsa);
+}
+
+static void run_self_test_ecc(void) {
+  if (!boringssl_self_test_ecc()) {
+    BORINGSSL_FIPS_abort();
+  }
+}
+
+DEFINE_STATIC_ONCE(g_self_test_once_ecc);
+
+void boringssl_ensure_ecc_self_test(void) {
+  CRYPTO_once(g_self_test_once_ecc_bss_get(), run_self_test_ecc);
+}
+
+static void run_self_test_ffdh(void) {
+  if (!boringssl_self_test_ffdh()) {
+    BORINGSSL_FIPS_abort();
+  }
+}
+
+DEFINE_STATIC_ONCE(g_self_test_once_ffdh);
+
+void boringssl_ensure_ffdh_self_test(void) {
+  CRYPTO_once(g_self_test_once_ffdh_bss_get(), run_self_test_ffdh);
+}
+
+#endif  // BORINGSSL_FIPS
+
+
+// Startup self tests.
+//
+// These tests are run at process start when in FIPS mode.
+
 int boringssl_self_test_sha256(void) {
   static const uint8_t kInput[16] = {
       0xff, 0x3b, 0x85, 0x7d, 0xa7, 0x23, 0x6a, 0x2b,
@@ -930,10 +944,10 @@
 
 int BORINGSSL_self_test(void) {
   if (!boringssl_self_test_fast() ||
-      !boringssl_self_test_slow() ||
       // When requested to run self tests, also run the lazy tests.
       !boringssl_self_test_rsa() ||
-      !boringssl_self_test_ecc()) {
+      !boringssl_self_test_ecc() ||
+      !boringssl_self_test_ffdh()) {
     return 0;
   }
 
@@ -942,12 +956,7 @@
 
 #if defined(BORINGSSL_FIPS)
 int boringssl_self_test_startup(void) {
-  if (!boringssl_self_test_fast() ||
-      !boringssl_self_test_slow()) {
-    return 0;
-  }
-
-  return 1;
+  return boringssl_self_test_fast();
 }
 #endif
 
diff --git a/crypto/internal.h b/crypto/internal.h
index d8647eb..78dbbbf 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -957,12 +957,18 @@
 // address space if unsuccessful.
 void boringssl_ensure_ecc_self_test(void);
 
+// boringssl_ensure_ffdh_self_test checks whether the FFDH self-test has been
+// run in this address space. If not, it runs it and crashes the address space
+// if unsuccessful.
+void boringssl_ensure_ffdh_self_test(void);
+
 #else
 
 // Outside of FIPS mode, the lazy tests are no-ops.
 
 OPENSSL_INLINE void boringssl_ensure_rsa_self_test(void) {}
 OPENSSL_INLINE void boringssl_ensure_ecc_self_test(void) {}
+OPENSSL_INLINE void boringssl_ensure_ffdh_self_test(void) {}
 
 #endif  // FIPS