// Copyright 2017 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <openssl/aead.h>

#include <assert.h>

#include <openssl/cipher.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/span.h>

#include "../fipsmodule/aes/internal.h"
#include "../fipsmodule/cipher/internal.h"
#include "../internal.h"


using namespace bssl;

#define EVP_AEAD_AES_GCM_SIV_NONCE_LEN 12
#define EVP_AEAD_AES_GCM_SIV_TAG_LEN 16

namespace {
void inc_counter(uint8_t tag[16], uint32_t by) {
  CRYPTO_store_u32_le(tag, CRYPTO_load_u32_le(tag) + by);
}

// TODO(davidben): AES-GCM-SIV assembly is not correct for Windows. It must save
// and restore xmm6 through xmm15.
#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) && \
    !defined(OPENSSL_WINDOWS)
#define AES_GCM_SIV_ASM

// Optimised AES-GCM-SIV

struct aead_aes_gcm_siv_asm_ctx {
  alignas(16) uint8_t key[16 * 15];
  int is_128_bit;
};

// The assembly code assumes 8-byte alignment of the EVP_AEAD_CTX's state, and
// aligns to 16 bytes itself.
static_assert(sizeof(((EVP_AEAD_CTX *)nullptr)->state) + 8 >=
                  sizeof(struct aead_aes_gcm_siv_asm_ctx),
              "AEAD state is too small");
static_assert(alignof(union evp_aead_ctx_st_state) >= 8,
              "AEAD state has insufficient alignment");

extern "C" {
// aes128gcmsiv_aes_ks writes an AES-128 key schedule for |key| to
// |out_expanded_key|.
extern void aes128gcmsiv_aes_ks(const uint8_t key[16],
                                uint8_t out_expanded_key[16 * 15]);

// aes256gcmsiv_aes_ks writes an AES-256 key schedule for |key| to
// |out_expanded_key|.
extern void aes256gcmsiv_aes_ks(const uint8_t key[32],
                                uint8_t out_expanded_key[16 * 15]);

// aesgcmsiv_polyval_horner updates the POLYVAL value in |in_out_poly| to
// include a number (|in_blocks|) of 16-byte blocks of data from |in|, given
// the POLYVAL key in |key|.
extern void aesgcmsiv_polyval_horner(const uint8_t in_out_poly[16],
                                     const uint8_t key[16], const uint8_t *in,
                                     size_t in_blocks);

// aesgcmsiv_htable_init writes powers 1..8 of |auth_key| to |out_htable|.
extern void aesgcmsiv_htable_init(uint8_t out_htable[16 * 8],
                                  const uint8_t auth_key[16]);

// aesgcmsiv_htable6_init writes powers 1..6 of |auth_key| to |out_htable|.
extern void aesgcmsiv_htable6_init(uint8_t out_htable[16 * 6],
                                   const uint8_t auth_key[16]);

// aesgcmsiv_htable_polyval updates the POLYVAL value in |in_out_poly| to
// include |in_len| bytes of data from |in|. (Where |in_len| must be a multiple
// of 16.) It uses the precomputed powers of the key given in |htable|.
extern void aesgcmsiv_htable_polyval(const uint8_t htable[16 * 8],
                                     const uint8_t *in, size_t in_len,
                                     uint8_t in_out_poly[16]);

// aes128gcmsiv_dec decrypts |in_len| & ~15 bytes from |out| and writes them to
// |in|. |in| and |out| may be equal, but must not otherwise alias.
//
// |in_out_calculated_tag_and_scratch|, on entry, must contain:
//    1. The current value of the calculated tag, which will be updated during
//       decryption and written back to the beginning of this buffer on exit.
//    2. The claimed tag, which is needed to derive counter values.
//
// While decrypting, the whole of |in_out_calculated_tag_and_scratch| may be
// used for other purposes. In order to decrypt and update the POLYVAL value, it
// uses the expanded key from |key| and the table of powers in |htable|.
extern void aes128gcmsiv_dec(const uint8_t *in, uint8_t *out,
                             uint8_t in_out_calculated_tag_and_scratch[16 * 8],
                             const uint8_t htable[16 * 6],
                             const struct aead_aes_gcm_siv_asm_ctx *key,
                             size_t in_len);

// aes256gcmsiv_dec acts like |aes128gcmsiv_dec|, but for AES-256.
extern void aes256gcmsiv_dec(const uint8_t *in, uint8_t *out,
                             uint8_t in_out_calculated_tag_and_scratch[16 * 8],
                             const uint8_t htable[16 * 6],
                             const struct aead_aes_gcm_siv_asm_ctx *key,
                             size_t in_len);

// aes128gcmsiv_kdf performs the AES-GCM-SIV KDF given the expanded key from
// |key_schedule| and the nonce in |nonce|. Note that, while only 12 bytes of
// the nonce are used, 16 bytes are read and so the value must be
// right-padded.
extern void aes128gcmsiv_kdf(const uint8_t nonce[16],
                             uint64_t out_key_material[8],
                             const uint8_t *key_schedule);

// aes256gcmsiv_kdf acts like |aes128gcmsiv_kdf|, but for AES-256.
extern void aes256gcmsiv_kdf(const uint8_t nonce[16],
                             uint64_t out_key_material[12],
                             const uint8_t *key_schedule);

// aes128gcmsiv_aes_ks_enc_x1 performs a key expansion of the AES-128 key in
// |key|, writes the expanded key to |out_expanded_key| and encrypts a single
// block from |in| to |out|.
extern void aes128gcmsiv_aes_ks_enc_x1(const uint8_t in[16], uint8_t out[16],
                                       uint8_t out_expanded_key[16 * 15],
                                       const uint64_t key[2]);

// aes256gcmsiv_aes_ks_enc_x1 acts like |aes128gcmsiv_aes_ks_enc_x1|, but for
// AES-256.
extern void aes256gcmsiv_aes_ks_enc_x1(const uint8_t in[16], uint8_t out[16],
                                       uint8_t out_expanded_key[16 * 15],
                                       const uint64_t key[4]);

// aes128gcmsiv_ecb_enc_block encrypts a single block from |in| to |out| using
// the expanded key in |expanded_key|.
extern void aes128gcmsiv_ecb_enc_block(
    const uint8_t in[16], uint8_t out[16],
    const struct aead_aes_gcm_siv_asm_ctx *expanded_key);

// aes256gcmsiv_ecb_enc_block acts like |aes128gcmsiv_ecb_enc_block|, but for
// AES-256.
extern void aes256gcmsiv_ecb_enc_block(
    const uint8_t in[16], uint8_t out[16],
    const struct aead_aes_gcm_siv_asm_ctx *expanded_key);

// aes128gcmsiv_enc_msg_x4 encrypts |in_len| bytes from |in| to |out| using the
// expanded key from |key|. (The value of |in_len| must be a multiple of 16.)
// The |in| and |out| buffers may be equal but must not otherwise overlap. The
// initial counter is constructed from the given |tag| as required by
// AES-GCM-SIV.
extern void aes128gcmsiv_enc_msg_x4(const uint8_t *in, uint8_t *out,
                                    const uint8_t *tag,
                                    const struct aead_aes_gcm_siv_asm_ctx *key,
                                    size_t in_len);

// aes256gcmsiv_enc_msg_x4 acts like |aes128gcmsiv_enc_msg_x4|, but for
// AES-256.
extern void aes256gcmsiv_enc_msg_x4(const uint8_t *in, uint8_t *out,
                                    const uint8_t *tag,
                                    const struct aead_aes_gcm_siv_asm_ctx *key,
                                    size_t in_len);

// aes128gcmsiv_enc_msg_x8 acts like |aes128gcmsiv_enc_msg_x4|, but is
// optimised for longer messages.
extern void aes128gcmsiv_enc_msg_x8(const uint8_t *in, uint8_t *out,
                                    const uint8_t *tag,
                                    const struct aead_aes_gcm_siv_asm_ctx *key,
                                    size_t in_len);

// aes256gcmsiv_enc_msg_x8 acts like |aes256gcmsiv_enc_msg_x4|, but is
// optimised for longer messages.
extern void aes256gcmsiv_enc_msg_x8(const uint8_t *in, uint8_t *out,
                                    const uint8_t *tag,
                                    const struct aead_aes_gcm_siv_asm_ctx *key,
                                    size_t in_len);
}

// asm_ctx_from_ctx returns a 16-byte aligned context pointer from |ctx|.
struct aead_aes_gcm_siv_asm_ctx *asm_ctx_from_ctx(const EVP_AEAD_CTX *ctx) {
  // ctx->state must already be 8-byte aligned. Thus, at most, we may need to
  // add eight to align it to 16 bytes.
  const uintptr_t offset = ((uintptr_t)&ctx->state) & 8;
  return (struct aead_aes_gcm_siv_asm_ctx *)(&ctx->state.opaque[offset]);
}

int aead_aes_gcm_siv_asm_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
                              size_t key_len, size_t tag_len) {
  const size_t key_bits = key_len * 8;

  if (key_bits != 128 && key_bits != 256) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH);
    return 0;  // EVP_AEAD_CTX_init should catch this.
  }

  if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) {
    tag_len = EVP_AEAD_AES_GCM_SIV_TAG_LEN;
  }

  if (tag_len != EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TAG_TOO_LARGE);
    return 0;
  }

  struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = asm_ctx_from_ctx(ctx);
  assert((((uintptr_t)gcm_siv_ctx) & 15) == 0);

  if (key_bits == 128) {
    aes128gcmsiv_aes_ks(key, &gcm_siv_ctx->key[0]);
    gcm_siv_ctx->is_128_bit = 1;
  } else {
    aes256gcmsiv_aes_ks(key, &gcm_siv_ctx->key[0]);
    gcm_siv_ctx->is_128_bit = 0;
  }

  ctx->tag_len = tag_len;

  return 1;
}

void aead_aes_gcm_siv_asm_cleanup(EVP_AEAD_CTX *ctx) {}

// gcm_siv_asm_polyval evaluates POLYVAL at |auth_key| on the given plaintext
// and AD. The result is written to |out_tag|.
void gcm_siv_asm_polyval(uint8_t out_tag[16], Span<const CRYPTO_IOVEC> iovecs,
                         Span<const CRYPTO_IVEC> aadvecs,
                         const uint8_t auth_key[16], const uint8_t nonce[12]) {
  OPENSSL_memset(out_tag, 0, 16);
  const size_t ad_len = bssl::iovec::TotalLength(aadvecs);
  const size_t ad_blocks = ad_len / 16;
  const size_t in_len = bssl::iovec::TotalLength(iovecs);
  const size_t in_blocks = in_len / 16;
  int htable_init = 0;
  alignas(16) uint8_t htable[16 * 8];

  if (ad_blocks > 8 || in_blocks > 8) {
    htable_init = 1;
    aesgcmsiv_htable_init(htable, auth_key);
  }

  auto f_whole = [&](const uint8_t *in, size_t len) {
    if (htable_init) {
      aesgcmsiv_htable_polyval(htable, in, len, out_tag);
    } else {
      aesgcmsiv_polyval_horner(out_tag, auth_key, in, len / AES_BLOCK_SIZE);
    }
    return true;
  };

  auto f_final = [&](const uint8_t *in, size_t len) {
    size_t len_whole = (len / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
    if (len_whole != 0) {
      f_whole(in, len_whole);
      in += len_whole;
      len -= len_whole;
    }
    if (len != 0) {
      uint8_t pad_buf[AES_BLOCK_SIZE];
      OPENSSL_memcpy(pad_buf, in, len);
      OPENSSL_memset(pad_buf + len, 0, AES_BLOCK_SIZE - len);
      aesgcmsiv_polyval_horner(out_tag, auth_key, pad_buf, 1);
    }
    return true;
  };

  bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/false>(aadvecs, f_whole,
                                                               f_final);

  bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/false>(iovecs, f_whole,
                                                               f_final);

  uint8_t length_block[16];
  CRYPTO_store_u64_le(length_block, ad_len * 8);
  CRYPTO_store_u64_le(length_block + 8, in_len * 8);
  aesgcmsiv_polyval_horner(out_tag, auth_key, length_block, 1);

  for (size_t i = 0; i < 12; i++) {
    out_tag[i] ^= nonce[i];
  }

  out_tag[15] &= 0x7f;
}

// aead_aes_gcm_siv_asm_crypt_last_block handles the encryption/decryption
// (same thing in CTR mode) of the final block of a plaintext/ciphertext. It
// writes |total_in_len| & 15 bytes to |out_last_block|, based on an initial
// counter derived from |tag|.
void aead_aes_gcm_siv_asm_crypt_last_block(
    int is_128_bit, uint8_t *out_last_block, const uint8_t *in_last_block,
    size_t total_in_len, const uint8_t tag[16],
    const struct aead_aes_gcm_siv_asm_ctx *enc_key_expanded) {
  alignas(16) uint8_t counter[16];
  OPENSSL_memcpy(&counter, tag, sizeof(counter));
  counter[15] |= 0x80;
  inc_counter(counter, static_cast<uint32_t>(total_in_len / 16));

  if (is_128_bit) {
    aes128gcmsiv_ecb_enc_block(counter, counter, enc_key_expanded);
  } else {
    aes256gcmsiv_ecb_enc_block(counter, counter, enc_key_expanded);
  }

  const size_t last_bytes_len = total_in_len & 15;
  for (size_t i = 0; i < last_bytes_len; i++) {
    out_last_block[i] = in_last_block[i] ^ counter[i];
  }
}

// aead_aes_gcm_siv_kdf calculates the record encryption and authentication
// keys given the |nonce|.
void aead_aes_gcm_siv_kdf(int is_128_bit,
                          const struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx,
                          uint64_t out_record_auth_key[2],
                          uint64_t out_record_enc_key[4],
                          const uint8_t nonce[12]) {
  alignas(16) uint8_t padded_nonce[16];
  OPENSSL_memcpy(padded_nonce, nonce, 12);

  alignas(16) uint64_t key_material[12];
  if (is_128_bit) {
    aes128gcmsiv_kdf(padded_nonce, key_material, &gcm_siv_ctx->key[0]);
    out_record_enc_key[0] = key_material[4];
    out_record_enc_key[1] = key_material[6];
  } else {
    aes256gcmsiv_kdf(padded_nonce, key_material, &gcm_siv_ctx->key[0]);
    out_record_enc_key[0] = key_material[4];
    out_record_enc_key[1] = key_material[6];
    out_record_enc_key[2] = key_material[8];
    out_record_enc_key[3] = key_material[10];
  }

  out_record_auth_key[0] = key_material[0];
  out_record_auth_key[1] = key_material[2];
}

int aead_aes_gcm_siv_asm_sealv(const EVP_AEAD_CTX *ctx,
                               Span<const CRYPTO_IOVEC> iovecs,
                               Span<uint8_t> out_tag, size_t *out_tag_len,
                               Span<const uint8_t> nonce,
                               Span<const CRYPTO_IVEC> aadvecs) {
  const struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = asm_ctx_from_ctx(ctx);
  const size_t in_len = bssl::iovec::TotalLength(iovecs);
  const uint64_t in_len_64 = in_len;
  const size_t ad_len = bssl::iovec::TotalLength(aadvecs);
  const uint64_t ad_len_64 = ad_len;

  if (in_len_64 > (UINT64_C(1) << 36) || ad_len_64 >= (UINT64_C(1) << 61)) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
    return 0;
  }

  if (out_tag.size() < EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
    return 0;
  }

  if (nonce.size() != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
    return 0;
  }

  alignas(16) uint64_t record_auth_key[2];
  alignas(16) uint64_t record_enc_key[4];
  aead_aes_gcm_siv_kdf(gcm_siv_ctx->is_128_bit, gcm_siv_ctx, record_auth_key,
                       record_enc_key, nonce.data());

  alignas(16) uint8_t tag[16] = {0};
  gcm_siv_asm_polyval(tag, iovecs, aadvecs, (const uint8_t *)record_auth_key,
                      nonce.data());

  struct aead_aes_gcm_siv_asm_ctx enc_key_expanded;

  if (gcm_siv_ctx->is_128_bit) {
    aes128gcmsiv_aes_ks_enc_x1(tag, tag, &enc_key_expanded.key[0],
                               record_enc_key);
    // Maintain a counter across calls to assembly. The functions internally set
    // the MSB of the last byte, so we only need to update the counter.
    alignas(16) uint8_t counter[16];
    OPENSSL_memcpy(counter, tag, 16);
    bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/true>(
        iovecs,
        [&](const uint8_t *in, uint8_t *out, size_t len) {
          if (len >= 128) {
            aes128gcmsiv_enc_msg_x8(in, out, counter, &enc_key_expanded, len);
          } else {
            aes128gcmsiv_enc_msg_x4(in, out, counter, &enc_key_expanded, len);
          }
          inc_counter(counter, static_cast<uint32_t>(len / AES_BLOCK_SIZE));
          return true;
        },
        [&](const uint8_t *in, uint8_t *out, size_t len) {
          size_t len_whole = (len / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
          if (len_whole != 0) {
            if (len_whole >= 128) {
              aes128gcmsiv_enc_msg_x8(in, out, counter, &enc_key_expanded,
                                      len_whole);
            } else {
              aes128gcmsiv_enc_msg_x4(in, out, counter, &enc_key_expanded,
                                      len_whole);
            }
            in += len_whole;
            out += len_whole;
            len -= len_whole;
          }
          if (len != 0) {
            aead_aes_gcm_siv_asm_crypt_last_block(
                /*is_128_bit=*/true, /*out_last_block=*/out,
                /*in_last_block=*/in, /*total_in_len=*/in_len, tag,
                &enc_key_expanded);
          }
          return true;
        });
  } else {
    aes256gcmsiv_aes_ks_enc_x1(tag, tag, &enc_key_expanded.key[0],
                               record_enc_key);
    alignas(16) uint8_t counter[16];
    OPENSSL_memcpy(counter, tag, 16);
    bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/true>(
        iovecs,
        [&](const uint8_t *in, uint8_t *out, size_t len) {
          if (len >= 128) {
            aes256gcmsiv_enc_msg_x8(in, out, counter, &enc_key_expanded, len);
          } else {
            aes256gcmsiv_enc_msg_x4(in, out, counter, &enc_key_expanded, len);
          }
          inc_counter(counter, static_cast<uint32_t>(len / AES_BLOCK_SIZE));
          return true;
        },
        [&](const uint8_t *in, uint8_t *out, size_t len) {
          size_t len_whole = (len / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
          if (len_whole != 0) {
            if (len_whole >= 128) {
              aes256gcmsiv_enc_msg_x8(in, out, counter, &enc_key_expanded,
                                      len_whole);
            } else {
              aes256gcmsiv_enc_msg_x4(in, out, counter, &enc_key_expanded,
                                      len_whole);
            }
            in += len_whole;
            out += len_whole;
            len -= len_whole;
          }
          if (len != 0) {
            aead_aes_gcm_siv_asm_crypt_last_block(
                /*is_128_bit=*/false, /*out_last_block=*/out,
                /*in_last_block=*/in, /*total_in_len=*/in_len, tag,
                &enc_key_expanded);
          }
          return true;
        });
  }

  CopyToPrefix(tag, out_tag);
  *out_tag_len = EVP_AEAD_AES_GCM_SIV_TAG_LEN;

  return 1;
}

int aead_aes_gcm_siv_asm_openv_detached(const EVP_AEAD_CTX *ctx,
                                        Span<const CRYPTO_IOVEC> iovecs,
                                        Span<const uint8_t> nonce,
                                        Span<const uint8_t> in_tag,
                                        Span<const CRYPTO_IVEC> aadvecs) {
  const size_t ad_len = bssl::iovec::TotalLength(aadvecs);
  const uint64_t ad_len_64 = ad_len;
  if (ad_len_64 >= (UINT64_C(1) << 61)) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
    return 0;
  }

  const size_t in_len = bssl::iovec::TotalLength(iovecs);
  const uint64_t in_len_64 = in_len;
  if (in_len_64 > UINT64_C(1) << 36 ||
      in_tag.size() != EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
    return 0;
  }

  if (nonce.size() != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
    return 0;
  }

  const struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = asm_ctx_from_ctx(ctx);

  alignas(16) uint64_t record_auth_key[2];
  alignas(16) uint64_t record_enc_key[4];
  aead_aes_gcm_siv_kdf(gcm_siv_ctx->is_128_bit, gcm_siv_ctx, record_auth_key,
                       record_enc_key, nonce.data());

  struct aead_aes_gcm_siv_asm_ctx expanded_key;
  if (gcm_siv_ctx->is_128_bit) {
    aes128gcmsiv_aes_ks((const uint8_t *)record_enc_key, &expanded_key.key[0]);
  } else {
    aes256gcmsiv_aes_ks((const uint8_t *)record_enc_key, &expanded_key.key[0]);
  }
  // calculated_tag is 16*8 bytes, rather than 16 bytes, because
  // aes[128|256]gcmsiv_dec uses the extra as scratch space.
  alignas(16) uint8_t calculated_tag[16 * 8] = {0};

  OPENSSL_memset(calculated_tag, 0, EVP_AEAD_AES_GCM_SIV_TAG_LEN);
  bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/false>(
      aadvecs,
      [&](const uint8_t *in, size_t len) {
        aesgcmsiv_polyval_horner(calculated_tag,
                                 (const uint8_t *)record_auth_key, in,
                                 len / AES_BLOCK_SIZE);
        return true;
      },
      [&](const uint8_t *in, size_t len) {
        size_t len_whole = (len / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
        if (len_whole != 0) {
          aesgcmsiv_polyval_horner(calculated_tag,
                                   (const uint8_t *)record_auth_key, in,
                                   len_whole / AES_BLOCK_SIZE);
          in += len_whole;
          len -= len_whole;
        }
        if (len != 0) {
          uint8_t pad_buf[AES_BLOCK_SIZE];
          OPENSSL_memcpy(pad_buf, in, len);
          OPENSSL_memset(pad_buf + len, 0, AES_BLOCK_SIZE - len);
          aesgcmsiv_polyval_horner(
              calculated_tag, (const uint8_t *)record_auth_key, pad_buf, 1);
        }
        return true;
      });

  alignas(16) uint8_t htable[16 * 6];
  aesgcmsiv_htable6_init(htable, (const uint8_t *)record_auth_key);

  size_t blocks = 0;
  bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/true>(
      iovecs,
      [&](const uint8_t *in, uint8_t *out, size_t len) {
        // aes[128|256]gcmsiv_dec needs access to the claimed tag. So it's put
        // into its scratch space. The function may clobber the claimed tag, so
        // this is copied before each call.
        OPENSSL_memcpy(calculated_tag + 16, in_tag.data(),
                       EVP_AEAD_AES_GCM_SIV_TAG_LEN);
        inc_counter(calculated_tag + 16, static_cast<uint32_t>(blocks));
        if (gcm_siv_ctx->is_128_bit) {
          aes128gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key, len);
        } else {
          aes256gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key, len);
        }
        blocks += len / AES_BLOCK_SIZE;
        return true;
      },
      [&](const uint8_t *in, uint8_t *out, size_t len) {
        size_t len_whole = (len / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
        if (len_whole != 0) {
          // aes[128|256]gcmsiv_dec needs access to the claimed tag. So it's put
          // into its scratch space. The function may clobber the claimed tag,
          // so this is copied before each call.
          OPENSSL_memcpy(calculated_tag + 16, in_tag.data(),
                         EVP_AEAD_AES_GCM_SIV_TAG_LEN);
          inc_counter(calculated_tag + 16, static_cast<uint32_t>(blocks));
          if (gcm_siv_ctx->is_128_bit) {
            aes128gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key,
                             len_whole);
          } else {
            aes256gcmsiv_dec(in, out, calculated_tag, htable, &expanded_key,
                             len_whole);
          }
          in += len_whole;
          out += len_whole;
          len -= len_whole;
        }
        if (len != 0) {
          aead_aes_gcm_siv_asm_crypt_last_block(
              gcm_siv_ctx->is_128_bit, /*out_last_block=*/out,
              /*in_last_block=*/in, /*total_in_len=*/in_len, in_tag.data(),
              &expanded_key);
          uint8_t pad_buf[AES_BLOCK_SIZE];
          OPENSSL_memcpy(pad_buf, out, len);
          OPENSSL_memset(pad_buf + len, 0, AES_BLOCK_SIZE - len);
          aesgcmsiv_polyval_horner(
              calculated_tag, (const uint8_t *)record_auth_key, pad_buf, 1);
        }
        return true;
      });

  uint8_t length_block[16];
  CRYPTO_store_u64_le(length_block, ad_len * 8);
  CRYPTO_store_u64_le(length_block + 8, in_len * 8);
  aesgcmsiv_polyval_horner(calculated_tag, (const uint8_t *)record_auth_key,
                           length_block, 1);

  for (size_t i = 0; i < 12; i++) {
    calculated_tag[i] ^= nonce[i];
  }

  calculated_tag[15] &= 0x7f;

  if (gcm_siv_ctx->is_128_bit) {
    aes128gcmsiv_ecb_enc_block(calculated_tag, calculated_tag, &expanded_key);
  } else {
    aes256gcmsiv_ecb_enc_block(calculated_tag, calculated_tag, &expanded_key);
  }

  if (CRYPTO_memcmp(calculated_tag, in_tag.data(),
                    EVP_AEAD_AES_GCM_SIV_TAG_LEN) != 0) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
    return 0;
  }

  return 1;
}

const EVP_AEAD aead_aes_128_gcm_siv_asm = {
    16,                              // key length
    EVP_AEAD_AES_GCM_SIV_NONCE_LEN,  // nonce length
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // overhead
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // max tag length

    aead_aes_gcm_siv_asm_init,
    nullptr /* init_with_direction */,
    aead_aes_gcm_siv_asm_cleanup,
    nullptr /* openv */,
    aead_aes_gcm_siv_asm_sealv,
    aead_aes_gcm_siv_asm_openv_detached,
    nullptr /* get_iv */,
    nullptr /* tag_len */,
};

const EVP_AEAD aead_aes_256_gcm_siv_asm = {
    32,                              // key length
    EVP_AEAD_AES_GCM_SIV_NONCE_LEN,  // nonce length
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // overhead
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // max tag length

    aead_aes_gcm_siv_asm_init,
    nullptr /* init_with_direction */,
    aead_aes_gcm_siv_asm_cleanup,
    nullptr /* openv */,
    aead_aes_gcm_siv_asm_sealv,
    aead_aes_gcm_siv_asm_openv_detached,
    nullptr /* get_iv */,
    nullptr /* tag_len */,
};

#endif  // X86_64 && !NO_ASM && !WINDOWS


struct aead_aes_gcm_siv_ctx {
  union {
    double align;
    AES_KEY ks;
  } ks;
  block128_f kgk_block;
  unsigned is_256 : 1;
};

static_assert(sizeof(((EVP_AEAD_CTX *)nullptr)->state) >=
                  sizeof(struct aead_aes_gcm_siv_ctx),
              "AEAD state is too small");
static_assert(alignof(union evp_aead_ctx_st_state) >=
                  alignof(struct aead_aes_gcm_siv_ctx),
              "AEAD state has insufficient alignment");

int aead_aes_gcm_siv_init(EVP_AEAD_CTX *ctx, const uint8_t *key, size_t key_len,
                          size_t tag_len) {
  const size_t key_bits = key_len * 8;

  if (key_bits != 128 && key_bits != 256) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH);
    return 0;  // EVP_AEAD_CTX_init should catch this.
  }

  if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) {
    tag_len = EVP_AEAD_AES_GCM_SIV_TAG_LEN;
  }
  if (tag_len != EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TAG_TOO_LARGE);
    return 0;
  }

  struct aead_aes_gcm_siv_ctx *gcm_siv_ctx =
      (struct aead_aes_gcm_siv_ctx *)&ctx->state;
  OPENSSL_memset(gcm_siv_ctx, 0, sizeof(struct aead_aes_gcm_siv_ctx));

  aes_ctr_set_key(&gcm_siv_ctx->ks.ks, nullptr, &gcm_siv_ctx->kgk_block, key,
                  key_len);
  gcm_siv_ctx->is_256 = (key_len == 32);
  ctx->tag_len = tag_len;

  return 1;
}

void aead_aes_gcm_siv_cleanup(EVP_AEAD_CTX *ctx) {}

// gcm_siv_crypt encrypts (or decrypts—it's the same thing) bytes from |in| to
// |out| in the |iovec|, using the block function |enc_block| with |key| in
// counter mode, starting at |initial_counter|. This differs from the
// traditional counter mode code in that the counter is handled little-endian,
// only the first four bytes are used and the GCM-SIV tweak to the final byte
// is applied. The |in| and |out| pointers may be equal but otherwise must not
// alias.
void gcm_siv_crypt(Span<const CRYPTO_IOVEC> iovecs,
                   const uint8_t initial_counter[AES_BLOCK_SIZE],
                   block128_f enc_block, const AES_KEY *key) {
  uint8_t counter[16];

  OPENSSL_memcpy(counter, initial_counter, AES_BLOCK_SIZE);
  counter[15] |= 0x80;

  auto crypt_bytes = [&](const uint8_t *in, uint8_t *out, size_t len) {
    for (size_t done = 0; done < len;) {
      uint8_t keystream[AES_BLOCK_SIZE];
      enc_block(counter, keystream, key);
      inc_counter(counter, 1);

      size_t todo = AES_BLOCK_SIZE;
      if (len - done < todo) {
        todo = len - done;
      }

      for (size_t i = 0; i < todo; i++) {
        out[done + i] = keystream[i] ^ in[done + i];
      }

      done += todo;
    }
    return true;
  };

  bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/true>(
      iovecs, crypt_bytes, crypt_bytes);
}


// POLYVAL.
//
// POLYVAL is a polynomial authenticator that operates over a field very
// similar to the one that GHASH uses. See
// https://www.rfc-editor.org/rfc/rfc8452.html#section-3.

// POLYVAL(H, X_1, ..., X_n) =
// ByteReverse(GHASH(mulX_GHASH(ByteReverse(H)), ByteReverse(X_1), ...,
// ByteReverse(X_n))).
//
// See https://www.rfc-editor.org/rfc/rfc8452.html#appendix-A.

struct polyval_ctx {
  uint8_t S[16];
  u128 Htable[16];
  gmult_func gmult;
  ghash_func ghash;
};

// byte_reverse reverses the order of the bytes in |b->c|.
void byte_reverse(uint8_t b[16]) {
  uint64_t hi = CRYPTO_load_u64_le(b);
  uint64_t lo = CRYPTO_load_u64_le(b + 8);
  CRYPTO_store_u64_le(b, CRYPTO_bswap8(lo));
  CRYPTO_store_u64_le(b + 8, CRYPTO_bswap8(hi));
}

// reverse_and_mulX_ghash interprets |b| as a reversed element of the GHASH
// field, multiplies that by 'x' and serialises the result back into |b|, but
// with GHASH's backwards bit ordering.
void reverse_and_mulX_ghash(uint8_t b[16]) {
  uint64_t hi = CRYPTO_load_u64_le(b);
  uint64_t lo = CRYPTO_load_u64_le(b + 8);
  const crypto_word_t carry = constant_time_eq_w(hi & 1, 1);
  hi >>= 1;
  hi |= lo << 63;
  lo >>= 1;
  lo ^= ((uint64_t)constant_time_select_w(carry, 0xe1, 0)) << 56;

  CRYPTO_store_u64_le(b, CRYPTO_bswap8(lo));
  CRYPTO_store_u64_le(b + 8, CRYPTO_bswap8(hi));
}

void crypto_polyval_init(struct polyval_ctx *ctx, const uint8_t key[16]) {
  alignas(8) uint8_t H[16];
  OPENSSL_memcpy(H, key, 16);
  reverse_and_mulX_ghash(H);

  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, ctx->Htable, H);
  OPENSSL_memset(&ctx->S, 0, sizeof(ctx->S));
}

void crypto_polyval_update_blocks(struct polyval_ctx *ctx, const uint8_t *in,
                                  size_t in_len) {
  assert((in_len & 15) == 0);
  alignas(8) uint8_t buf[32 * 16];

  while (in_len > 0) {
    size_t todo = in_len;
    if (todo > sizeof(buf)) {
      todo = sizeof(buf);
    }
    OPENSSL_memcpy(buf, in, todo);
    in += todo;
    in_len -= todo;

    size_t blocks = todo / 16;
    for (size_t i = 0; i < blocks; i++) {
      byte_reverse(buf + 16 * i);
    }

    ctx->ghash(ctx->S, ctx->Htable, buf, todo);
  }
}

void crypto_polyval_finish(const struct polyval_ctx *ctx, uint8_t out[16]) {
  OPENSSL_memcpy(out, &ctx->S, 16);
  byte_reverse(out);
}

// gcm_siv_polyval evaluates POLYVAL at |auth_key| on the given plaintext and
// AD. The result is written to |out_tag|.
void gcm_siv_polyval(uint8_t out_tag[16], Span<const CRYPTO_IOVEC> iovecs,
                     bool encrypt, Span<const CRYPTO_IVEC> aadvecs,
                     const uint8_t auth_key[16],
                     const uint8_t nonce[EVP_AEAD_AES_GCM_SIV_NONCE_LEN]) {
  struct polyval_ctx polyval_ctx;
  crypto_polyval_init(&polyval_ctx, auth_key);

  auto f_whole = [&](const uint8_t *in, size_t len) {
    crypto_polyval_update_blocks(&polyval_ctx, in, len);
    return true;
  };
  auto f_final = [&](const uint8_t *in, size_t len) {
    size_t len_whole = (len / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
    if (len_whole != 0) {
      crypto_polyval_update_blocks(&polyval_ctx, in, len_whole);
      in += len_whole;
      len -= len_whole;
    }
    if (len != 0) {
      uint8_t pad_buf[AES_BLOCK_SIZE];
      OPENSSL_memcpy(pad_buf, in, len);
      OPENSSL_memset(pad_buf + len, 0, AES_BLOCK_SIZE - len);
      crypto_polyval_update_blocks(&polyval_ctx, pad_buf, AES_BLOCK_SIZE);
    }
    return true;
  };

  bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/false>(aadvecs, f_whole,
                                                               f_final);

  if (encrypt) {
    bssl::iovec::ForEachBlockRange<AES_BLOCK_SIZE, /*WriteOut=*/false>(
        iovecs, f_whole, f_final);
  } else {
    bssl::iovec::ForEachOutBlockRange<AES_BLOCK_SIZE>(iovecs, f_whole, f_final);
  }

  uint8_t length_block[16];
  CRYPTO_store_u64_le(length_block,
                      ((uint64_t)bssl::iovec::TotalLength(aadvecs)) * 8);
  CRYPTO_store_u64_le(length_block + 8,
                      ((uint64_t)bssl::iovec::TotalLength(iovecs)) * 8);
  crypto_polyval_update_blocks(&polyval_ctx, length_block,
                               sizeof(length_block));

  crypto_polyval_finish(&polyval_ctx, out_tag);
  for (size_t i = 0; i < EVP_AEAD_AES_GCM_SIV_NONCE_LEN; i++) {
    out_tag[i] ^= nonce[i];
  }
  out_tag[15] &= 0x7f;
}

// gcm_siv_record_keys contains the keys used for a specific GCM-SIV record.
struct gcm_siv_record_keys {
  uint8_t auth_key[16];
  union {
    double align;
    AES_KEY ks;
  } enc_key;
  block128_f enc_block;
};

// gcm_siv_keys calculates the keys for a specific GCM-SIV record with the
// given nonce and writes them to |*out_keys|.
void gcm_siv_keys(const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx,
                  struct gcm_siv_record_keys *out_keys,
                  const uint8_t nonce[EVP_AEAD_AES_GCM_SIV_NONCE_LEN]) {
  const AES_KEY *const key = &gcm_siv_ctx->ks.ks;
  uint8_t key_material[(128 /* POLYVAL key */ + 256 /* max AES key */) / 8];
  const size_t blocks_needed = gcm_siv_ctx->is_256 ? 6 : 4;

  uint8_t counter[AES_BLOCK_SIZE];
  OPENSSL_memset(counter, 0, AES_BLOCK_SIZE - EVP_AEAD_AES_GCM_SIV_NONCE_LEN);
  OPENSSL_memcpy(counter + AES_BLOCK_SIZE - EVP_AEAD_AES_GCM_SIV_NONCE_LEN,
                 nonce, EVP_AEAD_AES_GCM_SIV_NONCE_LEN);
  for (size_t i = 0; i < blocks_needed; i++) {
    counter[0] = i;

    uint8_t ciphertext[AES_BLOCK_SIZE];
    gcm_siv_ctx->kgk_block(counter, ciphertext, key);
    OPENSSL_memcpy(&key_material[i * 8], ciphertext, 8);
  }

  OPENSSL_memcpy(out_keys->auth_key, key_material, 16);
  // Note the |ctr128_f| function uses a big-endian couner, while AES-GCM-SIV
  // uses a little-endian counter. We ignore the return value and only use
  // |block128_f|. This has a significant performance cost for the fallback
  // bitsliced AES implementations (bsaes and aes_nohw).
  //
  // We currently do not consider AES-GCM-SIV to be performance-sensitive on
  // client hardware. If this changes, we can write little-endian |ctr128_f|
  // functions.
  aes_ctr_set_key(&out_keys->enc_key.ks, nullptr, &out_keys->enc_block,
                  key_material + 16, gcm_siv_ctx->is_256 ? 32 : 16);
}

int aead_aes_gcm_siv_sealv(const EVP_AEAD_CTX *ctx,
                           Span<const CRYPTO_IOVEC> iovecs,
                           Span<uint8_t> out_tag, size_t *out_tag_len,
                           Span<const uint8_t> nonce,
                           Span<const CRYPTO_IVEC> aadvecs) {
  const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx =
      (struct aead_aes_gcm_siv_ctx *)&ctx->state;
  size_t in_len = bssl::iovec::TotalLength(iovecs);
  size_t ad_len = bssl::iovec::TotalLength(aadvecs);
  const uint64_t in_len_64 = in_len;
  const uint64_t ad_len_64 = ad_len;

  if (in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN < in_len ||
      in_len_64 > (UINT64_C(1) << 36) || ad_len_64 >= (UINT64_C(1) << 61)) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
    return 0;
  }

  if (out_tag.size() < EVP_AEAD_AES_GCM_SIV_TAG_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL);
    return 0;
  }

  if (nonce.size() != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
    return 0;
  }

  struct gcm_siv_record_keys keys;
  gcm_siv_keys(gcm_siv_ctx, &keys, nonce.data());

  uint8_t tag[EVP_AEAD_AES_GCM_SIV_TAG_LEN];
  gcm_siv_polyval(tag, iovecs, true, aadvecs, keys.auth_key, nonce.data());
  keys.enc_block(tag, tag, &keys.enc_key.ks);

  gcm_siv_crypt(iovecs, tag, keys.enc_block, &keys.enc_key.ks);

  CopyToPrefix(tag, out_tag);
  *out_tag_len = EVP_AEAD_AES_GCM_SIV_TAG_LEN;

  return 1;
}

int aead_aes_gcm_siv_openv_detached(const EVP_AEAD_CTX *ctx,
                                    Span<const CRYPTO_IOVEC> iovecs,
                                    Span<const uint8_t> nonce,
                                    Span<const uint8_t> in_tag,
                                    Span<const CRYPTO_IVEC> aadvecs) {
  const uint64_t ad_len_64 = bssl::iovec::TotalLength(aadvecs);
  if (ad_len_64 >= (UINT64_C(1) << 61)) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
    return 0;
  }

  const uint64_t in_len_64 = bssl::iovec::TotalLength(iovecs);
  if (in_tag.size() != EVP_AEAD_AES_GCM_SIV_TAG_LEN ||
      in_len_64 > (UINT64_C(1) << 36) + AES_BLOCK_SIZE) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
    return 0;
  }

  if (nonce.size() != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
    return 0;
  }

  const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx =
      (struct aead_aes_gcm_siv_ctx *)&ctx->state;

  struct gcm_siv_record_keys keys;
  gcm_siv_keys(gcm_siv_ctx, &keys, nonce.data());

  gcm_siv_crypt(iovecs, in_tag.data(), keys.enc_block, &keys.enc_key.ks);

  uint8_t expected_tag[EVP_AEAD_AES_GCM_SIV_TAG_LEN];
  gcm_siv_polyval(expected_tag, iovecs, false, aadvecs, keys.auth_key,
                  nonce.data());
  keys.enc_block(expected_tag, expected_tag, &keys.enc_key.ks);

  if (CRYPTO_memcmp(expected_tag, in_tag.data(), sizeof(expected_tag)) != 0) {
    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
    return 0;
  }

  return 1;
}

const EVP_AEAD aead_aes_128_gcm_siv = {
    16,                              // key length
    EVP_AEAD_AES_GCM_SIV_NONCE_LEN,  // nonce length
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // overhead
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // max tag length

    aead_aes_gcm_siv_init,
    nullptr /* init_with_direction */,
    aead_aes_gcm_siv_cleanup,
    nullptr /* openv */,
    aead_aes_gcm_siv_sealv,
    aead_aes_gcm_siv_openv_detached,
    nullptr /* get_iv */,
    nullptr /* tag_len */,
};

const EVP_AEAD aead_aes_256_gcm_siv = {
    32,                              // key length
    EVP_AEAD_AES_GCM_SIV_NONCE_LEN,  // nonce length
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // overhead
    EVP_AEAD_AES_GCM_SIV_TAG_LEN,    // max tag length

    aead_aes_gcm_siv_init,
    nullptr /* init_with_direction */,
    aead_aes_gcm_siv_cleanup,
    nullptr /* openv */,
    aead_aes_gcm_siv_sealv,
    aead_aes_gcm_siv_openv_detached,
    nullptr /* get_iv */,
    nullptr /* tag_len */,
};
}  // namespace

#if defined(AES_GCM_SIV_ASM)

const EVP_AEAD *EVP_aead_aes_128_gcm_siv() {
  if (CRYPTO_is_AVX_capable() && CRYPTO_is_AESNI_capable()) {
    return &aead_aes_128_gcm_siv_asm;
  }
  return &aead_aes_128_gcm_siv;
}

const EVP_AEAD *EVP_aead_aes_256_gcm_siv() {
  if (CRYPTO_is_AVX_capable() && CRYPTO_is_AESNI_capable()) {
    return &aead_aes_256_gcm_siv_asm;
  }
  return &aead_aes_256_gcm_siv;
}

#else

const EVP_AEAD *EVP_aead_aes_128_gcm_siv() { return &aead_aes_128_gcm_siv; }

const EVP_AEAD *EVP_aead_aes_256_gcm_siv() { return &aead_aes_256_gcm_siv; }

#endif  // AES_GCM_SIV_ASM
