Make ssl3_choose_cipher and dependencies static.

Each of these functions is called only once, but they're interspersed
between s3_lib.c and ssl_lib.c.

Change-Id: Ic496e364b091fc8e01fc0653fe73c83c47f690d9
Reviewed-on: https://boringssl-review.googlesource.com/12583
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 14c3a55..4bac0d5 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -652,6 +652,169 @@
   return 0;
 }
 
+static STACK_OF(SSL_CIPHER) *
+    ssl_parse_client_cipher_list(const SSL_CLIENT_HELLO *client_hello) {
+  CBS cipher_suites;
+  CBS_init(&cipher_suites, client_hello->cipher_suites,
+           client_hello->cipher_suites_len);
+
+  STACK_OF(SSL_CIPHER) *sk = sk_SSL_CIPHER_new_null();
+  if (sk == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  while (CBS_len(&cipher_suites) > 0) {
+    uint16_t cipher_suite;
+
+    if (!CBS_get_u16(&cipher_suites, &cipher_suite)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
+      goto err;
+    }
+
+    const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite);
+    if (c != NULL && !sk_SSL_CIPHER_push(sk, c)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
+
+  return sk;
+
+err:
+  sk_SSL_CIPHER_free(sk);
+  return NULL;
+}
+
+/* ssl_get_compatible_server_ciphers determines the key exchange and
+ * authentication cipher suite masks compatible with the server configuration
+ * and current ClientHello parameters of |hs|. It sets |*out_mask_k| to the key
+ * exchange mask and |*out_mask_a| to the authentication mask. */
+static void ssl_get_compatible_server_ciphers(SSL_HANDSHAKE *hs,
+                                              uint32_t *out_mask_k,
+                                              uint32_t *out_mask_a) {
+  SSL *const ssl = hs->ssl;
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    *out_mask_k = SSL_kGENERIC;
+    *out_mask_a = SSL_aGENERIC;
+    return;
+  }
+
+  uint32_t mask_k = 0;
+  uint32_t mask_a = 0;
+
+  if (ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl)) {
+    int type = ssl_private_key_type(ssl);
+    if (type == NID_rsaEncryption) {
+      mask_k |= SSL_kRSA;
+      mask_a |= SSL_aRSA;
+    } else if (ssl_is_ecdsa_key_type(type)) {
+      mask_a |= SSL_aECDSA;
+    }
+  }
+
+  if (ssl->cert->dh_tmp != NULL || ssl->cert->dh_tmp_cb != NULL) {
+    mask_k |= SSL_kDHE;
+  }
+
+  /* Check for a shared group to consider ECDHE ciphers. */
+  uint16_t unused;
+  if (tls1_get_shared_group(hs, &unused)) {
+    mask_k |= SSL_kECDHE;
+  }
+
+  /* CECPQ1 ciphers are always acceptable if supported by both sides. */
+  mask_k |= SSL_kCECPQ1;
+
+  /* PSK requires a server callback. */
+  if (ssl->psk_server_callback != NULL) {
+    mask_k |= SSL_kPSK;
+    mask_a |= SSL_aPSK;
+  }
+
+  *out_mask_k = mask_k;
+  *out_mask_a = mask_a;
+}
+
+static const SSL_CIPHER *ssl3_choose_cipher(
+    SSL_HANDSHAKE *hs, const SSL_CLIENT_HELLO *client_hello,
+    const struct ssl_cipher_preference_list_st *server_pref) {
+  SSL *const ssl = hs->ssl;
+  const SSL_CIPHER *c, *ret = NULL;
+  STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow;
+  int ok;
+  size_t cipher_index;
+  uint32_t alg_k, alg_a, mask_k, mask_a;
+  /* in_group_flags will either be NULL, or will point to an array of bytes
+   * which indicate equal-preference groups in the |prio| stack. See the
+   * comment about |in_group_flags| in the |ssl_cipher_preference_list_st|
+   * struct. */
+  const uint8_t *in_group_flags;
+  /* group_min contains the minimal index so far found in a group, or -1 if no
+   * such value exists yet. */
+  int group_min = -1;
+
+  STACK_OF(SSL_CIPHER) *clnt = ssl_parse_client_cipher_list(client_hello);
+  if (clnt == NULL) {
+    return NULL;
+  }
+
+  if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
+    prio = srvr;
+    in_group_flags = server_pref->in_group_flags;
+    allow = clnt;
+  } else {
+    prio = clnt;
+    in_group_flags = NULL;
+    allow = srvr;
+  }
+
+  ssl_get_compatible_server_ciphers(hs, &mask_k, &mask_a);
+
+  for (size_t i = 0; i < sk_SSL_CIPHER_num(prio); i++) {
+    c = sk_SSL_CIPHER_value(prio, i);
+
+    ok = 1;
+
+    /* Check the TLS version. */
+    if (SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
+        SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl)) {
+      ok = 0;
+    }
+
+    alg_k = c->algorithm_mkey;
+    alg_a = c->algorithm_auth;
+
+    ok = ok && (alg_k & mask_k) && (alg_a & mask_a);
+
+    if (ok && sk_SSL_CIPHER_find(allow, &cipher_index, c)) {
+      if (in_group_flags != NULL && in_group_flags[i] == 1) {
+        /* This element of |prio| is in a group. Update the minimum index found
+         * so far and continue looking. */
+        if (group_min == -1 || (size_t)group_min > cipher_index) {
+          group_min = cipher_index;
+        }
+      } else {
+        if (group_min != -1 && (size_t)group_min < cipher_index) {
+          cipher_index = group_min;
+        }
+        ret = sk_SSL_CIPHER_value(allow, cipher_index);
+        break;
+      }
+    }
+
+    if (in_group_flags != NULL && in_group_flags[i] == 0 && group_min != -1) {
+      /* We are about to leave a group, but we found a match in it, so that's
+       * our answer. */
+      ret = sk_SSL_CIPHER_value(allow, group_min);
+      break;
+    }
+  }
+
+  sk_SSL_CIPHER_free(clnt);
+  return ret;
+}
+
 static int ssl3_get_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   uint8_t al = SSL_AD_INTERNAL_ERROR;