summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShane Lontis <shane.lontis@oracle.com>2019-07-31 21:55:16 +1000
committerShane Lontis <shane.lontis@oracle.com>2019-07-31 21:55:16 +1000
commita672a02a6443a29aa368c0d8abeebc809c1a9f28 (patch)
tree4148c9b4b3b8442961818740660607db38efc126
parentf5b7f99e690b1875e6d047acc435f0029642bfeb (diff)
Add gcm ciphers (aes and aria) to providers.
The code has been modularized so that it can be shared by algorithms. A fixed size IV is now used instead of being allocated. The IV is not set into the low level struct now until the update (it uses an iv_state for this purpose). Hardware specific methods have been added to a PROV_GCM_HW object. The S390 code has been changed to just contain methods that can be accessed in a modular way. There are equivalent generic methods also for the other platforms. Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Patrick Steuer <patrick.steuer@de.ibm.com> (Merged from https://github.com/openssl/openssl/pull/9231)
-rw-r--r--crypto/err/openssl.txt3
-rw-r--r--crypto/evp/evp_enc.c8
-rw-r--r--crypto/evp/evp_lib.c8
-rw-r--r--crypto/modes/build.info4
-rw-r--r--doc/man3/EVP_EncryptInit.pod2
-rw-r--r--providers/common/ciphers/aes.c107
-rw-r--r--providers/common/ciphers/aes_basic.c8
-rw-r--r--providers/common/ciphers/build.info8
-rw-r--r--providers/common/ciphers/ciphers_gcm.h120
-rw-r--r--providers/common/ciphers/ciphers_locl.h43
-rw-r--r--providers/common/ciphers/gcm.c580
-rw-r--r--providers/common/ciphers/gcm_hw.c307
-rw-r--r--providers/common/ciphers/gcm_s390x.c303
-rw-r--r--providers/common/include/internal/provider_algs.h8
-rw-r--r--providers/common/include/internal/providercommonerr.h3
-rw-r--r--providers/common/provider_err.c3
-rw-r--r--providers/default/defltprov.c8
-rw-r--r--providers/fips/fipsprov.c3
18 files changed, 1467 insertions, 59 deletions
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 6b52193895..caa47324bf 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2694,7 +2694,10 @@ PROV_R_BAD_DECRYPT:100:bad decrypt
PROV_R_CIPHER_OPERATION_FAILED:102:cipher operation failed
PROV_R_FAILED_TO_GET_PARAMETER:103:failed to get parameter
PROV_R_FAILED_TO_SET_PARAMETER:104:failed to set parameter
+PROV_R_INVALID_AAD:108:invalid aad
+PROV_R_INVALID_IVLEN:109:invalid ivlen
PROV_R_INVALID_KEYLEN:105:invalid keylen
+PROV_R_INVALID_TAG:110:invalid tag
PROV_R_OUTPUT_BUFFER_TOO_SMALL:106:output buffer too small
PROV_R_WRONG_FINAL_BLOCK_LENGTH:107:wrong final block length
RAND_R_ADDITIONAL_INPUT_TOO_LONG:102:additional input too long
diff --git a/crypto/evp/evp_enc.c b/crypto/evp/evp_enc.c
index 8f5175b525..87c7bb0995 100644
--- a/crypto/evp/evp_enc.c
+++ b/crypto/evp/evp_enc.c
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2019 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
@@ -163,6 +163,12 @@ int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
case NID_aes_256_ctr:
case NID_aes_192_ctr:
case NID_aes_128_ctr:
+ case NID_aes_256_gcm:
+ case NID_aes_192_gcm:
+ case NID_aes_128_gcm:
+ case NID_aria_256_gcm:
+ case NID_aria_192_gcm:
+ case NID_aria_128_gcm:
break;
default:
goto legacy;
diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c
index e6daf684be..d112eaf65a 100644
--- a/crypto/evp/evp_lib.c
+++ b/crypto/evp/evp_lib.c
@@ -315,7 +315,13 @@ int EVP_CIPHER_iv_length(const EVP_CIPHER *cipher)
int EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *ctx)
{
- return EVP_CIPHER_iv_length(ctx->cipher);
+ int ok, v = EVP_CIPHER_iv_length(ctx->cipher);
+ OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+
+ params[0] = OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_IVLEN, &v);
+ ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->provctx, params);
+
+ return ok != 0 ? v : -1;
}
const unsigned char *EVP_CIPHER_CTX_original_iv(const EVP_CIPHER_CTX *ctx)
diff --git a/crypto/modes/build.info b/crypto/modes/build.info
index 81525a9916..a93586690c 100644
--- a/crypto/modes/build.info
+++ b/crypto/modes/build.info
@@ -48,9 +48,9 @@ IF[{- !$disabled{asm} -}]
ENDIF
ENDIF
-$COMMON=cbc128.c ctr128.c cfb128.c ofb128.c $MODESASM
+$COMMON=cbc128.c ctr128.c cfb128.c ofb128.c gcm128.c $MODESASM
SOURCE[../../libcrypto]=$COMMON \
- cts128.c gcm128.c ccm128.c xts128.c wrap128.c ocb128.c siv128.c
+ cts128.c ccm128.c xts128.c wrap128.c ocb128.c siv128.c
DEFINE[../../libcrypto]=$MODESDEF
SOURCE[../../providers/fips]=$COMMON
DEFINE[../../providers/fips]=$MODESDEF
diff --git a/doc/man3/EVP_EncryptInit.pod b/doc/man3/EVP_EncryptInit.pod
index 083bba7996..e46d401746 100644
--- a/doc/man3/EVP_EncryptInit.pod
+++ b/doc/man3/EVP_EncryptInit.pod
@@ -165,6 +165,8 @@ all parameters to NULL except B<type> in an initial call and supply
the remaining parameters in subsequent calls, all of which have B<type>
set to NULL. This is done when the default cipher parameters are not
appropriate.
+For EVP_CIPH_GCM_MODE the IV will be generated internally if it is not
+specified.
EVP_EncryptUpdate() encrypts B<inl> bytes from the buffer B<in> and
writes the encrypted version to B<out>. This function can be called
diff --git a/providers/common/ciphers/aes.c b/providers/common/ciphers/aes.c
index a151a8b393..a211694a88 100644
--- a/providers/common/ciphers/aes.c
+++ b/providers/common/ciphers/aes.c
@@ -13,6 +13,7 @@
#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/params.h>
+#include <openssl/rand.h>
#include "internal/cryptlib.h"
#include "internal/provider_algs.h"
#include "ciphers_locl.h"
@@ -248,50 +249,63 @@ static int aes_cipher(void *vctx,
return 1;
}
-#define IMPLEMENT_cipher(lcmode, UCMODE, flags, kbits, blkbits, ivbits) \
- static OSSL_OP_cipher_get_params_fn aes_##kbits##_##lcmode##_get_params; \
- static int aes_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
- { \
- OSSL_PARAM *p; \
- \
- p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE); \
- if (p != NULL) { \
- if (!OSSL_PARAM_set_int(p, EVP_CIPH_##UCMODE##_MODE)) \
- return 0; \
- } \
- p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_FLAGS); \
- if (p != NULL) { \
- if (!OSSL_PARAM_set_ulong(p, (flags))) \
- return 0; \
- } \
- p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); \
- if (p != NULL) { \
- if (!OSSL_PARAM_set_int(p, (kbits) / 8)) \
- return 0; \
- } \
- p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE); \
- if (p != NULL) { \
- if (!OSSL_PARAM_set_int(p, (blkbits) / 8)) \
- return 0; \
- } \
- p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); \
- if (p != NULL) { \
- if (!OSSL_PARAM_set_int(p, (ivbits) / 8)) \
- return 0; \
- } \
- \
- return 1; \
- } \
- static OSSL_OP_cipher_newctx_fn aes_##kbits##_##lcmode##_newctx; \
- static void *aes_##kbits##_##lcmode##_newctx(void *provctx) \
- { \
- PROV_AES_KEY *ctx = OPENSSL_zalloc(sizeof(*ctx)); \
- \
- ctx->pad = 1; \
- ctx->keylen = ((kbits) / 8); \
- ctx->ciph = PROV_AES_CIPHER_##lcmode(ctx->keylen); \
- ctx->mode = EVP_CIPH_##UCMODE##_MODE; \
- return ctx; \
+static void *aes_new_ctx(void *provctx, size_t mode, size_t kbits,
+ const PROV_AES_CIPHER *ciph)
+{
+ PROV_AES_KEY *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ ctx->pad = 1;
+ ctx->keylen = kbits / 8;
+ ctx->ciph = ciph;
+ ctx->mode = mode;
+ return ctx;
+}
+
+int aes_get_params(OSSL_PARAM params[], int md, unsigned long flags,
+ int kbits, int blkbits, int ivbits)
+{
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_int(p, md))
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_FLAGS);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_ulong(p, flags))
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_int(p, kbits / 8))
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_int(p, blkbits / 8))
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_int(p, ivbits / 8))
+ return 0;
+ }
+ return 1;
+}
+
+#define IMPLEMENT_cipher(lcmode, UCMODE, flags, kbits, blkbits, ivbits) \
+ static OSSL_OP_cipher_get_params_fn aes_##kbits##_##lcmode##_get_params; \
+ static int aes_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
+ { \
+ return aes_get_params(params, EVP_CIPH_##UCMODE##_MODE, flags, kbits, \
+ blkbits, ivbits); \
+ } \
+ static OSSL_OP_cipher_newctx_fn aes_##kbits##_##lcmode##_newctx; \
+ static void *aes_##kbits##_##lcmode##_newctx(void *provctx) \
+ { \
+ return aes_new_ctx(provctx, EVP_CIPH_##UCMODE##_MODE, kbits, \
+ PROV_AES_CIPHER_##lcmode(kbits / 8)); \
}
/* ECB */
@@ -351,6 +365,11 @@ static int aes_ctx_get_params(void *vctx, OSSL_PARAM params[])
PROV_AES_KEY *ctx = (PROV_AES_KEY *)vctx;
OSSL_PARAM *p;
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_int(p, AES_BLOCK_SIZE))
+ return 0;
+ }
p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_PADDING);
if (p != NULL && !OSSL_PARAM_set_int(p, ctx->pad)) {
PROVerr(PROV_F_AES_CTX_GET_PARAMS, PROV_R_FAILED_TO_SET_PARAMETER);
diff --git a/providers/common/ciphers/aes_basic.c b/providers/common/ciphers/aes_basic.c
index a1ca5a9be2..f2ba2f3c24 100644
--- a/providers/common/ciphers/aes_basic.c
+++ b/providers/common/ciphers/aes_basic.c
@@ -6,18 +6,16 @@
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
-
+#include <string.h>
+#include <assert.h>
#include <openssl/opensslconf.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
-#include <string.h>
-#include <assert.h>
#include <openssl/aes.h>
-#include "internal/modes_int.h"
-#include "internal/evp_int.h"
#include <openssl/rand.h>
#include <openssl/cmac.h>
#include "ciphers_locl.h"
+#include "internal/evp_int.h"
#include "internal/providercommonerr.h"
#include "internal/aes_platform.h"
diff --git a/providers/common/ciphers/build.info b/providers/common/ciphers/build.info
index fd49ccb994..8916a22469 100644
--- a/providers/common/ciphers/build.info
+++ b/providers/common/ciphers/build.info
@@ -1,8 +1,8 @@
LIBS=../../../libcrypto
-SOURCE[../../../libcrypto]=\
- block.c aes.c aes_basic.c
+$COMMON=block.c aes.c aes_basic.c gcm.c gcm_hw.c
+
+SOURCE[../../../libcrypto]=$COMMON
INCLUDE[../../../libcrypto]=. ../../../crypto
-SOURCE[../../fips]=\
- block.c aes.c aes_basic.c
+SOURCE[../../fips]=$COMMON
INCLUDE[../../fips]=. ../../../crypto
diff --git a/providers/common/ciphers/ciphers_gcm.h b/providers/common/ciphers/ciphers_gcm.h
new file mode 100644
index 0000000000..badab28aea
--- /dev/null
+++ b/providers/common/ciphers/ciphers_gcm.h
@@ -0,0 +1,120 @@
+
+/*
+ * Copyright 2019 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>
+
+typedef struct prov_gcm_hw_st PROV_GCM_HW;
+
+#define GCM_IV_DEFAULT_SIZE 12/* IV's for AES_GCM should normally be 12 bytes */
+#define GCM_IV_MAX_SIZE 64
+#define GCM_TAG_MAX_SIZE 16
+
+typedef struct prov_gcm_ctx_st {
+ int enc; /* Set to 1 if we are encrypting or 0 otherwise */
+ int mode; /* The mode that we are using */
+ size_t keylen;
+ int ivlen;
+ size_t ivlen_min;
+ int taglen;
+ int key_set; /* Set if key initialised */
+ int iv_state; /* set to one of IV_STATE_XXX */
+ int iv_gen_rand; /* No IV was specified, so generate a rand IV */
+ int iv_gen; /* It is OK to generate IVs */
+ int tls_aad_pad_sz;
+ int tls_aad_len; /* TLS AAD length */
+ uint64_t tls_enc_records; /* Number of TLS records encrypted */
+
+ /*
+ * num contains the number of bytes of |iv| which are valid for modes that
+ * manage partial blocks themselves.
+ */
+ size_t num;
+ size_t bufsz; /* Number of bytes in buf */
+ uint64_t flags;
+
+ unsigned int pad : 1; /* Whether padding should be used or not */
+
+ unsigned char iv[GCM_IV_MAX_SIZE]; /* Buffer to use for IV's */
+ unsigned char buf[AES_BLOCK_SIZE]; /* Buffer of partial blocks processed via update calls */
+
+ OPENSSL_CTX *libctx; /* needed for rand calls */
+ const PROV_GCM_HW *hw; /* hardware specific methods */
+ GCM128_CONTEXT gcm;
+ ctr128_f ctr;
+ const void *ks;
+} PROV_GCM_CTX;
+
+typedef struct prov_aes_gcm_ctx_st {
+ PROV_GCM_CTX base; /* must be first entry in struct */
+ union {
+ OSSL_UNION_ALIGN;
+ AES_KEY ks;
+ } ks; /* AES key schedule to use */
+
+ /* Platform specific data */
+ union {
+ int dummy;
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+ struct {
+ union {
+ OSSL_UNION_ALIGN;
+ S390X_KMA_PARAMS kma;
+ } param;
+ unsigned int fc;
+ unsigned char ares[16];
+ unsigned char mres[16];
+ unsigned char kres[16];
+ int areslen;
+ int mreslen;
+ int kreslen;
+ int res;
+ } s390x;
+#endif /* defined(OPENSSL_CPUID_OBJ) && defined(__s390__) */
+ } plat;
+} PROV_AES_GCM_CTX;
+
+OSSL_CIPHER_FUNC(int, GCM_setkey, (PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen));
+OSSL_CIPHER_FUNC(int, GCM_setiv, (PROV_GCM_CTX *dat, const unsigned char *iv,
+ size_t ivlen));
+OSSL_CIPHER_FUNC(int, GCM_aadupdate, (PROV_GCM_CTX *ctx,
+ const unsigned char *aad, size_t aadlen));
+OSSL_CIPHER_FUNC(int, GCM_cipherupdate, (PROV_GCM_CTX *ctx,
+ const unsigned char *in, size_t len,
+ unsigned char *out));
+OSSL_CIPHER_FUNC(int, GCM_cipherfinal, (PROV_GCM_CTX *ctx, unsigned char *tag));
+OSSL_CIPHER_FUNC(int, GCM_oneshot, (PROV_GCM_CTX *ctx, unsigned char *aad,
+ size_t aad_len, const unsigned char *in,
+ size_t in_len, unsigned char *out,
+ unsigned char *tag, size_t taglen));
+struct prov_gcm_hw_st {
+ OSSL_GCM_setkey_fn setkey;
+ OSSL_GCM_setiv_fn setiv;
+ OSSL_GCM_aadupdate_fn aadupdate;
+ OSSL_GCM_cipherupdate_fn cipherupdate;
+ OSSL_GCM_cipherfinal_fn cipherfinal;
+ OSSL_GCM_oneshot_fn oneshot;
+};
+const PROV_GCM_HW *PROV_AES_HW_gcm(size_t keybits);
+
+#if !defined(OPENSSL_NO_ARIA) && !defined(FIPS_MODE)
+
+#include "internal/aria.h"
+
+typedef struct prov_aria_gcm_ctx_st {
+ PROV_GCM_CTX base; /* must be first entry in struct */
+ union {
+ OSSL_UNION_ALIGN;
+ ARIA_KEY ks;
+ } ks;
+} PROV_ARIA_GCM_CTX;
+const PROV_GCM_HW *PROV_ARIA_HW_gcm(size_t keybits);
+
+#endif /* !defined(OPENSSL_NO_ARIA) && !defined(FIPS_MODE) */
diff --git a/providers/common/ciphers/ciphers_locl.h b/providers/common/ciphers/ciphers_locl.h
index 49248f099c..91033eb262 100644
--- a/providers/common/ciphers/ciphers_locl.h
+++ b/providers/common/ciphers/ciphers_locl.h
@@ -1,4 +1,3 @@
-
/*
* Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
*
@@ -8,12 +7,46 @@
* https://www.openssl.org/source/license.html
*/
+#include <openssl/opensslconf.h>
#include <openssl/aes.h>
-#include <openssl/modes.h>
+#include <openssl/params.h>
#include "internal/cryptlib.h"
+#include "internal/modes_int.h"
+
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+/*-
+ * KMA-GCM-AES parameter block - begin
+ * (see z/Architecture Principles of Operation >= SA22-7832-11)
+ */
+typedef struct S390X_kma_params_st {
+ unsigned char reserved[12];
+ union {
+ unsigned int w;
+ unsigned char b[4];
+ } cv; /* 32 bit counter value */
+ union {
+ unsigned long long g[2];
+ unsigned char b[16];
+ } t; /* tag */
+ unsigned char h[16]; /* hash subkey */
+ unsigned long long taadl; /* total AAD length */
+ unsigned long long tpcl; /* total plaintxt/ciphertxt len */
+ union {
+ unsigned long long g[2];
+ unsigned int w[4];
+ } j0; /* initial counter value */
+ unsigned char k[32]; /* key */
+} S390X_KMA_PARAMS;
+
+#endif
typedef struct prov_aes_cipher_st PROV_AES_CIPHER;
+#define IV_STATE_UNINITIALISED 0 /* initial state is not initialized */
+#define IV_STATE_BUFFERED 1 /* iv has been copied to the iv buffer */
+#define IV_STATE_COPIED 2 /* iv has been copied from the iv buffer */
+#define IV_STATE_FINISHED 3 /* the iv has been used - so don't reuse it */
+
typedef struct prov_aes_key_st {
union {
OSSL_UNION_ALIGN;
@@ -93,6 +126,10 @@ struct prov_aes_cipher_st {
size_t inl);
};
+#define OSSL_CIPHER_FUNC(type, name, args) typedef type (* OSSL_##name##_fn)args
+
+#include "ciphers_gcm.h"
+
const PROV_AES_CIPHER *PROV_AES_CIPHER_ecb(size_t keylen);
const PROV_AES_CIPHER *PROV_AES_CIPHER_cbc(size_t keylen);
const PROV_AES_CIPHER *PROV_AES_CIPHER_ofb(size_t keylen);
@@ -107,3 +144,5 @@ int trailingdata(unsigned char *buf, size_t *buflen, size_t blocksize,
const unsigned char **in, size_t *inlen);
void padblock(unsigned char *buf, size_t *buflen, size_t blocksize);
int unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize);
+int aes_get_params(OSSL_PARAM params[], int md, unsigned long flags,
+ int kbits, int blkbits, int ivbits);
diff --git a/providers/common/ciphers/gcm.c b/providers/common/ciphers/gcm.c
new file mode 100644
index 0000000000..235d81a932
--- /dev/null
+++ b/providers/common/ciphers/gcm.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2019 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/evp.h>
+#include <openssl/params.h>
+#include <openssl/core_numbers.h>
+#include <openssl/core_names.h>
+#include "internal/rand_int.h"
+#include "internal/provider_algs.h"
+#include "internal/provider_ctx.h"
+#include "internal/providercommonerr.h"
+#include "ciphers_locl.h"
+
+/* TODO(3.0) Figure out what flags are really needed */
+#define AEAD_GCM_FLAGS (EVP_CIPH_FLAG_AEAD_CIPHER | EVP_CIPH_FLAG_DEFAULT_ASN1 \
+ | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER \
+ | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT \
+ | EVP_CIPH_CUSTOM_COPY)
+
+static OSSL_OP_cipher_encrypt_init_fn gcm_einit;
+static OSSL_OP_cipher_decrypt_init_fn gcm_dinit;
+static OSSL_OP_cipher_ctx_get_params_fn gcm_ctx_get_params;
+static OSSL_OP_cipher_ctx_set_params_fn gcm_ctx_set_params;
+static OSSL_OP_cipher_cipher_fn gcm_cipher;
+static OSSL_OP_cipher_update_fn gcm_stream_update;
+static OSSL_OP_cipher_final_fn gcm_stream_final;
+
+static int gcm_tls_init(PROV_GCM_CTX *dat, unsigned char *aad, size_t aad_len);
+static int gcm_tls_iv_set_fixed(PROV_GCM_CTX *ctx, unsigned char *iv,
+ size_t len);
+static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen,
+ const unsigned char *in, size_t len);
+static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
+ size_t *padlen, const unsigned char *in,
+ size_t len);
+
+static void gcm_initctx(void *provctx, PROV_GCM_CTX *ctx, size_t keybits,
+ const PROV_GCM_HW *hw, size_t ivlen_min)
+{
+ ctx->pad = 1;
+ ctx->mode = EVP_CIPH_GCM_MODE;
+ ctx->taglen = -1;
+ ctx->tls_aad_len = -1;
+ ctx->ivlen_min = ivlen_min;
+ ctx->ivlen = (EVP_GCM_TLS_FIXED_IV_LEN + EVP_GCM_TLS_EXPLICIT_IV_LEN);
+ ctx->keylen = keybits / 8;
+ ctx->hw = hw;
+ ctx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
+}
+
+static void gcm_deinitctx(PROV_GCM_CTX *ctx)
+{
+ OPENSSL_cleanse(ctx->iv, sizeof(ctx->iv));
+}
+
+static int gcm_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen, int enc)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+
+ ctx->enc = enc;
+
+ if (iv != NULL) {
+ if (ivlen < ctx->ivlen_min || ivlen > sizeof(ctx->iv)) {
+ PROVerr(0, PROV_R_INVALID_IVLEN);
+ return 0;
+ }
+ ctx->ivlen = ivlen;
+ memcpy(ctx->iv, iv, ctx->ivlen);
+ ctx->iv_state = IV_STATE_BUFFERED;
+ }
+
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ PROVerr(0, PROV_R_INVALID_KEYLEN);
+ return 0;
+ }
+ return ctx->hw->setkey(ctx, key, ctx->keylen);
+ }
+ return 1;
+}
+
+static int gcm_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen)
+{
+ return gcm_init(vctx, key, keylen, iv, ivlen, 1);
+}
+
+static int gcm_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen)
+{
+ return gcm_init(vctx, key, keylen, iv, ivlen, 0);
+}
+
+static int gcm_ctx_get_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+ OSSL_PARAM *p;
+ size_t sz;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_int(p, ctx->ivlen))
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_int(p, ctx->keylen)) {
+ PROVerr(0, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
+ if (p != NULL) {
+ if (ctx->iv_gen != 1 && ctx->iv_gen_rand != 1)
+ return 0;
+ if (ctx->ivlen != (int)p->data_size) {
+ PROVerr(0, PROV_R_INVALID_IVLEN);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->iv, ctx->ivlen)) {
+ PROVerr(0, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) {
+ PROVerr(0, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ sz = p->data_size;
+ if (sz == 0 || sz > EVP_GCM_TLS_TAG_LEN || !ctx->enc || ctx->taglen < 0) {
+ PROVerr(0, PROV_R_INVALID_TAG);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->buf, sz)) {
+ PROVerr(0, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int gcm_ctx_set_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+ const OSSL_PARAM *p;
+ size_t sz;
+ void *vp;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ vp = ctx->buf;
+ if (!OSSL_PARAM_get_octet_string(p, &vp, EVP_GCM_TLS_TAG_LEN, &sz)) {
+ PROVerr(0, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (sz == 0 || ctx->enc) {
+ PROVerr(0, PROV_R_INVALID_TAG);
+ return 0;
+ }
+ ctx->taglen = sz;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &sz)) {
+ PROVerr(0, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (sz == 0 || sz > sizeof(ctx->iv)) {
+ PROVerr(0, PROV_R_INVALID_IVLEN);
+ return 0;
+ }
+ ctx->ivlen = sz;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ PROVerr(0, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ sz = gcm_tls_init(ctx, p->data, p->data_size);
+ if (sz == 0) {
+ PROVerr(0, PROV_R_INVALID_AAD);
+ return 0;
+ }
+ ctx->tls_aad_pad_sz = sz;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ PROVerr(0, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (gcm_tls_iv_set_fixed(ctx, p->data, p->data_size) == 0) {
+ PROVerr(0, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int gcm_stream_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+
+ if (outsize < inl) {
+ PROVerr(0, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return -1;
+ }
+
+ if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0) {
+ PROVerr(0, PROV_R_CIPHER_OPERATION_FAILED);
+ return -1;
+ }
+ return 1;
+}
+
+static int gcm_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+ int i;
+
+ i = gcm_cipher_internal(ctx, out, outl, NULL, 0);
+ if (i <= 0)
+ return 0;
+
+ *outl = 0;
+ return 1;
+}
+
+static int gcm_cipher(void *vctx,
+ unsigned char *out, size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+
+ if (outsize < inl) {
+ PROVerr(0, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return -1;
+ }
+
+ if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0)
+ return -1;
+
+ *outl = inl;
+ return 1;
+}
+
+/*
+ * See SP800-38D (GCM) Section 8 "Uniqueness requirement on IVS and keys"
+ *
+ * See also 8.2.2 RBG-based construction.
+ * Random construction consists of a free field (which can be NULL) and a
+ * random field which will use a DRBG that can return at least 96 bits of
+ * entropy strength. (The DRBG must be seeded by the FIPS module).
+ */
+static int gcm_iv_generate(PROV_GCM_CTX *ctx, int offset)
+{
+ int sz = ctx->ivlen - offset;
+
+ /* Must be at least 96 bits */
+ if (sz <= 0 || ctx->ivlen < GCM_IV_DEFAULT_SIZE)
+ return 0;
+
+ /* Use DRBG to generate random iv */
+ if (rand_bytes_ex(ctx->libctx, ctx->iv + offset, sz) <= 0)
+ return 0;
+ ctx->iv_state = IV_STATE_BUFFERED;
+ ctx->iv_gen_rand = 1;
+ return 1;
+}
+
+static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
+ size_t *padlen, const unsigned char *in,
+ size_t len)
+{
+ size_t olen = 0;
+ int rv = 0;
+ const PROV_GCM_HW *hw = ctx->hw;
+
+ if (ctx->tls_aad_len >= 0)
+ return gcm_tls_cipher(ctx, out, padlen, in, len);
+
+ if (!ctx->key_set || ctx->iv_state == IV_STATE_FINISHED)
+ goto err;
+
+ /*
+ * FIPS requires generation of AES-GCM IV's inside the FIPS module.
+ * The IV can still be set externally (the security policy will state that
+ * this is not FIPS compliant). There are some applications
+ * where setting the IV externally is the only option available.
+ */
+ if (ctx->iv_state == IV_STATE_UNINITIALISED) {
+ if (!ctx->enc || !gcm_iv_generate(ctx, 0))
+ goto err;
+ }
+
+ if (ctx->iv_state == IV_STATE_BUFFERED) {
+ if (!hw->setiv(ctx, ctx->iv, ctx->ivlen))
+ goto err;
+ ctx->iv_state = IV_STATE_COPIED;
+ }
+
+ if (in != NULL) {
+ /* The input is AAD if out is NULL */
+ if (out == NULL) {
+ if (!hw->aadupdate(ctx, in, len))
+ goto err;
+ } else {
+ /* The input is ciphertext OR plaintext */
+ if (!hw->cipherupdate(ctx, in, len, out))
+ goto err;
+ }
+ } else {
+ /* Finished when in == NULL */
+ if (!hw->cipherfinal(ctx, ctx->buf))
+ goto err;
+ ctx->iv_state = IV_STATE_FINISHED; /* Don't reuse the IV */
+ goto finish;
+ }
+ olen = len;
+finish:
+ rv = 1;
+err:
+ *padlen = olen;