Add a service indicator for FIPS 140-3.

This is cribbed, with perimssion, from AWS-LC. The FIPS service
indicator[1] signals when an approved service has been completed.

[1] FIPS 140-3 IG 2.4.C

Change-Id: Ib40210d69b3823f4d2a500b23a1606f8d6942f81
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/52568
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 610e068..99cb4b5 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -530,6 +530,7 @@
   fipsmodule/modes/gcm_test.cc
   fipsmodule/rand/ctrdrbg_test.cc
   fipsmodule/rand/fork_detect_test.cc
+  fipsmodule/service_indicator/service_indicator_test.cc
   fipsmodule/sha/sha_test.cc
   hkdf/hkdf_test.cc
   hpke/hpke_test.cc
diff --git a/crypto/evp/digestsign.c b/crypto/evp/digestsign.c
index 6e4d305..ec9322b 100644
--- a/crypto/evp/digestsign.c
+++ b/crypto/evp/digestsign.c
@@ -59,6 +59,9 @@
 
 #include "internal.h"
 #include "../fipsmodule/digest/internal.h"
+#include "../fipsmodule/service_indicator/internal.h"
+
+// TODO(agl): this will have to be moved into the FIPS module.
 
 
 enum evp_sign_verify_t {
@@ -159,11 +162,17 @@
     uint8_t md[EVP_MAX_MD_SIZE];
     unsigned int mdlen;
 
+    FIPS_service_indicator_lock_state();
     EVP_MD_CTX_init(&tmp_ctx);
     ret = EVP_MD_CTX_copy_ex(&tmp_ctx, ctx) &&
           EVP_DigestFinal_ex(&tmp_ctx, md, &mdlen) &&
           EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, md, mdlen);
     EVP_MD_CTX_cleanup(&tmp_ctx);
+    FIPS_service_indicator_unlock_state();
+
+    if (ret) {
+      EVP_DigestSign_verify_service_indicator(ctx);
+    }
 
     return ret;
   } else {
@@ -184,48 +193,76 @@
   uint8_t md[EVP_MAX_MD_SIZE];
   unsigned int mdlen;
 
+  FIPS_service_indicator_lock_state();
   EVP_MD_CTX_init(&tmp_ctx);
   ret = EVP_MD_CTX_copy_ex(&tmp_ctx, ctx) &&
         EVP_DigestFinal_ex(&tmp_ctx, md, &mdlen) &&
         EVP_PKEY_verify(ctx->pctx, sig, sig_len, md, mdlen);
+  FIPS_service_indicator_unlock_state();
   EVP_MD_CTX_cleanup(&tmp_ctx);
 
+  if (ret) {
+    EVP_DigestVerify_verify_service_indicator(ctx);
+  }
+
   return ret;
 }
 
 int EVP_DigestSign(EVP_MD_CTX *ctx, uint8_t *out_sig, size_t *out_sig_len,
                    const uint8_t *data, size_t data_len) {
+  FIPS_service_indicator_lock_state();
+  int ret = 0;
+
   if (uses_prehash(ctx, evp_sign)) {
     // If |out_sig| is NULL, the caller is only querying the maximum output
     // length. |data| should only be incorporated in the final call.
     if (out_sig != NULL &&
         !EVP_DigestSignUpdate(ctx, data, data_len)) {
-      return 0;
+      goto end;
     }
 
-    return EVP_DigestSignFinal(ctx, out_sig, out_sig_len);
+    ret = EVP_DigestSignFinal(ctx, out_sig, out_sig_len);
+    goto end;
   }
 
   if (ctx->pctx->pmeth->sign_message == NULL) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-    return 0;
+    goto end;
   }
 
-  return ctx->pctx->pmeth->sign_message(ctx->pctx, out_sig, out_sig_len, data,
-                                        data_len);
+  ret = ctx->pctx->pmeth->sign_message(ctx->pctx, out_sig, out_sig_len, data,
+                                       data_len);
+
+end:
+  FIPS_service_indicator_unlock_state();
+  if (ret) {
+    EVP_DigestSign_verify_service_indicator(ctx);
+  }
+  return ret;
 }
 
 int EVP_DigestVerify(EVP_MD_CTX *ctx, const uint8_t *sig, size_t sig_len,
                      const uint8_t *data, size_t len) {
+  FIPS_service_indicator_lock_state();
+  int ret = 0;
+
   if (uses_prehash(ctx, evp_verify)) {
-    return EVP_DigestVerifyUpdate(ctx, data, len) &&
-           EVP_DigestVerifyFinal(ctx, sig, sig_len);
+    ret = EVP_DigestVerifyUpdate(ctx, data, len) &&
+          EVP_DigestVerifyFinal(ctx, sig, sig_len);
+    goto end;
   }
 
   if (ctx->pctx->pmeth->verify_message == NULL) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-    return 0;
+    goto end;
   }
 
-  return ctx->pctx->pmeth->verify_message(ctx->pctx, sig, sig_len, data, len);
+  ret = ctx->pctx->pmeth->verify_message(ctx->pctx, sig, sig_len, data, len);
+
+end:
+  FIPS_service_indicator_unlock_state();
+  if (ret) {
+    EVP_DigestVerify_verify_service_indicator(ctx);
+  }
+  return ret;
 }
diff --git a/crypto/fipsmodule/aes/key_wrap.c b/crypto/fipsmodule/aes/key_wrap.c
index 9a5b28d..0ec5f26 100644
--- a/crypto/fipsmodule/aes/key_wrap.c
+++ b/crypto/fipsmodule/aes/key_wrap.c
@@ -55,6 +55,7 @@
 #include <openssl/mem.h>
 
 #include "../../internal.h"
+#include "../service_indicator/internal.h"
 
 
 // kDefaultIV is the default IV value given in RFC 3394, 2.2.3.1.
@@ -98,6 +99,7 @@
   }
 
   OPENSSL_memcpy(out, A, 8);
+  FIPS_service_indicator_update_state();
   return (int)in_len + 8;
 }
 
@@ -151,6 +153,7 @@
     return -1;
   }
 
+  FIPS_service_indicator_update_state();
   return (int)in_len - 8;
 }
 
@@ -190,12 +193,15 @@
   assert(padded_len >= 8);
   memset(padded_in + padded_len - 8, 0, 8);
   memcpy(padded_in, in, in_len);
+  FIPS_service_indicator_lock_state();
   const int ret = AES_wrap_key(key, block, out, padded_in, padded_len);
+  FIPS_service_indicator_unlock_state();
   OPENSSL_free(padded_in);
   if (ret < 0) {
     return 0;
   }
   *out_len = ret;
+  FIPS_service_indicator_update_state();
   return 1;
 }
 
@@ -232,5 +238,9 @@
   }
 
   *out_len = constant_time_select_w(ok, claimed_len, 0);
-  return ok & 1;
+  const int ret = ok & 1;
+  if (ret) {
+    FIPS_service_indicator_update_state();
+  }
+  return ret;
 }
diff --git a/crypto/fipsmodule/aes/mode_wrappers.c b/crypto/fipsmodule/aes/mode_wrappers.c
index d29fb27..10d98a6 100644
--- a/crypto/fipsmodule/aes/mode_wrappers.c
+++ b/crypto/fipsmodule/aes/mode_wrappers.c
@@ -52,6 +52,7 @@
 
 #include "../aes/internal.h"
 #include "../modes/internal.h"
+#include "../service_indicator/internal.h"
 
 
 void AES_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
@@ -74,6 +75,8 @@
     CRYPTO_ctr128_encrypt_ctr32(in, out, len, key, ivec, ecount_buf, num,
                                 aes_nohw_ctr32_encrypt_blocks);
   }
+
+  FIPS_service_indicator_update_state();
 }
 
 void AES_ecb_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key,
@@ -86,24 +89,23 @@
   } else {
     AES_decrypt(in, out, key);
   }
+
+  FIPS_service_indicator_update_state();
 }
 
 void AES_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t len,
                      const AES_KEY *key, uint8_t *ivec, const int enc) {
   if (hwaes_capable()) {
     aes_hw_cbc_encrypt(in, out, len, key, ivec, enc);
-    return;
-  }
-
-  if (!vpaes_capable()) {
+  } else if (!vpaes_capable()) {
     aes_nohw_cbc_encrypt(in, out, len, key, ivec, enc);
-    return;
-  }
-  if (enc) {
+  } else if (enc) {
     CRYPTO_cbc128_encrypt(in, out, len, key, ivec, AES_encrypt);
   } else {
     CRYPTO_cbc128_decrypt(in, out, len, key, ivec, AES_decrypt);
   }
+
+  FIPS_service_indicator_update_state();
 }
 
 void AES_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t length,
diff --git a/crypto/fipsmodule/bcm.c b/crypto/fipsmodule/bcm.c
index 3381b90..cc80b51 100644
--- a/crypto/fipsmodule/bcm.c
+++ b/crypto/fipsmodule/bcm.c
@@ -99,6 +99,7 @@
 #include "rsa/rsa_impl.c"
 #include "self_check/fips.c"
 #include "self_check/self_check.c"
+#include "service_indicator/service_indicator.c"
 #include "sha/sha1-altivec.c"
 #include "sha/sha1.c"
 #include "sha/sha256.c"
diff --git a/crypto/fipsmodule/bn/random.c b/crypto/fipsmodule/bn/random.c
index f6812f1..36ad679 100644
--- a/crypto/fipsmodule/bn/random.c
+++ b/crypto/fipsmodule/bn/random.c
@@ -115,9 +115,10 @@
 #include <openssl/rand.h>
 #include <openssl/type_check.h>
 
-#include "internal.h"
 #include "../../internal.h"
 #include "../rand/internal.h"
+#include "../service_indicator/internal.h"
+#include "internal.h"
 
 
 int BN_rand(BIGNUM *rnd, int bits, int top, int bottom) {
@@ -155,7 +156,10 @@
     return 0;
   }
 
+  FIPS_service_indicator_lock_state();
   RAND_bytes((uint8_t *)rnd->d, words * sizeof(BN_ULONG));
+  FIPS_service_indicator_unlock_state();
+
   rnd->d[words - 1] &= mask;
   if (top != BN_RAND_TOP_ANY) {
     if (top == BN_RAND_TOP_TWO && bits > 1) {
@@ -270,8 +274,10 @@
 
     // Steps 4 and 5. Use |words| and |mask| together to obtain a string of N
     // bits, where N is the bit length of |max_exclusive|.
+    FIPS_service_indicator_lock_state();
     RAND_bytes_with_additional_data((uint8_t *)out, words * sizeof(BN_ULONG),
                                     additional_data);
+    FIPS_service_indicator_unlock_state();
     out[words - 1] &= mask;
 
     // If out >= max_exclusive or out < min_inclusive, retry. This implements
@@ -313,7 +319,9 @@
   }
 
   // Select a uniform random number with num_bits(max_exclusive) bits.
+  FIPS_service_indicator_lock_state();
   RAND_bytes((uint8_t *)r->d, words * sizeof(BN_ULONG));
+  FIPS_service_indicator_unlock_state();
   r->d[words - 1] &= mask;
 
   // Check, in constant-time, if the value is in range.
diff --git a/crypto/fipsmodule/cipher/cipher.c b/crypto/fipsmodule/cipher/cipher.c
index 64ee544..a126986 100644
--- a/crypto/fipsmodule/cipher/cipher.c
+++ b/crypto/fipsmodule/cipher/cipher.c
@@ -65,6 +65,7 @@
 #include <openssl/nid.h>
 
 #include "internal.h"
+#include "../service_indicator/internal.h"
 #include "../../internal.h"
 
 
@@ -322,24 +323,26 @@
 }
 
 int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) {
-  int n, ret;
+  int n;
   unsigned int i, b, bl;
 
   if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
-    ret = ctx->cipher->cipher(ctx, out, NULL, 0);
-    if (ret < 0) {
+    // When EVP_CIPH_FLAG_CUSTOM_CIPHER is set, the return value of |cipher| is
+    // the number of bytes written, or -1 on error. Otherwise the return value
+    // is one on success and zero on error.
+    const int num_bytes = ctx->cipher->cipher(ctx, out, NULL, 0);
+    if (num_bytes < 0) {
       return 0;
-    } else {
-      *out_len = ret;
     }
-    return 1;
+    *out_len = num_bytes;
+    goto out;
   }
 
   b = ctx->cipher->block_size;
   assert(b <= sizeof(ctx->buf));
   if (b == 1) {
     *out_len = 0;
-    return 1;
+    goto out;
   }
 
   bl = ctx->buf_len;
@@ -349,20 +352,21 @@
       return 0;
     }
     *out_len = 0;
-    return 1;
+    goto out;
   }
 
   n = b - bl;
   for (i = bl; i < b; i++) {
     ctx->buf[i] = n;
   }
-  ret = ctx->cipher->cipher(ctx, out, ctx->buf, b);
-
-  if (ret) {
-    *out_len = b;
+  if (!ctx->cipher->cipher(ctx, out, ctx->buf, b)) {
+    return 0;
   }
+  *out_len = b;
 
-  return ret;
+out:
+  EVP_Cipher_verify_service_indicator(ctx);
+  return 1;
 }
 
 int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
@@ -436,7 +440,7 @@
     } else {
       *out_len = i;
     }
-    return 1;
+    goto out;
   }
 
   b = ctx->cipher->block_size;
@@ -446,7 +450,7 @@
       return 0;
     }
     *out_len = 0;
-    return 1;
+    goto out;
   }
 
   if (b > 1) {
@@ -480,12 +484,30 @@
     *out_len = 0;
   }
 
+out:
+  EVP_Cipher_verify_service_indicator(ctx);
   return 1;
 }
 
 int EVP_Cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in,
                size_t in_len) {
-  return ctx->cipher->cipher(ctx, out, in, in_len);
+  const int ret = ctx->cipher->cipher(ctx, out, in, in_len);
+
+  // |EVP_CIPH_FLAG_CUSTOM_CIPHER| never sets the FIPS indicator via
+  // |EVP_Cipher| because it's complicated whether the operation has completed
+  // or not. E.g. AES-GCM with a non-NULL |in| argument hasn't completed an
+  // operation. Callers should use the |EVP_AEAD| API or, at least,
+  // |EVP_CipherUpdate| etc.
+  //
+  // This call can't be pushed into |EVP_Cipher_verify_service_indicator|
+  // because whether |ret| indicates success or not depends on whether
+  // |EVP_CIPH_FLAG_CUSTOM_CIPHER| is set. (This unreasonable, but matches
+  // OpenSSL.)
+  if (!(ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) && ret) {
+    EVP_Cipher_verify_service_indicator(ctx);
+  }
+
+  return ret;
 }
 
 int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
diff --git a/crypto/fipsmodule/cipher/e_aes.c b/crypto/fipsmodule/cipher/e_aes.c
index 7e7c1a8..ec441e8 100644
--- a/crypto/fipsmodule/cipher/e_aes.c
+++ b/crypto/fipsmodule/cipher/e_aes.c
@@ -61,6 +61,7 @@
 #include "../../internal.h"
 #include "../aes/internal.h"
 #include "../modes/internal.h"
+#include "../service_indicator/internal.h"
 #include "../delocate.h"
 
 
@@ -485,8 +486,13 @@
       if (arg) {
         OPENSSL_memcpy(gctx->iv, ptr, arg);
       }
-      if (c->encrypt && !RAND_bytes(gctx->iv + arg, gctx->ivlen - arg)) {
-        return 0;
+      if (c->encrypt) {
+        // |RAND_bytes| calls within the fipsmodule should be wrapped with state
+        // lock functions to avoid updating the service indicator with the DRBG
+        // functions.
+        FIPS_service_indicator_lock_state();
+        RAND_bytes(gctx->iv + arg, gctx->ivlen - arg);
+        FIPS_service_indicator_unlock_state();
       }
       gctx->iv_gen = 1;
       return 1;
@@ -1099,9 +1105,14 @@
                                     const uint8_t *in_tag, size_t in_tag_len,
                                     const uint8_t *ad, size_t ad_len) {
   struct aead_aes_gcm_ctx *gcm_ctx = (struct aead_aes_gcm_ctx *)&ctx->state;
-  return aead_aes_gcm_open_gather_impl(gcm_ctx, out, nonce, nonce_len, in,
-                                       in_len, in_tag, in_tag_len, ad, ad_len,
-                                       ctx->tag_len);
+  if (!aead_aes_gcm_open_gather_impl(gcm_ctx, out, nonce, nonce_len, in, in_len,
+                                     in_tag, in_tag_len, ad, ad_len,
+                                     ctx->tag_len)) {
+    return 0;
+  }
+
+  AEAD_GCM_verify_service_indicator(ctx);
+  return 1;
 }
 
 DEFINE_METHOD_FUNCTION(EVP_AEAD, EVP_aead_aes_128_gcm) {
@@ -1186,7 +1197,12 @@
     return 0;
   }
 
+  // |RAND_bytes| calls within the fipsmodule should be wrapped with state lock
+  // functions to avoid updating the service indicator with the DRBG functions.
+  FIPS_service_indicator_lock_state();
   RAND_bytes(nonce, sizeof(nonce));
+  FIPS_service_indicator_unlock_state();
+
   const struct aead_aes_gcm_ctx *gcm_ctx =
       (const struct aead_aes_gcm_ctx *)&ctx->state;
   if (!aead_aes_gcm_seal_scatter_impl(gcm_ctx, out, out_tag, out_tag_len,
@@ -1201,6 +1217,7 @@
   memcpy(out_tag + *out_tag_len, nonce, sizeof(nonce));
   *out_tag_len += sizeof(nonce);
 
+  AEAD_GCM_verify_service_indicator(ctx);
   return 1;
 }
 
@@ -1223,10 +1240,15 @@
 
   const struct aead_aes_gcm_ctx *gcm_ctx =
       (const struct aead_aes_gcm_ctx *)&ctx->state;
-  return aead_aes_gcm_open_gather_impl(
+  if (!aead_aes_gcm_open_gather_impl(
       gcm_ctx, out, nonce, AES_GCM_NONCE_LENGTH, in, in_len, in_tag,
       in_tag_len - AES_GCM_NONCE_LENGTH, ad, ad_len,
-      ctx->tag_len - AES_GCM_NONCE_LENGTH);
+      ctx->tag_len - AES_GCM_NONCE_LENGTH)) {
+    return 0;
+  }
+
+  AEAD_GCM_verify_service_indicator(ctx);
+  return 1;
 }
 
 DEFINE_METHOD_FUNCTION(EVP_AEAD, EVP_aead_aes_128_gcm_randnonce) {
@@ -1316,9 +1338,14 @@
 
   gcm_ctx->min_next_nonce = given_counter + 1;
 
-  return aead_aes_gcm_seal_scatter(ctx, out, out_tag, out_tag_len,
-                                   max_out_tag_len, nonce, nonce_len, in,
-                                   in_len, extra_in, extra_in_len, ad, ad_len);
+  if (!aead_aes_gcm_seal_scatter(ctx, out, out_tag, out_tag_len,
+                                 max_out_tag_len, nonce, nonce_len, in, in_len,
+                                 extra_in, extra_in_len, ad, ad_len)) {
+    return 0;
+  }
+
+  AEAD_GCM_verify_service_indicator(ctx);
+  return 1;
 }
 
 DEFINE_METHOD_FUNCTION(EVP_AEAD, EVP_aead_aes_128_gcm_tls12) {
@@ -1422,9 +1449,14 @@
 
   gcm_ctx->min_next_nonce = given_counter + 1;
 
-  return aead_aes_gcm_seal_scatter(ctx, out, out_tag, out_tag_len,
-                                   max_out_tag_len, nonce, nonce_len, in,
-                                   in_len, extra_in, extra_in_len, ad, ad_len);
+  if (!aead_aes_gcm_seal_scatter(ctx, out, out_tag, out_tag_len,
+                                 max_out_tag_len, nonce, nonce_len, in, in_len,
+                                 extra_in, extra_in_len, ad, ad_len)) {
+    return 0;
+  }
+
+  AEAD_GCM_verify_service_indicator(ctx);
+  return 1;
 }
 
 DEFINE_METHOD_FUNCTION(EVP_AEAD, EVP_aead_aes_128_gcm_tls13) {
diff --git a/crypto/fipsmodule/cipher/e_aesccm.c b/crypto/fipsmodule/cipher/e_aesccm.c
index 33c8b16..8fb72cb 100644
--- a/crypto/fipsmodule/cipher/e_aesccm.c
+++ b/crypto/fipsmodule/cipher/e_aesccm.c
@@ -55,6 +55,7 @@
 #include <openssl/mem.h>
 
 #include "internal.h"
+#include "../service_indicator/internal.h"
 
 
 struct ccm128_context {
@@ -350,6 +351,7 @@
   }
 
   *out_tag_len = ctx->tag_len;
+  AEAD_CCM_verify_service_indicator(ctx);
   return 1;
 }
 
@@ -390,6 +392,7 @@
     return 0;
   }
 
+  AEAD_CCM_verify_service_indicator(ctx);
   return 1;
 }
 
diff --git a/crypto/fipsmodule/cmac/cmac.c b/crypto/fipsmodule/cmac/cmac.c
index 3421bbf..f5c805d 100644
--- a/crypto/fipsmodule/cmac/cmac.c
+++ b/crypto/fipsmodule/cmac/cmac.c
@@ -56,6 +56,7 @@
 #include <openssl/mem.h>
 
 #include "../../internal.h"
+#include "../service_indicator/internal.h"
 
 
 struct cmac_ctx_st {
@@ -85,6 +86,8 @@
              const uint8_t *in, size_t in_len) {
   const EVP_CIPHER *cipher;
   switch (key_len) {
+    // WARNING: this code assumes that all supported key sizes are FIPS
+    // Approved.
     case 16:
       cipher = EVP_aes_128_cbc();
       break;
@@ -99,10 +102,17 @@
   CMAC_CTX ctx;
   CMAC_CTX_init(&ctx);
 
+  // We have to verify that all the CMAC services actually succeed before
+  // updating the indicator state, so we lock the state here.
+  FIPS_service_indicator_lock_state();
   const int ok = CMAC_Init(&ctx, key, key_len, cipher, NULL /* engine */) &&
                  CMAC_Update(&ctx, in, in_len) &&
                  CMAC_Final(&ctx, out, &scratch_out_len);
+  FIPS_service_indicator_unlock_state();
 
+  if (ok) {
+    FIPS_service_indicator_update_state();
+  }
   CMAC_CTX_cleanup(&ctx);
   return ok;
 }
@@ -173,8 +183,13 @@
 
 int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t key_len,
               const EVP_CIPHER *cipher, ENGINE *engine) {
+  int ret = 0;
   uint8_t scratch[AES_BLOCK_SIZE];
 
+  // We have to avoid the underlying AES-CBC |EVP_CIPHER| services updating the
+  // indicator state, so we lock the state here.
+  FIPS_service_indicator_lock_state();
+
   size_t block_size = EVP_CIPHER_block_size(cipher);
   if ((block_size != AES_BLOCK_SIZE && block_size != 8 /* 3-DES */) ||
       EVP_CIPHER_key_length(cipher) != key_len ||
@@ -182,7 +197,7 @@
       !EVP_Cipher(&ctx->cipher_ctx, scratch, kZeroIV, block_size) ||
       // Reset context again ready for first data.
       !EVP_EncryptInit_ex(&ctx->cipher_ctx, NULL, NULL, NULL, kZeroIV)) {
-    return 0;
+    goto out;
   }
 
   if (block_size == AES_BLOCK_SIZE) {
@@ -193,8 +208,11 @@
     binary_field_mul_x_64(ctx->k2, ctx->k1);
   }
   ctx->block_used = 0;
+  ret = 1;
 
-  return 1;
+out:
+  FIPS_service_indicator_unlock_state();
+  return ret;
 }
 
 int CMAC_Reset(CMAC_CTX *ctx) {
@@ -203,6 +221,12 @@
 }
 
 int CMAC_Update(CMAC_CTX *ctx, const uint8_t *in, size_t in_len) {
+  int ret = 0;
+
+  // We have to avoid the underlying AES-CBC |EVP_Cipher| services updating the
+  // indicator state, so we lock the state here.
+  FIPS_service_indicator_lock_state();
+
   size_t block_size = EVP_CIPHER_CTX_block_size(&ctx->cipher_ctx);
   assert(block_size <= AES_BLOCK_SIZE);
   uint8_t scratch[AES_BLOCK_SIZE];
@@ -224,20 +248,21 @@
     // case we don't want to process this block now because it might be the last
     // block and that block is treated specially.
     if (in_len == 0) {
-      return 1;
+      ret = 1;
+      goto out;
     }
 
     assert(ctx->block_used == block_size);
 
     if (!EVP_Cipher(&ctx->cipher_ctx, scratch, ctx->block, block_size)) {
-      return 0;
+      goto out;
     }
   }
 
   // Encrypt all but one of the remaining blocks.
   while (in_len > block_size) {
     if (!EVP_Cipher(&ctx->cipher_ctx, scratch, in, block_size)) {
-      return 0;
+      goto out;
     }
     in += block_size;
     in_len -= block_size;
@@ -245,17 +270,26 @@
 
   OPENSSL_memcpy(ctx->block, in, in_len);
   ctx->block_used = in_len;
+  ret = 1;
 
-  return 1;
+out:
+  FIPS_service_indicator_unlock_state();
+  return ret;
 }
 
 int CMAC_Final(CMAC_CTX *ctx, uint8_t *out, size_t *out_len) {
+  int ret = 0;
   size_t block_size = EVP_CIPHER_CTX_block_size(&ctx->cipher_ctx);
   assert(block_size <= AES_BLOCK_SIZE);
 
+  // We have to avoid the underlying AES-CBC |EVP_Cipher| services updating the
+  // indicator state, so we lock the state here.
+  FIPS_service_indicator_lock_state();
+
   *out_len = block_size;
   if (out == NULL) {
-    return 1;
+    ret = 1;
+    goto out;
   }
 
   const uint8_t *mask = ctx->k1;
@@ -273,6 +307,12 @@
   for (unsigned i = 0; i < block_size; i++) {
     out[i] = ctx->block[i] ^ mask[i];
   }
+  ret = EVP_Cipher(&ctx->cipher_ctx, out, out, block_size);
 
-  return EVP_Cipher(&ctx->cipher_ctx, out, out, block_size);
+out:
+  FIPS_service_indicator_unlock_state();
+  if (ret) {
+    FIPS_service_indicator_update_state();
+  }
+  return ret;
 }
diff --git a/crypto/fipsmodule/dh/dh.c b/crypto/fipsmodule/dh/dh.c
index b59afc6..8b04117 100644
--- a/crypto/fipsmodule/dh/dh.c
+++ b/crypto/fipsmodule/dh/dh.c
@@ -64,9 +64,10 @@
 #include <openssl/mem.h>
 #include <openssl/thread.h>
 
-#include "internal.h"
 #include "../../internal.h"
 #include "../bn/internal.h"
+#include "../service_indicator/internal.h"
+#include "internal.h"
 
 
 #define OPENSSL_DH_MAX_MODULUS_BITS 10000
@@ -383,6 +384,8 @@
     return 0;
   }
 
+  FIPS_service_indicator_lock_state();
+
   int ret = 0;
   const size_t dh_len = DH_size(dh);
   uint8_t *shared_bytes = OPENSSL_malloc(dh_len);
@@ -404,6 +407,7 @@
   ret = 1;
 
  err:
+  FIPS_service_indicator_unlock_state();
   OPENSSL_free(shared_bytes);
   return ret;
 }
diff --git a/crypto/fipsmodule/ec/ec_key.c b/crypto/fipsmodule/ec/ec_key.c
index 2d04d13..8b89dc2 100644
--- a/crypto/fipsmodule/ec/ec_key.c
+++ b/crypto/fipsmodule/ec/ec_key.c
@@ -329,14 +329,17 @@
 }
 
 int EC_KEY_check_fips(const EC_KEY *key) {
+  int ret = 0;
+  FIPS_service_indicator_lock_state();
+
   if (EC_KEY_is_opaque(key)) {
     // Opaque keys can't be checked.
     OPENSSL_PUT_ERROR(EC, EC_R_PUBLIC_KEY_VALIDATION_FAILED);
-    return 0;
+    goto end;
   }
 
   if (!EC_KEY_check_key(key)) {
-    return 0;
+    goto end;
   }
 
   if (key->priv_key) {
@@ -350,11 +353,19 @@
     ECDSA_SIG_free(sig);
     if (!ok) {
       OPENSSL_PUT_ERROR(EC, EC_R_PUBLIC_KEY_VALIDATION_FAILED);
-      return 0;
+      goto end;
     }
   }
 
-  return 1;
+  ret = 1;
+
+end:
+  FIPS_service_indicator_unlock_state();
+  if (ret) {
+    EC_KEY_keygen_verify_service_indicator(key);
+  }
+
+  return ret;
 }
 
 int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, const BIGNUM *x,
diff --git a/crypto/fipsmodule/ecdh/ecdh.c b/crypto/fipsmodule/ecdh/ecdh.c
index 36fbadc..dfea1e1 100644
--- a/crypto/fipsmodule/ecdh/ecdh.c
+++ b/crypto/fipsmodule/ecdh/ecdh.c
@@ -103,6 +103,7 @@
     return 0;
   }
 
+  FIPS_service_indicator_lock_state();
   switch (out_len) {
     case SHA224_DIGEST_LENGTH:
       SHA224(buf, buflen, out);
@@ -118,8 +119,11 @@
       break;
     default:
       OPENSSL_PUT_ERROR(ECDH, ECDH_R_UNKNOWN_DIGEST_LENGTH);
+      FIPS_service_indicator_unlock_state();
       return 0;
   }
+  FIPS_service_indicator_unlock_state();
 
+  ECDH_verify_service_indicator(priv_key);
   return 1;
 }
diff --git a/crypto/fipsmodule/ecdsa/ecdsa.c b/crypto/fipsmodule/ecdsa/ecdsa.c
index 2aa5fe9..ef77e42 100644
--- a/crypto/fipsmodule/ecdsa/ecdsa.c
+++ b/crypto/fipsmodule/ecdsa/ecdsa.c
@@ -323,6 +323,9 @@
   // into the RBG. This is a hardening measure against entropy failure.
   OPENSSL_STATIC_ASSERT(SHA512_DIGEST_LENGTH >= 32,
                         "additional_data is too large for SHA-512");
+
+  FIPS_service_indicator_lock_state();
+
   SHA512_CTX sha;
   uint8_t additional_data[SHA512_DIGEST_LENGTH];
   SHA512_Init(&sha);
@@ -330,17 +333,22 @@
   SHA512_Update(&sha, digest, digest_len);
   SHA512_Final(additional_data, &sha);
 
+  ECDSA_SIG *ret = NULL;
   for (;;) {
     EC_SCALAR k;
     if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
-      return NULL;
+      ret = NULL;
+      goto out;
     }
 
     int retry;
-    ECDSA_SIG *sig =
-        ecdsa_sign_impl(group, &retry, priv_key, &k, digest, digest_len);
-    if (sig != NULL || !retry) {
-      return sig;
+    ret = ecdsa_sign_impl(group, &retry, priv_key, &k, digest, digest_len);
+    if (ret != NULL || !retry) {
+      goto out;
     }
   }
+
+out:
+  FIPS_service_indicator_unlock_state();
+  return ret;
 }
diff --git a/crypto/fipsmodule/hmac/hmac.c b/crypto/fipsmodule/hmac/hmac.c
index d37cec8..454d0c0 100644
--- a/crypto/fipsmodule/hmac/hmac.c
+++ b/crypto/fipsmodule/hmac/hmac.c
@@ -70,13 +70,22 @@
               unsigned int *out_len) {
   HMAC_CTX ctx;
   HMAC_CTX_init(&ctx);
-  if (!HMAC_Init_ex(&ctx, key, key_len, evp_md, NULL) ||
-      !HMAC_Update(&ctx, data, data_len) ||
-      !HMAC_Final(&ctx, out, out_len)) {
-    out = NULL;
-  }
+
+  // The underlying hash functions should not set the FIPS service indicator
+  // until all operations have completed.
+  FIPS_service_indicator_lock_state();
+  const int ok = HMAC_Init_ex(&ctx, key, key_len, evp_md, NULL) &&
+                 HMAC_Update(&ctx, data, data_len) &&
+                 HMAC_Final(&ctx, out, out_len);
+  FIPS_service_indicator_unlock_state();
 
   HMAC_CTX_cleanup(&ctx);
+
+  if (!ok) {
+    return NULL;
+  }
+
+  HMAC_verify_service_indicator(evp_md);
   return out;
 }
 
@@ -120,6 +129,9 @@
 
 int HMAC_Init_ex(HMAC_CTX *ctx, const void *key, size_t key_len,
                  const EVP_MD *md, ENGINE *impl) {
+  int ret = 0;
+  FIPS_service_indicator_lock_state();
+
   if (md == NULL) {
     md = ctx->md;
   }
@@ -143,7 +155,7 @@
       if (!EVP_DigestInit_ex(&ctx->md_ctx, md, impl) ||
           !EVP_DigestUpdate(&ctx->md_ctx, key, key_len) ||
           !EVP_DigestFinal_ex(&ctx->md_ctx, key_block, &key_block_len)) {
-        return 0;
+        goto out;
       }
     } else {
       assert(key_len <= sizeof(key_block));
@@ -160,7 +172,7 @@
     }
     if (!EVP_DigestInit_ex(&ctx->i_ctx, md, impl) ||
         !EVP_DigestUpdate(&ctx->i_ctx, pad, EVP_MD_block_size(md))) {
-      return 0;
+      goto out;
     }
 
     for (size_t i = 0; i < EVP_MAX_MD_BLOCK_SIZE; i++) {
@@ -168,17 +180,17 @@
     }
     if (!EVP_DigestInit_ex(&ctx->o_ctx, md, impl) ||
         !EVP_DigestUpdate(&ctx->o_ctx, pad, EVP_MD_block_size(md))) {
-      return 0;
+      goto out;
     }
 
     ctx->md = md;
   }
 
-  if (!EVP_MD_CTX_copy_ex(&ctx->md_ctx, &ctx->i_ctx)) {
-    return 0;
-  }
+  ret = EVP_MD_CTX_copy_ex(&ctx->md_ctx, &ctx->i_ctx);
 
-  return 1;
+out:
+  FIPS_service_indicator_unlock_state();
+  return ret;
 }
 
 int HMAC_Update(HMAC_CTX *ctx, const uint8_t *data, size_t data_len) {
@@ -186,9 +198,11 @@
 }
 
 int HMAC_Final(HMAC_CTX *ctx, uint8_t *out, unsigned int *out_len) {
+  int ret = 0;
   unsigned int i;
   uint8_t buf[EVP_MAX_MD_SIZE];
 
+  FIPS_service_indicator_lock_state();
   // TODO(davidben): The only thing that can officially fail here is
   // |EVP_MD_CTX_copy_ex|, but even that should be impossible in this case.
   if (!EVP_DigestFinal_ex(&ctx->md_ctx, buf, &i) ||
@@ -196,10 +210,17 @@
       !EVP_DigestUpdate(&ctx->md_ctx, buf, i) ||
       !EVP_DigestFinal_ex(&ctx->md_ctx, out, out_len)) {
     *out_len = 0;
-    return 0;
+    goto out;
   }
 
-  return 1;
+  ret = 1;
+
+ out:
+  FIPS_service_indicator_unlock_state();
+  if (ret) {
+    HMAC_verify_service_indicator(ctx->md);
+  }
+  return ret;
 }
 
 size_t HMAC_size(const HMAC_CTX *ctx) {
diff --git a/crypto/fipsmodule/rand/ctrdrbg.c b/crypto/fipsmodule/rand/ctrdrbg.c
index b2fda1d..83e7f5b 100644
--- a/crypto/fipsmodule/rand/ctrdrbg.c
+++ b/crypto/fipsmodule/rand/ctrdrbg.c
@@ -19,6 +19,7 @@
 
 #include "internal.h"
 #include "../cipher/internal.h"
+#include "../service_indicator/internal.h"
 
 
 // Section references in this file refer to SP 800-90Ar1:
@@ -194,6 +195,7 @@
   }
 
   drbg->reseed_counter++;
+  FIPS_service_indicator_update_state();
   return 1;
 }
 
diff --git a/crypto/fipsmodule/rsa/padding.c b/crypto/fipsmodule/rsa/padding.c
index 28f1b45..605647a 100644
--- a/crypto/fipsmodule/rsa/padding.c
+++ b/crypto/fipsmodule/rsa/padding.c
@@ -67,6 +67,7 @@
 #include <openssl/sha.h>
 
 #include "internal.h"
+#include "../service_indicator/internal.h"
 #include "../../internal.h"
 
 
@@ -145,20 +146,17 @@
   return 1;
 }
 
-static int rand_nonzero(uint8_t *out, size_t len) {
-  if (!RAND_bytes(out, len)) {
-    return 0;
-  }
+static void rand_nonzero(uint8_t *out, size_t len) {
+  FIPS_service_indicator_lock_state();
+  RAND_bytes(out, len);
 
   for (size_t i = 0; i < len; i++) {
     while (out[i] == 0) {
-      if (!RAND_bytes(out + i, 1)) {
-        return 0;
-      }
+      RAND_bytes(out + i, 1);
     }
   }
 
-  return 1;
+  FIPS_service_indicator_unlock_state();
 }
 
 int RSA_padding_add_PKCS1_type_2(uint8_t *to, size_t to_len,
@@ -178,10 +176,7 @@
   to[1] = 2;
 
   size_t padding_len = to_len - 3 - from_len;
-  if (!rand_nonzero(to + 2, padding_len)) {
-    return 0;
-  }
-
+  rand_nonzero(to + 2, padding_len);
   to[2 + padding_len] = 0;
   OPENSSL_memcpy(to + to_len - from_len, from, from_len);
   return 1;
@@ -275,6 +270,7 @@
   int ret = 0;
   EVP_MD_CTX ctx;
   EVP_MD_CTX_init(&ctx);
+  FIPS_service_indicator_lock_state();
 
   size_t md_len = EVP_MD_size(md);
 
@@ -310,6 +306,7 @@
 
 err:
   EVP_MD_CTX_cleanup(&ctx);
+  FIPS_service_indicator_unlock_state();
   return ret;
 }
 
@@ -346,23 +343,25 @@
   uint8_t *seed = to + 1;
   uint8_t *db = to + mdlen + 1;
 
+  uint8_t *dbmask = NULL;
+  int ret = 0;
+  FIPS_service_indicator_lock_state();
   if (!EVP_Digest(param, param_len, db, NULL, md, NULL)) {
-    return 0;
+    goto out;
   }
   OPENSSL_memset(db + mdlen, 0, emlen - from_len - 2 * mdlen - 1);
   db[emlen - from_len - mdlen - 1] = 0x01;
   OPENSSL_memcpy(db + emlen - from_len - mdlen, from, from_len);
   if (!RAND_bytes(seed, mdlen)) {
-    return 0;
+    goto out;
   }
 
-  uint8_t *dbmask = OPENSSL_malloc(emlen - mdlen);
+  dbmask = OPENSSL_malloc(emlen - mdlen);
   if (dbmask == NULL) {
     OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE);
-    return 0;
+    goto out;
   }
 
-  int ret = 0;
   if (!PKCS1_MGF1(dbmask, emlen - mdlen, seed, mdlen, mgf1md)) {
     goto out;
   }
@@ -381,6 +380,7 @@
 
 out:
   OPENSSL_free(dbmask);
+  FIPS_service_indicator_unlock_state();
   return ret;
 }
 
@@ -410,6 +410,7 @@
   }
 
   size_t dblen = from_len - mdlen - 1;
+  FIPS_service_indicator_lock_state();
   db = OPENSSL_malloc(dblen);
   if (db == NULL) {
     OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE);
@@ -470,6 +471,7 @@
   OPENSSL_memcpy(out, db + one_index, mlen);
   *out_len = mlen;
   OPENSSL_free(db);
+  FIPS_service_indicator_unlock_state();
   return 1;
 
 decoding_err:
@@ -478,6 +480,7 @@
   OPENSSL_PUT_ERROR(RSA, RSA_R_OAEP_DECODING_ERROR);
  err:
   OPENSSL_free(db);
+  FIPS_service_indicator_unlock_state();
   return 0;
 }
 
@@ -501,6 +504,7 @@
   }
 
   hLen = EVP_MD_size(Hash);
+  FIPS_service_indicator_lock_state();
 
   // Negative sLen has special meanings:
   //	-1	sLen == hLen
@@ -578,6 +582,7 @@
 err:
   OPENSSL_free(DB);
   EVP_MD_CTX_cleanup(&ctx);
+  FIPS_service_indicator_unlock_state();
 
   return ret;
 }
@@ -595,6 +600,7 @@
     mgf1Hash = Hash;
   }
 
+  FIPS_service_indicator_lock_state();
   hLen = EVP_MD_size(Hash);
 
   if (BN_is_zero(rsa->n)) {
@@ -690,6 +696,7 @@
 
 err:
   OPENSSL_free(salt);
+  FIPS_service_indicator_unlock_state();
 
   return ret;
 }
diff --git a/crypto/fipsmodule/rsa/rsa_impl.c b/crypto/fipsmodule/rsa/rsa_impl.c
index 1046f35..0958961 100644
--- a/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/crypto/fipsmodule/rsa/rsa_impl.c
@@ -66,11 +66,12 @@
 #include <openssl/thread.h>
 #include <openssl/type_check.h>
 
-#include "internal.h"
-#include "../bn/internal.h"
 #include "../../internal.h"
+#include "../bn/internal.h"
 #include "../delocate.h"
 #include "../rand/fork_detect.h"
+#include "../service_indicator/internal.h"
+#include "internal.h"
 
 
 int rsa_check_public_key(const RSA *rsa) {
@@ -1419,6 +1420,10 @@
             BN_set_word(e, RSA_F4) &&
             RSA_generate_key_ex_maybe_fips(rsa, bits, e, cb, /*check_fips=*/1);
   BN_free(e);
+
+  if (ret) {
+    FIPS_service_indicator_update_state();
+  }
   return ret;
 }
 
diff --git a/crypto/fipsmodule/self_check/self_check.c b/crypto/fipsmodule/self_check/self_check.c
index b248789..fe9c914 100644
--- a/crypto/fipsmodule/self_check/self_check.c
+++ b/crypto/fipsmodule/self_check/self_check.c
@@ -36,6 +36,7 @@
 #include "../ecdsa/internal.h"
 #include "../rand/internal.h"
 #include "../rsa/internal.h"
+#include "../service_indicator/internal.h"
 #include "../tls/internal.h"
 
 
@@ -616,9 +617,11 @@
 #if defined(BORINGSSL_FIPS)
 
 static void run_self_test_rsa(void) {
+  FIPS_service_indicator_lock_state();
   if (!boringssl_self_test_rsa()) {
     BORINGSSL_FIPS_abort();
   }
+  FIPS_service_indicator_unlock_state();
 }
 
 DEFINE_STATIC_ONCE(g_self_test_once_rsa);
@@ -628,9 +631,11 @@
 }
 
 static void run_self_test_ecc(void) {
+  FIPS_service_indicator_lock_state();
   if (!boringssl_self_test_ecc()) {
     BORINGSSL_FIPS_abort();
   }
+  FIPS_service_indicator_unlock_state();
 }
 
 DEFINE_STATIC_ONCE(g_self_test_once_ecc);
@@ -640,9 +645,11 @@
 }
 
 static void run_self_test_ffdh(void) {
+  FIPS_service_indicator_lock_state();
   if (!boringssl_self_test_ffdh()) {
     BORINGSSL_FIPS_abort();
   }
+  FIPS_service_indicator_unlock_state();
 }
 
 DEFINE_STATIC_ONCE(g_self_test_once_ffdh);
diff --git a/crypto/fipsmodule/service_indicator/internal.h b/crypto/fipsmodule/service_indicator/internal.h
new file mode 100644
index 0000000..bbc4e4e
--- /dev/null
+++ b/crypto/fipsmodule/service_indicator/internal.h
@@ -0,0 +1,89 @@
+/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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_SERVICE_INDICATOR_INTERNAL_H
+#define OPENSSL_HEADER_SERVICE_INDICATOR_INTERNAL_H
+
+#include <openssl/base.h>
+#include <openssl/service_indicator.h>
+
+#if defined(BORINGSSL_FIPS)
+
+// FIPS_service_indicator_update_state records that an approved service has been
+// invoked.
+void FIPS_service_indicator_update_state(void);
+
+// FIPS_service_indicator_lock_state and |FIPS_service_indicator_unlock_state|
+// stop |FIPS_service_indicator_update_state| from actually updating the service
+// indicator. This is used when a primitive calls a potentially approved
+// primitive to avoid false positives. For example, just because a key
+// generation calls |RAND_bytes| (and thus the approved DRBG) doesn't mean that
+// the key generation operation itself is approved.
+//
+// This lock nests: i.e. locking twice is fine so long as each lock is paired
+// with an unlock. If the (64-bit) counter overflows, the process aborts.
+void FIPS_service_indicator_lock_state(void);
+void FIPS_service_indicator_unlock_state(void);
+
+// The following functions may call |FIPS_service_indicator_update_state| if
+// their parameter specifies an approved operation.
+
+void AEAD_GCM_verify_service_indicator(const EVP_AEAD_CTX *ctx);
+void AEAD_CCM_verify_service_indicator(const EVP_AEAD_CTX *ctx);
+void EC_KEY_keygen_verify_service_indicator(const EC_KEY *eckey);
+void ECDH_verify_service_indicator(const EC_KEY *ec_key);
+void EVP_Cipher_verify_service_indicator(const EVP_CIPHER_CTX *ctx);
+void EVP_DigestSign_verify_service_indicator(const EVP_MD_CTX *ctx);
+void EVP_DigestVerify_verify_service_indicator(const EVP_MD_CTX *ctx);
+void HMAC_verify_service_indicator(const EVP_MD *evp_md);
+void TLSKDF_verify_service_indicator(const EVP_MD *dgst);
+
+#else
+
+// Service indicator functions are no-ops in non-FIPS builds.
+
+OPENSSL_INLINE void FIPS_service_indicator_update_state(void) {}
+OPENSSL_INLINE void FIPS_service_indicator_lock_state(void) {}
+OPENSSL_INLINE void FIPS_service_indicator_unlock_state(void) {}
+
+OPENSSL_INLINE void AEAD_GCM_verify_service_indicator(
+    OPENSSL_UNUSED const EVP_AEAD_CTX *ctx) {}
+
+OPENSSL_INLINE void AEAD_CCM_verify_service_indicator(
+    OPENSSL_UNUSED const EVP_AEAD_CTX *ctx) {}
+
+OPENSSL_INLINE void EC_KEY_keygen_verify_service_indicator(
+    OPENSSL_UNUSED const EC_KEY *eckey) {}
+
+OPENSSL_INLINE void ECDH_verify_service_indicator(
+    OPENSSL_UNUSED const EC_KEY *ec_key) {}
+
+OPENSSL_INLINE void EVP_Cipher_verify_service_indicator(
+    OPENSSL_UNUSED const EVP_CIPHER_CTX *ctx) {}
+
+OPENSSL_INLINE void EVP_DigestSign_verify_service_indicator(
+    OPENSSL_UNUSED const EVP_MD_CTX *ctx) {}
+
+OPENSSL_INLINE void EVP_DigestVerify_verify_service_indicator(
+    OPENSSL_UNUSED const EVP_MD_CTX *ctx) {}
+
+OPENSSL_INLINE void HMAC_verify_service_indicator(
+    OPENSSL_UNUSED const EVP_MD *evp_md) {}
+
+OPENSSL_INLINE void TLSKDF_verify_service_indicator(
+    OPENSSL_UNUSED const EVP_MD *dgst) {}
+
+#endif  // BORINGSSL_FIPS
+
+#endif  // OPENSSL_HEADER_SERVICE_INDICATOR_INTERNAL_H
diff --git a/crypto/fipsmodule/service_indicator/service_indicator.c b/crypto/fipsmodule/service_indicator/service_indicator.c
new file mode 100644
index 0000000..eb4669b
--- /dev/null
+++ b/crypto/fipsmodule/service_indicator/service_indicator.c
@@ -0,0 +1,333 @@
+/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/evp.h>
+#include <openssl/service_indicator.h>
+
+#include "../../evp/internal.h"
+#include "../../internal.h"
+#include "internal.h"
+
+#if defined(BORINGSSL_FIPS)
+
+#define STATE_UNLOCKED 0
+
+// fips_service_indicator_state is a thread-local structure that stores the
+// state of the FIPS service indicator.
+struct fips_service_indicator_state {
+  // lock_state records the number of times the indicator has been locked.
+  // When it is zero (i.e. |STATE_UNLOCKED|) then the indicator can be updated.
+  uint64_t lock_state;
+  // counter is the indicator state. It is incremented when an approved service
+  // completes.
+  uint64_t counter;
+};
+
+// service_indicator_get returns a pointer to the |fips_service_indicator_state|
+// for the current thread. It returns NULL on error.
+//
+// FIPS 140-3 requires that the module should provide the service indicator
+// for approved services irrespective of whether the user queries it or not.
+// Hence, it is lazily initialized in any call to an approved service.
+static struct fips_service_indicator_state *service_indicator_get(void) {
+  struct fips_service_indicator_state *indicator = CRYPTO_get_thread_local(
+      OPENSSL_THREAD_LOCAL_FIPS_SERVICE_INDICATOR_STATE);
+
+  if (indicator == NULL) {
+    indicator = OPENSSL_malloc(sizeof(struct fips_service_indicator_state));
+    if (indicator == NULL) {
+      OPENSSL_PUT_ERROR(CRYPTO, ERR_R_MALLOC_FAILURE);
+      return NULL;
+    }
+
+    indicator->lock_state = STATE_UNLOCKED;
+    indicator->counter = 0;
+
+    if (!CRYPTO_set_thread_local(
+            OPENSSL_THREAD_LOCAL_FIPS_SERVICE_INDICATOR_STATE, indicator,
+            OPENSSL_free)) {
+      OPENSSL_PUT_ERROR(CRYPTO, ERR_R_INTERNAL_ERROR);
+      return NULL;
+    }
+  }
+
+  return indicator;
+}
+
+static uint64_t service_indicator_get_counter(void) {
+  struct fips_service_indicator_state *indicator = service_indicator_get();
+  if (indicator == NULL) {
+    return 0;
+  }
+  return indicator->counter;
+}
+
+uint64_t FIPS_service_indicator_before_call(void) {
+  return service_indicator_get_counter();
+}
+
+uint64_t FIPS_service_indicator_after_call(void) {
+  return service_indicator_get_counter();
+}
+
+void FIPS_service_indicator_update_state(void) {
+  struct fips_service_indicator_state *indicator = service_indicator_get();
+  if (indicator && indicator->lock_state == STATE_UNLOCKED) {
+    indicator->counter++;
+  }
+}
+
+void FIPS_service_indicator_lock_state(void) {
+  struct fips_service_indicator_state *indicator = service_indicator_get();
+  if (indicator == NULL) {
+    return;
+  }
+
+  // |FIPS_service_indicator_lock_state| and
+  // |FIPS_service_indicator_unlock_state| should not under/overflow in normal
+  // operation. They are still checked and errors added to facilitate testing in
+  // service_indicator_test.cc. This should only happen if lock/unlock are
+  // called in an incorrect order or multiple times in the same function.
+  const uint64_t new_state = indicator->lock_state + 1;
+  if (new_state < indicator->lock_state) {
+    // Overflow. This would imply that our call stack length has exceeded a
+    // |uint64_t| which impossible on a 64-bit system.
+    abort();
+  }
+
+  indicator->lock_state = new_state;
+}
+
+void FIPS_service_indicator_unlock_state(void) {
+  struct fips_service_indicator_state *indicator = service_indicator_get();
+  if (indicator == NULL) {
+    return;
+  }
+
+  if (indicator->lock_state == 0) {
+    abort();
+  }
+
+  indicator->lock_state--;
+}
+
+void AEAD_GCM_verify_service_indicator(const EVP_AEAD_CTX *ctx) {
+  const size_t key_len = EVP_AEAD_key_length(ctx->aead);
+  if (key_len == 16 || key_len == 32) {
+    FIPS_service_indicator_update_state();
+  }
+}
+
+void AEAD_CCM_verify_service_indicator(const EVP_AEAD_CTX *ctx) {
+  if (EVP_AEAD_key_length(ctx->aead) == 16 && ctx->tag_len == 4) {
+    FIPS_service_indicator_update_state();
+  }
+}
+
+// is_ec_fips_approved returns one if the curve corresponding to the given NID
+// is FIPS approved, and zero otherwise.
+static int is_ec_fips_approved(int curve_nid) {
+  switch (curve_nid) {
+    case NID_secp224r1:
+    case NID_X9_62_prime256v1:
+    case NID_secp384r1:
+    case NID_secp521r1:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+// is_md_fips_approved_for_signing returns one if the given message digest type
+// is FIPS approved for signing, and zero otherwise.
+static int is_md_fips_approved_for_signing(int md_type) {
+  switch (md_type) {
+    case NID_sha224:
+    case NID_sha256:
+    case NID_sha384:
+    case NID_sha512:
+    case NID_sha512_256:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+// is_md_fips_approved_for_verifying returns one if the given message digest
+// type is FIPS approved for verifying, and zero otherwise.
+static int is_md_fips_approved_for_verifying(int md_type) {
+  switch (md_type) {
+    case NID_sha1:
+    case NID_sha224:
+    case NID_sha256:
+    case NID_sha384:
+    case NID_sha512:
+    case NID_sha512_256:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+static void evp_md_ctx_verify_service_indicator(const EVP_MD_CTX *ctx,
+                                                int rsa_1024_ok,
+                                                int (*md_ok)(int md_type)) {
+  if (EVP_MD_CTX_md(ctx) == NULL) {
+    // Signature schemes without a prehash are currently never FIPS approved.
+    goto err;
+  }
+
+  EVP_PKEY_CTX *const pctx = ctx->pctx;
+  const EVP_PKEY *const pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+  const int pkey_type = EVP_PKEY_id(pkey);
+  const int md_type = EVP_MD_CTX_type(ctx);
+
+  // EVP_PKEY_RSA_PSS SPKIs aren't supported.
+  if (pkey_type == EVP_PKEY_RSA) {
+    // Message digest used in the private key should be of the same type
+    // as the given one, so we extract the MD type from the |EVP_PKEY|
+    // and compare it with the type in |ctx|.
+    const EVP_MD *pctx_md;
+    if (!EVP_PKEY_CTX_get_signature_md(pctx, &pctx_md)) {
+      goto err;
+    }
+    if (EVP_MD_type(pctx_md) != md_type) {
+      goto err;
+    }
+
+    int padding;
+    if (!EVP_PKEY_CTX_get_rsa_padding(pctx, &padding)) {
+      goto err;
+    }
+    if (padding == RSA_PKCS1_PSS_PADDING) {
+      int salt_len;
+      const EVP_MD *mgf1_md;
+      if (!EVP_PKEY_CTX_get_rsa_pss_saltlen(pctx, &salt_len) ||
+          !EVP_PKEY_CTX_get_rsa_mgf1_md(pctx, &mgf1_md) ||
+          (salt_len != -1 && salt_len != (int)EVP_MD_size(pctx_md)) ||
+          EVP_MD_type(mgf1_md) != md_type) {
+        // Only PSS where saltLen == hashLen is tested with ACVP. Cases with
+        // non-standard padding functions are also excluded.
+        goto err;
+      }
+    }
+
+    // The approved RSA key sizes for signing are 2048, 3072 and 4096 bits.
+    // Note: |EVP_PKEY_size| returns the size in bytes.
+    size_t pkey_size = EVP_PKEY_size(ctx->pctx->pkey);
+
+    // Check if the MD type and the RSA key size are approved.
+    if (md_ok(md_type) &&
+        ((rsa_1024_ok && pkey_size == 128) || pkey_size == 256 ||
+         pkey_size == 384 || pkey_size == 512)) {
+      FIPS_service_indicator_update_state();
+    }
+  } else if (pkey_type == EVP_PKEY_EC) {
+    // Check if the MD type and the elliptic curve are approved.
+    if (md_ok(md_type) && is_ec_fips_approved(EC_GROUP_get_curve_name(
+                              ctx->pctx->pkey->pkey.ec->group))) {
+      FIPS_service_indicator_update_state();
+    }
+  }
+
+ err:
+  // Ensure that junk errors aren't left on the queue.
+  ERR_clear_error();
+}
+
+void EC_KEY_keygen_verify_service_indicator(const EC_KEY *eckey) {
+  if (is_ec_fips_approved(EC_GROUP_get_curve_name(eckey->group))) {
+    FIPS_service_indicator_update_state();
+  }
+}
+
+void ECDH_verify_service_indicator(const EC_KEY *ec_key) {
+  if (is_ec_fips_approved(EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)))) {
+    FIPS_service_indicator_update_state();
+  }
+}
+
+void EVP_Cipher_verify_service_indicator(const EVP_CIPHER_CTX *ctx) {
+  switch (EVP_CIPHER_CTX_nid(ctx)) {
+    case NID_aes_128_ecb:
+    case NID_aes_192_ecb:
+    case NID_aes_256_ecb:
+
+    case NID_aes_128_cbc:
+    case NID_aes_192_cbc:
+    case NID_aes_256_cbc:
+
+    case NID_aes_128_ctr:
+    case NID_aes_192_ctr:
+    case NID_aes_256_ctr:
+      FIPS_service_indicator_update_state();
+  }
+}
+
+void EVP_DigestVerify_verify_service_indicator(const EVP_MD_CTX *ctx) {
+  return evp_md_ctx_verify_service_indicator(ctx, /*rsa_1024_ok=*/1,
+                                             is_md_fips_approved_for_verifying);
+}
+
+void EVP_DigestSign_verify_service_indicator(const EVP_MD_CTX *ctx) {
+  return evp_md_ctx_verify_service_indicator(ctx, /*rsa_1024_ok=*/0,
+                                             is_md_fips_approved_for_signing);
+}
+
+void HMAC_verify_service_indicator(const EVP_MD *evp_md) {
+  switch (evp_md->type) {
+    case NID_sha1:
+    case NID_sha224:
+    case NID_sha256:
+    case NID_sha384:
+    case NID_sha512:
+    case NID_sha512_256:
+      FIPS_service_indicator_update_state();
+      break;
+  }
+}
+
+void TLSKDF_verify_service_indicator(const EVP_MD *md) {
+  // HMAC-MD5, HMAC-SHA1, and HMAC-MD5/HMAC-SHA1 (both used concurrently) are
+  // approved for use in the KDF in TLS 1.0/1.1.
+  // HMAC-SHA{256, 384, 512} are approved for use in the KDF in TLS 1.2.
+  // These Key Derivation functions are to be used in the context of the TLS
+  // protocol.
+  switch (EVP_MD_type(md)) {
+    case NID_md5:
+    case NID_sha1:
+    case NID_md5_sha1:
+    case NID_sha256:
+    case NID_sha384:
+    case NID_sha512:
+      FIPS_service_indicator_update_state();
+      break;
+  }
+}
+
+#else
+
+uint64_t FIPS_service_indicator_before_call(void) { return 0; }
+
+uint64_t FIPS_service_indicator_after_call(void) {
+  // One is returned so that the return value is always greater than zero, the
+  // return value of |FIPS_service_indicator_before_call|. This makes everything
+  // report as "approved" in non-FIPS builds.
+  return 1;
+}
+
+#endif  // BORINGSSL_FIPS
diff --git a/crypto/fipsmodule/service_indicator/service_indicator_test.cc b/crypto/fipsmodule/service_indicator/service_indicator_test.cc
new file mode 100644
index 0000000..d0da9c9
--- /dev/null
+++ b/crypto/fipsmodule/service_indicator/service_indicator_test.cc
@@ -0,0 +1,2410 @@
+/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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 <gtest/gtest.h>
+
+#include <openssl/aead.h>
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/cipher.h>
+#include <openssl/cmac.h>
+#include <openssl/crypto.h>
+#include <openssl/dh.h>
+#include <openssl/digest.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/md4.h>
+#include <openssl/md5.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/service_indicator.h>
+
+#include "../../test/abi_test.h"
+#include "../../test/test_util.h"
+#include "../bn/internal.h"
+#include "../rand/internal.h"
+#include "../tls/internal.h"
+
+
+using bssl::FIPSStatus;
+
+static const uint8_t kAESKey[16] = {'A', 'W', 'S', '-', 'L', 'C', 'C', 'r',
+                                    'y', 'p', 't', 'o', ' ', 'K', 'e', 'y'};
+
+static const uint8_t kPlaintext[64] = {
+    'A', 'W', 'S', '-', 'L', 'C', 'C', 'r', 'y', 'p', 't', 'o', 'M',
+    'o', 'd', 'u', 'l', 'e', ' ', 'F', 'I', 'P', 'S', ' ', 'K', 'A',
+    'T', ' ', 'E', 'n', 'c', 'r', 'y', 'p', 't', 'i', 'o', 'n', ' ',
+    'a', 'n', 'd', ' ', 'D', 'e', 'c', 'r', 'y', 'p', 't', 'i', 'o',
+    'n', ' ', 'P', 'l', 'a', 'i', 'n', 't', 'e', 'x', 't', '!'};
+
+#if defined(BORINGSSL_FIPS)
+
+// kEVPKeyGenShouldCallFIPSFunctions determines whether |EVP_PKEY_keygen_*|
+// functions should call the FIPS versions of the key-generation functions.
+static const bool kEVPKeyGenShouldCallFIPSFunctions = false;
+
+// kCurveSecp256k1Supported determines whether secp256k1 tests should be run.
+static const bool kCurveSecp256k1Supported = false;
+
+// kEVPDeriveSetsServiceIndicator is true if `EVP_PKEY_derive` should set the
+// service indicator for some algorithms.
+static const bool kEVPDeriveSetsServiceIndicator = false;
+
+template <typename T>
+class TestWithNoErrors : public testing::TestWithParam<T> {
+  void TearDown() override {
+    if (ERR_peek_error() != 0) {
+      auto f = [](const char *str, size_t len, void *unused) -> int {
+        fprintf(stderr, "%s\n", str);
+        return 1;
+      };
+      ERR_print_errors_cb(f, nullptr);
+      ADD_FAILURE();
+    }
+  }
+};
+
+static const uint8_t kAESKey_192[24] = {'A', 'W', 'S', '-', 'L', 'C', 'C', 'r',
+                                        'y', 'p', 't', 'o', ' ', '1', '9', '2',
+                                        '-', 'b', 'i', 't', ' ', 'K', 'e', 'y'};
+
+static const uint8_t kAESKey_256[32] = {'A', 'W', 'S', '-', 'L', 'C', 'C', 'r',
+                                        'y', 'p', 't', 'o', ' ', '2', '5', '6',
+                                        '-', 'b', 'i', 't', ' ', 'L', 'o', 'n',
+                                        'g', ' ', 'K', 'e', 'y', '!', '!', '!'};
+
+static const uint8_t kAESIV[AES_BLOCK_SIZE] = {0};
+
+static bssl::UniquePtr<DH> GetDH() {
+  // kFFDHE2048PrivateKeyData is a 225-bit value. (225 because that's the
+  // minimum private key size in
+  // https://tools.ietf.org/html/rfc7919#appendix-A.1.)
+  static const uint8_t kFFDHE2048PrivateKey[] = {
+      0x01, 0x91, 0x17, 0x3f, 0x2a, 0x05, 0x70, 0x18, 0x7e, 0xc4,
+      0x22, 0xee, 0xb7, 0x0a, 0x15, 0x2f, 0x39, 0x64, 0x58, 0xf3,
+      0xb8, 0x18, 0x7b, 0xe3, 0x6b, 0xd3, 0x8a, 0x4f, 0xa1};
+  bssl::UniquePtr<BIGNUM> priv(
+      BN_bin2bn(kFFDHE2048PrivateKey, sizeof(kFFDHE2048PrivateKey), nullptr));
+  if (!priv) {
+    return nullptr;
+  }
+  bssl::UniquePtr<DH> dh(DH_get_rfc7919_2048());
+  if (!dh || !DH_set0_key(dh.get(), nullptr, priv.get())) {
+    return nullptr;
+  }
+  priv.release();  // |DH_set0_key| takes ownership on success.
+  return dh;
+}
+
+static void DoCipherFinal(EVP_CIPHER_CTX *ctx, std::vector<uint8_t> *out,
+                          bssl::Span<const uint8_t> in,
+                          FIPSStatus expect_approved) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  size_t max_out = in.size();
+  if (EVP_CIPHER_CTX_encrypting(ctx)) {
+    unsigned block_size = EVP_CIPHER_CTX_block_size(ctx);
+    max_out += block_size - (max_out % block_size);
+  }
+  out->resize(max_out);
+
+  size_t total = 0;
+  int len;
+  ASSERT_TRUE(EVP_CipherUpdate(ctx, out->data(), &len, in.data(), in.size()));
+  total += static_cast<size_t>(len);
+  // Check if the overall service is approved by checking |EVP_CipherFinal_ex|,
+  // which should be the last part of the service.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_CipherFinal_ex(ctx, out->data() + total, &len)));
+  total += static_cast<size_t>(len);
+  ASSERT_LE(total, max_out);
+  out->resize(total);
+
+  EXPECT_EQ(approved, expect_approved);
+}
+
+static const uint8_t kTDES_EDE3_CipherText[64] = {
+    0x2a, 0x17, 0x79, 0x5a, 0x9b, 0x1d, 0xd8, 0x72, 0x06, 0xc6, 0xe7,
+    0x55, 0x14, 0xaa, 0x7b, 0x2a, 0x6e, 0xfc, 0x71, 0x29, 0xff, 0x9b,
+    0x67, 0x73, 0x7c, 0x9e, 0x15, 0x74, 0x80, 0xc8, 0x2f, 0xca, 0x93,
+    0xaa, 0x8e, 0xba, 0x2c, 0x48, 0x88, 0x51, 0xc7, 0xa4, 0xf4, 0xe3,
+    0x2b, 0x33, 0xe5, 0xa1, 0x58, 0x0a, 0x08, 0x3c, 0xb9, 0xf6, 0xf1,
+    0x20, 0x67, 0x02, 0x49, 0xa0, 0x92, 0x18, 0xde, 0x2b};
+
+static const uint8_t kTDES_EDE3_CBCCipherText[64] = {
+    0x2a, 0x17, 0x79, 0x5a, 0x9b, 0x1d, 0xd8, 0x72, 0xbf, 0x3f, 0xfd,
+    0xe4, 0x0d, 0x66, 0x33, 0x49, 0x3b, 0x8c, 0xa6, 0xd0, 0x0a, 0x66,
+    0xae, 0xf1, 0xd9, 0xa7, 0xd6, 0xfb, 0xa2, 0x39, 0x6f, 0xf6, 0x1b,
+    0x8f, 0x67, 0xe1, 0x2b, 0x58, 0x1c, 0xb6, 0xa2, 0xec, 0xb3, 0xc2,
+    0xe6, 0xd1, 0xcc, 0x11, 0x05, 0xdd, 0xee, 0x9d, 0x87, 0x95, 0xe9,
+    0x58, 0xc7, 0xef, 0xa4, 0x6d, 0x5e, 0xd6, 0x57, 0x01};
+
+// AES-OFB is not an approved service, and is only used to test we are not
+// validating un-approved services correctly.
+static const uint8_t kAESOFBCiphertext[64] = {
+    0x49, 0xf5, 0x6a, 0x7d, 0x3e, 0xd7, 0xb2, 0x47, 0x35, 0xca, 0x54,
+    0xf5, 0xf1, 0xb8, 0xd1, 0x48, 0x8e, 0x47, 0x09, 0x95, 0xd5, 0xa0,
+    0xc6, 0xa3, 0xe4, 0x94, 0xaf, 0xd4, 0x1b, 0x64, 0x25, 0x65, 0x28,
+    0x9e, 0x82, 0xba, 0x92, 0xca, 0x75, 0xb3, 0xf3, 0x78, 0x44, 0x87,
+    0xd6, 0x11, 0xf9, 0x22, 0xa3, 0xf3, 0xc6, 0x1d, 0x30, 0x00, 0x5b,
+    0x77, 0x18, 0x38, 0x39, 0x08, 0x5e, 0x0a, 0x56, 0x6b};
+
+static const uint8_t kAESECBCiphertext[64] = {
+    0xa4, 0xc1, 0x5c, 0x51, 0x2a, 0x2e, 0x2a, 0xda, 0xd9, 0x02, 0x23,
+    0xe7, 0xa9, 0x34, 0x9d, 0xd8, 0x15, 0xc5, 0xf5, 0x55, 0x8e, 0xb0,
+    0x29, 0x95, 0x48, 0x6c, 0x7f, 0xa9, 0x47, 0x19, 0x0b, 0x54, 0xe5,
+    0x0f, 0x05, 0x76, 0xbb, 0xd0, 0x1a, 0x6c, 0xab, 0xe9, 0xfd, 0x5b,
+    0xd8, 0x0b, 0x0a, 0xbd, 0x7f, 0xea, 0xda, 0x52, 0x07, 0x65, 0x13,
+    0x6c, 0xbe, 0xfc, 0x36, 0x82, 0x4b, 0x6a, 0xc3, 0xd5};
+
+static const uint8_t kAESECBCiphertext_192[64] = {
+    0x1d, 0xc8, 0xaa, 0xa7, 0x29, 0x01, 0x17, 0x09, 0x72, 0xc6, 0xe9,
+    0x63, 0x02, 0x9d, 0xeb, 0x01, 0xeb, 0xc0, 0xda, 0x82, 0x6c, 0x30,
+    0x7d, 0x60, 0x1b, 0x3e, 0xc7, 0x7b, 0xe3, 0x18, 0xa2, 0x43, 0x59,
+    0x15, 0x4a, 0xe4, 0x8a, 0x84, 0xda, 0x16, 0x90, 0x7b, 0xfa, 0x64,
+    0x37, 0x62, 0x19, 0xf1, 0x95, 0x11, 0x61, 0x84, 0xb0, 0x70, 0x49,
+    0x72, 0x9f, 0xe7, 0x3a, 0x18, 0x99, 0x01, 0xba, 0xb0};
+
+static const uint8_t kAESECBCiphertext_256[64] = {
+    0x6f, 0x2d, 0x6d, 0x7a, 0xc1, 0x8f, 0x00, 0x9f, 0x2d, 0xcf, 0xba,
+    0xe6, 0x4f, 0xdd, 0xe0, 0x09, 0x5b, 0xf3, 0xa4, 0xaf, 0xce, 0x45,
+    0x49, 0x6e, 0x28, 0x7b, 0x48, 0x57, 0xb5, 0xf5, 0xd8, 0x05, 0x16,
+    0x0f, 0xea, 0x21, 0x0c, 0x39, 0x78, 0xee, 0x9e, 0x57, 0x3c, 0x40,
+    0x11, 0x9c, 0xd9, 0x34, 0x97, 0xb9, 0xa6, 0x06, 0x40, 0x60, 0xa2,
+    0x0c, 0x01, 0xe3, 0x9c, 0xda, 0x3e, 0xad, 0x99, 0x3d};
+
+static const uint8_t kAESCBCCiphertext[64] = {
+    0xa4, 0xc1, 0x5c, 0x51, 0x2a, 0x2e, 0x2a, 0xda, 0xd9, 0x02, 0x23,
+    0xe7, 0xa9, 0x34, 0x9d, 0xd8, 0x5c, 0xb3, 0x65, 0x54, 0x72, 0xc8,
+    0x06, 0xf1, 0x36, 0xc3, 0x97, 0x73, 0x87, 0xca, 0x44, 0x99, 0x21,
+    0xb8, 0xdb, 0x93, 0x22, 0x00, 0x89, 0x7c, 0x1c, 0xea, 0x36, 0x23,
+    0x18, 0xdb, 0xc1, 0x52, 0x8c, 0x23, 0x66, 0x11, 0x0d, 0xa8, 0xe9,
+    0xb8, 0x08, 0x8b, 0xaa, 0x81, 0x47, 0x01, 0xa4, 0x4f};
+
+static const uint8_t kAESCBCCiphertext_192[64] = {
+    0x1d, 0xc8, 0xaa, 0xa7, 0x29, 0x01, 0x17, 0x09, 0x72, 0xc6, 0xe9,
+    0x63, 0x02, 0x9d, 0xeb, 0x01, 0xb4, 0x48, 0xa8, 0x00, 0x94, 0x46,
+    0x7f, 0xe3, 0xc1, 0x24, 0xea, 0x41, 0xa0, 0x2b, 0x47, 0x2f, 0xae,
+    0x19, 0xce, 0x0d, 0xfa, 0x90, 0x45, 0x85, 0xce, 0xc4, 0x21, 0x0c,
+    0x74, 0x38, 0x13, 0xfd, 0x64, 0xba, 0x58, 0x10, 0x37, 0x53, 0x48,
+    0x66, 0x02, 0x76, 0xfb, 0xb1, 0x3a, 0x19, 0xce, 0x61};
+
+static const uint8_t kAESCBCCiphertext_256[64] = {
+    0x6f, 0x2d, 0x6d, 0x7a, 0xc1, 0x8f, 0x00, 0x9f, 0x2d, 0xcf, 0xba,
+    0xe6, 0x4f, 0xdd, 0xe0, 0x09, 0x9e, 0xa8, 0x28, 0xdc, 0x27, 0xde,
+    0x89, 0x26, 0xc7, 0x94, 0x6a, 0xbf, 0xb6, 0x94, 0x05, 0x08, 0x6c,
+    0x39, 0x07, 0x52, 0xfa, 0x7b, 0xca, 0x7d, 0x9b, 0xbf, 0xb2, 0x43,
+    0x2b, 0x69, 0xee, 0xc5, 0x68, 0x4c, 0xdd, 0x62, 0xae, 0x8d, 0x7e,
+    0x71, 0x0c, 0x8f, 0x11, 0xce, 0x1d, 0x8b, 0xee, 0x94};
+
+static const uint8_t kAESCTRCiphertext[64] = {
+    0x49, 0xf5, 0x6a, 0x7d, 0x3e, 0xd7, 0xb2, 0x47, 0x35, 0xca, 0x54,
+    0xf5, 0xf1, 0xb8, 0xd1, 0x48, 0xb0, 0x18, 0xc4, 0x5e, 0xeb, 0x42,
+    0xfd, 0x10, 0x49, 0x1f, 0x2b, 0x11, 0xe9, 0xb0, 0x07, 0xa4, 0x00,
+    0x56, 0xec, 0x25, 0x53, 0x4d, 0x70, 0x98, 0x38, 0x85, 0x5d, 0x54,
+    0xab, 0x2c, 0x19, 0x13, 0x6d, 0xf3, 0x0e, 0x6f, 0x48, 0x2f, 0xab,
+    0xe1, 0x82, 0xd4, 0x30, 0xa9, 0x16, 0x73, 0x93, 0xc3};
+
+static const uint8_t kAESCTRCiphertext_192[64] = {
+    0x72, 0x7d, 0xbb, 0xd4, 0x8b, 0x16, 0x8b, 0x19, 0xa4, 0xeb, 0xa6,
+    0xfa, 0xa0, 0xd0, 0x2b, 0xbb, 0x9b, 0x1f, 0xbf, 0x4d, 0x67, 0xfb,
+    0xea, 0x89, 0x16, 0xd7, 0xa4, 0xb6, 0xbe, 0x1a, 0x78, 0x1c, 0x3d,
+    0x44, 0x49, 0xa0, 0xf2, 0xb2, 0xb3, 0x82, 0x0f, 0xdd, 0xac, 0xd6,
+    0xea, 0x6e, 0x1f, 0x09, 0x8d, 0xa5, 0xdb, 0x4f, 0x3f, 0x97, 0x90,
+    0x26, 0xed, 0xf6, 0xbb, 0x62, 0xb3, 0x6f, 0x52, 0x67};
+
+static const uint8_t kAESCTRCiphertext_256[64] = {
+    0x4a, 0x87, 0x44, 0x09, 0xf4, 0x1d, 0x80, 0x94, 0x51, 0x9a, 0xe4,
+    0x89, 0x49, 0xcb, 0x98, 0x0d, 0x27, 0xc5, 0xba, 0x20, 0x00, 0x45,
+    0xbb, 0x29, 0x75, 0xc0, 0xb7, 0x23, 0x0d, 0x81, 0x9f, 0x43, 0xaa,
+    0x78, 0x89, 0xc0, 0xc4, 0x6d, 0x99, 0x0d, 0xb8, 0x9b, 0xc3, 0x25,
+    0xa6, 0xd1, 0x7c, 0x98, 0x3e, 0xff, 0x06, 0x59, 0x41, 0xcf, 0xb2,
+    0xd5, 0x2f, 0x95, 0xea, 0x83, 0xb1, 0x42, 0xb8, 0xb2};
+
+static const uint8_t kAESCFBCiphertext[64] = {
+    0x49, 0xf5, 0x6a, 0x7d, 0x3e, 0xd7, 0xb2, 0x47, 0x35, 0xca, 0x54,
+    0xf5, 0xf1, 0xb8, 0xd1, 0x48, 0x01, 0xdc, 0xba, 0x43, 0x3a, 0x7b,
+    0xbf, 0x84, 0x91, 0x49, 0xc5, 0xc9, 0xd6, 0xcf, 0x6a, 0x2c, 0x3a,
+    0x66, 0x99, 0x68, 0xe3, 0xd0, 0x56, 0x05, 0xe7, 0x99, 0x7f, 0xc3,
+    0xbc, 0x09, 0x13, 0xa6, 0xf0, 0xde, 0x17, 0xf4, 0x85, 0x9a, 0xee,
+    0x29, 0xc3, 0x77, 0xab, 0xc4, 0xf6, 0xdb, 0xae, 0x24};
+
+static const uint8_t kAESCCMCiphertext[64 + 4] = {
+    0x7a, 0x02, 0x5d, 0x48, 0x02, 0x44, 0x78, 0x7f, 0xb4, 0x71, 0x74, 0x7b,
+    0xec, 0x4d, 0x90, 0x29, 0x7b, 0xa7, 0x65, 0xbb, 0x3e, 0x80, 0x41, 0x7e,
+    0xab, 0xb4, 0x58, 0x22, 0x4f, 0x86, 0xcd, 0xcc, 0xc2, 0x12, 0xeb, 0x36,
+    0x39, 0x89, 0xe3, 0x66, 0x2a, 0xbf, 0xe3, 0x6c, 0x95, 0x60, 0x13, 0x9e,
+    0x93, 0xcc, 0xb4, 0x06, 0xbe, 0xaf, 0x3f, 0xba, 0x13, 0x73, 0x09, 0x92,
+    0xd1, 0x80, 0x73, 0xb3, 0xc3, 0xa3, 0xa4, 0x8b,
+};
+
+static const uint8_t kAESKWCiphertext[72] = {
+    0x44, 0xec, 0x7d, 0x92, 0x2c, 0x9f, 0xf3, 0xe8, 0xac, 0xb1, 0xea, 0x3d,
+    0x0a, 0xc7, 0x51, 0x27, 0xe8, 0x03, 0x11, 0x78, 0xe5, 0xaf, 0x8d, 0xb1,
+    0x70, 0x96, 0x2e, 0xfa, 0x05, 0x48, 0x48, 0x99, 0x1a, 0x58, 0xcc, 0xfe,
+    0x11, 0x36, 0x5d, 0x49, 0x98, 0x1e, 0xbb, 0xd6, 0x0b, 0xf5, 0xb9, 0x64,
+    0xa4, 0x30, 0x3e, 0x60, 0xf6, 0xc5, 0xff, 0x82, 0x30, 0x9a, 0xa7, 0x48,
+    0x82, 0xe2, 0x00, 0xc1, 0xe9, 0xc2, 0x73, 0x6f, 0xbc, 0x89, 0x66, 0x9d};
+
+static const uint8_t kAESKWPCiphertext[72] = {
+    0x29, 0x5e, 0xb9, 0xea, 0x96, 0xa7, 0xa5, 0xca, 0xfa, 0xeb, 0xda, 0x78,
+    0x13, 0xea, 0x83, 0xca, 0x41, 0xdb, 0x4d, 0x36, 0x7d, 0x39, 0x8a, 0xd6,
+    0xef, 0xd3, 0xd2, 0x2d, 0x3a, 0xc8, 0x55, 0xc8, 0x73, 0xd7, 0x79, 0x55,
+    0xad, 0xc0, 0xce, 0xad, 0x12, 0x54, 0x51, 0xf0, 0x70, 0x76, 0xff, 0xe7,
+    0x0c, 0xb2, 0x8e, 0xdd, 0xb6, 0x9a, 0x27, 0x74, 0x98, 0x28, 0xe0, 0xfa,
+    0x11, 0xe6, 0x3f, 0x86, 0x93, 0x23, 0xf8, 0x0d, 0xcb, 0xaf, 0x2b, 0xb7};
+
+static const uint8_t kAESCMACOutput[16] = {0xe7, 0x32, 0x43, 0xb4, 0xae, 0x79,
+                                           0x08, 0x86, 0xe7, 0x9f, 0x0d, 0x3f,
+                                           0x88, 0x3f, 0x1a, 0xfd};
+
+const uint8_t kDHOutput[2048 / 8] = {
+    0x83, 0xf0, 0xd8, 0x4f, 0xdb, 0xe7, 0x65, 0xb6, 0x80, 0x6f, 0xa3, 0x22,
+    0x9b, 0x33, 0x1c, 0x87, 0x89, 0xc8, 0x1d, 0x2c, 0xa1, 0xba, 0xa3, 0xb8,
+    0xdf, 0xad, 0x42, 0xea, 0x9a, 0x75, 0xfe, 0xbf, 0xc1, 0xa8, 0xf6, 0xda,
+    0xec, 0xdf, 0x48, 0x61, 0x7d, 0x7f, 0x3d, 0xab, 0xbd, 0xda, 0xd1, 0xd3,
+    0xd8, 0xaf, 0x44, 0x4a, 0xba, 0x3f, 0x0e, 0x99, 0x8d, 0x11, 0xdc, 0x63,
+    0xb1, 0xe0, 0x65, 0xf2, 0xb9, 0x82, 0x81, 0x8c, 0x88, 0x75, 0x8f, 0xa0,
+    0x94, 0x52, 0x2a, 0x2f, 0x2d, 0x10, 0xb1, 0xf4, 0xd2, 0xdd, 0x0f, 0x8a,
+    0x7e, 0x49, 0x7b, 0x1e, 0xfd, 0x8c, 0x78, 0xf9, 0x11, 0xdf, 0x80, 0x8b,
+    0x2e, 0x86, 0x34, 0xbf, 0x4b, 0xca, 0x13, 0x3e, 0x85, 0x63, 0xeb, 0xe4,
+    0xff, 0xec, 0xb0, 0xe8, 0x83, 0xf6, 0x2c, 0x45, 0x21, 0x90, 0x34, 0x9c,
+    0x9d, 0x9d, 0xfe, 0x1a, 0x48, 0x53, 0xef, 0x97, 0xd5, 0xea, 0x6a, 0x65,
+    0xf5, 0xe9, 0x9f, 0x91, 0x4f, 0xb4, 0x43, 0xe7, 0x1f, 0x0a, 0x2e, 0xdb,
+    0xe6, 0x84, 0x30, 0xdb, 0xad, 0xe4, 0xaf, 0x2c, 0xf9, 0x93, 0xe8, 0x0a,
+    0xab, 0x7f, 0x1c, 0xde, 0xb3, 0x80, 0xb6, 0x02, 0x42, 0xba, 0x18, 0x0d,
+    0x0f, 0xc2, 0x1d, 0xa4, 0x4b, 0x2b, 0x84, 0x74, 0x10, 0x97, 0x6d, 0xdc,
+    0xfa, 0x99, 0xdc, 0xba, 0xf2, 0xcb, 0x1b, 0xe8, 0x1a, 0xba, 0x0c, 0x67,
+    0x60, 0x07, 0x87, 0xcc, 0xc6, 0x0d, 0xef, 0x56, 0x07, 0x80, 0x55, 0xae,
+    0x03, 0xa3, 0x62, 0x31, 0x4c, 0x50, 0xf7, 0xf6, 0x87, 0xb3, 0x8d, 0xe2,
+    0x11, 0x86, 0xe7, 0x9d, 0x98, 0x3c, 0x2a, 0x6c, 0x8a, 0xf0, 0xa7, 0x73,
+    0x33, 0x07, 0x4e, 0x70, 0xee, 0x14, 0x4b, 0xa3, 0xf7, 0x4f, 0x8f, 0x1a,
+    0xa2, 0xf6, 0xd1, 0xeb, 0x4d, 0x04, 0xf9, 0x4c, 0x07, 0x36, 0xb1, 0x46,
+    0x53, 0x55, 0xb1, 0x23};
+
+static const uint8_t kOutput_md4[MD4_DIGEST_LENGTH] = {
+    0xab, 0x6b, 0xda, 0x84, 0xc0, 0x6b, 0xd0, 0x1d,
+    0x19, 0xc0, 0x08, 0x11, 0x07, 0x8d, 0xce, 0x0e};
+
+static const uint8_t kOutput_md5[MD5_DIGEST_LENGTH] = {
+    0xe9, 0x70, 0xa2, 0xf7, 0x9c, 0x55, 0x57, 0xac,
+    0x4e, 0x7f, 0x6b, 0xbc, 0xa3, 0xb9, 0xb7, 0xdb};
+
+static const uint8_t kOutput_sha1[SHA_DIGEST_LENGTH] = {
+    0xaa, 0x18, 0x71, 0x34, 0x00, 0x71, 0x67, 0x9f, 0xa1, 0x6d,
+    0x20, 0x82, 0x91, 0x0f, 0x53, 0x0a, 0xcd, 0x6e, 0xa4, 0x34};
+
+static const uint8_t kOutput_sha224[SHA224_DIGEST_LENGTH] = {
+    0x5f, 0x1a, 0x9e, 0x68, 0x4c, 0xb7, 0x42, 0x68, 0xa0, 0x8b,
+    0x87, 0xd7, 0x96, 0xb6, 0xcf, 0x1e, 0x4f, 0x85, 0x1c, 0x47,
+    0xe9, 0x29, 0xb3, 0xb2, 0x73, 0x72, 0xd2, 0x69};
+
+static const uint8_t kOutput_sha256[SHA256_DIGEST_LENGTH] = {
+    0xe7, 0x63, 0x1c, 0xbb, 0x12, 0xb5, 0xbf, 0x4f, 0x99, 0x05, 0x9d,
+    0x40, 0x15, 0x55, 0x34, 0x9c, 0x26, 0x36, 0xd2, 0xfe, 0x6a, 0xd6,
+    0x26, 0xb4, 0x9d, 0x33, 0x07, 0xf5, 0xe6, 0x29, 0x13, 0x92};
+
+static const uint8_t kOutput_sha384[SHA384_DIGEST_LENGTH] = {
+    0x15, 0x81, 0x48, 0x8d, 0x95, 0xf2, 0x66, 0x84, 0x65, 0x94, 0x3e, 0xb9,
+    0x8c, 0xda, 0x36, 0x30, 0x2a, 0x85, 0xc0, 0xcd, 0xec, 0x38, 0xa0, 0x1f,
+    0x72, 0xe2, 0x68, 0xfe, 0x4e, 0xdb, 0x27, 0x8b, 0x50, 0x15, 0xe0, 0x24,
+    0xc3, 0x65, 0xd1, 0x66, 0x2a, 0x3e, 0xe7, 0x00, 0x16, 0x51, 0xf5, 0x18};
+
+static const uint8_t kOutput_sha512[SHA512_DIGEST_LENGTH] = {
+    0x71, 0xcc, 0xec, 0x03, 0xf8, 0x76, 0xf4, 0x0b, 0xf1, 0x1b, 0x89,
+    0x27, 0x83, 0xa1, 0x70, 0x02, 0x00, 0x2b, 0xe9, 0x3c, 0x3c, 0x65,
+    0x12, 0xb9, 0xa8, 0x8c, 0xc5, 0x9d, 0xae, 0x3c, 0x73, 0x43, 0x76,
+    0x4d, 0x98, 0xed, 0xd0, 0xbe, 0xb4, 0xf9, 0x0b, 0x5c, 0x5d, 0x34,
+    0x46, 0x30, 0x18, 0xc2, 0x05, 0x88, 0x8a, 0x3c, 0x25, 0xcc, 0x06,
+    0xf8, 0x73, 0xb9, 0xe4, 0x18, 0xa8, 0xc2, 0xf0, 0xe5};
+
+static const uint8_t kOutput_sha512_256[SHA512_256_DIGEST_LENGTH] = {
+    0x1a, 0x78, 0x68, 0x6b, 0x69, 0x6d, 0x28, 0x14, 0x6b, 0x37, 0x11,
+    0x2d, 0xfb, 0x72, 0x35, 0xfa, 0xc1, 0xc4, 0x5f, 0x5c, 0x49, 0x91,
+    0x08, 0x95, 0x0b, 0x0f, 0xc9, 0x88, 0x44, 0x12, 0x01, 0x6a};
+
+static const uint8_t kHMACOutput_sha1[SHA_DIGEST_LENGTH] = {
+    0x34, 0xac, 0x50, 0x9b, 0xa9, 0x4c, 0x39, 0xef, 0x45, 0xa0,
+    0x6b, 0xdc, 0xfc, 0xbd, 0x3d, 0x42, 0xe8, 0x0a, 0x97, 0x86};
+
+static const uint8_t kHMACOutput_sha224[SHA224_DIGEST_LENGTH] = {
+    0x30, 0x62, 0x97, 0x45, 0x9e, 0xea, 0x62, 0xe4, 0x5d, 0xbb,
+    0x7d, 0x25, 0x3f, 0x77, 0x0f, 0x9d, 0xa4, 0xbd, 0x17, 0x96,
+    0x23, 0x53, 0xe1, 0x76, 0xf3, 0xf8, 0x9b, 0x74};
+
+static const uint8_t kHMACOutput_sha256[SHA256_DIGEST_LENGTH] = {
+    0x68, 0x33, 0x3e, 0x74, 0x9a, 0x49, 0xab, 0x77, 0xb4, 0x1a, 0x40,
+    0xd8, 0x55, 0x07, 0xa7, 0xb6, 0x48, 0xa1, 0xa5, 0xa9, 0xd1, 0x7b,
+    0x85, 0xe9, 0x33, 0x09, 0x16, 0x79, 0xcc, 0xe9, 0x29, 0x97};
+
+static const uint8_t kHMACOutput_sha384[SHA384_DIGEST_LENGTH] = {
+    0xcc, 0x39, 0x22, 0x0e, 0x9f, 0x2e, 0x26, 0x4a, 0xb5, 0xf8, 0x4a, 0x0f,
+    0x73, 0x51, 0x26, 0x1a, 0xf2, 0xef, 0x15, 0xf3, 0x5f, 0x77, 0xce, 0xbb,
+    0x4c, 0x69, 0x86, 0x0e, 0x1f, 0x5c, 0x4d, 0xc9, 0x96, 0xd9, 0xed, 0x74,
+    0x6c, 0x45, 0x05, 0x7a, 0x0e, 0x3f, 0x36, 0x8a, 0xda, 0x2a, 0x35, 0xf9};
+
+static const uint8_t kHMACOutput_sha512[SHA512_DIGEST_LENGTH] = {
+    0x4c, 0x09, 0x46, 0x50, 0x7c, 0xb3, 0xa1, 0xfa, 0xbc, 0xf2, 0xc4,
+    0x4f, 0x1e, 0x3d, 0xa9, 0x0b, 0x29, 0x4e, 0x12, 0x09, 0x09, 0x32,
+    0xde, 0x82, 0xa0, 0xab, 0xf6, 0x5e, 0x66, 0x19, 0xd0, 0x86, 0x9a,
+    0x92, 0xe3, 0xf9, 0x13, 0xa7, 0xe6, 0xfc, 0x1a, 0x2e, 0x50, 0xda,
+    0xf6, 0x8f, 0xb2, 0xd5, 0xb2, 0x6e, 0x97, 0x82, 0x25, 0x5a, 0x1e,
+    0xbf, 0x9b, 0x99, 0x8c, 0xf0, 0x37, 0xe6, 0x3d, 0x40};
+
+static const uint8_t kHMACOutput_sha512_256[SHA512_256_DIGEST_LENGTH] = {
+    0x9c, 0x95, 0x9c, 0x03, 0xc9, 0x8c, 0x90, 0xee, 0x7a, 0xff, 0xed,
+    0x26, 0xba, 0x75, 0x90, 0xd0, 0xb9, 0xd4, 0x09, 0xf5, 0x22, 0xd6,
+    0xb6, 0xab, 0xa8, 0xb9, 0xae, 0x01, 0x06, 0x37, 0x8f, 0xd1};
+
+static const uint8_t kDRBGEntropy[48] = {
+    'B', 'C', 'M', ' ', 'K', 'n', 'o', 'w', 'n', ' ', 'A', 'n',
+    's', 'w', 'e', 'r', ' ', 'T', 'e', 's', 't', ' ', 'D', 'B',
+    'R', 'G', ' ', 'I', 'n', 'i', 't', 'i', 'a', 'l', ' ', 'E',
+    'n', 't', 'r', 'o', 'p', 'y', ' ', ' ', ' ', ' ', ' ', ' '};
+
+static const uint8_t kDRBGPersonalization[18] = {'B', 'C', 'M', 'P', 'e', 'r',
+                                                 's', 'o', 'n', 'a', 'l', 'i',
+                                                 'z', 'a', 't', 'i', 'o', 'n'};
+
+static const uint8_t kDRBGAD[16] = {'B', 'C', 'M', ' ', 'D', 'R', 'B', 'G',
+                                    ' ', 'K', 'A', 'T', ' ', 'A', 'D', ' '};
+
+const uint8_t kDRBGOutput[64] = {
+    0x1d, 0x63, 0xdf, 0x05, 0x51, 0x49, 0x22, 0x46, 0xcd, 0x9b, 0xc5,
+    0xbb, 0xf1, 0x5d, 0x44, 0xae, 0x13, 0x78, 0xb1, 0xe4, 0x7c, 0xf1,
+    0x96, 0x33, 0x3d, 0x60, 0xb6, 0x29, 0xd4, 0xbb, 0x6b, 0x44, 0xf9,
+    0xef, 0xd9, 0xf4, 0xa2, 0xba, 0x48, 0xea, 0x39, 0x75, 0x59, 0x32,
+    0xf7, 0x31, 0x2c, 0x98, 0x14, 0x2b, 0x49, 0xdf, 0x02, 0xb6, 0x5d,
+    0x71, 0x09, 0x50, 0xdb, 0x23, 0xdb, 0xe5, 0x22, 0x95};
+
+static const uint8_t kDRBGEntropy2[48] = {
+    'B', 'C', 'M', ' ', 'K', 'n', 'o', 'w', 'n', ' ', 'A', 'n',
+    's', 'w', 'e', 'r', ' ', 'T', 'e', 's', 't', ' ', 'D', 'B',
+    'R', 'G', ' ', 'R', 'e', 's', 'e', 'e', 'd', ' ', 'E', 'n',
+    't', 'r', 'o', 'p', 'y', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+
+static const uint8_t kDRBGReseedOutput[64] = {
+    0xa4, 0x77, 0x05, 0xdb, 0x14, 0x11, 0x76, 0x71, 0x42, 0x5b, 0xd8,
+    0xd7, 0xa5, 0x4f, 0x8b, 0x39, 0xf2, 0x10, 0x4a, 0x50, 0x5b, 0xa2,
+    0xc8, 0xf0, 0xbb, 0x3e, 0xa1, 0xa5, 0x90, 0x7d, 0x54, 0xd9, 0xc6,
+    0xb0, 0x96, 0xc0, 0x2b, 0x7e, 0x9b, 0xc9, 0xa1, 0xdd, 0x78, 0x2e,
+    0xd5, 0xa8, 0x66, 0x16, 0xbd, 0x18, 0x3c, 0xf2, 0xaa, 0x7a, 0x2b,
+    0x37, 0xf9, 0xab, 0x35, 0x64, 0x15, 0x01, 0x3f, 0xc4,
+};
+
+static const uint8_t kTLSSecret[32] = {
+    0xbf, 0xe4, 0xb7, 0xe0, 0x26, 0x55, 0x5f, 0x6a, 0xdf, 0x5d, 0x27,
+    0xd6, 0x89, 0x99, 0x2a, 0xd6, 0xf7, 0x65, 0x66, 0x07, 0x4b, 0x55,
+    0x5f, 0x64, 0x55, 0xcd, 0xd5, 0x77, 0xa4, 0xc7, 0x09, 0x61,
+};
+static const char kTLSLabel[] = "FIPS self test";
+static const uint8_t kTLSSeed1[16] = {
+    0x8f, 0x0d, 0xe8, 0xb6, 0x90, 0x8f, 0xb1, 0xd2,
+    0x6d, 0x51, 0xf4, 0x79, 0x18, 0x63, 0x51, 0x65,
+};
+static const uint8_t kTLSSeed2[16] = {
+    0x7d, 0x24, 0x1a, 0x9d, 0x3c, 0x59, 0xbf, 0x3c,
+    0x31, 0x1e, 0x2b, 0x21, 0x41, 0x8d, 0x32, 0x81,
+};
+
+static const uint8_t kTLSOutput_mdsha1[32] = {
+    0x36, 0xa9, 0x31, 0xb0, 0x43, 0xe3, 0x64, 0x72, 0xb9, 0x47, 0x54,
+    0x0d, 0x8a, 0xfc, 0xe3, 0x5c, 0x1c, 0x15, 0x67, 0x7e, 0xa3, 0x5d,
+    0xf2, 0x3a, 0x57, 0xfd, 0x50, 0x16, 0xe1, 0xa4, 0xa6, 0x37,
+};
+
+static const uint8_t kTLSOutput_md[32] = {
+    0x79, 0xef, 0x46, 0xc4, 0x35, 0xbc, 0xe5, 0xda, 0xd3, 0x66, 0x91,
+    0xdc, 0x86, 0x09, 0x41, 0x66, 0xf2, 0x0c, 0xeb, 0xe6, 0xab, 0x5c,
+    0x58, 0xf4, 0x65, 0xce, 0x2f, 0x5f, 0x4b, 0x34, 0x1e, 0xa1,
+};
+
+static const uint8_t kTLSOutput_sha1[32] = {
+    0xbb, 0x0a, 0x73, 0x52, 0xf8, 0x85, 0xd7, 0xbd, 0x12, 0x34, 0x78,
+    0x3b, 0x54, 0x4c, 0x75, 0xfe, 0xd7, 0x23, 0x6e, 0x22, 0x3f, 0x42,
+    0x34, 0x99, 0x57, 0x6b, 0x14, 0xc4, 0xc8, 0xae, 0x9f, 0x4c,
+};
+
+static const uint8_t kTLSOutput_sha224[32] = {
+    0xdd, 0xaf, 0x6f, 0xaa, 0xd9, 0x2b, 0x3d, 0xb9, 0x46, 0x4c, 0x55,
+    0x8a, 0xf7, 0xa6, 0x9b, 0x0b, 0x35, 0xcc, 0x07, 0xa7, 0x55, 0x5b,
+    0x5e, 0x39, 0x12, 0xc0, 0xd4, 0x30, 0xdf, 0x0c, 0xdf, 0x6b,
+};
+
+static const uint8_t kTLSOutput_sha256[32] = {
+    0x67, 0x85, 0xde, 0x60, 0xfc, 0x0a, 0x83, 0xe9, 0xa2, 0x2a, 0xb3,
+    0xf0, 0x27, 0x0c, 0xba, 0xf7, 0xfa, 0x82, 0x3d, 0x14, 0x77, 0x1d,
+    0x86, 0x29, 0x79, 0x39, 0x77, 0x8a, 0xd5, 0x0e, 0x9d, 0x32,
+};
+
+static const uint8_t kTLSOutput_sha384[32] = {
+    0x75, 0x15, 0x3f, 0x44, 0x7a, 0xfd, 0x34, 0xed, 0x2b, 0x67, 0xbc,
+    0xd8, 0x57, 0x96, 0xab, 0xff, 0xf4, 0x0c, 0x05, 0x94, 0x02, 0x23,
+    0x81, 0xbf, 0x0e, 0xd2, 0xec, 0x7c, 0xe0, 0xa7, 0xc3, 0x7d,
+};
+
+static const uint8_t kTLSOutput_sha512[32] = {
+    0x68, 0xb9, 0xc8, 0x4c, 0xf5, 0x51, 0xfc, 0x7a, 0x1f, 0x6c, 0xe5,
+    0x43, 0x73, 0x80, 0x53, 0x7c, 0xae, 0x76, 0x55, 0x67, 0xe0, 0x79,
+    0xbf, 0x3a, 0x53, 0x71, 0xb7, 0x9c, 0xb5, 0x03, 0x15, 0x3f,
+};
+
+static const uint8_t kAESGCMCiphertext_128[64 + 16] = {
+    0x38, 0x71, 0xcb, 0x61, 0x70, 0x60, 0x13, 0x8b, 0x2f, 0x91, 0x09, 0x7f,
+    0x83, 0x20, 0x0f, 0x1f, 0x71, 0xe2, 0x47, 0x46, 0x6f, 0x5f, 0xa8, 0xad,
+    0xa8, 0xfc, 0x0a, 0xfd, 0x36, 0x65, 0x84, 0x90, 0x28, 0x2b, 0xcb, 0x4f,
+    0x68, 0xae, 0x09, 0xba, 0xae, 0xdd, 0xdb, 0x91, 0xcc, 0x38, 0xb3, 0xad,
+    0x10, 0x84, 0xb8, 0x45, 0x36, 0xf3, 0x96, 0xb4, 0xef, 0xba, 0xda, 0x10,
+    0xf8, 0x8b, 0xf3, 0xda, 0x91, 0x1f, 0x8c, 0xd8, 0x39, 0x7b, 0x1c, 0xfd,
+    0xe7, 0x99, 0x7d, 0xb7, 0x22, 0x69, 0x67, 0xbd,
+};
+
+static const uint8_t kAESGCMCiphertext_192[64 + 16] = {
+    0x05, 0x63, 0x6e, 0xe4, 0xd1, 0x9f, 0xd0, 0x91, 0x18, 0xc9, 0xf8, 0xfd,
+    0xc2, 0x62, 0x09, 0x05, 0x91, 0xb4, 0x92, 0x66, 0x18, 0xe7, 0x93, 0x6a,
+    0xc7, 0xde, 0x81, 0x36, 0x93, 0x79, 0x45, 0x34, 0xc0, 0x6d, 0x14, 0x94,
+    0x93, 0x39, 0x2b, 0x7f, 0x4f, 0x10, 0x1c, 0xa5, 0xfe, 0x3b, 0x37, 0xd7,
+    0x0a, 0x98, 0xd7, 0xb5, 0xe0, 0xdc, 0xe4, 0x9f, 0x36, 0x40, 0xad, 0x03,
+    0xbf, 0x53, 0xe0, 0x7c, 0x3f, 0x57, 0x4f, 0x80, 0x99, 0xe6, 0x90, 0x4e,
+    0x59, 0x2e, 0xe0, 0x76, 0x53, 0x09, 0xc3, 0xd3,
+};
+
+static const uint8_t kAESGCMCiphertext_256[64 + 16] = {
+    0x92, 0x5f, 0xae, 0x84, 0xe7, 0x40, 0xfa, 0x1e, 0xaf, 0x8f, 0x97, 0x0e,
+    0x8e, 0xdd, 0x6a, 0x94, 0x22, 0xee, 0x4f, 0x70, 0x66, 0xbf, 0xb1, 0x99,
+    0x05, 0xbd, 0xd0, 0xd7, 0x91, 0x54, 0xaf, 0xe1, 0x52, 0xc9, 0x4e, 0x55,
+    0xa5, 0x23, 0x62, 0x8b, 0x23, 0x40, 0x90, 0x56, 0xe0, 0x68, 0x63, 0xe5,
+    0x7e, 0x5b, 0xbe, 0x96, 0x7b, 0xc4, 0x16, 0xf9, 0xbe, 0x18, 0x06, 0x79,
+    0x8f, 0x99, 0x35, 0xe3, 0x2a, 0x82, 0xb5, 0x5e, 0x8a, 0x06, 0xbe, 0x99,
+    0x57, 0xb1, 0x76, 0xe1, 0xc5, 0xaa, 0x82, 0xe7,
+};
+
+static const struct AEADTestVector {
+  const char *name;
+  const EVP_AEAD *aead;
+  const uint8_t *key;
+  const int key_length;
+  const uint8_t *expected_ciphertext;
+  const int cipher_text_length;
+  const FIPSStatus expect_approved;
+  const bool test_repeat_nonce;
+} kAEADTestVectors[] = {
+    // Internal IV usage of AES-GCM is approved.
+    {
+        "AES-GCM 128-bit key internal iv test",
+        EVP_aead_aes_128_gcm_randnonce(),
+        kAESKey,
+        sizeof(kAESKey),
+        nullptr,
+        0,
+        FIPSStatus::APPROVED,
+        false,
+    },
+    {
+        "AES-GCM 256-bit key internal iv test",
+        EVP_aead_aes_256_gcm_randnonce(),
+        kAESKey_256,
+        sizeof(kAESKey_256),
+        nullptr,
+        0,
+        FIPSStatus::APPROVED,
+        false,
+    },
+    // External IV usage of AES-GCM is not approved unless used within a TLS
+    // context.
+    {
+        "Generic AES-GCM 128-bit key external iv test",
+        EVP_aead_aes_128_gcm(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESGCMCiphertext_128,
+        sizeof(kAESGCMCiphertext_128),
+        FIPSStatus::NOT_APPROVED,
+        false,
+    },
+    {
+        "Generic AES-GCM 192-bit key external iv test",
+        EVP_aead_aes_192_gcm(),
+        kAESKey_192,
+        24,
+        kAESGCMCiphertext_192,
+        sizeof(kAESGCMCiphertext_192),
+        FIPSStatus::NOT_APPROVED,
+        false,
+    },
+    {
+        "Generic AES-GCM 256-bit key external iv test",
+        EVP_aead_aes_256_gcm(),
+        kAESKey_256,
+        sizeof(kAESKey_256),
+        kAESGCMCiphertext_256,
+        sizeof(kAESGCMCiphertext_256),
+        FIPSStatus::NOT_APPROVED,
+        false,
+    },
+    // External IV usage of AEAD AES-GCM APIs specific for TLS is approved.
+    {
+        "TLS1.2 AES-GCM 128-bit key external iv test",
+        EVP_aead_aes_128_gcm_tls12(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESGCMCiphertext_128,
+        sizeof(kAESGCMCiphertext_128),
+        FIPSStatus::APPROVED,
+        true,
+    },
+    {
+        "TLS1.2 AES-GCM 256-bit key external iv test",
+        EVP_aead_aes_256_gcm_tls12(),
+        kAESKey_256,
+        sizeof(kAESKey_256),
+        kAESGCMCiphertext_256,
+        sizeof(kAESGCMCiphertext_256),
+        FIPSStatus::APPROVED,
+        true,
+    },
+    {
+        "TLS1.3 AES-GCM 128-bit key external iv test",
+        EVP_aead_aes_128_gcm_tls13(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESGCMCiphertext_128,
+        sizeof(kAESGCMCiphertext_128),
+        FIPSStatus::APPROVED,
+        true,
+    },
+    {
+        "TLS1.3 AES-GCM 256-bit key external iv test",
+        EVP_aead_aes_256_gcm_tls13(),
+        kAESKey_256,
+        sizeof(kAESKey_256),
+        kAESGCMCiphertext_256,
+        sizeof(kAESGCMCiphertext_256),
+        FIPSStatus::APPROVED,
+        true,
+    },
+    // 128 bit keys with 32 bit tag lengths are approved for AES-CCM.
+    {
+        "AES-CCM 128-bit key test",
+        EVP_aead_aes_128_ccm_bluetooth(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESCCMCiphertext,
+        sizeof(kAESCCMCiphertext),
+        FIPSStatus::APPROVED,
+        false,
+    },
+};
+
+class AEADServiceIndicatorTest : public TestWithNoErrors<AEADTestVector> {};
+
+INSTANTIATE_TEST_SUITE_P(All, AEADServiceIndicatorTest,
+                         testing::ValuesIn(kAEADTestVectors));
+
+TEST_P(AEADServiceIndicatorTest, EVP_AEAD) {
+  const AEADTestVector &test = GetParam();
+  SCOPED_TRACE(test.name);
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  bssl::ScopedEVP_AEAD_CTX aead_ctx;
+  std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(test.aead), 0);
+  std::vector<uint8_t> encrypt_output(256);
+  std::vector<uint8_t> decrypt_output(256);
+  size_t out_len;
+
+  // Test running the EVP_AEAD_CTX interfaces one by one directly, and check
+  // |EVP_AEAD_CTX_seal| and |EVP_AEAD_CTX_open| for approval at the end.
+  // |EVP_AEAD_CTX_init| should not be approved because the function does not
+  // indicate that a service has been fully completed yet.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_AEAD_CTX_init(aead_ctx.get(), test.aead, test.key,
+                                  test.key_length, 0, nullptr)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_AEAD_CTX_seal(aead_ctx.get(), encrypt_output.data(),
+                                  &out_len, encrypt_output.size(), nonce.data(),
+                                  EVP_AEAD_nonce_length(test.aead), kPlaintext,
+                                  sizeof(kPlaintext), nullptr, 0)));
+  EXPECT_EQ(approved, test.expect_approved);
+  encrypt_output.resize(out_len);
+  if (test.expected_ciphertext) {
+    EXPECT_EQ(Bytes(test.expected_ciphertext, test.cipher_text_length),
+              Bytes(encrypt_output));
+  }
+
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_AEAD_CTX_open(aead_ctx.get(), decrypt_output.data(), &out_len,
+                        decrypt_output.size(), nonce.data(), nonce.size(),
+                        encrypt_output.data(), out_len, nullptr, 0)));
+  // Decryption doesn't have nonce uniqueness requirements and so is always
+  // approved for approved key lengths.
+  EXPECT_EQ(approved, test.key_length != 24 ? FIPSStatus::APPROVED
+                                            : FIPSStatus::NOT_APPROVED);
+  decrypt_output.resize(out_len);
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(decrypt_output));
+
+  // Second call when encrypting using the same nonce for AES-GCM TLS specific
+  // functions should fail and return |FIPSStatus::NOT_APPROVED|.
+  if (test.test_repeat_nonce) {
+    ASSERT_FALSE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved,
+        EVP_AEAD_CTX_seal(aead_ctx.get(), encrypt_output.data(), &out_len,
+                          encrypt_output.size(), nonce.data(), nonce.size(),
+                          kPlaintext, sizeof(kPlaintext), nullptr, 0)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+    EXPECT_EQ(ERR_GET_REASON(ERR_get_error()), CIPHER_R_INVALID_NONCE);
+  }
+}
+
+static const struct CipherTestVector {
+  const EVP_CIPHER *cipher;
+  const uint8_t *key;
+  const int key_length;
+  const uint8_t *expected_ciphertext;
+  const int cipher_text_length;
+  const bool has_iv;
+  const FIPSStatus expect_approved;
+} kTestVectors[] = {
+    {
+        EVP_aes_128_ecb(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESECBCiphertext,
+        sizeof(kAESECBCiphertext),
+        false,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_192_ecb(),
+        kAESKey_192,
+        sizeof(kAESKey_192),
+        kAESECBCiphertext_192,
+        sizeof(kAESECBCiphertext_192),
+        false,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_256_ecb(),
+        kAESKey_256,
+        sizeof(kAESKey_256),
+        kAESECBCiphertext_256,
+        sizeof(kAESECBCiphertext_256),
+        false,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_128_cbc(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESCBCCiphertext,
+        sizeof(kAESCBCCiphertext),
+        true,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_192_cbc(),
+        kAESKey_192,
+        sizeof(kAESKey_192),
+        kAESCBCCiphertext_192,
+        sizeof(kAESCBCCiphertext_192),
+        true,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_256_cbc(),
+        kAESKey_256,
+        sizeof(kAESKey_256),
+        kAESCBCCiphertext_256,
+        sizeof(kAESCBCCiphertext_256),
+        true,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_128_ctr(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESCTRCiphertext,
+        sizeof(kAESCTRCiphertext),
+        true,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_192_ctr(),
+        kAESKey_192,
+        sizeof(kAESKey_192),
+        kAESCTRCiphertext_192,
+        sizeof(kAESCTRCiphertext_192),
+        true,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_256_ctr(),
+        kAESKey_256,
+        sizeof(kAESKey_256),
+        kAESCTRCiphertext_256,
+        sizeof(kAESCTRCiphertext_256),
+        true,
+        FIPSStatus::APPROVED,
+    },
+    {
+        EVP_aes_128_ofb(),
+        kAESKey,
+        sizeof(kAESKey),
+        kAESOFBCiphertext,
+        sizeof(kAESOFBCiphertext),
+        true,
+        FIPSStatus::NOT_APPROVED,
+    },
+    {
+        EVP_des_ede3(),
+        kAESKey_192,
+        sizeof(kAESKey_192),
+        kTDES_EDE3_CipherText,
+        sizeof(kTDES_EDE3_CipherText),
+        false,
+        FIPSStatus::NOT_APPROVED,
+    },
+    {
+        EVP_des_ede3_cbc(),
+        kAESKey_192,
+        sizeof(kAESKey_192),
+        kTDES_EDE3_CBCCipherText,
+        sizeof(kTDES_EDE3_CBCCipherText),
+        false,
+        FIPSStatus::NOT_APPROVED,
+    },
+};
+
+class EVPServiceIndicatorTest : public TestWithNoErrors<CipherTestVector> {};
+
+static void TestOperation(const EVP_CIPHER *cipher, bool encrypt,
+                          const bssl::Span<const uint8_t> key,
+                          const bssl::Span<const uint8_t> plaintext,
+                          const bssl::Span<const uint8_t> ciphertext,
+                          FIPSStatus expect_approved) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  bssl::Span<const uint8_t> in, out;
+  if (encrypt) {
+    in = plaintext;
+    out = ciphertext;
+  } else {
+    in = ciphertext;
+    out = plaintext;
+  }
+
+  bssl::ScopedEVP_CIPHER_CTX ctx;
+  // Test running the EVP_Cipher interfaces one by one directly, and check
+  // |EVP_EncryptFinal_ex| and |EVP_DecryptFinal_ex| for approval at the end.
+  ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr,
+                                encrypt ? 1 : 0));
+  ASSERT_LE(EVP_CIPHER_CTX_iv_length(ctx.get()), sizeof(kAESIV));
+
+  ASSERT_TRUE(EVP_CIPHER_CTX_set_key_length(ctx.get(), key.size()));
+  ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), cipher, nullptr, key.data(), kAESIV,
+                                encrypt ? 1 : 0));
+  ASSERT_TRUE(EVP_CIPHER_CTX_set_padding(ctx.get(), 0));
+  std::vector<uint8_t> encrypt_result;
+  DoCipherFinal(ctx.get(), &encrypt_result, in, expect_approved);
+  EXPECT_EQ(Bytes(out), Bytes(encrypt_result));
+
+  // Test using the one-shot |EVP_Cipher| function for approval.
+  bssl::ScopedEVP_CIPHER_CTX ctx2;
+  uint8_t output[256];
+  ASSERT_TRUE(EVP_CipherInit_ex(ctx2.get(), cipher, nullptr, key.data(), kAESIV,
+                                encrypt ? 1 : 0));
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_Cipher(ctx2.get(), output, in.data(), in.size()));
+  EXPECT_EQ(approved, expect_approved);
+  EXPECT_EQ(Bytes(out), Bytes(output, in.size()));
+}
+
+INSTANTIATE_TEST_SUITE_P(All, EVPServiceIndicatorTest,
+                         testing::ValuesIn(kTestVectors));
+
+TEST_P(EVPServiceIndicatorTest, EVP_Ciphers) {
+  const CipherTestVector &test = GetParam();
+
+  const EVP_CIPHER *cipher = test.cipher;
+  std::vector<uint8_t> key(test.key, test.key + test.key_length);
+  std::vector<uint8_t> plaintext(kPlaintext, kPlaintext + sizeof(kPlaintext));
+  std::vector<uint8_t> ciphertext(
+      test.expected_ciphertext,
+      test.expected_ciphertext + test.cipher_text_length);
+
+  TestOperation(cipher, true /* encrypt */, key, plaintext, ciphertext,
+                test.expect_approved);
+  TestOperation(cipher, false /* decrypt */, key, plaintext, ciphertext,
+                test.expect_approved);
+}
+
+static const struct DigestTestVector {
+  // name is the name of the digest test.
+  const char *name;
+  // length of digest.
+  const int length;
+  // func is the digest to test.
+  const EVP_MD *(*func)();
+  // one_shot_func is the convenience one-shot version of the digest.
+  uint8_t *(*one_shot_func)(const uint8_t *, size_t, uint8_t *);
+  // expected_digest is the expected digest.
+  const uint8_t *expected_digest;
+  // expected to be approved or not.
+  const FIPSStatus expect_approved;
+} kDigestTestVectors[] = {
+    {
+        "MD4",
+        MD4_DIGEST_LENGTH,
+        &EVP_md4,
+        &MD4,
+        kOutput_md4,
+        FIPSStatus::NOT_APPROVED,
+    },
+    {
+        "MD5",
+        MD5_DIGEST_LENGTH,
+        &EVP_md5,
+        &MD5,
+        kOutput_md5,
+        FIPSStatus::NOT_APPROVED,
+    },
+    {
+        "SHA-1",
+        SHA_DIGEST_LENGTH,
+        &EVP_sha1,
+        &SHA1,
+        kOutput_sha1,
+        FIPSStatus::APPROVED,
+    },
+    {
+        "SHA-224",
+        SHA224_DIGEST_LENGTH,
+        &EVP_sha224,
+        &SHA224,
+        kOutput_sha224,
+        FIPSStatus::APPROVED,
+    },
+    {
+        "SHA-256",
+        SHA256_DIGEST_LENGTH,
+        &EVP_sha256,
+        &SHA256,
+        kOutput_sha256,
+        FIPSStatus::APPROVED,
+    },
+    {
+        "SHA-384",
+        SHA384_DIGEST_LENGTH,
+        &EVP_sha384,
+        &SHA384,
+        kOutput_sha384,
+        FIPSStatus::APPROVED,
+    },
+    {
+        "SHA-512",
+        SHA512_DIGEST_LENGTH,
+        &EVP_sha512,
+        &SHA512,
+        kOutput_sha512,
+        FIPSStatus::APPROVED,
+    },
+    {
+        "SHA-512/256",
+        SHA512_256_DIGEST_LENGTH,
+        &EVP_sha512_256,
+        &SHA512_256,
+        kOutput_sha512_256,
+        FIPSStatus::APPROVED,
+    },
+};
+
+class EVPMDServiceIndicatorTest : public TestWithNoErrors<DigestTestVector> {};
+
+INSTANTIATE_TEST_SUITE_P(All, EVPMDServiceIndicatorTest,
+                         testing::ValuesIn(kDigestTestVectors));
+
+TEST_P(EVPMDServiceIndicatorTest, EVP_Digests) {
+  const DigestTestVector &test = GetParam();
+  SCOPED_TRACE(test.name);
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  bssl::ScopedEVP_MD_CTX ctx;
+  std::vector<uint8_t> digest(test.length);
+  unsigned digest_len;
+
+  // Test running the EVP_Digest interfaces one by one directly, and check
+  // |EVP_DigestFinal_ex| for approval at the end. |EVP_DigestInit_ex| and
+  // |EVP_DigestUpdate| should not be approved, because the functions do not
+  // indicate that a service has been fully completed yet.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestInit_ex(ctx.get(), test.func(), nullptr)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestUpdate(ctx.get(), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestFinal_ex(ctx.get(), digest.data(), &digest_len)));
+  EXPECT_EQ(approved, test.expect_approved);
+  EXPECT_EQ(Bytes(test.expected_digest, digest_len), Bytes(digest));
+
+  // Test using the one-shot |EVP_Digest| function for approval.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_Digest(kPlaintext, sizeof(kPlaintext), digest.data(),
+                           &digest_len, test.func(), nullptr)));
+  EXPECT_EQ(approved, test.expect_approved);
+  EXPECT_EQ(Bytes(test.expected_digest, test.length), Bytes(digest));
+
+  // Test using the one-shot API for approval.
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      test.one_shot_func(kPlaintext, sizeof(kPlaintext), digest.data()));
+  EXPECT_EQ(approved, test.expect_approved);
+  EXPECT_EQ(Bytes(test.expected_digest, test.length), Bytes(digest));
+}
+
+static const struct HMACTestVector {
+  // func is the hash function for HMAC to test.
+  const EVP_MD *(*func)(void);
+  // expected_digest is the expected digest.
+  const uint8_t *expected_digest;
+  // expected to be approved or not.
+  const FIPSStatus expect_approved;
+} kHMACTestVectors[] = {
+    {EVP_sha1, kHMACOutput_sha1, FIPSStatus::APPROVED},
+    {EVP_sha224, kHMACOutput_sha224, FIPSStatus::APPROVED},
+    {EVP_sha256, kHMACOutput_sha256, FIPSStatus::APPROVED},
+    {EVP_sha384, kHMACOutput_sha384, FIPSStatus::APPROVED},
+    {EVP_sha512, kHMACOutput_sha512, FIPSStatus::APPROVED},
+    {EVP_sha512_256, kHMACOutput_sha512_256, FIPSStatus::APPROVED},
+};
+
+class HMACServiceIndicatorTest : public TestWithNoErrors<HMACTestVector> {};
+
+INSTANTIATE_TEST_SUITE_P(All, HMACServiceIndicatorTest,
+                         testing::ValuesIn(kHMACTestVectors));
+
+TEST_P(HMACServiceIndicatorTest, HMACTest) {
+  const HMACTestVector &test = GetParam();
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  // The key is deliberately long in order to trigger digesting it down to a
+  // block size. This tests that doing so does not cause the indicator to be
+  // mistakenly set in |HMAC_Init_ex|.
+  const uint8_t kHMACKey[512] = {0};
+  const EVP_MD *const digest = test.func();
+  const unsigned expected_mac_len = EVP_MD_size(digest);
+  std::vector<uint8_t> mac(expected_mac_len);
+
+  // Test running the HMAC interfaces one by one directly, and check
+  // |HMAC_Final| for approval at the end. |HMAC_Init_ex| and |HMAC_Update|
+  // should not be approved, because the functions do not indicate that a
+  // service has been fully completed yet.
+  unsigned mac_len;
+  bssl::ScopedHMAC_CTX ctx;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      HMAC_Init_ex(ctx.get(), kHMACKey, sizeof(kHMACKey), digest, nullptr)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, HMAC_Update(ctx.get(), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, HMAC_Final(ctx.get(), mac.data(), &mac_len)));
+  EXPECT_EQ(approved, test.expect_approved);
+  EXPECT_EQ(Bytes(test.expected_digest, expected_mac_len),
+            Bytes(mac.data(), mac_len));
+
+  // Test using the one-shot API for approval.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, HMAC(digest, kHMACKey, sizeof(kHMACKey), kPlaintext,
+                     sizeof(kPlaintext), mac.data(), &mac_len)));
+  EXPECT_EQ(approved, test.expect_approved);
+  EXPECT_EQ(Bytes(test.expected_digest, expected_mac_len),
+            Bytes(mac.data(), mac_len));
+}
+
+// RSA tests are not parameterized with the |kRSATestVectors| as key
+// generation for RSA is time consuming.
+TEST(ServiceIndicatorTest, RSAKeyGen) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  bssl::UniquePtr<RSA> rsa(RSA_new());
+  ASSERT_TRUE(rsa);
+
+  // |RSA_generate_key_fips| may only be used for 2048-, 3072-, and 4096-bit
+  // keys.
+  for (const size_t bits : {512, 1024, 3071, 4095}) {
+    SCOPED_TRACE(bits);
+
+    rsa.reset(RSA_new());
+    EXPECT_FALSE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, RSA_generate_key_fips(rsa.get(), bits, nullptr)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  }
+
+  // Test that we can generate keys of the supported lengths:
+  for (const size_t bits : {2048, 3072, 4096}) {
+    SCOPED_TRACE(bits);
+
+    rsa.reset(RSA_new());
+    EXPECT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, RSA_generate_key_fips(rsa.get(), bits, nullptr)));
+    EXPECT_EQ(approved, FIPSStatus::APPROVED);
+    EXPECT_EQ(bits, BN_num_bits(rsa->n));
+  }
+
+  // Test running the EVP_PKEY_keygen interfaces one by one directly, and check
+  // |EVP_PKEY_keygen| for approval at the end. |EVP_PKEY_keygen_init| should
+  // not be approved because it does not indicate an entire service has been
+  // completed.
+  bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr));
+  EVP_PKEY *raw = nullptr;
+  bssl::UniquePtr<EVP_PKEY> pkey(raw);
+  ASSERT_TRUE(ctx);
+
+  if (kEVPKeyGenShouldCallFIPSFunctions) {
+    // Test unapproved key sizes of RSA.
+    for (const size_t bits : {512, 1024, 3071, 4095}) {
+      SCOPED_TRACE(bits);
+      ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+          approved, EVP_PKEY_keygen_init(ctx.get())));
+      EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+      ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), bits));
+      ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+          approved, EVP_PKEY_keygen(ctx.get(), &raw)));
+      EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+      pkey.reset(raw);
+      raw = nullptr;
+    }
+
+    // Test approved key sizes of RSA.
+    for (const size_t bits : {2048, 3072, 4096}) {
+      SCOPED_TRACE(bits);
+      ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+          approved, EVP_PKEY_keygen_init(ctx.get())));
+      EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+      ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), bits));
+      ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+          approved, EVP_PKEY_keygen(ctx.get(), &raw)));
+      EXPECT_EQ(approved, FIPSStatus::APPROVED);
+      pkey.reset(raw);
+      raw = nullptr;
+    }
+  }
+}
+
+struct RSATestVector {
+  // key_size is the input rsa key size.
+  int key_size;
+  // md_func is the digest to test.
+  const EVP_MD *(*func)();
+  // whether to use pss testing or not.
+  bool use_pss;
+  // expected to be approved or not for signature generation.
+  FIPSStatus sig_gen_expect_approved;
+  // expected to be approved or not for signature verification.
+  FIPSStatus sig_ver_expect_approved;
+};
+
+static const struct RSATestVector kRSATestVectors[] = {
+    // RSA test cases that are not approved in any case.
+    {512, &EVP_sha1, false, FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    // PSS with hashLen == saltLen is not possible for 512-bit modulus.
+    {1024, &EVP_md5, false, FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    {1536, &EVP_sha256, false, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED},
+    {1536, &EVP_sha512, true, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED},
+    {2048, &EVP_md5, false, FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    {3071, &EVP_md5, true, FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    {3071, &EVP_sha256, false, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED},
+    {3071, &EVP_sha512, true, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED},
+    {4096, &EVP_md5, false, FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+
+    // RSA test cases that are approved.
+    {1024, &EVP_sha1, false, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {1024, &EVP_sha256, false, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {1024, &EVP_sha512, false, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {1024, &EVP_sha1, true, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {1024, &EVP_sha256, true, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    // PSS with hashLen == saltLen is not possible for 1024-bit modulus and
+    // SHA-512.
+
+    {2048, &EVP_sha1, false, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha224, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha256, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha384, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha512, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha1, true, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha224, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha256, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha384, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {2048, &EVP_sha512, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+
+    {3072, &EVP_sha1, false, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha224, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha256, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha384, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha512, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha1, true, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha224, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha256, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha384, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {3072, &EVP_sha512, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+
+    {4096, &EVP_sha1, false, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha224, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha256, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha384, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha512, false, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha1, true, FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha224, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha256, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha384, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {4096, &EVP_sha512, true, FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+};
+
+class RSAServiceIndicatorTest : public TestWithNoErrors<RSATestVector> {};
+
+INSTANTIATE_TEST_SUITE_P(All, RSAServiceIndicatorTest,
+                         testing::ValuesIn(kRSATestVectors));
+
+static std::map<unsigned, bssl::UniquePtr<RSA>> &CachedRSAKeys() {
+  static std::map<unsigned, bssl::UniquePtr<RSA>> keys;
+  return keys;
+}
+
+static RSA *GetRSAKey(unsigned bits) {
+  auto it = CachedRSAKeys().find(bits);
+  if (it != CachedRSAKeys().end()) {
+    return it->second.get();
+  }
+
+  bssl::UniquePtr<BIGNUM> e(BN_new());
+  if (!e || !BN_set_word(e.get(), RSA_F4)) {
+    abort();
+  }
+
+  bssl::UniquePtr<RSA> key(RSA_new());
+  if (!key || !RSA_generate_key_ex(key.get(), bits, e.get(), nullptr)) {
+    abort();
+  }
+
+  RSA *const ret = key.get();
+  CachedRSAKeys().emplace(static_cast<unsigned>(bits), std::move(key));
+
+  return ret;
+}
+
+TEST_P(RSAServiceIndicatorTest, RSASigGen) {
+  const RSATestVector &test = GetParam();
+  SCOPED_TRACE(test.key_size);
+
+  bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+  ASSERT_TRUE(pkey);
+
+  RSA *const rsa = GetRSAKey(test.key_size);
+  ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa));
+
+  // Test running the EVP_DigestSign interfaces one by one directly, and check
+  // |EVP_DigestSignFinal| for approval at the end. |EVP_DigestSignInit|, and
+  // |EVP_DigestSignUpdate| should not be approved because they do not indicate
+  // an entire service has been completed.
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  bssl::ScopedEVP_MD_CTX md_ctx;
+  EVP_PKEY_CTX *pctx = nullptr;
+  size_t sig_len;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignInit(md_ctx.get(), &pctx, test.func(), nullptr,
+                                   pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  if (test.use_pss) {
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  }
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestSignUpdate(md_ctx.get(), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  // Determine the size of the signature. The first call of
+  // |EVP_DigestSignFinal| should not return an approval check because no crypto
+  // is being done when |nullptr| is inputted in the |*out_sig| field.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignFinal(md_ctx.get(), nullptr, &sig_len)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  std::vector<uint8_t> signature(sig_len);
+  // The second call performs the actual operation.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignFinal(md_ctx.get(), signature.data(), &sig_len)));
+  EXPECT_EQ(approved, test.sig_gen_expect_approved);
+
+  // Test using the one-shot |EVP_DigestSign| function for approval.
+  md_ctx.Reset();
+  std::vector<uint8_t> oneshot_output(sig_len);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignInit(md_ctx.get(), &pctx, test.func(), nullptr,
+                                   pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  if (test.use_pss) {
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  }
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSign(md_ctx.get(), oneshot_output.data(), &sig_len,
+                               kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, test.sig_gen_expect_approved);
+
+  if (test.use_pss) {
+    // Odd configurations of PSS, for example where the salt length is not equal
+    // to the hash length, are not approved.
+    md_ctx.Reset();
+    ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), &pctx, test.func(), nullptr,
+                                   pkey.get()));
+    ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING));
+    ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, 10));
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_DigestSign(md_ctx.get(), oneshot_output.data(), &sig_len,
+                                 kPlaintext, sizeof(kPlaintext))));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  }
+}
+
+TEST_P(RSAServiceIndicatorTest, RSASigVer) {
+  const RSATestVector &test = GetParam();
+
+  bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+  RSA *const rsa = GetRSAKey(test.key_size);
+
+  ASSERT_TRUE(pkey);
+  ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa));
+
+  std::vector<uint8_t> signature;
+  size_t sig_len;
+  bssl::ScopedEVP_MD_CTX md_ctx;
+  EVP_PKEY_CTX *pctx = nullptr;
+  ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), &pctx, test.func(), nullptr,
+                                 pkey.get()));
+  if (test.use_pss) {
+    ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING));
+    ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1));
+  }
+  ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, nullptr, 0));
+  signature.resize(sig_len);
+  ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), signature.data(), &sig_len,
+                             kPlaintext, sizeof(kPlaintext)));
+  signature.resize(sig_len);
+
+  // Service Indicator approval checks for RSA signature verification.
+
+  // Test running the EVP_DigestVerify interfaces one by one directly, and check
+  // |EVP_DigestVerifyFinal| for approval at the end. |EVP_DigestVerifyInit|,
+  // |EVP_DigestVerifyUpdate| should not be approved because they do not
+  // indicate an entire service has been done.
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  md_ctx.Reset();
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestVerifyInit(md_ctx.get(), &pctx, test.func(), nullptr,
+                                     pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  if (test.use_pss) {
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+    ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1));
+  }
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestVerifyUpdate(md_ctx.get(), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestVerifyFinal(md_ctx.get(), signature.data(), signature.size())));
+  EXPECT_EQ(approved, test.sig_ver_expect_approved);
+
+  // Test using the one-shot |EVP_DigestVerify| function for approval.
+  md_ctx.Reset();
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestVerifyInit(md_ctx.get(), &pctx, test.func(), nullptr,
+                                     pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  if (test.use_pss) {
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING)));
+    EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+    ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1));
+  }
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestVerify(md_ctx.get(), signature.data(), signature.size(),
+                       kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, test.sig_ver_expect_approved);
+}
+
+struct ECDSATestVector {
+  // nid is the input curve nid.
+  int nid;
+  // md_func is the digest to test.
+  const EVP_MD *(*func)();
+  // expected to be approved or not for signature generation.
+  FIPSStatus key_check_expect_approved;
+  // expected to be approved or not for signature generation.
+  FIPSStatus sig_gen_expect_approved;
+  // expected to be approved or not for signature verification.
+  FIPSStatus sig_ver_expect_approved;
+};
+
+static const struct ECDSATestVector kECDSATestVectors[] = {
+    // Only the following NIDs for |EC_GROUP| are creatable with
+    // |EC_GROUP_new_by_curve_name|, and |NID_secp256k1| will only work if
+    // |kCurveSecp256k1Supported| is true.
+    {NID_secp224r1, &EVP_sha1, FIPSStatus::APPROVED, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp224r1, &EVP_sha224, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp224r1, &EVP_sha256, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp224r1, &EVP_sha384, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp224r1, &EVP_sha512, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+
+    {NID_X9_62_prime256v1, &EVP_sha1, FIPSStatus::APPROVED,
+     FIPSStatus::NOT_APPROVED, FIPSStatus::APPROVED},
+    {NID_X9_62_prime256v1, &EVP_sha224, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {NID_X9_62_prime256v1, &EVP_sha256, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {NID_X9_62_prime256v1, &EVP_sha384, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+    {NID_X9_62_prime256v1, &EVP_sha512, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED, FIPSStatus::APPROVED},
+
+    {NID_secp384r1, &EVP_sha1, FIPSStatus::APPROVED, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp384r1, &EVP_sha224, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp384r1, &EVP_sha256, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp384r1, &EVP_sha384, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp384r1, &EVP_sha512, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+
+    {NID_secp521r1, &EVP_sha1, FIPSStatus::APPROVED, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp521r1, &EVP_sha224, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp521r1, &EVP_sha256, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp521r1, &EVP_sha384, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+    {NID_secp521r1, &EVP_sha512, FIPSStatus::APPROVED, FIPSStatus::APPROVED,
+     FIPSStatus::APPROVED},
+
+    {NID_secp256k1, &EVP_sha1, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    {NID_secp256k1, &EVP_sha224, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    {NID_secp256k1, &EVP_sha256, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    {NID_secp256k1, &EVP_sha384, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+    {NID_secp256k1, &EVP_sha512, FIPSStatus::NOT_APPROVED,
+     FIPSStatus::NOT_APPROVED, FIPSStatus::NOT_APPROVED},
+};
+
+class ECDSAServiceIndicatorTest : public TestWithNoErrors<ECDSATestVector> {};
+
+INSTANTIATE_TEST_SUITE_P(All, ECDSAServiceIndicatorTest,
+                         testing::ValuesIn(kECDSATestVectors));
+
+TEST_P(ECDSAServiceIndicatorTest, ECDSAKeyCheck) {
+  const ECDSATestVector &test = GetParam();
+  if (test.nid == NID_secp256k1 && !kCurveSecp256k1Supported) {
+    GTEST_SKIP();
+  }
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  // Test service indicator approval for |EC_KEY_generate_key_fips| and
+  // |EC_KEY_check_fips|.
+  bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(test.nid));
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EC_KEY_generate_key_fips(key.get())));
+  EXPECT_EQ(approved, test.key_check_expect_approved);
+  ASSERT_TRUE(
+      CALL_SERVICE_AND_CHECK_APPROVED(approved, EC_KEY_check_fips(key.get())));
+  EXPECT_EQ(approved, test.key_check_expect_approved);
+
+  // See if |EC_KEY_check_fips| still returns approval with only the public
+  // component.
+  bssl::UniquePtr<EC_KEY> key_only_public(EC_KEY_new_by_curve_name(test.nid));
+  ASSERT_TRUE(EC_KEY_set_public_key(key_only_public.get(),
+                                    EC_KEY_get0_public_key(key.get())));
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EC_KEY_check_fips(key_only_public.get())));
+  EXPECT_EQ(approved, test.key_check_expect_approved);
+
+  if (kEVPKeyGenShouldCallFIPSFunctions) {
+    // Test running the EVP_PKEY_keygen interfaces one by one directly, and
+    // check |EVP_PKEY_keygen| for approval at the end. |EVP_PKEY_keygen_init|
+    // should not be approved because it does not indicate that an entire
+    // service has been completed.
+    bssl::UniquePtr<EVP_PKEY_CTX> ctx(
+        EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
+    EVP_PKEY *raw = nullptr;
+    ASSERT_TRUE(ctx);
+    ASSERT_TRUE(EVP_PKEY_keygen_init(ctx.get()));
+    ASSERT_TRUE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), test.nid));
+    ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+        approved, EVP_PKEY_keygen(ctx.get(), &raw)));
+    EXPECT_EQ(approved, test.key_check_expect_approved);
+
+    EVP_PKEY_free(raw);
+  }
+}
+
+TEST_P(ECDSAServiceIndicatorTest, ECDSASigGen) {
+  const ECDSATestVector &test = GetParam();
+  if (test.nid == NID_secp256k1 && !kCurveSecp256k1Supported) {
+    GTEST_SKIP();
+  }
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
+  bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+  bssl::ScopedEVP_MD_CTX md_ctx;
+  ASSERT_TRUE(eckey);
+  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get()));
+
+  // Generate a generic EC key.
+  ASSERT_TRUE(EC_KEY_generate_key(eckey.get()));
+  ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), eckey.get()));
+
+  // Test running the EVP_DigestSign interfaces one by one directly, and check
+  // |EVP_DigestSignFinal| for approval at the end. |EVP_DigestSignInit|,
+  // |EVP_DigestSignUpdate| should not be approved because they do not indicate
+  // an entire service has been done.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignInit(md_ctx.get(), nullptr, test.func(), nullptr,
+                                   pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestSignUpdate(md_ctx.get(), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  // Determine the size of the signature. The first call of
+  // |EVP_DigestSignFinal| should not return an approval check because no crypto
+  // is being done when |nullptr| is given as the |out_sig| field.
+  size_t max_sig_len;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignFinal(md_ctx.get(), nullptr, &max_sig_len)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  std::vector<uint8_t> signature(max_sig_len);
+  // The second call performs the actual operation.
+  size_t sig_len = max_sig_len;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignFinal(md_ctx.get(), signature.data(), &sig_len)));
+  ASSERT_LE(sig_len, signature.size());
+  EXPECT_EQ(approved, test.sig_gen_expect_approved);
+
+  // Test using the one-shot |EVP_DigestSign| function for approval.
+  md_ctx.Reset();
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSignInit(md_ctx.get(), nullptr, test.func(), nullptr,
+                                   pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  sig_len = max_sig_len;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestSign(md_ctx.get(), signature.data(), &sig_len,
+                               kPlaintext, sizeof(kPlaintext))));
+  ASSERT_LE(sig_len, signature.size());
+  EXPECT_EQ(approved, test.sig_gen_expect_approved);
+}
+
+TEST_P(ECDSAServiceIndicatorTest, ECDSASigVer) {
+  const ECDSATestVector &test = GetParam();
+  if (test.nid == NID_secp256k1 && !kCurveSecp256k1Supported) {
+    GTEST_SKIP();
+  }
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
+  bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+  bssl::ScopedEVP_MD_CTX md_ctx;
+  ASSERT_TRUE(eckey);
+  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get()));
+
+  // Generate ECDSA signatures for ECDSA verification.
+  ASSERT_TRUE(EC_KEY_generate_key(eckey.get()));
+  ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), eckey.get()));
+  std::vector<uint8_t> signature;
+  size_t sig_len = 0;
+  ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), nullptr, test.func(), nullptr,
+                                 pkey.get()));
+  ASSERT_TRUE(EVP_DigestSignFinal(md_ctx.get(), nullptr, &sig_len));
+  signature.resize(sig_len);
+  ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), signature.data(), &sig_len,
+                             kPlaintext, sizeof(kPlaintext)));
+  signature.resize(sig_len);
+
+  // Service Indicator approval checks for ECDSA signature verification.
+
+  // Test running the EVP_DigestVerify interfaces one by one directly, and check
+  // |EVP_DigestVerifyFinal| for approval at the end. |EVP_DigestVerifyInit|,
+  // |EVP_DigestVerifyUpdate| should not be approved because they do not
+  // indicate an entire service has been done.
+  md_ctx.Reset();
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestVerifyInit(md_ctx.get(), nullptr, test.func(),
+                                     nullptr, pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestVerifyUpdate(md_ctx.get(), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestVerifyFinal(md_ctx.get(), signature.data(), signature.size())));
+  EXPECT_EQ(approved, test.sig_ver_expect_approved);
+
+  // Test using the one-shot |EVP_DigestVerify| function for approval.
+  md_ctx.Reset();
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_DigestVerifyInit(md_ctx.get(), nullptr, test.func(),
+                                     nullptr, pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_DigestVerify(md_ctx.get(), signature.data(), signature.size(),
+                       kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, test.sig_ver_expect_approved);
+}
+
+#if defined(AWSLC_FIPS)
+
+// Test that |EVP_DigestSignFinal| and |EVP_DigestSignVerify| are approved with
+// manually constructing using the context setting functions.
+TEST_P(ECDSAServiceIndicatorTest, ManualECDSASignVerify) {
+  const ECDSATestVector &test = GetParam();
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  bssl::ScopedEVP_MD_CTX ctx;
+  ASSERT_TRUE(EVP_DigestInit(ctx.get(), test.func()));
+  ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), kPlaintext, sizeof(kPlaintext)));
+
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
+  bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+  bssl::ScopedEVP_MD_CTX md_ctx;
+  ASSERT_TRUE(eckey);
+  ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get()));
+
+  // Generate a generic ec key.
+  EC_KEY_generate_key(eckey.get());
+  ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), eckey.get()));
+
+  // Manual construction for signing.
+  bssl::UniquePtr<EVP_PKEY_CTX> pctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
+  ASSERT_TRUE(EVP_PKEY_sign_init(pctx.get()));
+  ASSERT_TRUE(EVP_PKEY_CTX_set_signature_md(pctx.get(), test.func()));
+  EVP_MD_CTX_set_pkey_ctx(ctx.get(), pctx.get());
+  // Determine the size of the signature.
+  size_t sig_len = 0;
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, ASSERT_TRUE(EVP_DigestSignFinal(ctx.get(), nullptr, &sig_len)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+
+  std::vector<uint8_t> sig;
+  sig.resize(sig_len);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      ASSERT_TRUE(EVP_DigestSignFinal(ctx.get(), sig.data(), &sig_len)));
+  EXPECT_EQ(approved, test.sig_gen_expect_approved);
+  sig.resize(sig_len);
+
+  // Manual construction for verification.
+  ASSERT_TRUE(EVP_PKEY_verify_init(pctx.get()));
+  ASSERT_TRUE(EVP_PKEY_CTX_set_signature_md(pctx.get(), test.func()));
+  EVP_MD_CTX_set_pkey_ctx(ctx.get(), pctx.get());
+
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      ASSERT_TRUE(EVP_DigestVerifyFinal(ctx.get(), sig.data(), sig_len)));
+  EXPECT_EQ(approved, test.sig_ver_expect_approved);
+}
+
+#endif  // AWSLC_FIPS
+
+struct ECDHTestVector {
+  // nid is the input curve nid.
+  const int nid;
+  // digest_length is the length of the hash output to test with.
+  const int digest_length;
+  // expect_approved to be approved or not.
+  const FIPSStatus expect_approved;
+};
+
+static const struct ECDHTestVector kECDHTestVectors[] = {
+    // Only the following NIDs for |EC_GROUP| are creatable with
+    // |EC_GROUP_new_by_curve_name|.
+    // |ECDH_compute_key_fips| fails directly when an invalid hash length is
+    // inputted.
+    {NID_secp224r1, SHA224_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp224r1, SHA256_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp224r1, SHA384_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp224r1, SHA512_DIGEST_LENGTH, FIPSStatus::APPROVED},
+
+    {NID_X9_62_prime256v1, SHA224_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_X9_62_prime256v1, SHA256_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_X9_62_prime256v1, SHA384_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_X9_62_prime256v1, SHA512_DIGEST_LENGTH, FIPSStatus::APPROVED},
+
+    {NID_secp384r1, SHA224_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp384r1, SHA256_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp384r1, SHA384_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp384r1, SHA512_DIGEST_LENGTH, FIPSStatus::APPROVED},
+
+    {NID_secp521r1, SHA224_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp521r1, SHA256_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp521r1, SHA384_DIGEST_LENGTH, FIPSStatus::APPROVED},
+    {NID_secp521r1, SHA512_DIGEST_LENGTH, FIPSStatus::APPROVED},
+
+    {NID_secp256k1, SHA224_DIGEST_LENGTH, FIPSStatus::NOT_APPROVED},
+    {NID_secp256k1, SHA256_DIGEST_LENGTH, FIPSStatus::NOT_APPROVED},
+    {NID_secp256k1, SHA384_DIGEST_LENGTH, FIPSStatus::NOT_APPROVED},
+    {NID_secp256k1, SHA512_DIGEST_LENGTH, FIPSStatus::NOT_APPROVED},
+};
+
+class ECDH_ServiceIndicatorTest : public TestWithNoErrors<ECDHTestVector> {};
+
+INSTANTIATE_TEST_SUITE_P(All, ECDH_ServiceIndicatorTest,
+                         testing::ValuesIn(kECDHTestVectors));
+
+TEST_P(ECDH_ServiceIndicatorTest, ECDH) {
+  const ECDHTestVector &test = GetParam();
+  if (test.nid == NID_secp256k1 && !kCurveSecp256k1Supported) {
+    GTEST_SKIP();
+  }
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(test.nid));
+  bssl::UniquePtr<EC_KEY> our_key(EC_KEY_new());
+  bssl::UniquePtr<EC_KEY> peer_key(EC_KEY_new());
+  bssl::ScopedEVP_MD_CTX md_ctx;
+  ASSERT_TRUE(our_key);
+  ASSERT_TRUE(peer_key);
+
+  // Generate two generic ec key pairs.
+  ASSERT_TRUE(EC_KEY_set_group(our_key.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_generate_key(our_key.get()));
+  ASSERT_TRUE(EC_KEY_check_key(our_key.get()));
+
+  ASSERT_TRUE(EC_KEY_set_group(peer_key.get(), group.get()));
+  ASSERT_TRUE(EC_KEY_generate_key(peer_key.get()));
+  ASSERT_TRUE(EC_KEY_check_key(peer_key.get()));
+
+  // Test that |ECDH_compute_key_fips| has service indicator approval as
+  // expected.
+  std::vector<uint8_t> digest(test.digest_length);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, ECDH_compute_key_fips(digest.data(), digest.size(),
+                                      EC_KEY_get0_public_key(peer_key.get()),
+                                      our_key.get())));
+  EXPECT_EQ(approved, test.expect_approved);
+
+  // Test running the EVP_PKEY_derive interfaces one by one directly, and check
+  // |EVP_PKEY_derive| for approval at the end. |EVP_PKEY_derive_init| and
+  // |EVP_PKEY_derive_set_peer| should not be approved because they do not
+  // indicate an entire service has been done.
+  bssl::UniquePtr<EVP_PKEY> our_pkey(EVP_PKEY_new());
+  ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(our_pkey.get(), our_key.get()));
+  bssl::UniquePtr<EVP_PKEY_CTX> our_ctx(
+      EVP_PKEY_CTX_new(our_pkey.get(), nullptr));
+  bssl::UniquePtr<EVP_PKEY> peer_pkey(EVP_PKEY_new());
+  ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(peer_pkey.get(), peer_key.get()));
+
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_PKEY_derive_init(our_ctx.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_PKEY_derive_set_peer(our_ctx.get(), peer_pkey.get())));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  // Determine the size of the output key. The first call of |EVP_PKEY_derive|
+  // should not return an approval check because no crypto is being done when
+  // |nullptr| is inputted in the |*key| field
+  size_t out_len = 0;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_PKEY_derive(our_ctx.get(), nullptr, &out_len)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  std::vector<uint8_t> derive_output(out_len);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_PKEY_derive(our_ctx.get(), derive_output.data(), &out_len)));
+  EXPECT_EQ(approved, kEVPDeriveSetsServiceIndicator
+                          ? test.expect_approved
+                          : FIPSStatus::NOT_APPROVED);
+}
+
+static const struct KDFTestVector {
+  // func is the hash function for KDF to test.
+  const EVP_MD *(*func)();
+  const uint8_t *expected_output;
+  const FIPSStatus expect_approved;
+} kKDFTestVectors[] = {
+    {EVP_md5, kTLSOutput_md, FIPSStatus::APPROVED},
+    {EVP_sha1, kTLSOutput_sha1, FIPSStatus::APPROVED},
+    {EVP_md5_sha1, kTLSOutput_mdsha1, FIPSStatus::APPROVED},
+    {EVP_sha224, kTLSOutput_sha224, FIPSStatus::NOT_APPROVED},
+    {EVP_sha256, kTLSOutput_sha256, FIPSStatus::APPROVED},
+    {EVP_sha384, kTLSOutput_sha384, FIPSStatus::APPROVED},
+    {EVP_sha512, kTLSOutput_sha512, FIPSStatus::APPROVED},
+};
+
+class KDF_ServiceIndicatorTest : public TestWithNoErrors<KDFTestVector> {};
+
+INSTANTIATE_TEST_SUITE_P(All, KDF_ServiceIndicatorTest,
+                         testing::ValuesIn(kKDFTestVectors));
+
+TEST_P(KDF_ServiceIndicatorTest, TLSKDF) {
+  const KDFTestVector &test = GetParam();
+
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  uint8_t output[32];
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, CRYPTO_tls1_prf(test.func(), output, sizeof(output), kTLSSecret,
+                                sizeof(kTLSSecret), kTLSLabel,
+                                sizeof(kTLSLabel), kTLSSeed1, sizeof(kTLSSeed1),
+                                kTLSSeed2, sizeof(kTLSSeed2))));
+  EXPECT_EQ(Bytes(test.expected_output, sizeof(output)),
+            Bytes(output, sizeof(output)));
+  EXPECT_EQ(approved, test.expect_approved);
+}
+
+TEST(ServiceIndicatorTest, CMAC) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  bssl::UniquePtr<CMAC_CTX> ctx(CMAC_CTX_new());
+  ASSERT_TRUE(ctx);
+
+  // Test running the CMAC interfaces one by one directly, and check
+  // |CMAC_Final| for approval at the end. |CMAC_Init| and |CMAC_Update|
+  // should not be approved, because the functions do not indicate that a
+  // service has been fully completed yet.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, CMAC_Init(ctx.get(), kAESKey, sizeof(kAESKey),
+                          EVP_aes_128_cbc(), nullptr)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, CMAC_Update(ctx.get(), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+
+  uint8_t mac[16];
+  size_t out_len;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, CMAC_Final(ctx.get(), mac, &out_len)));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  EXPECT_EQ(Bytes(kAESCMACOutput), Bytes(mac));
+
+  // Test using the one-shot API for approval.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      AES_CMAC(mac, kAESKey, sizeof(kAESKey), kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(Bytes(kAESCMACOutput), Bytes(mac));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+TEST(ServiceIndicatorTest, BasicTest) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  bssl::ScopedEVP_AEAD_CTX aead_ctx;
+  ASSERT_TRUE(EVP_AEAD_CTX_init(aead_ctx.get(),
+                                EVP_aead_aes_128_gcm_randnonce(), kAESKey,
+                                sizeof(kAESKey), 0, nullptr));
+  // This test ensures that the counter gets incremented once, i.e. it was
+  // locked through the internal calls.
+  const int counter_before = FIPS_service_indicator_after_call();
+  size_t out_len;
+  uint8_t output[256];
+  EVP_AEAD_CTX_seal(aead_ctx.get(), output, &out_len, sizeof(output), nullptr,
+                    0, kPlaintext, sizeof(kPlaintext), nullptr, 0);
+  const int counter_after = FIPS_service_indicator_after_call();
+  EXPECT_EQ(counter_after, counter_before + 1);
+
+  // Call an approved service.
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_AEAD_CTX_seal(aead_ctx.get(), output, &out_len,
+                                  sizeof(output), nullptr, 0, kPlaintext,
+                                  sizeof(kPlaintext), nullptr, 0));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // Fail an approved service on purpose.
+  ASSERT_FALSE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_AEAD_CTX_seal(aead_ctx.get(), output, &out_len, 0, nullptr, 0,
+                        kPlaintext, sizeof(kPlaintext), nullptr, 0)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+
+  // Call a non-approved service.
+  uint8_t aes_iv[sizeof(kAESIV)];
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  AES_KEY aes_key;
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  int num = 0;
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_ofb128_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                   &aes_key, aes_iv, &num));
+  EXPECT_EQ(Bytes(kAESOFBCiphertext), Bytes(output, sizeof(kAESOFBCiphertext)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+}
+
+// Test the SHA interfaces one by one and check that only |*_Final| does the
+// approval at the end.
+TEST(ServiceIndicatorTest, SHA) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  std::vector<uint8_t> digest;
+
+  digest.resize(MD4_DIGEST_LENGTH);
+  MD4_CTX md4_ctx;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(approved, MD4_Init(&md4_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, MD4_Update(&md4_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, MD4_Final(digest.data(), &md4_ctx)));
+  EXPECT_EQ(Bytes(kOutput_md4), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+
+  digest.resize(MD5_DIGEST_LENGTH);
+  MD5_CTX md5_ctx;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(approved, MD5_Init(&md5_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, MD5_Update(&md5_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, MD5_Final(digest.data(), &md5_ctx)));
+  EXPECT_EQ(Bytes(kOutput_md5), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+
+  digest.resize(SHA_DIGEST_LENGTH);
+  SHA_CTX sha_ctx;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(approved, SHA1_Init(&sha_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA1_Update(&sha_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA1_Final(digest.data(), &sha_ctx)));
+  EXPECT_EQ(Bytes(kOutput_sha1), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  digest.resize(SHA224_DIGEST_LENGTH);
+  SHA256_CTX sha224_ctx;
+  ASSERT_TRUE(
+      CALL_SERVICE_AND_CHECK_APPROVED(approved, SHA224_Init(&sha224_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA224_Update(&sha224_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA224_Final(digest.data(), &sha224_ctx)));
+  EXPECT_EQ(Bytes(kOutput_sha224), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  digest.resize(SHA256_DIGEST_LENGTH);
+  SHA256_CTX sha256_ctx;
+  ASSERT_TRUE(
+      CALL_SERVICE_AND_CHECK_APPROVED(approved, SHA256_Init(&sha256_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA256_Update(&sha256_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA256_Final(digest.data(), &sha256_ctx)));
+  EXPECT_EQ(Bytes(kOutput_sha256), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  digest.resize(SHA384_DIGEST_LENGTH);
+  SHA512_CTX sha384_ctx;
+  ASSERT_TRUE(
+      CALL_SERVICE_AND_CHECK_APPROVED(approved, SHA384_Init(&sha384_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA384_Update(&sha384_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA384_Final(digest.data(), &sha384_ctx)));
+  EXPECT_EQ(Bytes(kOutput_sha384), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  digest.resize(SHA512_DIGEST_LENGTH);
+  SHA512_CTX sha512_ctx;
+  ASSERT_TRUE(
+      CALL_SERVICE_AND_CHECK_APPROVED(approved, SHA512_Init(&sha512_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA512_Update(&sha512_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA512_Final(digest.data(), &sha512_ctx)));
+  EXPECT_EQ(Bytes(kOutput_sha512), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  digest.resize(SHA512_256_DIGEST_LENGTH);
+  SHA512_CTX sha512_256_ctx;
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA512_256_Init(&sha512_256_ctx)));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      SHA512_256_Update(&sha512_256_ctx, kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, SHA512_256_Final(digest.data(), &sha512_256_ctx)));
+  EXPECT_EQ(Bytes(kOutput_sha512_256), Bytes(digest));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+TEST(ServiceIndicatorTest, AESECB) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  AES_KEY aes_key;
+  uint8_t output[256];
+
+  // AES-ECB Encryption KAT for 128 bit key.
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  // AES_ecb_encrypt encrypts (or decrypts) a single, 16 byte block from in to
+  // out.
+  for (size_t i = 0; i < sizeof(kPlaintext) / AES_BLOCK_SIZE; i++) {
+    CALL_SERVICE_AND_CHECK_APPROVED(
+        approved,
+        AES_ecb_encrypt(&kPlaintext[i * AES_BLOCK_SIZE],
+                        &output[i * AES_BLOCK_SIZE], &aes_key, AES_ENCRYPT));
+    EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  }
+  EXPECT_EQ(Bytes(kAESECBCiphertext), Bytes(output, sizeof(kAESECBCiphertext)));
+
+  // AES-ECB Decryption KAT for 128 bit key.
+  ASSERT_TRUE(AES_set_decrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  for (size_t i = 0; i < sizeof(kPlaintext) / AES_BLOCK_SIZE; i++) {
+    CALL_SERVICE_AND_CHECK_APPROVED(
+        approved,
+        AES_ecb_encrypt(&kAESECBCiphertext[i * AES_BLOCK_SIZE],
+                        &output[i * AES_BLOCK_SIZE], &aes_key, AES_DECRYPT));
+    EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  }
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output, sizeof(kPlaintext)));
+
+  // AES-ECB Encryption KAT for 192 bit key.
+  ASSERT_TRUE(
+      AES_set_encrypt_key(kAESKey_192, 8 * sizeof(kAESKey_192), &aes_key) == 0);
+  for (size_t i = 0; i < sizeof(kPlaintext) / AES_BLOCK_SIZE; i++) {
+    CALL_SERVICE_AND_CHECK_APPROVED(
+        approved,
+        AES_ecb_encrypt(&kPlaintext[i * AES_BLOCK_SIZE],
+                        &output[i * AES_BLOCK_SIZE], &aes_key, AES_ENCRYPT));
+    EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  }
+  EXPECT_EQ(Bytes(kAESECBCiphertext_192),
+            Bytes(output, sizeof(kAESECBCiphertext_192)));
+
+  // AES-ECB Decryption KAT for 192 bit key.
+  ASSERT_TRUE(
+      AES_set_decrypt_key(kAESKey_192, 8 * sizeof(kAESKey_192), &aes_key) == 0);
+  for (size_t i = 0; i < sizeof(kPlaintext) / AES_BLOCK_SIZE; i++) {
+    CALL_SERVICE_AND_CHECK_APPROVED(
+        approved,
+        AES_ecb_encrypt(&kAESECBCiphertext_192[i * AES_BLOCK_SIZE],
+                        &output[i * AES_BLOCK_SIZE], &aes_key, AES_DECRYPT));
+    EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  }
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output, sizeof(kPlaintext)));
+
+  // AES-ECB Encryption KAT for 256 bit key.
+  ASSERT_TRUE(
+      AES_set_encrypt_key(kAESKey_256, 8 * sizeof(kAESKey_256), &aes_key) == 0);
+  for (size_t i = 0; i < sizeof(kPlaintext) / AES_BLOCK_SIZE; i++) {
+    CALL_SERVICE_AND_CHECK_APPROVED(
+        approved,
+        AES_ecb_encrypt(&kPlaintext[i * AES_BLOCK_SIZE],
+                        &output[i * AES_BLOCK_SIZE], &aes_key, AES_ENCRYPT));
+    EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  }
+  EXPECT_EQ(Bytes(kAESECBCiphertext_256),
+            Bytes(output, sizeof(kAESECBCiphertext_256)));
+
+  // AES-ECB Decryption KAT for 256 bit key.
+  ASSERT_TRUE(
+      AES_set_decrypt_key(kAESKey_256, 8 * sizeof(kAESKey_256), &aes_key) == 0);
+  for (size_t i = 0; i < sizeof(kPlaintext) / AES_BLOCK_SIZE; i++) {
+    CALL_SERVICE_AND_CHECK_APPROVED(
+        approved,
+        AES_ecb_encrypt(&kAESECBCiphertext_256[i * AES_BLOCK_SIZE],
+                        &output[i * AES_BLOCK_SIZE], &aes_key, AES_DECRYPT));
+    EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  }
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output, sizeof(kPlaintext)));
+}
+
+TEST(ServiceIndicatorTest, AESCBC) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  AES_KEY aes_key;
+  uint8_t aes_iv[sizeof(kAESIV)];
+  uint8_t output[sizeof(kPlaintext)];
+
+  // AES-CBC Encryption KAT for 128 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_cbc_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                &aes_key, aes_iv, AES_ENCRYPT));
+  EXPECT_EQ(Bytes(kAESCBCCiphertext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CBC Decryption KAT for 128 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(AES_set_decrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      AES_cbc_encrypt(kAESCBCCiphertext, output, sizeof(kAESCBCCiphertext),
+                      &aes_key, aes_iv, AES_DECRYPT));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CBC Encryption KAT for 192 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(
+      AES_set_encrypt_key(kAESKey_192, 8 * sizeof(kAESKey_192), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_cbc_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                &aes_key, aes_iv, AES_ENCRYPT));
+  EXPECT_EQ(Bytes(kAESCBCCiphertext_192), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CBC Decryption KAT for 192 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(
+      AES_set_decrypt_key(kAESKey_192, 8 * sizeof(kAESKey_192), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_cbc_encrypt(kAESCBCCiphertext_192, output,
+                                sizeof(kAESCBCCiphertext_192), &aes_key, aes_iv,
+                                AES_DECRYPT));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CBC Encryption KAT for 256 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(
+      AES_set_encrypt_key(kAESKey_256, 8 * sizeof(kAESKey_256), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_cbc_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                &aes_key, aes_iv, AES_ENCRYPT));
+  EXPECT_EQ(Bytes(kAESCBCCiphertext_256), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CBC Decryption KAT for 256 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(
+      AES_set_decrypt_key(kAESKey_256, 8 * sizeof(kAESKey_256), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_cbc_encrypt(kAESCBCCiphertext_256, output,
+                                sizeof(kAESCBCCiphertext_256), &aes_key, aes_iv,
+                                AES_DECRYPT));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+TEST(ServiceIndicatorTest, AESCTR) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  AES_KEY aes_key;
+  uint8_t aes_iv[sizeof(kAESIV)];
+  uint8_t output[sizeof(kPlaintext)];
+  unsigned num = 0;
+  uint8_t ecount_buf[AES_BLOCK_SIZE];
+
+  // AES-CTR Encryption KAT
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_ctr128_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                   &aes_key, aes_iv, ecount_buf, &num));
+  EXPECT_EQ(Bytes(kAESCTRCiphertext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CTR Decryption KAT
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      AES_ctr128_encrypt(kAESCTRCiphertext, output, sizeof(kAESCTRCiphertext),
+                         &aes_key, aes_iv, ecount_buf, &num));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CTR Encryption KAT for 192 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(
+      AES_set_encrypt_key(kAESKey_192, 8 * sizeof(kAESKey_192), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_ctr128_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                   &aes_key, aes_iv, ecount_buf, &num));
+  EXPECT_EQ(Bytes(kAESCTRCiphertext_192), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CTR Decryption KAT for 192 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_ctr128_encrypt(kAESCTRCiphertext_192, output,
+                                   sizeof(kAESCTRCiphertext_192), &aes_key,
+                                   aes_iv, ecount_buf, &num));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CTR Encryption KAT for 256 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(
+      AES_set_encrypt_key(kAESKey_256, 8 * sizeof(kAESKey_256), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_ctr128_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                   &aes_key, aes_iv, ecount_buf, &num));
+  EXPECT_EQ(Bytes(kAESCTRCiphertext_256), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-CTR Decryption KAT for 256 bit key.
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_ctr128_encrypt(kAESCTRCiphertext_256, output,
+                                   sizeof(kAESCTRCiphertext_256), &aes_key,
+                                   aes_iv, ecount_buf, &num));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+TEST(ServiceIndicatorTest, AESOFB) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  AES_KEY aes_key;
+  uint8_t aes_iv[sizeof(kAESIV)];
+  uint8_t output[sizeof(kPlaintext)];
+  int num = 0;
+
+  // AES-OFB Encryption KAT
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_ofb128_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                   &aes_key, aes_iv, &num));
+  EXPECT_EQ(Bytes(kAESOFBCiphertext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+
+  // AES-OFB Decryption KAT
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      AES_ofb128_encrypt(kAESOFBCiphertext, output, sizeof(kAESOFBCiphertext),
+                         &aes_key, aes_iv, &num));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+}
+
+TEST(ServiceIndicatorTest, AESCFB) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  AES_KEY aes_key;
+  uint8_t aes_iv[sizeof(kAESIV)];
+  uint8_t output[sizeof(kPlaintext)];
+  int num = 0;
+
+  // AES-CFB Encryption KAT
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_cfb128_encrypt(kPlaintext, output, sizeof(kPlaintext),
+                                   &aes_key, aes_iv, &num, AES_ENCRYPT));
+  EXPECT_EQ(Bytes(kAESCFBCiphertext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+
+  // AES-CFB Decryption KAT
+  memcpy(aes_iv, kAESIV, sizeof(aes_iv));
+  CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      AES_cfb128_encrypt(kAESCFBCiphertext, output, sizeof(kAESCFBCiphertext),
+                         &aes_key, aes_iv, &num, AES_DECRYPT));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+}
+
+TEST(ServiceIndicatorTest, AESKW) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  AES_KEY aes_key;
+  uint8_t output[sizeof(kPlaintext) + 8];
+  size_t outlen;
+
+  // AES-KW Encryption KAT
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  outlen = CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      AES_wrap_key(&aes_key, nullptr, output, kPlaintext, sizeof(kPlaintext)));
+  ASSERT_EQ(outlen, sizeof(kAESKWCiphertext));
+  EXPECT_EQ(Bytes(kAESKWCiphertext), Bytes(output));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-KW Decryption KAT
+  ASSERT_TRUE(AES_set_decrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  outlen = CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_unwrap_key(&aes_key, nullptr, output, kAESKWCiphertext,
+                               sizeof(kAESKWCiphertext)));
+  ASSERT_EQ(outlen, sizeof(kPlaintext));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output, sizeof(kPlaintext)));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+TEST(ServiceIndicatorTest, AESKWP) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  AES_KEY aes_key;
+  uint8_t output[sizeof(kPlaintext) + 15];
+  size_t outlen;
+
+  // AES-KWP Encryption KAT
+  ASSERT_TRUE(AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, AES_wrap_key_padded(&aes_key, output, &outlen, sizeof(output),
+                                    kPlaintext, sizeof(kPlaintext))));
+  EXPECT_EQ(Bytes(kAESKWPCiphertext), Bytes(output, outlen));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // AES-KWP Decryption KAT
+  ASSERT_TRUE(AES_set_decrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) == 0);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      AES_unwrap_key_padded(&aes_key, output, &outlen, sizeof(output),
+                            kAESKWPCiphertext, sizeof(kAESKWPCiphertext))));
+  EXPECT_EQ(Bytes(kPlaintext), Bytes(output, outlen));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+TEST(ServiceIndicatorTest, FFDH) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+
+  // |DH_compute_key_padded| should be a non-approved service.
+  bssl::UniquePtr<DH> dh(GetDH());
+  uint8_t dh_out[sizeof(kDHOutput)];
+  ASSERT_EQ(DH_size(dh.get()), static_cast<int>(sizeof(dh_out)));
+  ASSERT_EQ(CALL_SERVICE_AND_CHECK_APPROVED(
+                approved, DH_compute_key_padded(
+                              dh_out, DH_get0_priv_key(dh.get()), dh.get())),
+            static_cast<int>(sizeof(dh_out)));
+  EXPECT_EQ(Bytes(kDHOutput), Bytes(dh_out));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+}
+
+TEST(ServiceIndicatorTest, DRBG) {
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  CTR_DRBG_STATE drbg;
+  uint8_t output[sizeof(kDRBGOutput)];
+
+  // Test running the DRBG interfaces and check |CTR_DRBG_generate| for approval
+  // at the end since it indicates a service is being done. |CTR_DRBG_init| and
+  // |CTR_DRBG_reseed| should not be approved, because the functions do not
+  // indicate that a service has been fully completed yet.
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, CTR_DRBG_init(&drbg, kDRBGEntropy, kDRBGPersonalization,
+                              sizeof(kDRBGPersonalization))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, CTR_DRBG_generate(&drbg, output, sizeof(kDRBGOutput), kDRBGAD,
+                                  sizeof(kDRBGAD))));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  EXPECT_EQ(Bytes(kDRBGOutput), Bytes(output));
+
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      CTR_DRBG_reseed(&drbg, kDRBGEntropy2, kDRBGAD, sizeof(kDRBGAD))));
+  EXPECT_EQ(approved, FIPSStatus::NOT_APPROVED);
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, CTR_DRBG_generate(&drbg, output, sizeof(kDRBGReseedOutput),
+                                  kDRBGAD, sizeof(kDRBGAD))));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+  EXPECT_EQ(Bytes(kDRBGReseedOutput), Bytes(output));
+
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, RAND_bytes(output, sizeof(output))));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+#else  // !BORINGSSL_FIPS
+
+// Service indicator calls should not be used in non-FIPS builds. However, if
+// used, the macro |CALL_SERVICE_AND_CHECK_APPROVED| will return
+// |FIPSStatus::APPROVED|, but the direct calls to
+// |FIPS_service_indicator_xxx| will not indicate an approved state.
+TEST(ServiceIndicatorTest, BasicTest) {
+  // Reset and check the initial state and counter.
+  FIPSStatus approved = FIPSStatus::NOT_APPROVED;
+  uint64_t before = FIPS_service_indicator_before_call();
+  ASSERT_EQ(before, (uint64_t)0);
+
+  // Call an approved service.
+  bssl::ScopedEVP_AEAD_CTX aead_ctx;
+  uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH] = {0};
+  uint8_t output[256];
+  size_t out_len;
+  ASSERT_TRUE(EVP_AEAD_CTX_init(aead_ctx.get(),
+                                EVP_aead_aes_128_gcm_randnonce(), kAESKey,
+                                sizeof(kAESKey), 0, nullptr));
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved, EVP_AEAD_CTX_seal(aead_ctx.get(), output, &out_len,
+                                  sizeof(output), nullptr, 0, kPlaintext,
+                                  sizeof(kPlaintext), nullptr, 0)));
+  // Macro should return true, to ensure FIPS/non-FIPS compatibility.
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+
+  // Call a non-approved service.
+  ASSERT_TRUE(EVP_AEAD_CTX_init(aead_ctx.get(), EVP_aead_aes_128_gcm(), kAESKey,
+                                sizeof(kAESKey), 0, nullptr));
+  ASSERT_TRUE(CALL_SERVICE_AND_CHECK_APPROVED(
+      approved,
+      EVP_AEAD_CTX_seal(aead_ctx.get(), output, &out_len, sizeof(output), nonce,
+                        EVP_AEAD_nonce_length(EVP_aead_aes_128_gcm()),
+                        kPlaintext, sizeof(kPlaintext), nullptr, 0)));
+  EXPECT_EQ(approved, FIPSStatus::APPROVED);
+}
+
+#endif  // BORINGSSL_FIPS
diff --git a/crypto/fipsmodule/sha/sha1.c b/crypto/fipsmodule/sha/sha1.c
index e482c77..f921e31 100644
--- a/crypto/fipsmodule/sha/sha1.c
+++ b/crypto/fipsmodule/sha/sha1.c
@@ -62,6 +62,7 @@
 
 #include "../../internal.h"
 #include "../digest/md32_common.h"
+#include "../service_indicator/internal.h"
 #include "internal.h"
 
 
@@ -108,6 +109,7 @@
   CRYPTO_store_u32_be(out + 8, c->h[2]);
   CRYPTO_store_u32_be(out + 12, c->h[3]);
   CRYPTO_store_u32_be(out + 16, c->h[4]);
+  FIPS_service_indicator_update_state();
   return 1;
 }
 
diff --git a/crypto/fipsmodule/sha/sha256.c b/crypto/fipsmodule/sha/sha256.c
index afa0aa3..454b947 100644
--- a/crypto/fipsmodule/sha/sha256.c
+++ b/crypto/fipsmodule/sha/sha256.c
@@ -62,6 +62,7 @@
 
 #include "../../internal.h"
 #include "../digest/md32_common.h"
+#include "../service_indicator/internal.h"
 #include "internal.h"
 
 
@@ -150,6 +151,8 @@
     CRYPTO_store_u32_be(out, c->h[i]);
     out += 4;
   }
+
+  FIPS_service_indicator_update_state();
   return 1;
 }
 
diff --git a/crypto/fipsmodule/sha/sha512.c b/crypto/fipsmodule/sha/sha512.c
index d94de28..708358e 100644
--- a/crypto/fipsmodule/sha/sha512.c
+++ b/crypto/fipsmodule/sha/sha512.c
@@ -60,8 +60,9 @@
 
 #include <openssl/mem.h>
 
-#include "internal.h"
 #include "../../internal.h"
+#include "../service_indicator/internal.h"
+#include "internal.h"
 
 
 // The 32-bit hash algorithms share a common byte-order neutral collector and
@@ -274,6 +275,7 @@
     out += 8;
   }
 
+  FIPS_service_indicator_update_state();
   return 1;
 }
 
diff --git a/crypto/fipsmodule/tls/kdf.c b/crypto/fipsmodule/tls/kdf.c
index 347e998..6f2b68b 100644
--- a/crypto/fipsmodule/tls/kdf.c
+++ b/crypto/fipsmodule/tls/kdf.c
@@ -58,6 +58,7 @@
 
 #include "internal.h"
 #include "../../internal.h"
+#include "../service_indicator/internal.h"
 
 
 // tls1_P_hash computes the TLS P_<hash> function as described in RFC 5246,
@@ -146,12 +147,16 @@
 
   OPENSSL_memset(out, 0, out_len);
 
+  const EVP_MD *const original_digest = digest;
+  FIPS_service_indicator_lock_state();
+  int ret = 0;
+
   if (digest == EVP_md5_sha1()) {
     // If using the MD5/SHA1 PRF, |secret| is partitioned between MD5 and SHA-1.
     size_t secret_half = secret_len - (secret_len / 2);
     if (!tls1_P_hash(out, out_len, EVP_md5(), secret, secret_half, label,
                      label_len, seed1, seed1_len, seed2, seed2_len)) {
-      return 0;
+      goto end;
     }
 
     // Note that, if |secret_len| is odd, the two halves share a byte.
@@ -160,6 +165,13 @@
     digest = EVP_sha1();
   }
 
-  return tls1_P_hash(out, out_len, digest, secret, secret_len, label, label_len,
-                     seed1, seed1_len, seed2, seed2_len);
+  ret = tls1_P_hash(out, out_len, digest, secret, secret_len, label, label_len,
+                    seed1, seed1_len, seed2, seed2_len);
+
+end:
+  FIPS_service_indicator_unlock_state();
+  if (ret) {
+    TLSKDF_verify_service_indicator(original_digest);
+  }
+  return ret;
 }
diff --git a/crypto/internal.h b/crypto/internal.h
index a7db329..c9b5e8e 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -648,6 +648,7 @@
   OPENSSL_THREAD_LOCAL_ERR = 0,
   OPENSSL_THREAD_LOCAL_RAND,
   OPENSSL_THREAD_LOCAL_FIPS_COUNTERS,
+  OPENSSL_THREAD_LOCAL_FIPS_SERVICE_INDICATOR_STATE,
   OPENSSL_THREAD_LOCAL_TEST,
   NUM_OPENSSL_THREAD_LOCALS,
 } thread_local_data_t;
diff --git a/include/openssl/service_indicator.h b/include/openssl/service_indicator.h
new file mode 100644
index 0000000..33b38b2
--- /dev/null
+++ b/include/openssl/service_indicator.h
@@ -0,0 +1,96 @@
+/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * 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_SERVICE_INDICATOR_H
+#define OPENSSL_HEADER_SERVICE_INDICATOR_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// FIPS_service_indicator_before_call and |FIPS_service_indicator_after_call|
+// both currently return the same local thread counter which is slowly
+// incremented whenever approved services are called. The
+// |CALL_SERVICE_AND_CHECK_APPROVED| macro is strongly recommended over calling
+// these functions directly.
+//
+// |FIPS_service_indicator_before_call| is intended to be called immediately
+// before an approved service, while |FIPS_service_indicator_after_call| should
+// be called immediately after. If the values returned from these two functions
+// are not equal, this means that the service called inbetween is deemed to be
+// approved. If the values are still the same, this means the counter has not
+// been incremented, and the service called is not approved for FIPS.
+//
+// In non-FIPS builds, |FIPS_service_indicator_before_call| always returns zero
+// and |FIPS_service_indicator_after_call| always returns one. Thus calls always
+// appear to be approved. This is intended to simplify testing.
+OPENSSL_EXPORT uint64_t FIPS_service_indicator_before_call(void);
+OPENSSL_EXPORT uint64_t FIPS_service_indicator_after_call(void);
+
+#if defined(__cplusplus)
+}
+
+#if !defined(BORINGSSL_NO_CXX)
+
+extern "C++" {
+
+// CALL_SERVICE_AND_CHECK_APPROVED runs |func| and sets |approved| to one of the
+// |FIPSStatus*| values, above, depending on whether |func| invoked an
+// approved service. The result of |func| becomes the result of this macro.
+#define CALL_SERVICE_AND_CHECK_APPROVED(approved, func)         \
+  [&] {                                                       \
+    bssl::FIPSIndicatorHelper fips_indicator_helper(&approved); \
+    return func;                                                \
+  }()
+
+namespace bssl {
+
+enum class FIPSStatus {
+  NOT_APPROVED = 0,
+  APPROVED = 1,
+};
+
+// FIPSIndicatorHelper records whether the service indicator counter advanced
+// during its lifetime.
+class FIPSIndicatorHelper {
+ public:
+  FIPSIndicatorHelper(FIPSStatus *result)
+      : result_(result), before_(FIPS_service_indicator_before_call()) {
+    *result_ = FIPSStatus::NOT_APPROVED;
+  }
+
+  ~FIPSIndicatorHelper() {
+    uint64_t after = FIPS_service_indicator_after_call();
+    if (after != before_) {
+      *result_ = FIPSStatus::APPROVED;
+    }
+  }
+
+  FIPSIndicatorHelper(const FIPSIndicatorHelper&) = delete;
+  FIPSIndicatorHelper &operator=(const FIPSIndicatorHelper &) = delete;
+
+ private:
+  FIPSStatus *const result_;
+  const uint64_t before_;
+};
+
+}  // namespace bssl
+}  // extern "C++"
+
+#endif  // !BORINGSSL_NO_CXX
+#endif  // __cplusplus
+
+#endif  // OPENSSL_HEADER_SERVICE_INDICATOR_H