Preliminary support for compressed certificates.

This change adds server-side support for compressed certificates.

(Although some definitions for client-side support are included in the
headers, there's no code behind them yet.)

Change-Id: I0f98abf0b782b7337ddd014c58e19e6b8cc5a3c2
Reviewed-on: https://boringssl-review.googlesource.com/27964
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc
index 40b7ff0..4687f52 100644
--- a/ssl/t1_lib.cc
+++ b/ssl/t1_lib.cc
@@ -2761,6 +2761,93 @@
   return true;
 }
 
+// Certificate compression
+
+static bool cert_compression_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+  return true;
+}
+
+static bool cert_compression_parse_serverhello(SSL_HANDSHAKE *hs,
+                                               uint8_t *out_alert,
+                                               CBS *contents) {
+  if (contents == nullptr) {
+    return true;
+  }
+
+  // The server may not echo this extension. Any server to client negotiation is
+  // advertised in the CertificateRequest message.
+  return false;
+}
+
+static bool cert_compression_parse_clienthello(SSL_HANDSHAKE *hs,
+                                               uint8_t *out_alert,
+                                               CBS *contents) {
+  if (contents == nullptr) {
+    return true;
+  }
+
+  const size_t num_algs =
+      sk_CertCompressionAlg_num(hs->ssl->ctx->cert_compression_algs);
+
+  CBS alg_ids;
+  if (!CBS_get_u8_length_prefixed(contents, &alg_ids) ||
+      CBS_len(contents) != 0 ||
+      CBS_len(&alg_ids) == 0 ||
+      CBS_len(&alg_ids) % 2 == 1) {
+    return false;
+  }
+
+  const size_t num_given_alg_ids = CBS_len(&alg_ids) / 2;
+  Array<uint16_t> given_alg_ids;
+  if (!given_alg_ids.Init(num_given_alg_ids)) {
+    return false;
+  }
+
+  size_t best_index = num_algs;
+  size_t given_alg_idx = 0;
+
+  while (CBS_len(&alg_ids) > 0) {
+    uint16_t alg_id;
+    if (!CBS_get_u16(&alg_ids, &alg_id)) {
+      return false;
+    }
+
+    given_alg_ids[given_alg_idx++] = alg_id;
+
+    for (size_t i = 0; i < num_algs; i++) {
+      const auto *alg =
+          sk_CertCompressionAlg_value(hs->ssl->ctx->cert_compression_algs, i);
+      if (alg->alg_id == alg_id && alg->compress != nullptr) {
+        if (i < best_index) {
+          best_index = i;
+        }
+        break;
+      }
+    }
+  }
+
+  qsort(given_alg_ids.data(), given_alg_ids.size(), sizeof(uint16_t),
+        compare_uint16_t);
+  for (size_t i = 1; i < num_given_alg_ids; i++) {
+    if (given_alg_ids[i - 1] == given_alg_ids[i]) {
+      return false;
+    }
+  }
+
+  if (best_index < num_algs &&
+      ssl_protocol_version(hs->ssl) >= TLS1_3_VERSION) {
+    hs->cert_compression_negotiated = true;
+    hs->cert_compression_alg_id =
+        sk_CertCompressionAlg_value(hs->ssl->ctx->cert_compression_algs,
+                                    best_index)->alg_id;
+  }
+
+  return true;
+}
+
+static bool cert_compression_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  return true;
+}
 
 // kExtensions contains all the supported extensions.
 static const struct tls_extension kExtensions[] = {
@@ -2945,6 +3032,14 @@
     ext_token_binding_parse_clienthello,
     ext_token_binding_add_serverhello,
   },
+  {
+    TLSEXT_TYPE_cert_compression,
+    NULL,
+    cert_compression_add_clienthello,
+    cert_compression_parse_serverhello,
+    cert_compression_parse_clienthello,
+    cert_compression_add_serverhello,
+  },
 };
 
 #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))