mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-24 17:44:17 +01:00
47d767dab5
gcm_*_aesni() are used when the AVX512 implementation is not available.
Fix two bugs which manifest when handling operations spanning multiple
segments:
- Avoid underflow when the length of the input is smaller than the
residual.
- In gcm_decrypt_aesni(), ensure that we begin the operation at the
right offset into the input and output buffers.
Reviewed by: jhb
Fixes: 9b1d87286c
("ossl: Add a fallback AES-GCM implementation using AES-NI")
MFC after: 3 days
Differential Revision: https://reviews.freebsd.org/D42838
703 lines
16 KiB
C
703 lines
16 KiB
C
/*
|
|
* Copyright 2010-2022 The OpenSSL Project Authors. All Rights Reserved.
|
|
* Copyright (c) 2021, Intel Corporation. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
|
* this file except in compliance with the License. You can obtain a copy
|
|
* in the file LICENSE in the source distribution or at
|
|
* https://www.openssl.org/source/license.html
|
|
*/
|
|
|
|
/*
|
|
* This file contains 2 AES-GCM wrapper implementations from OpenSSL, using
|
|
* AES-NI and VAES extensions respectively. These were ported from
|
|
* cipher_aes_gcm_hw_aesni.inc and cipher_aes_gcm_hw_vaes_avx512.inc. The
|
|
* AES-NI implementation makes use of a generic C implementation for partial
|
|
* blocks, ported from gcm128.c with OPENSSL_SMALL_FOOTPRINT defined.
|
|
*/
|
|
|
|
#include <sys/endian.h>
|
|
#include <sys/systm.h>
|
|
|
|
#include <crypto/openssl/ossl.h>
|
|
#include <crypto/openssl/ossl_aes_gcm.h>
|
|
#include <crypto/openssl/ossl_cipher.h>
|
|
|
|
#include <opencrypto/cryptodev.h>
|
|
|
|
_Static_assert(
|
|
sizeof(struct ossl_gcm_context) <= sizeof(struct ossl_cipher_context),
|
|
"ossl_gcm_context too large");
|
|
|
|
void aesni_set_encrypt_key(const void *key, int bits, void *ctx);
|
|
|
|
static void
|
|
gcm_init(struct ossl_gcm_context *ctx, const void *key, size_t keylen)
|
|
{
|
|
KASSERT(keylen == 128 || keylen == 192 || keylen == 256,
|
|
("%s: invalid key length %zu", __func__, keylen));
|
|
|
|
memset(&ctx->gcm, 0, sizeof(ctx->gcm));
|
|
memset(&ctx->aes_ks, 0, sizeof(ctx->aes_ks));
|
|
aesni_set_encrypt_key(key, keylen, &ctx->aes_ks);
|
|
ctx->ops->init(ctx, key, keylen);
|
|
}
|
|
|
|
static void
|
|
gcm_tag(struct ossl_gcm_context *ctx, unsigned char *tag, size_t len)
|
|
{
|
|
(void)ctx->ops->finish(ctx, NULL, 0);
|
|
memcpy(tag, ctx->gcm.Xi.c, len);
|
|
}
|
|
|
|
void ossl_gcm_gmult_avx512(uint64_t Xi[2], void *gcm128ctx);
|
|
void ossl_aes_gcm_init_avx512(const void *ks, void *gcm128ctx);
|
|
void ossl_aes_gcm_setiv_avx512(const void *ks, void *gcm128ctx,
|
|
const unsigned char *iv, size_t ivlen);
|
|
void ossl_aes_gcm_update_aad_avx512(void *gcm128ctx, const unsigned char *aad,
|
|
size_t len);
|
|
void ossl_aes_gcm_encrypt_avx512(const void *ks, void *gcm128ctx,
|
|
unsigned int *pblocklen, const unsigned char *in, size_t len,
|
|
unsigned char *out);
|
|
void ossl_aes_gcm_decrypt_avx512(const void *ks, void *gcm128ctx,
|
|
unsigned int *pblocklen, const unsigned char *in, size_t len,
|
|
unsigned char *out);
|
|
void ossl_aes_gcm_finalize_avx512(void *gcm128ctx, unsigned int pblocklen);
|
|
|
|
static void
|
|
gcm_init_avx512(struct ossl_gcm_context *ctx, const void *key, size_t keylen)
|
|
{
|
|
ossl_aes_gcm_init_avx512(&ctx->aes_ks, &ctx->gcm);
|
|
}
|
|
|
|
static void
|
|
gcm_setiv_avx512(struct ossl_gcm_context *ctx, const unsigned char *iv,
|
|
size_t len)
|
|
{
|
|
KASSERT(len == AES_GCM_IV_LEN,
|
|
("%s: invalid IV length %zu", __func__, len));
|
|
|
|
ctx->gcm.Yi.u[0] = 0; /* Current counter */
|
|
ctx->gcm.Yi.u[1] = 0;
|
|
ctx->gcm.Xi.u[0] = 0; /* AAD hash */
|
|
ctx->gcm.Xi.u[1] = 0;
|
|
ctx->gcm.len.u[0] = 0; /* AAD length */
|
|
ctx->gcm.len.u[1] = 0; /* Message length */
|
|
ctx->gcm.ares = 0;
|
|
ctx->gcm.mres = 0;
|
|
|
|
ossl_aes_gcm_setiv_avx512(&ctx->aes_ks, ctx, iv, len);
|
|
}
|
|
|
|
static int
|
|
gcm_aad_avx512(struct ossl_gcm_context *ctx, const unsigned char *aad,
|
|
size_t len)
|
|
{
|
|
uint64_t alen = ctx->gcm.len.u[0];
|
|
size_t lenblks;
|
|
unsigned int ares;
|
|
|
|
/* Bad sequence: call of AAD update after message processing */
|
|
if (ctx->gcm.len.u[1])
|
|
return -2;
|
|
|
|
alen += len;
|
|
/* AAD is limited by 2^64 bits, thus 2^61 bytes */
|
|
if (alen > (1ull << 61) || (sizeof(len) == 8 && alen < len))
|
|
return -1;
|
|
ctx->gcm.len.u[0] = alen;
|
|
|
|
ares = ctx->gcm.ares;
|
|
/* Partial AAD block left from previous AAD update calls */
|
|
if (ares > 0) {
|
|
/*
|
|
* Fill partial block buffer till full block
|
|
* (note, the hash is stored reflected)
|
|
*/
|
|
while (ares > 0 && len > 0) {
|
|
ctx->gcm.Xi.c[15 - ares] ^= *(aad++);
|
|
--len;
|
|
ares = (ares + 1) % AES_BLOCK_LEN;
|
|
}
|
|
/* Full block gathered */
|
|
if (ares == 0) {
|
|
ossl_gcm_gmult_avx512(ctx->gcm.Xi.u, ctx);
|
|
} else { /* no more AAD */
|
|
ctx->gcm.ares = ares;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Bulk AAD processing */
|
|
lenblks = len & ((size_t)(-AES_BLOCK_LEN));
|
|
if (lenblks > 0) {
|
|
ossl_aes_gcm_update_aad_avx512(ctx, aad, lenblks);
|
|
aad += lenblks;
|
|
len -= lenblks;
|
|
}
|
|
|
|
/* Add remaining AAD to the hash (note, the hash is stored reflected) */
|
|
if (len > 0) {
|
|
ares = (unsigned int)len;
|
|
for (size_t i = 0; i < len; ++i)
|
|
ctx->gcm.Xi.c[15 - i] ^= aad[i];
|
|
}
|
|
|
|
ctx->gcm.ares = ares;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_gcm_encrypt_avx512(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len, bool encrypt)
|
|
{
|
|
uint64_t mlen = ctx->gcm.len.u[1];
|
|
|
|
mlen += len;
|
|
if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len))
|
|
return -1;
|
|
|
|
ctx->gcm.len.u[1] = mlen;
|
|
|
|
/* Finalize GHASH(AAD) if AAD partial blocks left unprocessed */
|
|
if (ctx->gcm.ares > 0) {
|
|
ossl_gcm_gmult_avx512(ctx->gcm.Xi.u, ctx);
|
|
ctx->gcm.ares = 0;
|
|
}
|
|
|
|
if (encrypt) {
|
|
ossl_aes_gcm_encrypt_avx512(&ctx->aes_ks, ctx, &ctx->gcm.mres,
|
|
in, len, out);
|
|
} else {
|
|
ossl_aes_gcm_decrypt_avx512(&ctx->aes_ks, ctx, &ctx->gcm.mres,
|
|
in, len, out);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_encrypt_avx512(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
return _gcm_encrypt_avx512(ctx, in, out, len, true);
|
|
}
|
|
|
|
static int
|
|
gcm_decrypt_avx512(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
return _gcm_encrypt_avx512(ctx, in, out, len, false);
|
|
}
|
|
|
|
static int
|
|
gcm_finish_avx512(struct ossl_gcm_context *ctx, const unsigned char *tag,
|
|
size_t len)
|
|
{
|
|
unsigned int *res = &ctx->gcm.mres;
|
|
|
|
/* Finalize AAD processing */
|
|
if (ctx->gcm.ares > 0)
|
|
res = &ctx->gcm.ares;
|
|
|
|
ossl_aes_gcm_finalize_avx512(ctx, *res);
|
|
|
|
ctx->gcm.ares = ctx->gcm.mres = 0;
|
|
|
|
if (tag != NULL)
|
|
return timingsafe_bcmp(ctx->gcm.Xi.c, tag, len);
|
|
return 0;
|
|
}
|
|
|
|
static const struct ossl_aes_gcm_ops gcm_ops_avx512 = {
|
|
.init = gcm_init_avx512,
|
|
.setiv = gcm_setiv_avx512,
|
|
.aad = gcm_aad_avx512,
|
|
.encrypt = gcm_encrypt_avx512,
|
|
.decrypt = gcm_decrypt_avx512,
|
|
.finish = gcm_finish_avx512,
|
|
.tag = gcm_tag,
|
|
};
|
|
|
|
size_t aesni_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len,
|
|
const void *key, unsigned char ivec[16], uint64_t *Xi);
|
|
size_t aesni_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len,
|
|
const void *key, unsigned char ivec[16], uint64_t *Xi);
|
|
void aesni_encrypt(const unsigned char *in, unsigned char *out, void *ks);
|
|
void aesni_ctr32_encrypt_blocks(const unsigned char *in, unsigned char *out,
|
|
size_t blocks, void *ks, const unsigned char *iv);
|
|
|
|
void gcm_init_avx(__uint128_t Htable[16], uint64_t Xi[2]);
|
|
void gcm_gmult_avx(uint64_t Xi[2], const __uint128_t Htable[16]);
|
|
void gcm_ghash_avx(uint64_t Xi[2], const __uint128_t Htable[16], const void *in,
|
|
size_t len);
|
|
|
|
static void
|
|
gcm_init_aesni(struct ossl_gcm_context *ctx, const void *key, size_t keylen)
|
|
{
|
|
aesni_encrypt(ctx->gcm.H.c, ctx->gcm.H.c, &ctx->aes_ks);
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.H.u[0] = bswap64(ctx->gcm.H.u[0]);
|
|
ctx->gcm.H.u[1] = bswap64(ctx->gcm.H.u[1]);
|
|
#endif
|
|
|
|
gcm_init_avx(ctx->gcm.Htable, ctx->gcm.H.u);
|
|
}
|
|
|
|
static void
|
|
gcm_setiv_aesni(struct ossl_gcm_context *ctx, const unsigned char *iv,
|
|
size_t len)
|
|
{
|
|
uint32_t ctr;
|
|
|
|
KASSERT(len == AES_GCM_IV_LEN,
|
|
("%s: invalid IV length %zu", __func__, len));
|
|
|
|
ctx->gcm.len.u[0] = 0;
|
|
ctx->gcm.len.u[1] = 0;
|
|
ctx->gcm.ares = ctx->gcm.mres = 0;
|
|
|
|
memcpy(ctx->gcm.Yi.c, iv, len);
|
|
ctx->gcm.Yi.c[12] = 0;
|
|
ctx->gcm.Yi.c[13] = 0;
|
|
ctx->gcm.Yi.c[14] = 0;
|
|
ctx->gcm.Yi.c[15] = 1;
|
|
ctr = 1;
|
|
|
|
ctx->gcm.Xi.u[0] = 0;
|
|
ctx->gcm.Xi.u[1] = 0;
|
|
|
|
aesni_encrypt(ctx->gcm.Yi.c, ctx->gcm.EK0.c, &ctx->aes_ks);
|
|
ctr++;
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.Yi.d[3] = bswap32(ctr);
|
|
#else
|
|
ctx->gcm.Yi.d[3] = ctr;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
gcm_aad_aesni(struct ossl_gcm_context *ctx, const unsigned char *aad,
|
|
size_t len)
|
|
{
|
|
size_t i;
|
|
unsigned int n;
|
|
uint64_t alen = ctx->gcm.len.u[0];
|
|
|
|
if (ctx->gcm.len.u[1])
|
|
return -2;
|
|
|
|
alen += len;
|
|
if (alen > (1ull << 61) || (sizeof(len) == 8 && alen < len))
|
|
return -1;
|
|
ctx->gcm.len.u[0] = alen;
|
|
|
|
n = ctx->gcm.ares;
|
|
if (n) {
|
|
while (n && len) {
|
|
ctx->gcm.Xi.c[n] ^= *(aad++);
|
|
--len;
|
|
n = (n + 1) % 16;
|
|
}
|
|
if (n == 0)
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
else {
|
|
ctx->gcm.ares = n;
|
|
return 0;
|
|
}
|
|
}
|
|
if ((i = (len & (size_t)-AES_BLOCK_LEN))) {
|
|
gcm_ghash_avx(ctx->gcm.Xi.u, ctx->gcm.Htable, aad, i);
|
|
aad += i;
|
|
len -= i;
|
|
}
|
|
if (len) {
|
|
n = (unsigned int)len;
|
|
for (i = 0; i < len; ++i)
|
|
ctx->gcm.Xi.c[i] ^= aad[i];
|
|
}
|
|
|
|
ctx->gcm.ares = n;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_encrypt(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
unsigned int n, ctr, mres;
|
|
size_t i;
|
|
uint64_t mlen = ctx->gcm.len.u[1];
|
|
|
|
mlen += len;
|
|
if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len))
|
|
return -1;
|
|
ctx->gcm.len.u[1] = mlen;
|
|
|
|
mres = ctx->gcm.mres;
|
|
|
|
if (ctx->gcm.ares) {
|
|
/* First call to encrypt finalizes GHASH(AAD) */
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
ctx->gcm.ares = 0;
|
|
}
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctr = bswap32(ctx->gcm.Yi.d[3]);
|
|
#else
|
|
ctr = ctx->gcm.Yi.d[3];
|
|
#endif
|
|
|
|
n = mres % 16;
|
|
for (i = 0; i < len; ++i) {
|
|
if (n == 0) {
|
|
aesni_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c,
|
|
&ctx->aes_ks);
|
|
++ctr;
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.Yi.d[3] = bswap32(ctr);
|
|
#else
|
|
ctx->gcm.Yi.d[3] = ctr;
|
|
#endif
|
|
}
|
|
ctx->gcm.Xi.c[n] ^= out[i] = in[i] ^ ctx->gcm.EKi.c[n];
|
|
mres = n = (n + 1) % 16;
|
|
if (n == 0)
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
}
|
|
|
|
ctx->gcm.mres = mres;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_encrypt_ctr32(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
unsigned int n, ctr, mres;
|
|
size_t i;
|
|
uint64_t mlen = ctx->gcm.len.u[1];
|
|
|
|
mlen += len;
|
|
if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len))
|
|
return -1;
|
|
ctx->gcm.len.u[1] = mlen;
|
|
|
|
mres = ctx->gcm.mres;
|
|
|
|
if (ctx->gcm.ares) {
|
|
/* First call to encrypt finalizes GHASH(AAD) */
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
ctx->gcm.ares = 0;
|
|
}
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctr = bswap32(ctx->gcm.Yi.d[3]);
|
|
#else
|
|
ctr = ctx->gcm.Yi.d[3];
|
|
#endif
|
|
|
|
n = mres % 16;
|
|
if (n) {
|
|
while (n && len) {
|
|
ctx->gcm.Xi.c[n] ^= *(out++) = *(in++) ^ ctx->gcm.EKi.c[n];
|
|
--len;
|
|
n = (n + 1) % 16;
|
|
}
|
|
if (n == 0) {
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
mres = 0;
|
|
} else {
|
|
ctx->gcm.mres = n;
|
|
return 0;
|
|
}
|
|
}
|
|
if ((i = (len & (size_t)-16))) {
|
|
size_t j = i / 16;
|
|
|
|
aesni_ctr32_encrypt_blocks(in, out, j, &ctx->aes_ks, ctx->gcm.Yi.c);
|
|
ctr += (unsigned int)j;
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.Yi.d[3] = bswap32(ctr);
|
|
#else
|
|
ctx->gcm.Yi.d[3] = ctr;
|
|
#endif
|
|
in += i;
|
|
len -= i;
|
|
while (j--) {
|
|
for (i = 0; i < 16; ++i)
|
|
ctx->gcm.Xi.c[i] ^= out[i];
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
out += 16;
|
|
}
|
|
}
|
|
if (len) {
|
|
aesni_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, &ctx->aes_ks);
|
|
++ctr;
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.Yi.d[3] = bswap32(ctr);
|
|
#else
|
|
ctx->gcm.Yi.d[3] = ctr;
|
|
#endif
|
|
while (len--) {
|
|
ctx->gcm.Xi.c[mres++] ^= out[n] = in[n] ^ ctx->gcm.EKi.c[n];
|
|
++n;
|
|
}
|
|
}
|
|
|
|
ctx->gcm.mres = mres;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_encrypt_aesni(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
size_t bulk = 0, res;
|
|
int error;
|
|
|
|
res = MIN(len, (AES_BLOCK_LEN - ctx->gcm.mres) % AES_BLOCK_LEN);
|
|
if ((error = gcm_encrypt(ctx, in, out, res)) != 0)
|
|
return error;
|
|
|
|
bulk = aesni_gcm_encrypt(in + res, out + res, len - res,
|
|
&ctx->aes_ks, ctx->gcm.Yi.c, ctx->gcm.Xi.u);
|
|
ctx->gcm.len.u[1] += bulk;
|
|
bulk += res;
|
|
|
|
if ((error = gcm_encrypt_ctr32(ctx, in + bulk, out + bulk,
|
|
len - bulk)) != 0)
|
|
return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_decrypt(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
unsigned int n, ctr, mres;
|
|
size_t i;
|
|
uint64_t mlen = ctx->gcm.len.u[1];
|
|
|
|
mlen += len;
|
|
if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len))
|
|
return -1;
|
|
ctx->gcm.len.u[1] = mlen;
|
|
|
|
mres = ctx->gcm.mres;
|
|
|
|
if (ctx->gcm.ares) {
|
|
/* First call to encrypt finalizes GHASH(AAD) */
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
ctx->gcm.ares = 0;
|
|
}
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctr = bswap32(ctx->gcm.Yi.d[3]);
|
|
#else
|
|
ctr = ctx->gcm.Yi.d[3];
|
|
#endif
|
|
|
|
n = mres % 16;
|
|
for (i = 0; i < len; ++i) {
|
|
uint8_t c;
|
|
if (n == 0) {
|
|
aesni_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c,
|
|
&ctx->aes_ks);
|
|
++ctr;
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.Yi.d[3] = bswap32(ctr);
|
|
#else
|
|
ctx->gcm.Yi.d[3] = ctr;
|
|
#endif
|
|
}
|
|
c = in[i];
|
|
out[i] = c ^ ctx->gcm.EKi.c[n];
|
|
ctx->gcm.Xi.c[n] ^= c;
|
|
mres = n = (n + 1) % 16;
|
|
if (n == 0)
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
}
|
|
|
|
ctx->gcm.mres = mres;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_decrypt_ctr32(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
unsigned int n, ctr, mres;
|
|
size_t i;
|
|
uint64_t mlen = ctx->gcm.len.u[1];
|
|
|
|
mlen += len;
|
|
if (mlen > ((1ull << 36) - 32) || (sizeof(len) == 8 && mlen < len))
|
|
return -1;
|
|
ctx->gcm.len.u[1] = mlen;
|
|
|
|
mres = ctx->gcm.mres;
|
|
|
|
if (ctx->gcm.ares) {
|
|
/* First call to decrypt finalizes GHASH(AAD) */
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
ctx->gcm.ares = 0;
|
|
}
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctr = bswap32(ctx->gcm.Yi.d[3]);
|
|
#else
|
|
ctr = ctx->gcm.Yi.d[3];
|
|
#endif
|
|
|
|
n = mres % 16;
|
|
if (n) {
|
|
while (n && len) {
|
|
uint8_t c = *(in++);
|
|
*(out++) = c ^ ctx->gcm.EKi.c[n];
|
|
ctx->gcm.Xi.c[n] ^= c;
|
|
--len;
|
|
n = (n + 1) % 16;
|
|
}
|
|
if (n == 0) {
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
mres = 0;
|
|
} else {
|
|
ctx->gcm.mres = n;
|
|
return 0;
|
|
}
|
|
}
|
|
if ((i = (len & (size_t)-16))) {
|
|
size_t j = i / 16;
|
|
|
|
while (j--) {
|
|
size_t k;
|
|
for (k = 0; k < 16; ++k)
|
|
ctx->gcm.Xi.c[k] ^= in[k];
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
in += 16;
|
|
}
|
|
j = i / 16;
|
|
in -= i;
|
|
aesni_ctr32_encrypt_blocks(in, out, j, &ctx->aes_ks, ctx->gcm.Yi.c);
|
|
ctr += (unsigned int)j;
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.Yi.d[3] = bswap32(ctr);
|
|
#else
|
|
ctx->gcm.Yi.d[3] = ctr;
|
|
#endif
|
|
out += i;
|
|
in += i;
|
|
len -= i;
|
|
}
|
|
if (len) {
|
|
aesni_encrypt(ctx->gcm.Yi.c, ctx->gcm.EKi.c, &ctx->aes_ks);
|
|
++ctr;
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
ctx->gcm.Yi.d[3] = bswap32(ctr);
|
|
#else
|
|
ctx->gcm.Yi.d[3] = ctr;
|
|
#endif
|
|
while (len--) {
|
|
uint8_t c = in[n];
|
|
ctx->gcm.Xi.c[mres++] ^= c;
|
|
out[n] = c ^ ctx->gcm.EKi.c[n];
|
|
++n;
|
|
}
|
|
}
|
|
|
|
ctx->gcm.mres = mres;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_decrypt_aesni(struct ossl_gcm_context *ctx, const unsigned char *in,
|
|
unsigned char *out, size_t len)
|
|
{
|
|
size_t bulk = 0, res;
|
|
int error;
|
|
|
|
res = MIN(len, (AES_BLOCK_LEN - ctx->gcm.mres) % AES_BLOCK_LEN);
|
|
if ((error = gcm_decrypt(ctx, in, out, res)) != 0)
|
|
return error;
|
|
|
|
bulk = aesni_gcm_decrypt(in + res, out + res, len - res, &ctx->aes_ks,
|
|
ctx->gcm.Yi.c, ctx->gcm.Xi.u);
|
|
ctx->gcm.len.u[1] += bulk;
|
|
bulk += res;
|
|
|
|
if ((error = gcm_decrypt_ctr32(ctx, in + bulk, out + bulk, len - bulk)) != 0)
|
|
return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
gcm_finish_aesni(struct ossl_gcm_context *ctx, const unsigned char *tag,
|
|
size_t len)
|
|
{
|
|
uint64_t alen = ctx->gcm.len.u[0] << 3;
|
|
uint64_t clen = ctx->gcm.len.u[1] << 3;
|
|
|
|
if (ctx->gcm.mres || ctx->gcm.ares)
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
alen = bswap64(alen);
|
|
clen = bswap64(clen);
|
|
#endif
|
|
|
|
ctx->gcm.Xi.u[0] ^= alen;
|
|
ctx->gcm.Xi.u[1] ^= clen;
|
|
gcm_gmult_avx(ctx->gcm.Xi.u, ctx->gcm.Htable);
|
|
|
|
ctx->gcm.Xi.u[0] ^= ctx->gcm.EK0.u[0];
|
|
ctx->gcm.Xi.u[1] ^= ctx->gcm.EK0.u[1];
|
|
|
|
if (tag != NULL)
|
|
return timingsafe_bcmp(ctx->gcm.Xi.c, tag, len);
|
|
return 0;
|
|
}
|
|
|
|
static const struct ossl_aes_gcm_ops gcm_ops_aesni = {
|
|
.init = gcm_init_aesni,
|
|
.setiv = gcm_setiv_aesni,
|
|
.aad = gcm_aad_aesni,
|
|
.encrypt = gcm_encrypt_aesni,
|
|
.decrypt = gcm_decrypt_aesni,
|
|
.finish = gcm_finish_aesni,
|
|
.tag = gcm_tag,
|
|
};
|
|
|
|
int ossl_aes_gcm_setkey_aesni(const unsigned char *key, int klen, void *_ctx);
|
|
|
|
int
|
|
ossl_aes_gcm_setkey_aesni(const unsigned char *key, int klen,
|
|
void *_ctx)
|
|
{
|
|
struct ossl_gcm_context *ctx;
|
|
|
|
ctx = _ctx;
|
|
ctx->ops = &gcm_ops_aesni;
|
|
gcm_init(ctx, key, klen);
|
|
return (0);
|
|
}
|
|
|
|
int ossl_aes_gcm_setkey_avx512(const unsigned char *key, int klen, void *_ctx);
|
|
|
|
int
|
|
ossl_aes_gcm_setkey_avx512(const unsigned char *key, int klen,
|
|
void *_ctx)
|
|
{
|
|
struct ossl_gcm_context *ctx;
|
|
|
|
ctx = _ctx;
|
|
ctx->ops = &gcm_ops_avx512;
|
|
gcm_init(ctx, key, klen);
|
|
return (0);
|
|
}
|