Add functions to allow the mocking of AES hw support for testing.

Bug: 586
Change-Id: I5bc8e6df3a5a14e6b218f41181d06406e835f9c1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58605
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index b4d669a..68253bd 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -5413,6 +5413,18 @@
     const SSL *ssl, Span<const uint8_t> *out_read_traffic_secret,
     Span<const uint8_t> *out_write_traffic_secret);
 
+// SSL_CTX_set_aes_hw_override_for_testing sets |override_value| to
+// override checking for aes hardware support for testing. If |override_value|
+// is set to true, the library will behave as if aes hardware support is
+// present. If it is set to false, the library will behave as if aes hardware
+// support is not present.
+OPENSSL_EXPORT void SSL_CTX_set_aes_hw_override_for_testing(
+    SSL_CTX *ctx, bool override_value);
+
+// SSL_set_aes_hw_override_for_testing acts the same as
+// |SSL_CTX_set_aes_override_for_testing| but only configures a single |SSL*|.
+OPENSSL_EXPORT void SSL_set_aes_hw_override_for_testing(SSL *ssl,
+                                                        bool override_value);
 
 BSSL_NAMESPACE_END
 
diff --git a/ssl/encrypted_client_hello.cc b/ssl/encrypted_client_hello.cc
index 8d35f18..a5492e9 100644
--- a/ssl/encrypted_client_hello.cc
+++ b/ssl/encrypted_client_hello.cc
@@ -601,8 +601,8 @@
 
 static bool select_ech_cipher_suite(const EVP_HPKE_KDF **out_kdf,
                                     const EVP_HPKE_AEAD **out_aead,
-                                    Span<const uint8_t> cipher_suites) {
-  const bool has_aes_hardware = EVP_has_aes_hardware();
+                                    Span<const uint8_t> cipher_suites,
+                                    const bool has_aes_hardware) {
   const EVP_HPKE_AEAD *aead = nullptr;
   CBS cbs = cipher_suites;
   while (CBS_len(&cbs) != 0) {
@@ -660,7 +660,10 @@
       const EVP_HPKE_AEAD *aead;
       if (supported &&  //
           ech_config.kem_id == EVP_HPKE_DHKEM_X25519_HKDF_SHA256 &&
-          select_ech_cipher_suite(&kdf, &aead, ech_config.cipher_suites)) {
+          select_ech_cipher_suite(&kdf, &aead, ech_config.cipher_suites,
+                                  hs->ssl->config->aes_hw_override
+                                      ? hs->ssl->config->aes_hw_override_value
+                                      : EVP_has_aes_hardware())) {
         ScopedCBB info;
         static const uint8_t kInfoLabel[] = "tls ech";  // includes trailing NUL
         if (!CBB_init(info.get(), sizeof(kInfoLabel) + ech_config.raw.size()) ||
@@ -714,9 +717,11 @@
   }
 
   const uint16_t kdf_id = EVP_HPKE_HKDF_SHA256;
-  const EVP_HPKE_AEAD *aead = EVP_has_aes_hardware()
-                                  ? EVP_hpke_aes_128_gcm()
-                                  : EVP_hpke_chacha20_poly1305();
+  const bool has_aes_hw = hs->ssl->config->aes_hw_override
+                              ? hs->ssl->config->aes_hw_override_value
+                              : EVP_has_aes_hardware();
+  const EVP_HPKE_AEAD *aead =
+      has_aes_hw ? EVP_hpke_aes_128_gcm() : EVP_hpke_chacha20_poly1305();
   static_assert(ssl_grease_ech_config_id < sizeof(hs->grease_seed),
                 "hs->grease_seed is too small");
   uint8_t config_id = hs->grease_seed[ssl_grease_ech_config_id];
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 3e63019..7fe2a2b 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -239,8 +239,12 @@
         TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff,
         ssl->config->only_fips_cipher_suites_in_tls13);
 
-    if (!EVP_has_aes_hardware() &&  //
-        include_chacha20 &&         //
+    const bool has_aes_hw = ssl->config->aes_hw_override
+                                ? ssl->config->aes_hw_override_value
+                                : EVP_has_aes_hardware();
+
+    if (!has_aes_hw &&       //
+        include_chacha20 &&  //
         !CBB_add_u16(&child, TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
       return false;
     }
@@ -248,8 +252,8 @@
         !CBB_add_u16(&child, TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff)) {
       return false;
     }
-    if (EVP_has_aes_hardware() &&  //
-        include_chacha20 &&        //
+    if (has_aes_hw &&        //
+        include_chacha20 &&  //
         !CBB_add_u16(&child, TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
       return false;
     }
diff --git a/ssl/internal.h b/ssl/internal.h
index b4d853a..15ad6c2 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -633,9 +633,11 @@
 // newly-allocated |SSLCipherPreferenceList| containing the result. It returns
 // true on success and false on failure. If |strict| is true, nonsense will be
 // rejected. If false, nonsense will be silently ignored. An empty result is
-// considered an error regardless of |strict|.
+// considered an error regardless of |strict|. |has_aes_hw| indicates if the
+// list should be ordered based on having support for AES in hardware or not.
 bool ssl_create_cipher_list(UniquePtr<SSLCipherPreferenceList> *out_cipher_list,
-                            const char *rule_str, bool strict);
+                            const bool has_aes_hw, const char *rule_str,
+                            bool strict);
 
 // ssl_cipher_auth_mask_for_key returns the mask of cipher |algorithm_auth|
 // values suitable for use with |key| in TLS 1.2 and below.
@@ -659,9 +661,12 @@
 
 // ssl_choose_tls13_cipher returns an |SSL_CIPHER| corresponding with the best
 // available from |cipher_suites| compatible with |version|, |group_id|, and
-// |only_fips|. It returns NULL if there isn't a compatible cipher.
-const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version,
-                                          uint16_t group_id, bool only_fips);
+// |only_fips|. It returns NULL if there isn't a compatible cipher. |has_aes_hw|
+// indicates if the choice should be made as if support for AES in hardware
+// is available.
+const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, bool has_aes_hw,
+                                          uint16_t version, uint16_t group_id,
+                                          bool only_fips);
 
 // ssl_tls13_cipher_meets_policy returns true if |cipher_id| is acceptable given
 // |only_fips|. (For now there's only a single policy and so the policy argument
@@ -3111,6 +3116,15 @@
   // only_fips_cipher_suites_in_tls13 constrains the selection of cipher suites
   // in TLS 1.3 such that only FIPS approved ones will be selected.
   bool only_fips_cipher_suites_in_tls13 : 1;
+
+  // aes_hw_override if set indicates we should override checking for aes
+  // hardware support, and use the value in aes_hw_override_value instead.
+  bool aes_hw_override : 1;
+
+  // aes_hw_override_value is used for testing to indicate the support or lack
+  // of support for AES hw. The value is only considered if |aes_hw_override| is
+  // true.
+  bool aes_hw_override_value : 1;
 };
 
 // From RFC 8446, used in determining PSK modes.
@@ -3722,6 +3736,15 @@
   // in TLS 1.3 such that only FIPS approved ones will be selected.
   bool only_fips_cipher_suites_in_tls13 : 1;
 
+  // aes_hw_override if set indicates we should override checking for AES
+  // hardware support, and use the value in aes_hw_override_value instead.
+  bool aes_hw_override : 1;
+
+  // aes_hw_override_value is used for testing to indicate the support or lack
+  // of support for AES hardware. The value is only considered if
+  // |aes_hw_override| is true.
+  bool aes_hw_override_value : 1;
+
  private:
   ~ssl_ctx_st();
   friend OPENSSL_EXPORT void SSL_CTX_free(SSL_CTX *);
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc
index eb9da0d..f8352f3 100644
--- a/ssl/s3_both.cc
+++ b/ssl/s3_both.cc
@@ -663,7 +663,7 @@
 // the client.
 class CipherScorer {
  public:
-  CipherScorer() : aes_is_fine_(EVP_has_aes_hardware()) {}
+  CipherScorer(bool has_aes_hw) : aes_is_fine_(has_aes_hw) {}
 
   typedef std::tuple<bool, bool> Score;
 
@@ -702,14 +702,15 @@
   }
 }
 
-const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version,
-                                          uint16_t group_id, bool only_fips) {
+const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, bool has_aes_hw,
+                                          uint16_t version, uint16_t group_id,
+                                          bool only_fips) {
   if (CBS_len(&cipher_suites) % 2 != 0) {
     return nullptr;
   }
 
   const SSL_CIPHER *best = nullptr;
-  CipherScorer scorer;
+  CipherScorer scorer(has_aes_hw);
   CipherScorer::Score best_score = scorer.MinScore();
 
   while (CBS_len(&cipher_suites) > 0) {
diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc
index b08eb20..73564b3 100644
--- a/ssl/ssl_cipher.cc
+++ b/ssl/ssl_cipher.cc
@@ -1148,7 +1148,8 @@
 }
 
 bool ssl_create_cipher_list(UniquePtr<SSLCipherPreferenceList> *out_cipher_list,
-                            const char *rule_str, bool strict) {
+                            const bool has_aes_hw, const char *rule_str,
+                            bool strict) {
   // Return with error if nothing to do.
   if (rule_str == NULL || out_cipher_list == NULL) {
     return false;
@@ -1179,7 +1180,7 @@
   // CHACHA20 unless there is hardware support for fast and constant-time
   // AES_GCM. Of the two CHACHA20 variants, the new one is preferred over the
   // old one.
-  if (EVP_has_aes_hardware()) {
+  if (has_aes_hw) {
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1,
                           false, &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1,
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 86e8eb3..c035825 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -484,6 +484,17 @@
   return true;
 }
 
+void SSL_CTX_set_aes_hw_override_for_testing(SSL_CTX *ctx,
+                                             bool override_value) {
+  ctx->aes_hw_override = true;
+  ctx->aes_hw_override_value = override_value;
+}
+
+void SSL_set_aes_hw_override_for_testing(SSL *ssl, bool override_value) {
+  ssl->config->aes_hw_override = true;
+  ssl->config->aes_hw_override_value = override_value;
+}
+
 BSSL_NAMESPACE_END
 
 using namespace bssl;
@@ -525,7 +536,9 @@
       false_start_allowed_without_alpn(false),
       handoff(false),
       enable_early_data(false),
-      only_fips_cipher_suites_in_tls13(false) {
+      only_fips_cipher_suites_in_tls13(false),
+      aes_hw_override(false),
+      aes_hw_override_value(false) {
   CRYPTO_MUTEX_init(&lock);
   CRYPTO_new_ex_data(&ex_data);
 }
@@ -647,6 +660,8 @@
   ssl->config->permute_extensions = ctx->permute_extensions;
   ssl->config->only_fips_cipher_suites_in_tls13 =
       ctx->only_fips_cipher_suites_in_tls13;
+  ssl->config->aes_hw_override = ctx->aes_hw_override;
+  ssl->config->aes_hw_override_value = ctx->aes_hw_override_value;
 
   if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) ||
       !ssl->config->alpn_client_proto_list.CopyFrom(
@@ -2026,18 +2041,27 @@
 }
 
 int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) {
-  return ssl_create_cipher_list(&ctx->cipher_list, str, false /* not strict */);
+  const bool has_aes_hw = ctx->aes_hw_override ? ctx->aes_hw_override_value
+                                               : EVP_has_aes_hardware();
+  return ssl_create_cipher_list(&ctx->cipher_list, has_aes_hw, str,
+                                false /* not strict */);
 }
 
 int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, const char *str) {
-  return ssl_create_cipher_list(&ctx->cipher_list, str, true /* strict */);
+  const bool has_aes_hw = ctx->aes_hw_override ? ctx->aes_hw_override_value
+                                               : EVP_has_aes_hardware();
+  return ssl_create_cipher_list(&ctx->cipher_list, has_aes_hw, str,
+                                true /* strict */);
 }
 
 int SSL_set_cipher_list(SSL *ssl, const char *str) {
   if (!ssl->config) {
     return 0;
   }
-  return ssl_create_cipher_list(&ssl->config->cipher_list, str,
+  const bool has_aes_hw = ssl->config->aes_hw_override
+                              ? ssl->config->aes_hw_override_value
+                              : EVP_has_aes_hardware();
+  return ssl_create_cipher_list(&ssl->config->cipher_list, has_aes_hw, str,
                                 false /* not strict */);
 }
 
@@ -2045,7 +2069,10 @@
   if (!ssl->config) {
     return 0;
   }
-  return ssl_create_cipher_list(&ssl->config->cipher_list, str,
+  const bool has_aes_hw = ssl->config->aes_hw_override
+                              ? ssl->config->aes_hw_override_value
+                              : EVP_has_aes_hardware();
+  return ssl_create_cipher_list(&ssl->config->cipher_list, has_aes_hw, str,
                                 true /* strict */);
 }
 
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 684c254..c64bb16 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -583,16 +583,14 @@
   }
 
   uint16_t cipher_id = SSL_CIPHER_get_protocol_id(SSL_get_current_cipher(ssl));
-  if (config->expect_cipher_aes != 0 &&
-      EVP_has_aes_hardware() &&
+  if (config->expect_cipher_aes != 0 && EVP_has_aes_hardware() &&
       config->expect_cipher_aes != cipher_id) {
     fprintf(stderr, "Cipher ID was %04x, wanted %04x (has AES hardware)\n",
             cipher_id, config->expect_cipher_aes);
     return false;
   }
 
-  if (config->expect_cipher_no_aes != 0 &&
-      !EVP_has_aes_hardware() &&
+  if (config->expect_cipher_no_aes != 0 && !EVP_has_aes_hardware() &&
       config->expect_cipher_no_aes != cipher_id) {
     fprintf(stderr, "Cipher ID was %04x, wanted %04x (no AES hardware)\n",
             cipher_id, config->expect_cipher_no_aes);
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index 0bbe97f..c97e3f5 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -116,8 +116,11 @@
 
   const uint16_t version = ssl_protocol_version(ssl);
 
-  return ssl_choose_tls13_cipher(cipher_suites, version, group_id,
-                                 ssl->config->only_fips_cipher_suites_in_tls13);
+  return ssl_choose_tls13_cipher(
+      cipher_suites,
+      ssl->config->aes_hw_override ? ssl->config->aes_hw_override_value
+                                   : EVP_has_aes_hardware(),
+      version, group_id, ssl->config->only_fips_cipher_suites_in_tls13);
 }
 
 static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {