summaryrefslogtreecommitdiff
path: root/providers
diff options
context:
space:
mode:
authorTodd Short <tshort@akamai.com>2022-04-28 14:56:11 -0400
committerTodd Short <todd.short@me.com>2022-07-29 08:32:16 -0400
commit0113ec8460a918f8bc782130db8f75540b3b1ab2 (patch)
tree2e8c7100cd3be8c2a0cc32efed6330daf3f8395f /providers
parentdffafaf48174497a724d546c3483d2493fc9b64c (diff)
downloadopenssl-new-0113ec8460a918f8bc782130db8f75540b3b1ab2.tar.gz
Implement AES-GCM-SIV (RFC8452)
Fixes #16721 This uses AES-ECB to create a counter mode AES-CTR32 (32bit counter, I could not get AES-CTR to work as-is), and GHASH to implement POLYVAL. Optimally, there would be separate polyval assembly implementation(s), but the only one I could find (and it was SSE2 x86_64 code) was not Apache 2.0 licensed. This implementation lives only in the default provider; there is no legacy implementation. The code offered in #16721 is not used; that implementation sits on top of OpenSSL, this one is embedded inside OpenSSL. Full test vectors from RFC8452 are included, except the 0 length plaintext; that is not supported; and I'm not sure it's worthwhile to do so. Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/openssl/pull/18693)
Diffstat (limited to 'providers')
-rw-r--r--providers/defltprov.c3
-rw-r--r--providers/implementations/ciphers/build.info48
-rw-r--r--providers/implementations/ciphers/cipher_aes_gcm_siv.c327
-rw-r--r--providers/implementations/ciphers/cipher_aes_gcm_siv.h78
-rw-r--r--providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c373
-rw-r--r--providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c84
-rw-r--r--providers/implementations/include/prov/implementations.h3
-rw-r--r--providers/implementations/include/prov/names.h3
8 files changed, 918 insertions, 1 deletions
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 3add4cdaf6..edfcc97bae 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -194,6 +194,9 @@ static const OSSL_ALGORITHM_CAPABLE deflt_ciphers[] = {
ALG(PROV_NAMES_AES_128_SIV, ossl_aes128siv_functions),
ALG(PROV_NAMES_AES_192_SIV, ossl_aes192siv_functions),
ALG(PROV_NAMES_AES_256_SIV, ossl_aes256siv_functions),
+ ALG(PROV_NAMES_AES_128_GCM_SIV, ossl_aes128gcm_siv_functions),
+ ALG(PROV_NAMES_AES_192_GCM_SIV, ossl_aes192gcm_siv_functions),
+ ALG(PROV_NAMES_AES_256_GCM_SIV, ossl_aes256gcm_siv_functions),
#endif /* OPENSSL_NO_SIV */
ALG(PROV_NAMES_AES_256_GCM, ossl_aes256gcm_functions),
ALG(PROV_NAMES_AES_192_GCM, ossl_aes192gcm_functions),
diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info
index b5d9d4f6c1..c8f35bb1d8 100644
--- a/providers/implementations/ciphers/build.info
+++ b/providers/implementations/ciphers/build.info
@@ -26,6 +26,50 @@ $CHACHA_GOAL=../../libdefault.a
$CHACHAPOLY_GOAL=../../libdefault.a
$SIV_GOAL=../../libdefault.a
+IF[{- !$disabled{asm} -}]
+ $GHASHDEF_x86=GHASH_ASM
+ $GHASHDEF_x86_sse2=OPENSSL_IA32_SSE2
+
+ $GHASHDEF_x86_64=GHASH_ASM
+ $GHASHDEF_x86_64_sse2=OPENSSL_IA32_SSE2
+
+ # ghash-ia64.s doesn't work on VMS
+ IF[{- $config{target} !~ /^vms-/ -}]
+ $GHASHDEF_ia64=GHASH_ASM
+ ENDIF
+
+ $GHASHDEF_sparcv9=GHASH_ASM
+
+ $GHASHDEF_alpha=GHASH_ASM
+
+ $GHASHDEF_s390x=GHASH_ASM
+
+ $GHASHDEF_armv4=GHASH_ASM
+ $GHASHDEF_aarch64=
+
+ $GHASHDEF_parisc11=GHASH_ASM
+ $GHASHDEF_parisc20_64=$GHASHDEF_parisc11
+
+ $GHASHDEF_ppc32=
+ $GHASHDEF_ppc64=$GHASHDEF_ppc32
+
+ $GHASHDEF_c64xplus=GHASH_ASM
+
+ $GHASHDEF_riscv64=GHASH_ASM
+
+ # Now that we have defined all the arch specific variables, use the
+ # appropriate one, and define the appropriate macros
+
+ IF[$GHASHDEF_{- $target{asm_arch} -}]
+ $GHASHDEF=$GHASHDEF_{- $target{asm_arch} -}
+ IF[{- !$disabled{sse2} -}]
+ IF[$GHASHDEF_{- $target{asm_arch} -}_sse2]
+ $GHASHDEF=$GHASHDEF_{- $target{asm_arch} -}_sse2
+ ENDIF
+ ENDIF
+ ENDIF
+ENDIF
+
# This source is common building blocks for all ciphers in all our providers.
SOURCE[$COMMON_GOAL]=\
ciphercommon.c ciphercommon_hw.c ciphercommon_block.c \
@@ -54,8 +98,10 @@ SOURCE[$AES_GOAL]=\
SOURCE[$AES_GOAL]=cipher_aes_xts_fips.c
IF[{- !$disabled{siv} -}]
+ DEFINE[$SIV_GOAL]=$GHASHDEF
SOURCE[$SIV_GOAL]=\
- cipher_aes_siv.c cipher_aes_siv_hw.c
+ cipher_aes_siv.c cipher_aes_siv_hw.c \
+ cipher_aes_gcm_siv.c cipher_aes_gcm_siv_hw.c cipher_aes_gcm_siv_polyval.c
ENDIF
IF[{- !$disabled{des} -}]
diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv.c b/providers/implementations/ciphers/cipher_aes_gcm_siv.c
new file mode 100644
index 0000000000..93e65d530e
--- /dev/null
+++ b/providers/implementations/ciphers/cipher_aes_gcm_siv.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. 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
+ */
+
+/* Dispatch functions for AES SIV mode */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/ciphercommon_aead.h"
+#include "prov/provider_ctx.h"
+#include "cipher_aes_gcm_siv.h"
+
+static int ossl_aes_gcm_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]);
+
+static void *ossl_aes_gcm_siv_newctx(void *provctx, size_t keybits)
+{
+ PROV_AES_GCM_SIV_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ ctx->key_len = keybits / 8;
+ ctx->hw = ossl_prov_cipher_hw_aes_gcm_siv(keybits);
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ ctx->provctx = provctx;
+ }
+ return ctx;
+}
+
+static void ossl_aes_gcm_siv_freectx(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ if (ctx == NULL)
+ return;
+
+ OPENSSL_clear_free(ctx->aad, ctx->aad_len);
+ ctx->hw->clean_ctx(ctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *ossl_aes_gcm_siv_dupctx(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *in = (PROV_AES_GCM_SIV_CTX *)vctx;
+ PROV_AES_GCM_SIV_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if (in->hw == NULL)
+ return NULL;
+
+ ret = OPENSSL_memdup(in, sizeof(*in));
+ if (ret == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ /* NULL-out these things we create later */
+ ret->aad = NULL;
+ ret->ecb_ctx = NULL;
+
+ if (in->aad == NULL) {
+ if ((ret->aad = OPENSSL_memdup(in->aad, UP16(ret->aad_len))) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ }
+
+ if (!in->hw->dup_ctx(ret, in))
+ goto err;
+
+ return ret;
+ err:
+ if (ret != NULL) {
+ OPENSSL_clear_free(ret->aad, ret->aad_len);
+ OPENSSL_free(ret);
+ }
+ return NULL;
+}
+
+static int ossl_aes_gcm_siv_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+
+ if (key != NULL) {
+ if (keylen != ctx->key_len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ memcpy(ctx->key_gen_key, key, ctx->key_len);
+ }
+ if (iv != NULL) {
+ if (ivlen != sizeof(ctx->nonce)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ memcpy(ctx->nonce, iv, sizeof(ctx->nonce));
+ }
+
+ if (!ctx->hw->initkey(ctx))
+ return 0;
+
+ return ossl_aes_gcm_siv_set_ctx_params(ctx, params);
+}
+
+static int ossl_aes_gcm_siv_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return ossl_aes_gcm_siv_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int ossl_aes_gcm_siv_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return ossl_aes_gcm_siv_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+#define ossl_aes_gcm_siv_stream_update ossl_aes_gcm_siv_cipher
+static int ossl_aes_gcm_siv_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ int error = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* The RFC has a test case for this, but we don't try to do anything */
+ if (inl == 0) {
+ if (outl != NULL)
+ *outl = 0;
+ return 1;
+ }
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ error |= !ctx->hw->cipher(ctx, out, in, inl);
+
+ if (outl != NULL && !error)
+ *outl = inl;
+ return !error;
+}
+
+static int ossl_aes_gcm_siv_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ int error = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ error |= !ctx->hw->cipher(vctx, out, NULL, 0);
+
+ if (outl != NULL && !error)
+ *outl = 0;
+ return !error;
+}
+
+static int ossl_aes_gcm_siv_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING) {
+ if (!ctx->enc || !ctx->generated_tag
+ || p->data_size != sizeof(ctx->tag)
+ || !OSSL_PARAM_set_octet_string(p, ctx->tag, sizeof(ctx->tag))) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, sizeof(ctx->tag))) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->key_len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM aes_gcm_siv_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ossl_aes_gcm_siv_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return aes_gcm_siv_known_gettable_ctx_params;
+}
+
+static int ossl_aes_gcm_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ const OSSL_PARAM *p;
+ unsigned int speed = 0;
+
+ if (params == NULL)
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || p->data_size != sizeof(ctx->user_tag)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (!ctx->enc) {
+ memcpy(ctx->user_tag, p->data, sizeof(ctx->tag));
+ ctx->have_user_tag = 1;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_SPEED);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_uint(p, &speed)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ ctx->speed = !!speed;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ size_t key_len;
+
+ if (!OSSL_PARAM_get_size_t(p, &key_len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ /* The key length can not be modified */
+ if (key_len != ctx->key_len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static const OSSL_PARAM aes_gcm_siv_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_SPEED, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *ossl_aes_gcm_siv_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return aes_gcm_siv_known_settable_ctx_params;
+}
+
+#define IMPLEMENT_cipher(alg, lc, UCMODE, flags, kbits, blkbits, ivbits) \
+static OSSL_FUNC_cipher_newctx_fn ossl_##alg##kbits##_##lc##_newctx; \
+static OSSL_FUNC_cipher_freectx_fn ossl_##alg##_##lc##_freectx; \
+static OSSL_FUNC_cipher_dupctx_fn ossl_##alg##_##lc##_dupctx; \
+static OSSL_FUNC_cipher_encrypt_init_fn ossl_##alg##_##lc##_einit; \
+static OSSL_FUNC_cipher_decrypt_init_fn ossl_##alg##_##lc##_dinit; \
+static OSSL_FUNC_cipher_update_fn ossl_##alg##_##lc##_stream_update; \
+static OSSL_FUNC_cipher_final_fn ossl_##alg##_##lc##_stream_final; \
+static OSSL_FUNC_cipher_cipher_fn ossl_##alg##_##lc##_cipher; \
+static OSSL_FUNC_cipher_get_params_fn ossl_##alg##_##kbits##_##lc##_get_params; \
+static OSSL_FUNC_cipher_get_ctx_params_fn ossl_##alg##_##lc##_get_ctx_params; \
+static OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_##alg##_##lc##_gettable_ctx_params; \
+static OSSL_FUNC_cipher_set_ctx_params_fn ossl_##alg##_##lc##_set_ctx_params; \
+static OSSL_FUNC_cipher_settable_ctx_params_fn ossl_##alg##_##lc##_settable_ctx_params; \
+static int ossl_##alg##_##kbits##_##lc##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+static void * ossl_##alg##kbits##_##lc##_newctx(void *provctx) \
+{ \
+ return ossl_##alg##_##lc##_newctx(provctx, kbits); \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##lc##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))ossl_##alg##kbits##_##lc##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))ossl_##alg##_##lc##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))ossl_##alg##_##lc##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_##alg##_##lc##_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_##alg##_##lc##_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_##alg##_##lc##_stream_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_##alg##_##lc##_stream_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_##alg##_##lc##_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))ossl_##alg##_##kbits##_##lc##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_settable_ctx_params }, \
+ { 0, NULL } \
+}
+
+IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 128, 8, 96);
+IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 192, 8, 96);
+IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 256, 8, 96);
diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv.h b/providers/implementations/ciphers/cipher_aes_gcm_siv.h
new file mode 100644
index 0000000000..1224512e3a
--- /dev/null
+++ b/providers/implementations/ciphers/cipher_aes_gcm_siv.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. 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
+ */
+
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+
+#define BLOCK_SIZE 16
+#define NONCE_SIZE 12
+#define TAG_SIZE 16
+
+/* AAD manipulation macros */
+#define UP16(x) (((x) + 15) & ~0x0F)
+#define DOWN16(x) ((x) & ~0x0F)
+#define REMAINDER16(x) ((x) & 0x0F)
+#define IS16(x) (((x) & 0x0F) == 0)
+
+typedef struct prov_cipher_hw_aes_gcm_siv_st {
+ int (*initkey)(void *vctx);
+ int (*cipher)(void *vctx, unsigned char *out, const unsigned char *in,
+ size_t len);
+ int (*dup_ctx)(void *vdst, void *vsrc);
+ void (*clean_ctx)(void *vctx);
+} PROV_CIPHER_HW_AES_GCM_SIV;
+
+/* Arranged for alignment purposes */
+typedef struct prov_aes_gcm_siv_ctx_st {
+ EVP_CIPHER_CTX *ecb_ctx;
+ const PROV_CIPHER_HW_AES_GCM_SIV *hw; /* maybe not used, yet? */
+ uint8_t *aad; /* Allocated, rounded up to 16 bytes, from user */
+ OSSL_LIB_CTX *libctx;
+ OSSL_PROVIDER *provctx;
+ size_t aad_len; /* actual AAD length */
+ size_t key_len;
+ uint8_t key_gen_key[32]; /* from user */
+ uint8_t msg_enc_key[32]; /* depends on key size */
+ uint8_t msg_auth_key[BLOCK_SIZE];
+ uint8_t tag[TAG_SIZE]; /* generated tag, given to user or compared to user */
+ uint8_t user_tag[TAG_SIZE]; /* from user */
+ uint8_t nonce[NONCE_SIZE]; /* from user */
+ u128 Htable[16]; /* Polyval calculations via ghash */
+ unsigned int enc : 1; /* Set to 1 if we are encrypting or 0 otherwise */
+ unsigned int have_user_tag : 1;
+ unsigned int generated_tag : 1;
+ unsigned int used_enc : 1;
+ unsigned int used_dec : 1;
+ unsigned int speed : 1;
+} PROV_AES_GCM_SIV_CTX;
+
+const PROV_CIPHER_HW_AES_GCM_SIV *ossl_prov_cipher_hw_aes_gcm_siv(size_t keybits);
+
+void ossl_polyval_ghash_init(u128 Htable[16], const uint64_t H[2]);
+void ossl_polyval_ghash_hash(const u128 Htable[16], uint8_t *tag, const uint8_t *inp, size_t len);
+
+/* Define our own BSWAP8/BSWAP4, if not already defined */
+#ifndef BSWAP8
+static ossl_inline uint64_t BSWAP8(uint64_t n)
+{
+ uint8_t *p = (uint8_t *)&n;
+
+ return (uint64_t)GETU32(p) << 32 | GETU32(p + 4);
+}
+#endif
+
+#ifndef BSWAP4
+static ossl_inline uint32_t BSWAP4(uint32_t n)
+{
+ uint8_t *p = (uint8_t *)&n;
+
+ return GETU32(p);
+}
+#endif
diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
new file mode 100644
index 0000000000..9ee5c32f4f
--- /dev/null
+++ b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. 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
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/evp.h>
+#include <internal/endian.h>
+#include <prov/implementations.h>
+#include "cipher_aes_gcm_siv.h"
+
+static int aes_gcm_siv_ctr32(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *init_counter,
+ unsigned char *out, const unsigned char *in, size_t len);
+
+static int aes_gcm_siv_initkey(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ uint8_t output[BLOCK_SIZE];
+ uint32_t counter = 0x0;
+ size_t i;
+ union {
+ uint32_t counter;
+ uint8_t block[BLOCK_SIZE];
+ } data;
+ int out_len;
+ EVP_CIPHER *ecb = NULL;
+ DECLARE_IS_ENDIAN;
+
+ switch (ctx->key_len) {
+ case 16:
+ ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-128-ECB", NULL);
+ break;
+ case 24:
+ ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-192-ECB", NULL);
+ break;
+ case 32:
+ ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-256-ECB", NULL);
+ break;
+ default:
+ goto err;
+ }
+
+ if (ctx->ecb_ctx == NULL && (ctx->ecb_ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+ if (!EVP_EncryptInit_ex2(ctx->ecb_ctx, ecb, ctx->key_gen_key, NULL, NULL))
+ goto err;
+
+ memset(&data, 0, sizeof(data));
+ memcpy(&data.block[sizeof(data.counter)], ctx->nonce, NONCE_SIZE);
+
+ /* msg_auth_key is always 16 bytes in size, regardless of AES128/AES256 */
+ /* counter is stored little-endian */
+ for (i = 0; i < BLOCK_SIZE; i += 8) {
+ if (IS_LITTLE_ENDIAN) {
+ data.counter = counter;
+ } else {
+ data.counter = BSWAP4(counter);
+ }
+ /* Block size is 16 (128 bits), but only 8 bytes are used */
+ out_len = BLOCK_SIZE;
+ if (!EVP_EncryptUpdate(ctx->ecb_ctx, output, &out_len, data.block, BLOCK_SIZE))
+ goto err;
+ memcpy(&ctx->msg_auth_key[i], output, 8);
+ counter++;
+ }
+
+ /* msg_enc_key length is directly tied to key length AES128/AES256 */
+ for (i = 0; i < ctx->key_len; i += 8) {
+ if (IS_LITTLE_ENDIAN) {
+ data.counter = counter;
+ } else {
+ data.counter = BSWAP4(counter);
+ }
+ /* Block size is 16 bytes (128 bits), but only 8 bytes are used */
+ out_len = BLOCK_SIZE;
+ if (!EVP_EncryptUpdate(ctx->ecb_ctx, output, &out_len, data.block, BLOCK_SIZE))
+ goto err;
+ memcpy(&ctx->msg_enc_key[i], output, 8);
+ counter++;
+ }
+
+ if (!EVP_EncryptInit_ex2(ctx->ecb_ctx, ecb, ctx->msg_enc_key, NULL, NULL))
+ goto err;
+
+ /* Freshen up the state */
+ ctx->used_enc = 0;
+ ctx->used_dec = 0;
+ EVP_CIPHER_free(ecb);
+ return 1;
+ err:
+ EVP_CIPHER_CTX_free(ctx->ecb_ctx);
+ EVP_CIPHER_free(ecb);
+ ctx->ecb_ctx = NULL;
+ return 0;
+}
+
+static int aes_gcm_siv_aad(PROV_AES_GCM_SIV_CTX *ctx,
+ const unsigned char *aad, size_t len)
+{
+ size_t to_alloc;
+ uint8_t *ptr;
+ uint64_t len64;
+
+ /* length of 0 resets the AAD */
+ if (len == 0) {
+ OPENSSL_free(ctx->aad);
+ ctx->aad = NULL;
+ ctx->aad_len = 0;
+ return 1;
+ }
+ to_alloc = UP16(ctx->aad_len + len);
+ /* need to check the size of the AAD per RFC8452 */
+ len64 = to_alloc;
+ if (len64 > ((uint64_t)1 << 36))
+ return 0;
+ ptr = OPENSSL_realloc(ctx->aad, to_alloc);
+ if (ptr == NULL)
+ return 0;
+ ctx->aad = ptr;
+ memcpy(&ctx->aad[ctx->aad_len], aad, len);
+ ctx->aad_len += len;
+ if (to_alloc > ctx->aad_len)
+ memset(&ctx->aad[ctx->aad_len], 0, to_alloc - ctx->aad_len);
+ return 1;
+}
+
+static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
+{
+ int ret = 0;
+
+ if (ctx->enc)
+ return ctx->generated_tag;
+ ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
+ ret &= ctx->have_user_tag;
+ return ret;
+}
+
+static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ uint64_t len_blk[2];
+ uint8_t S_s[TAG_SIZE];
+ uint8_t counter_block[TAG_SIZE];
+ uint8_t padding[BLOCK_SIZE];
+ size_t i;
+ int64_t len64 = len;
+ int out_len;
+ int error = 0;
+ DECLARE_IS_ENDIAN;
+
+ ctx->generated_tag = 0;
+ if (!ctx->speed && ctx->used_enc)
+ return 0;
+ /* need to check the size of the input! */
+ if (len64 > ((int64_t)1 << 36) || len == 0)
+ return 0;
+
+ if (IS_LITTLE_ENDIAN) {
+ len_blk[0] = (uint64_t)ctx->aad_len * 8;
+ len_blk[1] = (uint64_t)len * 8;
+ } else {
+ len_blk[0] = BSWAP8((uint64_t)ctx->aad_len * 8);
+ len_blk[1] = BSWAP8((uint64_t)len * 8);
+ }
+ memset(S_s, 0, TAG_SIZE);
+ ossl_polyval_ghash_init(ctx->Htable, (const uint64_t*)ctx->msg_auth_key);
+
+ if (ctx->aad != NULL) {
+ /* AAD is allocated with padding, but need to adjust length */
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, ctx->aad, UP16(ctx->aad_len));
+ }
+ if (DOWN16(len) > 0)
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *) in, DOWN16(len));
+ if (!IS16(len)) {
+ /* deal with padding - probably easier to memset the padding first rather than calculate */
+ memset(padding, 0, sizeof(padding));
+ memcpy(padding, &in[DOWN16(len)], REMAINDER16(len));
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, padding, sizeof(padding));
+ }
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *) len_blk, sizeof(len_blk));
+
+ for (i = 0; i < NONCE_SIZE; i++)
+ S_s[i] ^= ctx->nonce[i];
+
+ S_s[TAG_SIZE - 1] &= 0x7f;
+ out_len = sizeof(ctx->tag);
+ error |= !EVP_EncryptUpdate(ctx->ecb_ctx, ctx->tag, &out_len, S_s, sizeof(S_s));
+ memcpy(counter_block, ctx->tag, TAG_SIZE);
+ counter_block[TAG_SIZE - 1] |= 0x80;
+
+ error |= !aes_gcm_siv_ctr32(ctx, counter_block, out, in, len);
+
+ ctx->generated_tag = !error;
+ /* Regardless of error */
+ ctx->used_enc = 1;
+ return !error;
+}
+
+static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ uint8_t counter_block[TAG_SIZE];
+ uint64_t len_blk[2];
+ uint8_t S_s[TAG_SIZE];
+ size_t i;
+ uint64_t padding[2];
+ int64_t len64 = len;
+ int out_len;
+ int error = 0;
+ DECLARE_IS_ENDIAN;
+
+ ctx->generated_tag = 0;
+ if (!ctx->speed && ctx->used_dec)
+ return 0;
+ /* need to check the size of the input! */
+ if (len64 > ((int64_t)1 << 36) || len == 0)
+ return 0;
+
+ memcpy(counter_block, ctx->user_tag, sizeof(counter_block));
+ counter_block[TAG_SIZE - 1] |= 0x80;
+
+ error |= !aes_gcm_siv_ctr32(ctx, counter_block, out, in, len);
+
+ if (IS_LITTLE_ENDIAN) {
+ len_blk[0] = (uint64_t)ctx->aad_len * 8;
+ len_blk[1] = (uint64_t)len * 8;
+ } else {
+ len_blk[0] = BSWAP8((uint64_t)ctx->aad_len * 8);
+ len_blk[1] = BSWAP8((uint64_t)len * 8);
+ }
+ memset(S_s, 0, TAG_SIZE);
+ ossl_polyval_ghash_init(ctx->Htable, (const uint64_t*)ctx->msg_auth_key);
+ if (ctx->aad != NULL) {
+ /* AAD allocated with padding, but need to adjust length */
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, ctx->aad, UP16(ctx->aad_len));
+ }
+ if (DOWN16(len) > 0)
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, out, DOWN16(len));
+ if (!IS16(len)) {
+ /* deal with padding - probably easier to "memset" the padding first rather than calculate */
+ padding[0] = padding[1] = 0;
+ memcpy(padding, &out[DOWN16(len)], REMAINDER16(len));
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *)padding, sizeof(padding));
+ }
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *)len_blk, TAG_SIZE);
+
+ for (i = 0; i < NONCE_SIZE; i++)
+ S_s[i] ^= ctx->nonce[i];
+
+ S_s[TAG_SIZE - 1] &= 0x7f;
+
+ /*
+ * In the ctx, user_tag is the one received/set by the user,
+ * and tag is generated from the input
+ */
+ out_len = sizeof(ctx->tag);
+ error |= !EVP_EncryptUpdate(ctx->ecb_ctx, ctx->tag, &out_len, S_s, sizeof(S_s));
+ ctx->generated_tag = !error;
+ /* Regardless of error */
+ ctx->used_dec = 1;
+ return !error;
+}
+
+static int aes_gcm_siv_cipher(void *vctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ /* EncryptFinal or DecryptFinal */
+ if (in == NULL)
+ return aes_gcm_siv_finish(ctx);
+
+ /* Deal with associated data */
+ if (out == NULL)
+ return aes_gcm_siv_aad(ctx, in, len);
+
+ if (ctx->enc)
+ return aes_gcm_siv_encrypt(ctx, in, out, len);
+
+ return aes_gcm_siv_decrypt(ctx, in, out, len);
+}
+
+static void aes_gcm_siv_clean_ctx(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ EVP_CIPHER_CTX_free(ctx->ecb_ctx);
+ ctx->ecb_ctx = NULL;
+}
+
+static int aes_gcm_siv_dup_ctx(void *vdst, void *vsrc)
+{
+ PROV_AES_GCM_SIV_CTX *dst = (PROV_AES_GCM_SIV_CTX *)vdst;
+ PROV_AES_GCM_SIV_CTX *src = (PROV_AES_GCM_SIV_CTX *)vsrc;
+
+ dst->ecb_ctx = NULL;
+ if (src->ecb_ctx != NULL) {
+ if ((dst->ecb_ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+ if (!EVP_CIPHER_CTX_copy(dst->ecb_ctx, src->ecb_ctx))
+ goto err;
+ }
+ return 1;
+
+ err:
+ EVP_CIPHER_CTX_free(dst->ecb_ctx);
+ dst->ecb_ctx = NULL;
+ return 0;
+}
+
+static const PROV_CIPHER_HW_AES_GCM_SIV aes_gcm_siv_hw =
+{
+ aes_gcm_siv_initkey,
+ aes_gcm_siv_cipher,
+ aes_gcm_siv_dup_ctx,
+ aes_gcm_siv_clean_ctx,
+};
+
+const PROV_CIPHER_HW_AES_GCM_SIV *ossl_prov_cipher_hw_aes_gcm_siv(size_t keybits)
+{
+ return &aes_gcm_siv_hw;
+}
+
+/* AES-GCM-SIV needs AES-CTR32, which is different than the AES-CTR implementation */
+static int aes_gcm_siv_ctr32(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *init_counter,
+ unsigned char *out, const unsigned char *in, size_t len)
+{
+ uint8_t keystream[BLOCK_SIZE];
+ int out_len;
+ size_t i;
+ size_t j;
+ size_t todo;
+ uint32_t counter;
+ int error = 0;
+ union {
+ uint32_t x32[BLOCK_SIZE / sizeof(uint32_t)];
+ uint8_t x8[BLOCK_SIZE];
+ } block;
+ DECLARE_IS_ENDIAN;
+
+ memcpy(&block, init_counter, sizeof(block));
+ if (IS_BIG_ENDIAN) {
+ counter = BSWAP4(block.x32[0]);
+ }
+
+ for (i = 0; i < len; i += sizeof(block)) {
+ out_len = BLOCK_SIZE;
+ error |= !EVP_EncryptUpdate(ctx->ecb_ctx, keystream, &out_len, (uint8_t*)&block, sizeof(block));
+ if (IS_LITTLE_ENDIAN) {
+ block.x32[0]++;
+ } else {
+ counter++;
+ block.x32[0] = BSWAP4(counter);
+ }
+ todo = len - i;
+ if (todo > sizeof(keystream))
+ todo = sizeof(keystream);
+ /* Non optimal, but avoids alignment issues */
+ for (j = 0; j < todo; j++)
+ out[i + j] = in[i + j] ^ keystream[j];
+ }
+ return !error;
+}
diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c b/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c
new file mode 100644
index 0000000000..66f6ed457e
--- /dev/null
+++ b/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. 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
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/evp.h>
+#include <internal/endian.h>
+#include <prov/implementations.h>
+#include "cipher_aes_gcm_siv.h"
+
+static ossl_inline void mulx_ghash(uint64_t *a)
+{
+ uint64_t t[2], mask;
+
+ t[0] = BSWAP8(a[0]);
+ t[1] = BSWAP8(a[1]);
+ mask = -(int64_t)(t[1] & 1) & 0xe1;
+ mask <<= 56;
+
+ a[1] = BSWAP8((t[1] >> 1) ^ (t[0] << 63));
+ a[0] = BSWAP8((t[0] >> 1) ^ mask);
+}
+
+#define aligned64(p) (((uintptr_t)p & 0x07) == 0)
+static ossl_inline void byte_reverse16(uint8_t *out, const uint8_t *in)
+{
+ if (aligned64(out) && aligned64(in)) {
+ ((uint64_t *)out)[0] = BSWAP8(((uint64_t *)in)[1]);
+ ((uint64_t *)out)[1] = BSWAP8(((uint64_t *)in)[0]);
+ } else {
+ int i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = in[15 - i];
+ }
+}
+
+/* Initialization of POLYVAL via existing GHASH implementation */
+void ossl_polyval_ghash_init(u128 Htable[16], const uint64_t H[2])
+{
+ uint64_t tmp[2];
+ DECLARE_IS_ENDIAN;
+
+ byte_reverse16((uint8_t *)tmp, (const uint8_t *)H);
+ mulx_ghash(tmp);
+ if (IS_LITTLE_ENDIAN) {
+ /* "H is stored in host byte order" */
+ tmp[0] = BSWAP8(tmp[0]);
+ tmp[1] = BSWAP8(tmp[1]);
+ }
+
+ ossl_gcm_init_4bit(Htable, (u64*)tmp);
+}
+
+/* Implmentation of POLYVAL via existing GHASH implementation */
+void ossl_polyval_ghash_hash(const u128 Htable[16], uint8_t *tag, const uint8_t *inp, size_t len)
+{
+ uint64_t out[2];
+ uint64_t tmp[2];
+ size_t i;
+
+ byte_reverse16((uint8_t *)out, (uint8_t *)tag);
+
+ /*
+ * This implementation doesn't deal with partials, callers do,
+ * so, len is a multiple of 16
+ */
+ for (i = 0; i < len; i += 16) {
+ byte_reverse16((uint8_t *)tmp, &inp[i]);
+ ossl_gcm_ghash_4bit((u64*)out, Htable, (uint8_t *)tmp, 16);
+ }
+ byte_reverse16(tag, (uint8_t *)out);
+}
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index 5740028287..03ce43719e 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -245,6 +245,9 @@ extern const OSSL_DISPATCH ossl_chacha20_ossl_poly1305_functions[];
extern const OSSL_DISPATCH ossl_aes128siv_functions[];
extern const OSSL_DISPATCH ossl_aes192siv_functions[];
extern const OSSL_DISPATCH ossl_aes256siv_functions[];
+extern const OSSL_DISPATCH ossl_aes128gcm_siv_functions[];
+extern const OSSL_DISPATCH ossl_aes192gcm_siv_functions[];
+extern const OSSL_DISPATCH ossl_aes256gcm_siv_functions[];
#endif /* OPENSSL_NO_SIV */
/* MACs */
diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h
index 97cbae70f1..3acea6eaa0 100644
--- a/providers/implementations/include/prov/names.h
+++ b/providers/implementations/include/prov/names.h
@@ -97,6 +97,9 @@
#define PROV_NAMES_AES_128_SIV "AES-128-SIV"
#define PROV_NAMES_AES_192_SIV "AES-192-SIV"
#define PROV_NAMES_AES_256_SIV "AES-256-SIV"
+#define PROV_NAMES_AES_128_GCM_SIV "AES-128-GCM-SIV"
+#define PROV_NAMES_AES_192_GCM_SIV "AES-192-GCM-SIV"
+#define PROV_NAMES_AES_256_GCM_SIV "AES-256-GCM-SIV"
#define PROV_NAMES_ARIA_256_GCM "ARIA-256-GCM:1.2.410.200046.1.1.36"
#define PROV_NAMES_ARIA_192_GCM "ARIA-192-GCM:1.2.410.200046.1.1.35"
#define PROV_NAMES_ARIA_128_GCM "ARIA-128-GCM:1.2.410.200046.1.1.34"