fips: add counters.

In order to provide evidence to auditors that high-level functions end
up calling into the FIPS module, provide counters that allow for such
monitoring.

Change-Id: I55d45299f3050bf58077715ffa280210db156116
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46124
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/crypto_test.cc b/crypto/crypto_test.cc
index f6c2374..ccb0956 100644
--- a/crypto/crypto_test.cc
+++ b/crypto/crypto_test.cc
@@ -19,6 +19,7 @@
 
 #include <openssl/base.h>
 #include <openssl/crypto.h>
+#include <openssl/cipher.h>
 
 #include <gtest/gtest.h>
 
@@ -33,3 +34,33 @@
   EXPECT_EQ(expected,
             std::string(OPENSSL_VERSION_TEXT).substr(0, strlen(expected)));
 }
+
+#if defined(BORINGSSL_FIPS_COUNTERS)
+TEST(CryptoTest, FIPSCountersEVP) {
+  constexpr struct {
+    const EVP_CIPHER *(*cipher)();
+    fips_counter_t counter;
+  } kTests[] = {
+    {
+        EVP_aes_128_gcm,
+        fips_counter_evp_aes_128_gcm,
+    },
+    {
+        EVP_aes_256_gcm,
+        fips_counter_evp_aes_256_gcm,
+    },
+  };
+
+  uint8_t key[EVP_MAX_KEY_LENGTH] = {0};
+  uint8_t iv[EVP_MAX_IV_LENGTH] = {1};
+
+  for (const auto& test : kTests) {
+    const size_t before = FIPS_read_counter(test.counter);
+
+    bssl::ScopedEVP_CIPHER_CTX ctx;
+    ASSERT_TRUE(EVP_EncryptInit_ex(ctx.get(), test.cipher(), /*engine=*/nullptr,
+                                   key, iv));
+    ASSERT_GT(FIPS_read_counter(test.counter), before);
+  }
+}
+#endif  // BORINGSSL_FIPS_COUNTERS
diff --git a/crypto/fipsmodule/CMakeLists.txt b/crypto/fipsmodule/CMakeLists.txt
index 83cf3f7..d22100f 100644
--- a/crypto/fipsmodule/CMakeLists.txt
+++ b/crypto/fipsmodule/CMakeLists.txt
@@ -223,7 +223,6 @@
     OBJECT
 
     fips_shared_support.c
-    is_fips.c
   )
 
   add_dependencies(fipsmodule global_target)
@@ -240,7 +239,6 @@
     OBJECT
 
     fips_shared_support.c
-    is_fips.c
   )
 
   add_dependencies(fipsmodule global_target)
@@ -273,7 +271,6 @@
 
     bcm.c
     fips_shared_support.c
-    is_fips.c
 
     ${BCM_ASM_SOURCES}
   )
diff --git a/crypto/fipsmodule/bcm.c b/crypto/fipsmodule/bcm.c
index 601c4a8..61479ed 100644
--- a/crypto/fipsmodule/bcm.c
+++ b/crypto/fipsmodule/bcm.c
@@ -79,6 +79,7 @@
 #include "ec/simple_mul.c"
 #include "ec/util.c"
 #include "ec/wnaf.c"
+#include "fips.c"
 #include "hmac/hmac.c"
 #include "md4/md4.c"
 #include "md5/md5.c"
diff --git a/crypto/fipsmodule/cipher/e_aes.c b/crypto/fipsmodule/cipher/e_aes.c
index 6df2b7b..9186186 100644
--- a/crypto/fipsmodule/cipher/e_aes.c
+++ b/crypto/fipsmodule/cipher/e_aes.c
@@ -353,6 +353,17 @@
   if (!iv && !key) {
     return 1;
   }
+
+  switch (ctx->key_len) {
+    case 16:
+      boringssl_fips_inc_counter(fips_counter_evp_aes_128_gcm);
+      break;
+
+    case 32:
+      boringssl_fips_inc_counter(fips_counter_evp_aes_256_gcm);
+      break;
+  }
+
   if (key) {
     OPENSSL_memset(&gctx->gcm, 0, sizeof(gctx->gcm));
     gctx->ctr = aes_ctr_set_key(&gctx->ks.ks, &gctx->gcm.gcm_key, NULL, key,
diff --git a/crypto/fipsmodule/fips.c b/crypto/fipsmodule/fips.c
new file mode 100644
index 0000000..07554e4
--- /dev/null
+++ b/crypto/fipsmodule/fips.c
@@ -0,0 +1,76 @@
+/* Copyright (c) 2017, 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. */
+
+#include <openssl/crypto.h>
+
+#include "../internal.h"
+#include "delocate.h"
+
+
+int FIPS_mode(void) {
+#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN)
+  return 1;
+#else
+  return 0;
+#endif
+}
+
+int FIPS_mode_set(int on) { return on == FIPS_mode(); }
+
+#if defined(BORINGSSL_FIPS_COUNTERS)
+
+size_t FIPS_read_counter(enum fips_counter_t counter) {
+  if (counter < 0 || counter > fips_counter_max) {
+    abort();
+  }
+
+  const size_t *array =
+      CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_FIPS_COUNTERS);
+  if (!array) {
+    return 0;
+  }
+
+  return array[counter];
+}
+
+void boringssl_fips_inc_counter(enum fips_counter_t counter) {
+  if (counter < 0 || counter > fips_counter_max) {
+    abort();
+  }
+
+  size_t *array =
+      CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_FIPS_COUNTERS);
+  if (!array) {
+    array = OPENSSL_malloc(sizeof(size_t) * (fips_counter_max + 1));
+    if (!array) {
+      return;
+    }
+    if (!CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_FIPS_COUNTERS, array,
+                                 OPENSSL_free)) {
+      // |OPENSSL_free| has already been called by |CRYPTO_set_thread_local|.
+      return;
+    }
+  }
+
+  array[counter]++;
+}
+
+#else
+
+size_t FIPS_read_counter(enum fips_counter_t counter) { return 0; }
+
+// boringssl_fips_inc_counter is a no-op, inline function in internal.h in this
+// case. That should let the compiler optimise away the callsites.
+
+#endif
diff --git a/crypto/fipsmodule/is_fips.c b/crypto/fipsmodule/is_fips.c
deleted file mode 100644
index 2f8e408..0000000
--- a/crypto/fipsmodule/is_fips.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2017, 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. */
-
-#include <openssl/crypto.h>
-
-
-// This file exists in order to give the fipsmodule target, in non-FIPS mode,
-// something to compile.
-
-int FIPS_mode(void) {
-#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN)
-  return 1;
-#else
-  return 0;
-#endif
-}
-
-int FIPS_mode_set(int on) { return on == FIPS_mode(); }
diff --git a/crypto/internal.h b/crypto/internal.h
index edba9f9..c574645 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -109,6 +109,7 @@
 #ifndef OPENSSL_HEADER_CRYPTO_INTERNAL_H
 #define OPENSSL_HEADER_CRYPTO_INTERNAL_H
 
+#include <openssl/crypto.h>
 #include <openssl/ex_data.h>
 #include <openssl/stack.h>
 #include <openssl/thread.h>
@@ -607,6 +608,7 @@
 typedef enum {
   OPENSSL_THREAD_LOCAL_ERR = 0,
   OPENSSL_THREAD_LOCAL_RAND,
+  OPENSSL_THREAD_LOCAL_FIPS_COUNTERS,
   OPENSSL_THREAD_LOCAL_TEST,
   NUM_OPENSSL_THREAD_LOCALS,
 } thread_local_data_t;
@@ -826,6 +828,11 @@
 int boringssl_fips_self_test(const uint8_t *module_hash,
                              size_t module_hash_len);
 
+#if defined(BORINGSSL_FIPS_COUNTERS)
+void boringssl_fips_inc_counter(enum fips_counter_t counter);
+#else
+OPENSSL_INLINE void boringssl_fips_inc_counter(enum fips_counter_t counter) {}
+#endif
 
 #if defined(__cplusplus)
 }  // extern C
diff --git a/include/openssl/crypto.h b/include/openssl/crypto.h
index b820e40..e3773e5 100644
--- a/include/openssl/crypto.h
+++ b/include/openssl/crypto.h
@@ -55,10 +55,6 @@
 // in which case it returns zero.
 OPENSSL_EXPORT int CRYPTO_has_asm(void);
 
-// FIPS_mode returns zero unless BoringSSL is built with BORINGSSL_FIPS, in
-// 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);
@@ -72,6 +68,28 @@
 OPENSSL_EXPORT void CRYPTO_pre_sandbox_init(void);
 
 
+// FIPS monitoring
+
+// FIPS_mode returns zero unless BoringSSL is built with BORINGSSL_FIPS, in
+// which case it returns one.
+OPENSSL_EXPORT int FIPS_mode(void);
+
+// fips_counter_t denotes specific APIs/algorithms. A counter is maintained for
+// each in FIPS mode so that tests can be written to assert that the expected,
+// FIPS functions are being called by a certain peice of code.
+enum fips_counter_t {
+  fips_counter_evp_aes_128_gcm = 0,
+  fips_counter_evp_aes_256_gcm = 1,
+
+  fips_counter_max = 1,
+};
+
+// FIPS_read_counter returns a counter of the number of times the specific
+// function denoted by |counter| has been used. This always returns zero unless
+// BoringSSL was built with BORINGSSL_FIPS_COUNTERS defined.
+OPENSSL_EXPORT size_t FIPS_read_counter(enum fips_counter_t counter);
+
+
 // Deprecated functions.
 
 // OPENSSL_VERSION_TEXT contains a string the identifies the version of