EVP_AEAD: implement sealv/openv for AES-CCM.

Performance note: this seems to cost 46 to 106 CPU cycles per call with
LTO, and 48 to 105 CPU cycles without; no measurable per-byte overhead
though.

Bug: 383343306
Change-Id: Ia12fabd1a592f00583419a174709e5ff0d944c01
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/84588
Auto-Submit: Rudolf Polzer <rpolzer@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/cipher/e_aesccm.cc.inc b/crypto/fipsmodule/cipher/e_aesccm.cc.inc
index af5077e..7499610 100644
--- a/crypto/fipsmodule/cipher/e_aesccm.cc.inc
+++ b/crypto/fipsmodule/cipher/e_aesccm.cc.inc
@@ -19,6 +19,7 @@
 #include <openssl/cipher.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
+#include <openssl/span.h>
 
 #include "../aes/internal.h"
 #include "../delocate.h"
@@ -58,7 +59,7 @@
 static int ccm128_init_state(const struct ccm128_context *ctx,
                              struct ccm128_state *state, const AES_KEY *key,
                              const uint8_t *nonce, size_t nonce_len,
-                             const uint8_t *aad, size_t aad_len,
+                             bssl::Span<const CRYPTO_IVEC> aadvecs,
                              size_t plaintext_len) {
   const block128_f block = ctx->block;
   const unsigned M = ctx->M;
@@ -73,6 +74,7 @@
   // Assemble the first block for computing the MAC.
   OPENSSL_memset(state, 0, sizeof(*state));
   state->nonce[0] = (uint8_t)((L - 1) | ((M - 2) / 2) << 3);
+  size_t aad_len = bssl::iovec::TotalLength(aadvecs);
   if (aad_len != 0) {
     state->nonce[0] |= 0x40;  // Set AAD Flag
   }
@@ -114,16 +116,26 @@
       i = 10;
     }
 
-    do {
-      for (; i < 16 && aad_len != 0; i++) {
-        state->cmac[i] ^= *aad;
-        aad++;
-        aad_len--;
+    while (!aadvecs.empty() && aadvecs.front().len == 0) {
+      aadvecs = aadvecs.subspan(1);
+    }
+    size_t offset = 0;
+    while (!aadvecs.empty()) {
+      for (; i < 16 && !aadvecs.empty(); i++) {
+        state->cmac[i] ^= aadvecs.front().in[offset];
+        ++offset;
+        if (offset >= aadvecs.front().len) {
+          aadvecs = aadvecs.subspan(1);
+          while (!aadvecs.empty() && aadvecs.front().len == 0) {
+            aadvecs = aadvecs.subspan(1);
+          }
+          offset = 0;
+        }
       }
       (*block)(state->cmac, state->cmac, key);
       blocks++;
       i = 0;
-    } while (aad_len != 0);
+    }
   }
 
   // Per RFC 3610, section 2.6, the total number of block cipher operations done
@@ -145,7 +157,7 @@
 
 static int ccm128_encrypt(const struct ccm128_context *ctx,
                           struct ccm128_state *state, const AES_KEY *key,
-                          uint8_t *out, const uint8_t *in, size_t len) {
+                          bssl::Span<const CRYPTO_IOVEC> iovecs) {
   // The counter for encryption begins at one.
   for (unsigned i = 0; i < ctx->L; i++) {
     state->nonce[15 - i] = 0;
@@ -154,32 +166,44 @@
 
   uint8_t partial_buf[16];
   unsigned num = 0;
-  CRYPTO_ctr128_encrypt_ctr32(in, out, len, key, state->nonce, partial_buf,
-                              &num, ctx->ctr);
+  for (const CRYPTO_IOVEC &iovec : iovecs) {
+    CRYPTO_ctr128_encrypt_ctr32(iovec.in, iovec.out, iovec.len, key,
+                                state->nonce, partial_buf, &num, ctx->ctr);
+  }
   return 1;
 }
 
 static int ccm128_compute_mac(const struct ccm128_context *ctx,
                               struct ccm128_state *state, const AES_KEY *key,
                               uint8_t *out_tag, size_t tag_len,
-                              const uint8_t *in, size_t len) {
+                              bssl::Span<const CRYPTO_IOVEC> iovecs,
+                              bool encrypt) {
   block128_f block = ctx->block;
   if (tag_len != ctx->M) {
     return 0;
   }
 
-  // Incorporate |in| into the MAC.
-  while (len >= 16) {
-    CRYPTO_xor16(state->cmac, state->cmac, in);
-    (*block)(state->cmac, state->cmac, key);
-    in += 16;
-    len -= 16;
-  }
-  if (len > 0) {
-    for (size_t i = 0; i < len; i++) {
-      state->cmac[i] ^= in[i];
+  auto update_mac = [&](const uint8_t *in, size_t len) {
+    while (len >= 16) {
+      CRYPTO_xor16(state->cmac, state->cmac, in);
+      (*block)(state->cmac, state->cmac, key);
+      in += 16;
+      len -= 16;
     }
-    (*block)(state->cmac, state->cmac, key);
+    if (len != 0) {
+      for (size_t i = 0; i < len; i++) {
+        state->cmac[i] ^= in[i];
+      }
+      (*block)(state->cmac, state->cmac, key);
+    }
+    return true;
+  };
+
+  // Incorporate the plaintext into the MAC.
+  if (encrypt) {
+    bssl::iovec::ForEachBlockRange<16>(iovecs, update_mac, update_mac);
+  } else {
+    bssl::iovec::ForEachOutBlockRange<16>(iovecs, update_mac, update_mac);
   }
 
   // Encrypt the MAC with counter zero.
@@ -195,29 +219,31 @@
 }
 
 static int CRYPTO_ccm128_encrypt(const struct ccm128_context *ctx,
-                                 const AES_KEY *key, uint8_t *out,
+                                 const AES_KEY *key,
+                                 bssl::Span<const CRYPTO_IOVEC> iovecs,
                                  uint8_t *out_tag, size_t tag_len,
                                  const uint8_t *nonce, size_t nonce_len,
-                                 const uint8_t *in, size_t len,
-                                 const uint8_t *aad, size_t aad_len) {
+                                 bssl::Span<const CRYPTO_IVEC> aadvecs) {
   struct ccm128_state state;
-  return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aad, aad_len,
-                           len) &&
-         ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, in, len) &&
-         ccm128_encrypt(ctx, &state, key, out, in, len);
+  return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aadvecs,
+                           bssl::iovec::TotalLength(iovecs)) &&
+         ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, iovecs,
+                            /*encrypt=*/true) &&
+         ccm128_encrypt(ctx, &state, key, iovecs);
 }
 
 static int CRYPTO_ccm128_decrypt(const struct ccm128_context *ctx,
-                                 const AES_KEY *key, uint8_t *out,
+                                 const AES_KEY *key,
+                                 bssl::Span<const CRYPTO_IOVEC> iovecs,
                                  uint8_t *out_tag, size_t tag_len,
                                  const uint8_t *nonce, size_t nonce_len,
-                                 const uint8_t *in, size_t len,
-                                 const uint8_t *aad, size_t aad_len) {
+                                 bssl::Span<const CRYPTO_IVEC> aadvecs) {
   struct ccm128_state state;
-  return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aad, aad_len,
-                           len) &&
-         ccm128_encrypt(ctx, &state, key, out, in, len) &&
-         ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, out, len);
+  return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aadvecs,
+                           bssl::iovec::TotalLength(iovecs)) &&
+         ccm128_encrypt(ctx, &state, key, iovecs) &&
+         ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, iovecs,
+                            /*encrypt=*/false);
 }
 
 #define EVP_AEAD_AES_CCM_MAX_TAG_LEN 16
@@ -276,15 +302,17 @@
 
 static void aead_aes_ccm_cleanup(EVP_AEAD_CTX *ctx) {}
 
-static int aead_aes_ccm_seal_scatter(
-    const EVP_AEAD_CTX *ctx, uint8_t *out, uint8_t *out_tag,
-    size_t *out_tag_len, size_t max_out_tag_len, const uint8_t *nonce,
-    size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in,
-    size_t extra_in_len, const uint8_t *ad, size_t ad_len) {
+static int aead_aes_ccm_sealv(const EVP_AEAD_CTX *ctx,
+                              bssl::Span<const CRYPTO_IOVEC> iovecs,
+                              uint8_t *out_tag, size_t *out_tag_len,
+                              size_t max_out_tag_len, const uint8_t *nonce,
+                              size_t nonce_len,
+                              bssl::Span<const CRYPTO_IVEC> aadvecs) {
   const struct aead_aes_ccm_ctx *ccm_ctx =
       (struct aead_aes_ccm_ctx *)&ctx->state;
 
-  if (in_len > CRYPTO_ccm128_max_input(&ccm_ctx->ccm)) {
+  if (bssl::iovec::TotalLength(iovecs) >
+      CRYPTO_ccm128_max_input(&ccm_ctx->ccm)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
     return 0;
   }
@@ -299,9 +327,8 @@
     return 0;
   }
 
-  if (!CRYPTO_ccm128_encrypt(&ccm_ctx->ccm, &ccm_ctx->ks.ks, out, out_tag,
-                             ctx->tag_len, nonce, nonce_len, in, in_len, ad,
-                             ad_len)) {
+  if (!CRYPTO_ccm128_encrypt(&ccm_ctx->ccm, &ccm_ctx->ks.ks, iovecs, out_tag,
+                             ctx->tag_len, nonce, nonce_len, aadvecs)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
     return 0;
   }
@@ -311,15 +338,16 @@
   return 1;
 }
 
-static int aead_aes_ccm_open_gather(const EVP_AEAD_CTX *ctx, uint8_t *out,
-                                    const uint8_t *nonce, size_t nonce_len,
-                                    const uint8_t *in, size_t in_len,
-                                    const uint8_t *in_tag, size_t in_tag_len,
-                                    const uint8_t *ad, size_t ad_len) {
+static int aead_aes_ccm_openv_detached(const EVP_AEAD_CTX *ctx,
+                                       bssl::Span<const CRYPTO_IOVEC> iovecs,
+                                       const uint8_t *nonce, size_t nonce_len,
+                                       const uint8_t *in_tag, size_t in_tag_len,
+                                       bssl::Span<const CRYPTO_IVEC> aadvecs) {
   const struct aead_aes_ccm_ctx *ccm_ctx =
       (struct aead_aes_ccm_ctx *)&ctx->state;
 
-  if (in_len > CRYPTO_ccm128_max_input(&ccm_ctx->ccm)) {
+  if (bssl::iovec::TotalLength(iovecs) >
+      CRYPTO_ccm128_max_input(&ccm_ctx->ccm)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
     return 0;
   }
@@ -336,9 +364,8 @@
 
   uint8_t tag[EVP_AEAD_AES_CCM_MAX_TAG_LEN];
   assert(ctx->tag_len <= EVP_AEAD_AES_CCM_MAX_TAG_LEN);
-  if (!CRYPTO_ccm128_decrypt(&ccm_ctx->ccm, &ccm_ctx->ks.ks, out, tag,
-                             ctx->tag_len, nonce, nonce_len, in, in_len, ad,
-                             ad_len)) {
+  if (!CRYPTO_ccm128_decrypt(&ccm_ctx->ccm, &ccm_ctx->ks.ks, iovecs, tag,
+                             ctx->tag_len, nonce, nonce_len, aadvecs)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
     return 0;
   }
@@ -367,8 +394,8 @@
 
   out->init = aead_aes_ccm_bluetooth_init;
   out->cleanup = aead_aes_ccm_cleanup;
-  out->seal_scatter = aead_aes_ccm_seal_scatter;
-  out->open_gather = aead_aes_ccm_open_gather;
+  out->sealv = aead_aes_ccm_sealv;
+  out->openv_detached = aead_aes_ccm_openv_detached;
 }
 
 static int aead_aes_ccm_bluetooth_8_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
@@ -386,8 +413,8 @@
 
   out->init = aead_aes_ccm_bluetooth_8_init;
   out->cleanup = aead_aes_ccm_cleanup;
-  out->seal_scatter = aead_aes_ccm_seal_scatter;
-  out->open_gather = aead_aes_ccm_open_gather;
+  out->sealv = aead_aes_ccm_sealv;
+  out->openv_detached = aead_aes_ccm_openv_detached;
 }
 
 static int aead_aes_ccm_matter_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
@@ -405,6 +432,6 @@
 
   out->init = aead_aes_ccm_matter_init;
   out->cleanup = aead_aes_ccm_cleanup;
-  out->seal_scatter = aead_aes_ccm_seal_scatter;
-  out->open_gather = aead_aes_ccm_open_gather;
+  out->sealv = aead_aes_ccm_sealv;
+  out->openv_detached = aead_aes_ccm_openv_detached;
 }