Move SCSV handling out of cipher list parsing.

It's odd that a function like ssl_bytes_to_cipher_list secretly has side
effects all over the place. This removes the need for the TLS 1.3 code
to re-query the version range, and it removes the requirement that the
RI extension be first.

Change-Id: Ic9af549db3aaa8880f3c591b8a13ba9ae91d6a46
Reviewed-on: https://boringssl-review.googlesource.com/10220
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index d760d10..51d816e 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -621,8 +621,6 @@
     if (!CBB_add_u16(&child, SSL3_CK_SCSV & 0xffff)) {
       return 0;
     }
-    /* The renegotiation extension is required to be at index zero. */
-    ssl->s3->tmp.extensions.sent |= (1u << 0);
   }
 
   if ((ssl->mode & SSL_MODE_SEND_FALLBACK_SCSV) ||
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index d10d7f5..43abf39 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -530,6 +530,26 @@
   return ret;
 }
 
+int ssl_client_cipher_list_contains_cipher(
+    const struct ssl_early_callback_ctx *client_hello, uint16_t id) {
+  CBS cipher_suites;
+  CBS_init(&cipher_suites, client_hello->cipher_suites,
+           client_hello->cipher_suites_len);
+
+  while (CBS_len(&cipher_suites) > 0) {
+    uint16_t got_id;
+    if (!CBS_get_u16(&cipher_suites, &got_id)) {
+      return 0;
+    }
+
+    if (got_id == id) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
 static int ssl3_get_client_hello(SSL *ssl) {
   int al = SSL_AD_INTERNAL_ERROR, ret = -1;
   const SSL_CIPHER *c;
@@ -600,11 +620,22 @@
     if (version > max_version) {
       version = max_version;
     }
+
     if (version < min_version) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
       al = SSL_AD_PROTOCOL_VERSION;
       goto f_err;
     }
+
+    /* Handle FALLBACK_SCSV. */
+    if (ssl_client_cipher_list_contains_cipher(
+            &client_hello, SSL3_CK_FALLBACK_SCSV & 0xffff) &&
+        version < max_version) {
+      al = SSL3_AD_INAPPROPRIATE_FALLBACK;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
+      goto f_err;
+    }
+
     ssl->version = ssl->method->version_to_wire(version);
     ssl->s3->enc_method = ssl3_get_enc_method(version);
     assert(ssl->s3->enc_method != NULL);
@@ -698,7 +729,7 @@
     goto f_err;
   }
 
-  ciphers = ssl_parse_client_cipher_list(ssl, &client_hello, max_version);
+  ciphers = ssl_parse_client_cipher_list(&client_hello);
   if (ciphers == NULL) {
     goto err;
   }
diff --git a/ssl/internal.h b/ssl/internal.h
index 77759e4..827e5fd 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -967,9 +967,10 @@
                                      CBS *out, uint16_t extension_type);
 
 STACK_OF(SSL_CIPHER) *
-    ssl_parse_client_cipher_list(SSL *ssl,
-                                 const struct ssl_early_callback_ctx *ctx,
-                                 uint16_t max_version);
+    ssl_parse_client_cipher_list(const struct ssl_early_callback_ctx *ctx);
+
+int ssl_client_cipher_list_contains_cipher(
+    const struct ssl_early_callback_ctx *client_hello, uint16_t id);
 
 
 /* Underdocumented functions.
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 04477e5..1872f57 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1630,14 +1630,10 @@
 }
 
 STACK_OF(SSL_CIPHER) *
-    ssl_parse_client_cipher_list(SSL *ssl,
-                                 const struct ssl_early_callback_ctx *ctx,
-                                 uint16_t max_version) {
+    ssl_parse_client_cipher_list(const struct ssl_early_callback_ctx *ctx) {
   CBS cipher_suites;
   CBS_init(&cipher_suites, ctx->cipher_suites, ctx->cipher_suites_len);
 
-  ssl->s3->send_connection_binding = 0;
-
   STACK_OF(SSL_CIPHER) *sk = sk_SSL_CIPHER_new_null();
   if (sk == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
@@ -1652,28 +1648,6 @@
       goto err;
     }
 
-    /* Check for SCSV. */
-    if (ssl->s3 && cipher_suite == (SSL3_CK_SCSV & 0xffff)) {
-      /* SCSV is fatal if renegotiating. */
-      if (ssl->s3->initial_handshake_complete) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
-        goto err;
-      }
-      ssl->s3->send_connection_binding = 1;
-      continue;
-    }
-
-    /* Check for FALLBACK_SCSV. */
-    if (ssl->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff)) {
-      if (ssl3_protocol_version(ssl) < max_version) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK);
-        goto err;
-      }
-      continue;
-    }
-
     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);
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 64b2dac..62f3824 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -893,25 +893,11 @@
     return 1;
   }
 
-  CBS fake_contents;
-  static const uint8_t kFakeExtension[] = {0};
-
   if (contents == NULL) {
-    if (ssl->s3->send_connection_binding) {
-      /* The renegotiation SCSV was received so pretend that we received a
-       * renegotiation extension. */
-      CBS_init(&fake_contents, kFakeExtension, sizeof(kFakeExtension));
-      contents = &fake_contents;
-      /* We require that the renegotiation extension is at index zero of
-       * kExtensions. */
-      ssl->s3->tmp.extensions.received |= (1u << 0);
-    } else {
-      return 1;
-    }
+    return 1;
   }
 
   CBS renegotiated_connection;
-
   if (!CBS_get_u8_length_prefixed(contents, &renegotiated_connection) ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_ENCODING_ERR);
@@ -2236,9 +2222,6 @@
 /* kExtensions contains all the supported extensions. */
 static const struct tls_extension kExtensions[] = {
   {
-    /* The renegotiation extension must always be at index zero because the
-     * |received| and |sent| bitsets need to be tweaked when the "extension" is
-     * sent as an SCSV. */
     TLSEXT_TYPE_renegotiate,
     NULL,
     ext_ri_add_clienthello,
@@ -2513,8 +2496,7 @@
 static int ssl_scan_clienthello_tlsext(
     SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
     int *out_alert) {
-  size_t i;
-  for (i = 0; i < kNumExtensions; i++) {
+  for (size_t i = 0; i < kNumExtensions; i++) {
     if (kExtensions[i].init != NULL) {
       kExtensions[i].init(ssl);
     }
@@ -2522,10 +2504,6 @@
 
   ssl->s3->tmp.extensions.received = 0;
   ssl->s3->tmp.custom_extensions.received = 0;
-  /* The renegotiation extension must always be at index zero because the
-   * |received| and |sent| bitsets need to be tweaked when the "extension" is
-   * sent as an SCSV. */
-  assert(kExtensions[0].value == TLSEXT_TYPE_renegotiate);
 
   CBS extensions;
   CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len);
@@ -2568,17 +2546,32 @@
     }
   }
 
-  for (i = 0; i < kNumExtensions; i++) {
-    if (!(ssl->s3->tmp.extensions.received & (1u << i))) {
-      /* Extension wasn't observed so call the callback with a NULL
-       * parameter. */
-      uint8_t alert = SSL_AD_DECODE_ERROR;
-      if (!kExtensions[i].parse_clienthello(ssl, &alert, NULL)) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
-        ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
-        *out_alert = alert;
-        return 0;
-      }
+  for (size_t i = 0; i < kNumExtensions; i++) {
+    if (ssl->s3->tmp.extensions.received & (1u << i)) {
+      continue;
+    }
+
+    CBS *contents = NULL, fake_contents;
+    static const uint8_t kFakeRenegotiateExtension[] = {0};
+    if (kExtensions[i].value == TLSEXT_TYPE_renegotiate &&
+        ssl_client_cipher_list_contains_cipher(client_hello,
+                                               SSL3_CK_SCSV & 0xffff)) {
+      /* The renegotiation SCSV was received so pretend that we received a
+       * renegotiation extension. */
+      CBS_init(&fake_contents, kFakeRenegotiateExtension,
+               sizeof(kFakeRenegotiateExtension));
+      contents = &fake_contents;
+      ssl->s3->tmp.extensions.received |= (1u << i);
+    }
+
+    /* Extension wasn't observed so call the callback with a NULL
+     * parameter. */
+    uint8_t alert = SSL_AD_DECODE_ERROR;
+    if (!kExtensions[i].parse_clienthello(ssl, &alert, contents)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
+      ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+      *out_alert = alert;
+      return 0;
     }
   }
 
@@ -2640,8 +2633,10 @@
       continue;
     }
 
-    if (!(ssl->s3->tmp.extensions.sent & (1u << ext_index))) {
-      /* If the extension was never sent then it is illegal. */
+    if (!(ssl->s3->tmp.extensions.sent & (1u << ext_index)) &&
+        type != TLSEXT_TYPE_renegotiate) {
+      /* If the extension was never sent then it is illegal, except for the
+       * renegotiation extension which, in SSL 3.0, is signaled via SCSV. */
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
       ERR_add_error_dataf("extension :%u", (unsigned)type);
       *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index 96ceb79..5964233 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -166,14 +166,7 @@
     }
   }
 
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-    return ssl_hs_error;
-  }
-
-  STACK_OF(SSL_CIPHER) *ciphers =
-      ssl_parse_client_cipher_list(ssl, &client_hello, max_version);
+  STACK_OF(SSL_CIPHER) *ciphers = ssl_parse_client_cipher_list(&client_hello);
   if (ciphers == NULL) {
     return ssl_hs_error;
   }