EVP_AEAD: implement sealv/openv for the TLS ciphers.

Performance note: this seems to cost 13 to 261 CPU cycles per call with
LTO, and 8 to 158 CPU cycles without; no measurable per-byte overhead
though.

Bug: 383343306
Change-Id: I7eeacf3044aca9bd40993d8f70a8898f90156671
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/84667
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/cipher/aead_test.cc b/crypto/cipher/aead_test.cc
index e50a2c5..f4c16e3 100644
--- a/crypto/cipher/aead_test.cc
+++ b/crypto/cipher/aead_test.cc
@@ -614,6 +614,7 @@
         }
         SCOPED_TRACE(FormatSplits(splits));
 
+        ctx.Reset();
         ASSERT_TRUE(EVP_AEAD_CTX_init_with_direction(
             ctx.get(), aead_config.func(), key.data(), key.size(), tag_len,
             evp_aead_seal));
diff --git a/crypto/cipher/e_tls.cc b/crypto/cipher/e_tls.cc
index 7be0644..da35da6 100644
--- a/crypto/cipher/e_tls.cc
+++ b/crypto/cipher/e_tls.cc
@@ -23,9 +23,11 @@
 #include <openssl/md5.h>
 #include <openssl/mem.h>
 #include <openssl/sha.h>
+#include <openssl/span.h>
 
 #include "../fipsmodule/cipher/internal.h"
 #include "../internal.h"
+#include "../mem_internal.h"
 #include "internal.h"
 
 
@@ -113,14 +115,12 @@
   return hmac_len + pad_len;
 }
 
-static int aead_tls_seal_scatter(const EVP_AEAD_CTX *ctx, uint8_t *out,
-                                 uint8_t *out_tag, size_t *out_tag_len,
-                                 const size_t max_out_tag_len,
-                                 const uint8_t *nonce, const size_t nonce_len,
-                                 const uint8_t *in, const size_t in_len,
-                                 const uint8_t *extra_in,
-                                 const size_t extra_in_len, const uint8_t *ad,
-                                 const size_t ad_len) {
+static int aead_tls_sealv(const EVP_AEAD_CTX *ctx,
+                          bssl::Span<const CRYPTO_IOVEC> iovecs,
+                          uint8_t *out_tag, size_t *out_tag_len,
+                          const size_t max_out_tag_len, const uint8_t *nonce,
+                          const size_t nonce_len,
+                          bssl::Span<const CRYPTO_IVEC> aadvecs) {
   AEAD_TLS_CTX *tls_ctx = (AEAD_TLS_CTX *)&ctx->state;
 
   if (!tls_ctx->cipher_ctx.encrypt) {
@@ -129,7 +129,8 @@
     return 0;
   }
 
-  if (max_out_tag_len < aead_tls_tag_len(ctx, in_len, extra_in_len)) {
+  size_t in_len = bssl::iovec::TotalLength(iovecs);
+  if (max_out_tag_len < aead_tls_tag_len(ctx, in_len, 0)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
     return 0;
   }
@@ -139,6 +140,7 @@
     return 0;
   }
 
+  size_t ad_len = bssl::iovec::TotalLength(aadvecs);
   if (ad_len != 13 - 2 /* length bytes */) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INVALID_AD_SIZE);
     return 0;
@@ -153,12 +155,24 @@
   // Compute the MAC. This must be first in case the operation is being done
   // in-place.
   uint8_t mac[EVP_MAX_MD_SIZE];
+  if (!HMAC_Init_ex(tls_ctx->hmac_ctx, nullptr, 0, nullptr, nullptr)) {
+    return 0;
+  }
+  for (const CRYPTO_IVEC &aadvec : aadvecs) {
+    if (!HMAC_Update(tls_ctx->hmac_ctx, aadvec.in, aadvec.len)) {
+      return 0;
+    }
+  }
+  if (!HMAC_Update(tls_ctx->hmac_ctx, ad_extra, sizeof(ad_extra))) {
+    return 0;
+  }
+  for (const CRYPTO_IOVEC &iovec : iovecs) {
+    if (!HMAC_Update(tls_ctx->hmac_ctx, iovec.in, iovec.len)) {
+      return 0;
+    }
+  }
   unsigned mac_len;
-  if (!HMAC_Init_ex(tls_ctx->hmac_ctx, nullptr, 0, nullptr, nullptr) ||
-      !HMAC_Update(tls_ctx->hmac_ctx, ad, ad_len) ||
-      !HMAC_Update(tls_ctx->hmac_ctx, ad_extra, sizeof(ad_extra)) ||
-      !HMAC_Update(tls_ctx->hmac_ctx, in, in_len) ||
-      !HMAC_Final(tls_ctx->hmac_ctx, mac, &mac_len)) {
+  if (!HMAC_Final(tls_ctx->hmac_ctx, mac, &mac_len)) {
     return 0;
   }
 
@@ -170,34 +184,59 @@
     return 0;
   }
 
-  // Encrypt the input.
-  size_t len;
-  if (!EVP_EncryptUpdate_ex(&tls_ctx->cipher_ctx, out, &len, in_len, in,
-                            in_len)) {
-    return 0;
-  }
-
-  unsigned block_size = EVP_CIPHER_CTX_block_size(&tls_ctx->cipher_ctx);
+  size_t block_size = EVP_CIPHER_CTX_block_size(&tls_ctx->cipher_ctx);
   assert(block_size == 8 /*3DES*/ || block_size == 16 /*AES*/);
 
-  // Feed the MAC into the cipher in two steps. First complete the final partial
-  // block from encrypting the input and split the result between |out| and
-  // |out_tag|. Then feed the rest.
+  // Encrypt the input.
+  size_t len = 0;
+  size_t tag_len = 0;
+  if (!bssl::iovec::ForEachBlockRange_Dynamic</*WriteOut=*/true>(
+          block_size, iovecs,
+          [&](const uint8_t *in, uint8_t *out, size_t chunk_len) {
+            // Complete block(s).
+            size_t out_len;
+            if (!EVP_EncryptUpdate_ex(&tls_ctx->cipher_ctx, out, &out_len,
+                                      chunk_len, in, chunk_len)) {
+              return false;
+            }
+            assert(out_len == chunk_len);
+            len += out_len;
+            return true;
+          },
+          [&](const uint8_t *in, uint8_t *out, size_t chunk_len) {
+            // Final chunk, possibly with a partial block.
+            size_t out_len;
+            if (!EVP_EncryptUpdate_ex(&tls_ctx->cipher_ctx, out, &out_len,
+                                      chunk_len, in, chunk_len)) {
+              return false;
+            }
+            len += out_len;
+            size_t remaining = chunk_len - out_len;
+            assert(remaining < block_size);
+            if (remaining == 0) {
+              return true;
+            }
 
-  const size_t early_mac_len = (block_size - in_len) & (block_size - 1);
-  if (early_mac_len != 0) {
-    assert(len + block_size - early_mac_len == in_len);
-    uint8_t buf[EVP_MAX_BLOCK_LENGTH];
-    size_t buf_len;
-    if (!EVP_EncryptUpdate_ex(&tls_ctx->cipher_ctx, buf, &buf_len, sizeof(buf),
-                              mac, early_mac_len)) {
-      return 0;
-    }
-    assert(buf_len == block_size);
-    OPENSSL_memcpy(out + len, buf, block_size - early_mac_len);
-    OPENSSL_memcpy(out_tag, buf + block_size - early_mac_len, early_mac_len);
+            // Feed the MAC into the cipher in two steps. First complete the
+            // final partial block from encrypting the input and split the
+            // result between |out| and |out_tag|. Then feed the rest.
+            const size_t early_mac_len = block_size - remaining;
+            assert(early_mac_len < block_size);
+            assert(len + block_size - early_mac_len == in_len);
+            uint8_t buf[EVP_MAX_BLOCK_LENGTH];
+            size_t buf_len;
+            if (!EVP_EncryptUpdate_ex(&tls_ctx->cipher_ctx, buf, &buf_len,
+                                      sizeof(buf), mac, early_mac_len)) {
+              return false;
+            }
+            assert(buf_len == block_size);
+            OPENSSL_memcpy(out + out_len, buf, remaining);
+            OPENSSL_memcpy(out_tag, buf + remaining, early_mac_len);
+            tag_len = early_mac_len;
+            return true;
+          })) {
+    return 0;
   }
-  size_t tag_len = early_mac_len;
 
   if (!EVP_EncryptUpdate_ex(&tls_ctx->cipher_ctx, out_tag + tag_len, &len,
                             max_out_tag_len - tag_len, mac + tag_len,
@@ -221,16 +260,17 @@
     return 0;
   }
   assert(len == 0);  // Padding is explicit.
-  assert(tag_len == aead_tls_tag_len(ctx, in_len, extra_in_len));
+  assert(tag_len == aead_tls_tag_len(ctx, in_len, 0));
 
   *out_tag_len = tag_len;
   return 1;
 }
 
-static int aead_tls_open(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
-                         size_t max_out_len, const uint8_t *nonce,
-                         size_t nonce_len, const uint8_t *in, size_t in_len,
-                         const uint8_t *ad, size_t ad_len) {
+static int aead_tls_openv(const EVP_AEAD_CTX *ctx,
+                          bssl::Span<const CRYPTO_IOVEC> iovecs,
+                          size_t *out_total_bytes, const uint8_t *nonce,
+                          size_t nonce_len,
+                          bssl::Span<const CRYPTO_IVEC> aadvecs) {
   AEAD_TLS_CTX *tls_ctx = (AEAD_TLS_CTX *)&ctx->state;
 
   if (tls_ctx->cipher_ctx.encrypt) {
@@ -239,23 +279,18 @@
     return 0;
   }
 
+  size_t in_len = bssl::iovec::TotalLength(iovecs);
   if (in_len < HMAC_size(tls_ctx->hmac_ctx)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
     return 0;
   }
 
-  if (max_out_len < in_len) {
-    // This requires that the caller provide space for the MAC, even though it
-    // will always be removed on return.
-    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
-    return 0;
-  }
-
   if (nonce_len != EVP_AEAD_nonce_length(ctx->aead)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INVALID_NONCE_SIZE);
     return 0;
   }
 
+  size_t ad_len = bssl::iovec::TotalLength(aadvecs);
   if (ad_len != 13 - 2 /* length bytes */) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INVALID_AD_SIZE);
     return 0;
@@ -271,61 +306,91 @@
 
   // Decrypt to get the plaintext + MAC + padding.
   size_t total = 0;
-  size_t len;
-  if (!EVP_DecryptUpdate_ex(&tls_ctx->cipher_ctx, out, &len, max_out_len, in,
-                            in_len)) {
-    return 0;
+  size_t block_size = EVP_CIPHER_CTX_block_size(&tls_ctx->cipher_ctx);
+  auto decrypt_update = [&](const uint8_t *in, uint8_t *out, size_t len) {
+    size_t out_len;
+    if (!EVP_DecryptUpdate_ex(&tls_ctx->cipher_ctx, out, &out_len, len, in,
+                              len)) {
+      return false;
+    }
+    CONSTTIME_SECRET(out, out_len);
+    if (out_len != len) {
+      // A byte sequence that was not a multiple of the block size was provided
+      // as ciphertext. This is generally invalid and thus should be rejected.
+      OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
+      return false;
+    }
+    total += len;
+    return true;
+  };
+  if (!bssl::iovec::ForEachBlockRange_Dynamic</*WriteOut=*/true>(
+          block_size, iovecs, decrypt_update, decrypt_update)) {
+    return false;
   }
-  total += len;
-  if (!EVP_DecryptFinal_ex2(&tls_ctx->cipher_ctx, out + total, &len,
-                            max_out_len - total)) {
-    return 0;
-  }
-  total += len;
   assert(total == in_len);
 
-  CONSTTIME_SECRET(out, total);
+  for (const CRYPTO_IOVEC &iovec : iovecs) {
+    CONSTTIME_SECRET(iovec.out, iovec.len);
+  }
+
+  const size_t mac_len = HMAC_size(tls_ctx->hmac_ctx);
+
+  // Split the decrypted record into |iovecs_without_trailer| and |trailer|,
+  // based on the public lower bound of where the plaintext ends. The plaintext
+  // is followed by |mac_len| and then at most 256 bytes of padding.
+  bssl::InplaceVector<CRYPTO_IOVEC, CRYPTO_IOVEC_MAX> iovecs_without_trailer;
+  iovecs_without_trailer.CopyFrom(iovecs);
+  uint8_t trailer_buf[EVP_MAX_MD_SIZE + 256];
+  const size_t trailer_len = std::min(in_len, mac_len + 256);
+  std::optional<bssl::Span<const uint8_t>> trailer =
+      bssl::iovec::GetAndRemoveOutSuffix(
+          bssl::Span(trailer_buf).first(trailer_len),
+          bssl::Span(iovecs_without_trailer));
+  BSSL_CHECK(trailer.has_value());
 
   // Remove CBC padding. Code from here on is timing-sensitive with respect to
   // |padding_ok| and |data_plus_mac_len| for CBC ciphers.
-  size_t data_plus_mac_len;
   crypto_word_t padding_ok;
-  if (!EVP_tls_cbc_remove_padding(
-          &padding_ok, &data_plus_mac_len, out, total,
-          EVP_CIPHER_CTX_block_size(&tls_ctx->cipher_ctx),
-          HMAC_size(tls_ctx->hmac_ctx))) {
+  size_t reduced_trailer;
+  if (!EVP_tls_cbc_remove_padding(&padding_ok, &reduced_trailer,
+                                  trailer->data(), trailer->size(), block_size,
+                                  mac_len)) {
     // Publicly invalid. This can be rejected in non-constant time.
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
     return 0;
   }
-  size_t data_len = data_plus_mac_len - HMAC_size(tls_ctx->hmac_ctx);
+  assert(reduced_trailer >= mac_len);
+  size_t data_plus_mac_len = total + reduced_trailer - trailer->size();
+  size_t data_len = data_plus_mac_len - mac_len;
+  size_t data_in_trailer_len = reduced_trailer - mac_len;
 
-  // At this point, if the padding is valid, the first |data_plus_mac_len| bytes
-  // after |out| are the plaintext and MAC. Otherwise, |data_plus_mac_len| is
-  // still large enough to extract a MAC, but it will be irrelevant.
+  // At this point, if the padding is valid, |trailer->first(reduced_trailer)|
+  // is the last bytes of plaintext and the MAC. Otherwise, it is still large
+  // enough to extract a MAC, but it will be irrelevant. Note that
+  // |reduced_trailer| is secret.
 
-  // To allow for CBC mode which changes cipher length, |ad| doesn't include the
-  // length for legacy ciphers.
-  uint8_t ad_fixed[13];
-  OPENSSL_memcpy(ad_fixed, ad, 11);
-  ad_fixed[11] = (uint8_t)(data_len >> 8);
-  ad_fixed[12] = (uint8_t)(data_len & 0xff);
-  ad_len += 2;
+  // To allow for CBC mode which changes cipher length, |ad_len| doesn't
+  // include the length for legacy ciphers.
+  uint8_t ad_extra[2];
+  ad_extra[0] = (uint8_t)(data_len >> 8);
+  ad_extra[1] = (uint8_t)(data_len & 0xff);
 
   // Compute the MAC and extract the one in the record.
   uint8_t mac[EVP_MAX_MD_SIZE];
-  size_t mac_len;
+  size_t got_mac_len;
   assert(EVP_tls_cbc_record_digest_supported(tls_ctx->hmac_ctx->md));
-  if (!EVP_tls_cbc_digest_record(tls_ctx->hmac_ctx->md, mac, &mac_len, ad_fixed,
-                                 out, data_len, total, tls_ctx->mac_key,
-                                 tls_ctx->mac_key_len)) {
+  if (!EVP_tls_cbc_digest_record(tls_ctx->hmac_ctx->md, mac, &got_mac_len,
+                                 ad_extra, aadvecs, iovecs_without_trailer,
+                                 *trailer, data_in_trailer_len,
+                                 tls_ctx->mac_key, tls_ctx->mac_key_len)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
     return 0;
   }
-  assert(mac_len == HMAC_size(tls_ctx->hmac_ctx));
+  assert(got_mac_len == mac_len);
 
   uint8_t record_mac[EVP_MAX_MD_SIZE];
-  EVP_tls_cbc_copy_mac(record_mac, mac_len, out, data_plus_mac_len, total);
+  EVP_tls_cbc_copy_mac(record_mac, mac_len, trailer->data(), reduced_trailer,
+                       trailer->size());
 
   // Perform the MAC check and the padding check in constant-time. It should be
   // safe to simply perform the padding check first, but it would not be under a
@@ -341,11 +406,13 @@
   }
 
   CONSTTIME_DECLASSIFY(&data_len, sizeof(data_len));
-  CONSTTIME_DECLASSIFY(out, data_len);
+  for (const CRYPTO_IOVEC &iovec : iovecs) {
+    CONSTTIME_SECRET(iovec.out, iovec.len);
+  }
 
   // End of timing-sensitive code.
 
-  *out_len = data_len;
+  *out_total_bytes = data_len;
   return 1;
 }
 
@@ -424,11 +491,11 @@
     nullptr,  // init
     aead_aes_128_cbc_sha1_tls_init,
     aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
+    nullptr,  // open
+    nullptr,  // seal_scatter,
     nullptr,  // open_gather
-    nullptr,  // openv
-    nullptr,  // sealv
+    aead_tls_openv,
+    aead_tls_sealv,
     nullptr,  // openv_detached
     nullptr,  // get_iv
     aead_tls_tag_len,
@@ -444,11 +511,11 @@
     nullptr,  // init
     aead_aes_128_cbc_sha1_tls_implicit_iv_init,
     aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
-    nullptr,          // open_gather
-    nullptr,          // openv
-    nullptr,          // sealv
+    nullptr,  // open
+    nullptr,  // seal_scatter,
+    nullptr,  // open_gather
+    aead_tls_openv,
+    aead_tls_sealv,
     nullptr,          // openv_detached
     aead_tls_get_iv,  // get_iv
     aead_tls_tag_len,
@@ -464,11 +531,11 @@
     nullptr,  // init
     aead_aes_128_cbc_sha256_tls_init,
     aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
+    nullptr,  // open
+    nullptr,  // seal_scatter,
     nullptr,  // open_gather
-    nullptr,  // openv
-    nullptr,  // sealv
+    aead_tls_openv,
+    aead_tls_sealv,
     nullptr,  // openv_detached
     nullptr,  // get_iv
     aead_tls_tag_len,
@@ -484,11 +551,11 @@
     nullptr,  // init
     aead_aes_256_cbc_sha1_tls_init,
     aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
+    nullptr,  // open
+    nullptr,  // seal_scatter,
     nullptr,  // open_gather
-    nullptr,  // openv
-    nullptr,  // sealv
+    aead_tls_openv,
+    aead_tls_sealv,
     nullptr,  // openv_detached
     nullptr,  // get_iv
     aead_tls_tag_len,
@@ -504,11 +571,11 @@
     nullptr,  // init
     aead_aes_256_cbc_sha1_tls_implicit_iv_init,
     aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
-    nullptr,          // open_gather
-    nullptr,          // openv
-    nullptr,          // sealv
+    nullptr,  // open
+    nullptr,  // seal_scatter,
+    nullptr,  // open_gather
+    aead_tls_openv,
+    aead_tls_sealv,
     nullptr,          // openv_detached
     aead_tls_get_iv,  // get_iv
     aead_tls_tag_len,
@@ -524,11 +591,11 @@
     nullptr,  // init
     aead_des_ede3_cbc_sha1_tls_init,
     aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
+    nullptr,  // open
+    nullptr,  // seal_scatter,
     nullptr,  // open_gather
-    nullptr,  // openv
-    nullptr,  // sealv
+    aead_tls_openv,
+    aead_tls_sealv,
     nullptr,  // openv_detached
     nullptr,  // get_iv
     aead_tls_tag_len,
@@ -544,11 +611,11 @@
     nullptr,  // init
     aead_des_ede3_cbc_sha1_tls_implicit_iv_init,
     aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
-    nullptr,          // open_gather
-    nullptr,          // openv
-    nullptr,          // sealv
+    nullptr,  // open
+    nullptr,  // seal_scatter,
+    nullptr,  // open_gather
+    aead_tls_openv,
+    aead_tls_sealv,
     nullptr,          // openv_detached
     aead_tls_get_iv,  // get_iv
     aead_tls_tag_len,
diff --git a/crypto/cipher/internal.h b/crypto/cipher/internal.h
index 1a517bb..ed09f4b 100644
--- a/crypto/cipher/internal.h
+++ b/crypto/cipher/internal.h
@@ -20,6 +20,7 @@
 
 #include <openssl/base.h>
 #include <openssl/sha.h>
+#include <openssl/span.h>
 
 #include "../internal.h"
 
@@ -83,23 +84,25 @@
 //     EVP_tls_cbc_record_digest_supported must return true for this hash.
 //   md_out: the digest output. At most EVP_MAX_MD_SIZE bytes will be written.
 //   md_out_size: the number of output bytes is written here.
-//   header: the 13-byte, TLS record header.
-//   data: the record data itself
-//   data_size: the secret, reported length of the data once the padding and MAC
-//     have been removed.
-//   data_plus_mac_plus_padding_size: the public length of the whole
-//     record, including padding.
+//   len_header: the two length bytes of the TLS record header.
+//   aadvecs: the 11-byte TLS record header as it was provided by the caller.
+//   iovecs_without_trailer: the section of the plaintext that does not include
+//     the trailer whose length is secret (typically the entire plaintext with
+//     an upper bound of padding and MAC size removed)
+//   trailer: the remaining portion of plaintext, MAC and padding
+//   data_in_trailer_size: the secret, reported length of the data portion in
+//     |trailer| once the padding and MAC have been removed.
 //
 // On entry: by virtue of having been through one of the remove_padding
 // functions, above, we know that data_plus_mac_size is large enough to contain
 // a padding byte and MAC. (If the padding was invalid, it might contain the
 // padding too. )
-int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out,
-                              size_t *md_out_size, const uint8_t header[13],
-                              const uint8_t *data, size_t data_size,
-                              size_t data_plus_mac_plus_padding_size,
-                              const uint8_t *mac_secret,
-                              unsigned mac_secret_length);
+int EVP_tls_cbc_digest_record(
+    const EVP_MD *md, uint8_t *md_out, size_t *md_out_size,
+    const uint8_t len_header[2], bssl::Span<const CRYPTO_IVEC> aadvecs,
+    bssl::Span<const CRYPTO_IOVEC> iovecs_without_trailer,
+    bssl::Span<const uint8_t> trailer, size_t data_in_trailer_size,
+    const uint8_t *mac_secret, unsigned mac_secret_length);
 
 #define POLY1305_TAG_LEN 16
 
diff --git a/crypto/cipher/tls_cbc.cc b/crypto/cipher/tls_cbc.cc
index 31d3218..9dc220f 100644
--- a/crypto/cipher/tls_cbc.cc
+++ b/crypto/cipher/tls_cbc.cc
@@ -18,10 +18,11 @@
 #include <openssl/digest.h>
 #include <openssl/nid.h>
 #include <openssl/sha.h>
+#include <openssl/span.h>
 
+#include "../fipsmodule/cipher/internal.h"
 #include "../internal.h"
 #include "internal.h"
-#include "../fipsmodule/cipher/internal.h"
 
 
 int EVP_tls_cbc_remove_padding(crypto_word_t *out_padding_ok, size_t *out_len,
@@ -335,12 +336,12 @@
   }
 }
 
-static int tls_cbc_digest_record_sha1(uint8_t *md_out, size_t *md_out_size,
-                                      const uint8_t header[13],
-                                      const uint8_t *data, size_t data_size,
-                                      size_t data_plus_mac_plus_padding_size,
-                                      const uint8_t *mac_secret,
-                                      unsigned mac_secret_length) {
+static int tls_cbc_digest_record_sha1(
+    uint8_t *md_out, size_t *md_out_size, const uint8_t len_header[2],
+    bssl::Span<const CRYPTO_IVEC> aadvecs,
+    bssl::Span<const CRYPTO_IOVEC> iovecs_without_trailer,
+    bssl::Span<const uint8_t> trailer, size_t data_in_trailer_size,
+    const uint8_t *mac_secret, unsigned mac_secret_length) {
   if (mac_secret_length > SHA_CBLOCK) {
     // HMAC pads small keys with zeros and hashes large keys down. This function
     // should never reach the large key case.
@@ -359,24 +360,22 @@
   SHA_CTX ctx;
   SHA1_Init(&ctx);
   SHA1_Update(&ctx, hmac_pad, SHA_CBLOCK);
-  SHA1_Update(&ctx, header, 13);
-
-  // There are at most 256 bytes of padding, so we can compute the public
-  // minimum length for |data_size|.
-  size_t min_data_size = 0;
-  if (data_plus_mac_plus_padding_size > SHA_DIGEST_LENGTH + 256) {
-    min_data_size = data_plus_mac_plus_padding_size - SHA_DIGEST_LENGTH - 256;
+  for (const CRYPTO_IVEC &aadvec : aadvecs) {
+    SHA1_Update(&ctx, aadvec.in, aadvec.len);
   }
+  SHA1_Update(&ctx, len_header, 2);
 
   // Hash the public minimum length directly. This reduces the number of blocks
   // that must be computed in constant-time.
-  SHA1_Update(&ctx, data, min_data_size);
+  for (const CRYPTO_IOVEC &iovec : iovecs_without_trailer) {
+    SHA1_Update(&ctx, iovec.out, iovec.len);
+  }
 
-  // Hash the remaining data without leaking |data_size|.
+  // Hash the remaining data without leaking |data_in_trailer_size|.
   uint8_t mac_out[SHA_DIGEST_LENGTH];
-  if (!EVP_sha1_final_with_secret_suffix(
-          &ctx, mac_out, data + min_data_size, data_size - min_data_size,
-          data_plus_mac_plus_padding_size - min_data_size)) {
+  if (!EVP_sha1_final_with_secret_suffix(&ctx, mac_out, trailer.data(),
+                                         data_in_trailer_size,
+                                         trailer.size())) {
     return 0;
   }
 
@@ -393,12 +392,12 @@
   return 1;
 }
 
-static int tls_cbc_digest_record_sha256(uint8_t *md_out, size_t *md_out_size,
-                                        const uint8_t header[13],
-                                        const uint8_t *data, size_t data_size,
-                                        size_t data_plus_mac_plus_padding_size,
-                                        const uint8_t *mac_secret,
-                                        unsigned mac_secret_length) {
+static int tls_cbc_digest_record_sha256(
+    uint8_t *md_out, size_t *md_out_size, const uint8_t len_header[2],
+    bssl::Span<const CRYPTO_IVEC> aadvecs,
+    bssl::Span<const CRYPTO_IOVEC> iovecs_without_trailer,
+    bssl::Span<const uint8_t> trailer, size_t data_in_trailer_size,
+    const uint8_t *mac_secret, unsigned mac_secret_length) {
   if (mac_secret_length > SHA256_CBLOCK) {
     // HMAC pads small keys with zeros and hashes large keys down. This function
     // should never reach the large key case.
@@ -417,25 +416,22 @@
   SHA256_CTX ctx;
   SHA256_Init(&ctx);
   SHA256_Update(&ctx, hmac_pad, SHA256_CBLOCK);
-  SHA256_Update(&ctx, header, 13);
-
-  // There are at most 256 bytes of padding, so we can compute the public
-  // minimum length for |data_size|.
-  size_t min_data_size = 0;
-  if (data_plus_mac_plus_padding_size > SHA256_DIGEST_LENGTH + 256) {
-    min_data_size =
-        data_plus_mac_plus_padding_size - SHA256_DIGEST_LENGTH - 256;
+  for (const CRYPTO_IVEC &aadvec : aadvecs) {
+    SHA256_Update(&ctx, aadvec.in, aadvec.len);
   }
+  SHA256_Update(&ctx, len_header, 2);
 
   // Hash the public minimum length directly. This reduces the number of blocks
   // that must be computed in constant-time.
-  SHA256_Update(&ctx, data, min_data_size);
+  for (const CRYPTO_IOVEC &iovec : iovecs_without_trailer) {
+    SHA256_Update(&ctx, iovec.out, iovec.len);
+  }
 
-  // Hash the remaining data without leaking |data_size|.
+  // Hash the remaining data without leaking |data_in_trailer_size|.
   uint8_t mac_out[SHA256_DIGEST_LENGTH];
-  if (!EVP_sha256_final_with_secret_suffix(
-          &ctx, mac_out, data + min_data_size, data_size - min_data_size,
-          data_plus_mac_plus_padding_size - min_data_size)) {
+  if (!EVP_sha256_final_with_secret_suffix(&ctx, mac_out, trailer.data(),
+                                           data_in_trailer_size,
+                                           trailer.size())) {
     return 0;
   }
 
@@ -452,22 +448,22 @@
   return 1;
 }
 
-int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out,
-                              size_t *md_out_size, const uint8_t header[13],
-                              const uint8_t *data, size_t data_size,
-                              size_t data_plus_mac_plus_padding_size,
-                              const uint8_t *mac_secret,
-                              unsigned mac_secret_length) {
+int EVP_tls_cbc_digest_record(
+    const EVP_MD *md, uint8_t *md_out, size_t *md_out_size,
+    const uint8_t len_header[2], bssl::Span<const CRYPTO_IVEC> aadvecs,
+    bssl::Span<const CRYPTO_IOVEC> iovecs_without_trailer,
+    bssl::Span<const uint8_t> trailer, size_t data_in_trailer_size,
+    const uint8_t *mac_secret, unsigned mac_secret_length) {
   switch (EVP_MD_type(md)) {
     case NID_sha1:
-      return tls_cbc_digest_record_sha1(
-          md_out, md_out_size, header, data, data_size,
-          data_plus_mac_plus_padding_size, mac_secret, mac_secret_length);
+      return tls_cbc_digest_record_sha1(  //
+          md_out, md_out_size, len_header, aadvecs, iovecs_without_trailer,
+          trailer, data_in_trailer_size, mac_secret, mac_secret_length);
 
     case NID_sha256:
-      return tls_cbc_digest_record_sha256(
-          md_out, md_out_size, header, data, data_size,
-          data_plus_mac_plus_padding_size, mac_secret, mac_secret_length);
+      return tls_cbc_digest_record_sha256(  //
+          md_out, md_out_size, len_header, aadvecs, iovecs_without_trailer,
+          trailer, data_in_trailer_size, mac_secret, mac_secret_length);
 
     default:
       // EVP_tls_cbc_record_digest_supported should have been called first to
diff --git a/crypto/internal.h b/crypto/internal.h
index 1a22a94..10a041e 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -472,8 +472,17 @@
 
 #else
 
-#define CONSTTIME_SECRET(ptr, len)
-#define CONSTTIME_DECLASSIFY(ptr, len)
+// Just disable unused warnings for those.
+#define CONSTTIME_SECRET(ptr, len) \
+  do {                             \
+    (void)(ptr);                   \
+    (void)(len);                   \
+  } while (false)
+#define CONSTTIME_DECLASSIFY(ptr, len) \
+  do {                                 \
+    (void)(ptr);                       \
+    (void)(len);                       \
+  } while (false)
 
 #endif  // BORINGSSL_CONSTANT_TIME_VALIDATION