Add AES_CBC_HMAC_SHA ciphers to providers.
authorShane Lontis <shane.lontis@oracle.com>
Mon, 6 Jan 2020 03:02:16 +0000 (13:02 +1000)
committerShane Lontis <shane.lontis@oracle.com>
Mon, 6 Jan 2020 03:02:16 +0000 (13:02 +1000)
Also Add ability for providers to dynamically exclude cipher algorithms.
Cipher algorithms are only returned from providers if their capable() method is either NULL,
or the method returns 1.
This is mainly required for ciphers that only have hardware implementations.
If there is no hardware support, then the algorithm needs to be not available.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/10146)

17 files changed:
crypto/aes/build.info
crypto/evp/evp_enc.c
doc/man7/provider-cipher.pod
include/crypto/aes_platform.h
include/openssl/core_names.h
providers/common/include/prov/provider_util.h
providers/common/include/prov/providercommon.h
providers/common/provider_util.c
providers/defltprov.c
providers/fips/fipsprov.c
providers/implementations/ciphers/build.info
providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c [new file with mode: 0644]
providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h [new file with mode: 0644]
providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c [new file with mode: 0644]
providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c [new file with mode: 0644]
providers/implementations/include/prov/implementations.h
test/sslapitest.c

index 291bf2a..dc00df0 100644 (file)
@@ -68,8 +68,6 @@ SOURCE[../../providers/libfips.a]=$COMMON
 DEFINE[../../libcrypto]=$AESDEF
 DEFINE[../../providers/libfips.a]=$AESDEF
 DEFINE[../../providers/libimplementations.a]=$AESDEF
-# fipsprov.c needs access to AESNI.
-DEFINE[../../providers/fips]=$AESDEF
 
 GENERATE[aes-ia64.s]=asm/aes-ia64.S
 
index 3896cff..c650add 100644 (file)
@@ -174,6 +174,10 @@ int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
         case NID_aes_256_siv:
         case NID_aes_192_siv:
         case NID_aes_128_siv:
+        case NID_aes_256_cbc_hmac_sha256:
+        case NID_aes_128_cbc_hmac_sha256:
+        case NID_aes_256_cbc_hmac_sha1:
+        case NID_aes_128_cbc_hmac_sha1:
         case NID_id_aes256_wrap:
         case NID_id_aes256_wrap_pad:
         case NID_id_aes192_wrap:
@@ -1086,7 +1090,9 @@ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
     int set_params = 1;
     size_t sz = arg;
     unsigned int i;
-    OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    OSSL_PARAM params[4] = {
+        OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END
+    };
 
     if (ctx == NULL || ctx->cipher == NULL) {
         EVPerr(EVP_F_EVP_CIPHER_CTX_CTRL, EVP_R_NO_CIPHER_SET);
@@ -1154,13 +1160,8 @@ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
         params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
                                                       ptr, sz);
         break;
-    case EVP_CTRL_AEAD_SET_MAC_KEY:
-        params[0] =
-            OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_MAC_KEY,
-                                              ptr, sz);
-        break;
     case EVP_CTRL_AEAD_TLS1_AAD:
-        /* This one does a set and a get - since it returns a padding size */
+        /* This one does a set and a get - since it returns a size */
         params[0] =
             OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD,
                                               ptr, sz);
@@ -1180,6 +1181,76 @@ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
         params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, &sz);
         break;
 #endif /* OPENSSL_NO_RC2 */
+#if !defined(OPENSSL_NO_MULTIBLOCK)
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE:
+        params[0] = OSSL_PARAM_construct_size_t(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT, &sz);
+        ret = evp_do_ciph_ctx_setparams(ctx->cipher, ctx->provctx, params);
+        if (ret <= 0)
+            return 0;
+
+        params[0] = OSSL_PARAM_construct_size_t(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE, &sz);
+        params[1] = OSSL_PARAM_construct_end();
+        ret = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->provctx, params);
+        if (ret <= 0)
+            return 0;
+        return sz;
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_AAD: {
+        EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *p =
+            (EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *)ptr;
+
+        if (arg < (int)sizeof(EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM))
+            return 0;
+
+        params[0] = OSSL_PARAM_construct_octet_string(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD, (void*)p->inp, p->len);
+        params[1] = OSSL_PARAM_construct_uint(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, &p->interleave);
+        ret = evp_do_ciph_ctx_setparams(ctx->cipher, ctx->provctx, params);
+        if (ret <= 0)
+            return ret;
+        /* Retrieve the return values changed by the set */
+        params[0] = OSSL_PARAM_construct_size_t(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN, &sz);
+        params[1] = OSSL_PARAM_construct_uint(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, &p->interleave);
+        params[2] = OSSL_PARAM_construct_end();
+        ret = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->provctx, params);
+        if (ret <= 0)
+            return 0;
+        return sz;
+    }
+    case EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT: {
+        EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *p =
+            (EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *)ptr;
+
+        params[0] = OSSL_PARAM_construct_octet_string(
+                        OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC, p->out, p->len);
+
+        params[1] = OSSL_PARAM_construct_octet_string(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN, (void*)p->inp,
+                p->len);
+        params[2] = OSSL_PARAM_construct_uint(
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, &p->interleave);
+        ret = evp_do_ciph_ctx_setparams(ctx->cipher, ctx->provctx, params);
+        if (ret <= 0)
+            return ret;
+        params[0] = OSSL_PARAM_construct_size_t(
+                        OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN, &sz);
+        params[1] = OSSL_PARAM_construct_end();
+        ret = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->provctx, params);
+        if (ret <= 0)
+            return 0;
+        return sz;
+    }
+#endif /* OPENSSL_NO_MULTIBLOCK */
+    case EVP_CTRL_AEAD_SET_MAC_KEY:
+        if (arg < 0)
+            return -1;
+        params[0] = OSSL_PARAM_construct_octet_string(
+                OSSL_CIPHER_PARAM_AEAD_MAC_KEY, ptr, sz);
+        break;
     }
 
     if (set_params)
index 5e64c0e..7ad239b 100644 (file)
@@ -349,6 +349,55 @@ by AES SIV ciphers which disallow multiple operations by default.
 Setting "speed" to 1 allows another encrypt or decrypt operation to be
 performed. This is used for performance testing.
 
+=item "tls1multi_enc" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC>) <octet string>
+
+Triggers a multiblock tls1 encrypt operation for a tls1 aware cipher that supports
+sending 4 or 8 records in one go.
+The cipher performs both the MAC and encrypt stages and constructs the record
+headers itself.
+"tls1multi_enc" supplies the output buffer for the encrypt operation,
+"tls1multi_encin" & "tls1multi_interleave" must also be set in order to supply
+values to the encrypt operation.
+
+=item "tls1multi_enclen" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN>) <unsigned integer>
+
+Get the total length of the record returned from the "tls1multi_enc" operation.
+
+=item "tls1multi_interleave" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE>) <unsigned integer>
+
+Sets or gets the number of records being sent in one go for a tls1 multiblock
+cipher operation (either 4 or 8 records).
+
+=item "tls1multi_encin" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN>) <octet string>
+
+Supplies the data to encrypt for a tls1 multiblock cipher operation.
+
+=item "tls1multi_maxsndfrag" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT>) <unsigned integer>
+
+Sets the maximum send fragment size for a tls1 multiblock cipher operation.
+It must be set before using "tls1multi_maxbufsz".
+The length of the "tls1multi_maxsndfrag" parameter should not exceed that of a B<size_t>.
+
+=item "tls1multi_maxbufsz" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE>) <unsigned integer>
+
+Gets the maximum record length for a tls1 multiblock cipher operation.
+The length of the "tls1multi_maxbufsz" parameter should not exceed that of a B<size_t>.
+
+=item "tls1multi_aad" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD>) <octet string>
+
+Sets the authenticated additional data used by a tls1 multiblock cipher operation.
+The supplied data consists of 13 bytes of record data containing:
+Bytes 0-7: The sequence number of the first record
+Byte 8: The record type
+Byte 9-10: The protocol version
+Byte 11-12: Input length (Always 0)
+
+"tls1multi_interleave" must also be set for this operation.
+
+=item "tls1multi_aadpacklen" (B<OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN>) <unsigned integer>
+
+Gets the result of running the "tls1multi_aad" operation.
+
 =back
 
 =head1 RETURN VALUES
index 483a194..b478520 100644 (file)
@@ -121,6 +121,13 @@ void gcm_ghash_v8(u64 Xi[2],const u128 Htable[16],const u8 *inp, size_t len);
 #  endif
 # endif /* OPENSSL_CPUID_OBJ */
 
+# if     defined(AES_ASM) &&     ( \
+         defined(__x86_64)       || defined(__x86_64__)  || \
+         defined(_M_AMD64)       || defined(_M_X64)      )
+#  define AES_CBC_HMAC_SHA_CAPABLE 1
+#  define AESNI_CBC_HMAC_SHA_CAPABLE (OPENSSL_ia32cap_P[1]&(1<<(57-32)))
+# endif
+
 # if     defined(AES_ASM) && !defined(I386_ONLY) &&      (  \
          ((defined(__i386)       || defined(__i386__)    || \
            defined(_M_IX86)) && defined(OPENSSL_IA32_SSE2))|| \
index e441ddf..446af5f 100644 (file)
@@ -73,6 +73,22 @@ extern "C" {
 /* For passing the AlgorithmIdentifier parameter in DER form */
 #define OSSL_CIPHER_PARAM_ALG_ID             "alg_id_param" /* octet_string */
 
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT                    \
+    "tls1multi_maxsndfrag" /* uint */
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE                          \
+    "tls1multi_maxbufsz"   /* size_t */
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE                           \
+    "tls1multi_interleave" /* uint */
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD                                  \
+    "tls1multi_aad"        /* octet_string */
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN                          \
+    "tls1multi_aadpacklen" /* uint */
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC                                  \
+    "tls1multi_enc"        /* octet_string */
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN                               \
+    "tls1multi_encin"      /* octet_string */
+#define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN                              \
+    "tls1multi_enclen"     /* size_t */
 
 /* digest parameters */
 #define OSSL_DIGEST_PARAM_XOFLEN     "xoflen"    /* size_t */
index 9925ac2..ca3550b 100644 (file)
@@ -101,3 +101,15 @@ int ossl_prov_macctx_load_from_params(EVP_MAC_CTX **macctx,
                                       const char *ciphername,
                                       const char *mdname,
                                       OPENSSL_CTX *ctx);
+
+typedef struct ag_capable_st {
+    OSSL_ALGORITHM alg;
+    int (*capable)(void);
+} OSSL_ALGORITHM_CAPABLE;
+
+/*
+ * Dynamically select algorithms by calling a capable() method.
+ * If this method is NULL or the method returns 1 then the algorithm is added.
+ */
+void ossl_prov_cache_exported_algorithms(const OSSL_ALGORITHM_CAPABLE *in,
+                                         OSSL_ALGORITHM *out);
index 569c08c..995c685 100644 (file)
@@ -13,3 +13,5 @@ const OSSL_PROVIDER *FIPS_get_provider(OPENSSL_CTX *ctx);
 
 const char *ossl_prov_util_nid_to_name(int nid);
 
+int cipher_capable_aes_cbc_hmac_sha1(void);
+int cipher_capable_aes_cbc_hmac_sha256(void);
index ef4396f..504463d 100644 (file)
@@ -237,3 +237,17 @@ int ossl_prov_macctx_load_from_params(EVP_MAC_CTX **macctx,
     *macctx = NULL;
     return 0;
 }
+
+void ossl_prov_cache_exported_algorithms(const OSSL_ALGORITHM_CAPABLE *in,
+                                         OSSL_ALGORITHM *out)
+{
+    int i, j;
+
+    if (out[0].algorithm_names == NULL) {
+        for (i = j = 0; in[i].alg.algorithm_names != NULL; ++i) {
+            if (in[i].capable == NULL || in[i].capable())
+                out[j++] = in[i].alg;
+        }
+        out[j++] = in[i].alg;
+    }
+}
index 51cd2b9..5c11b4a 100644 (file)
 #include <openssl/core_names.h>
 #include <openssl/params.h>
 #include "prov/bio.h"
+#include "prov/providercommon.h"
 #include "prov/implementations.h"
+#include "prov/provider_util.h"
+#include "internal/nelem.h"
+
+#define ALGC(NAMES, FUNC, CHECK) { { NAMES, "default=yes", FUNC }, CHECK }
+#define ALG(NAMES, FUNC) ALGC(NAMES, FUNC, NULL)
 
 /* Functions provided by the core */
 static OSSL_core_gettable_params_fn *c_gettable_params = NULL;
@@ -131,190 +137,196 @@ static const OSSL_ALGORITHM deflt_digests[] = {
     { NULL, NULL, NULL }
 };
 
-static const OSSL_ALGORITHM deflt_ciphers[] = {
-    { "AES-256-ECB", "default=yes", aes256ecb_functions },
-    { "AES-192-ECB", "default=yes", aes192ecb_functions },
-    { "AES-128-ECB", "default=yes", aes128ecb_functions },
-    { "AES-256-CBC", "default=yes", aes256cbc_functions },
-    { "AES-192-CBC", "default=yes", aes192cbc_functions },
-    { "AES-128-CBC", "default=yes", aes128cbc_functions },
-    { "AES-256-OFB", "default=yes", aes256ofb_functions },
-    { "AES-192-OFB", "default=yes", aes192ofb_functions },
-    { "AES-128-OFB", "default=yes", aes128ofb_functions },
-    { "AES-256-CFB", "default=yes", aes256cfb_functions },
-    { "AES-192-CFB", "default=yes", aes192cfb_functions },
-    { "AES-128-CFB", "default=yes", aes128cfb_functions },
-    { "AES-256-CFB1", "default=yes", aes256cfb1_functions },
-    { "AES-192-CFB1", "default=yes", aes192cfb1_functions },
-    { "AES-128-CFB1", "default=yes", aes128cfb1_functions },
-    { "AES-256-CFB8", "default=yes", aes256cfb8_functions },
-    { "AES-192-CFB8", "default=yes", aes192cfb8_functions },
-    { "AES-128-CFB8", "default=yes", aes128cfb8_functions },
-    { "AES-256-CTR", "default=yes", aes256ctr_functions },
-    { "AES-192-CTR", "default=yes", aes192ctr_functions },
-    { "AES-128-CTR", "default=yes", aes128ctr_functions },
-    { "AES-256-XTS", "default=yes", aes256xts_functions },
-    { "AES-128-XTS", "default=yes", aes128xts_functions },
+static const OSSL_ALGORITHM_CAPABLE deflt_ciphers[] = {
+    ALG("AES-256-ECB", aes256ecb_functions),
+    ALG("AES-192-ECB", aes192ecb_functions),
+    ALG("AES-128-ECB", aes128ecb_functions),
+    ALG("AES-256-CBC", aes256cbc_functions),
+    ALG("AES-192-CBC", aes192cbc_functions),
+    ALG("AES-128-CBC", aes128cbc_functions),
+    ALG("AES-256-OFB", aes256ofb_functions),
+    ALG("AES-192-OFB", aes192ofb_functions),
+    ALG("AES-128-OFB", aes128ofb_functions),
+    ALG("AES-256-CFB", aes256cfb_functions),
+    ALG("AES-192-CFB", aes192cfb_functions),
+    ALG("AES-128-CFB", aes128cfb_functions),
+    ALG("AES-256-CFB1", aes256cfb1_functions),
+    ALG("AES-192-CFB1", aes192cfb1_functions),
+    ALG("AES-128-CFB1", aes128cfb1_functions),
+    ALG("AES-256-CFB8", aes256cfb8_functions),
+    ALG("AES-192-CFB8", aes192cfb8_functions),
+    ALG("AES-128-CFB8", aes128cfb8_functions),
+    ALG("AES-256-CTR", aes256ctr_functions),
+    ALG("AES-192-CTR", aes192ctr_functions),
+    ALG("AES-128-CTR", aes128ctr_functions),
+    ALG("AES-256-XTS", aes256xts_functions),
+    ALG("AES-128-XTS", aes128xts_functions),
 #ifndef OPENSSL_NO_OCB
-    { "AES-256-OCB", "default=yes", aes256ocb_functions },
-    { "AES-192-OCB", "default=yes", aes192ocb_functions },
-    { "AES-128-OCB", "default=yes", aes128ocb_functions },
+    ALG("AES-256-OCB", aes256ocb_functions),
+    ALG("AES-192-OCB", aes192ocb_functions),
+    ALG("AES-128-OCB", aes128ocb_functions),
 #endif /* OPENSSL_NO_OCB */
 #ifndef OPENSSL_NO_SIV
-    { "AES-128-SIV", "default=yes", aes128siv_functions },
-    { "AES-192-SIV", "default=yes", aes192siv_functions },
-    { "AES-256-SIV", "default=yes", aes256siv_functions },
+    ALG("AES-128-SIV", aes128siv_functions),
+    ALG("AES-192-SIV", aes192siv_functions),
+    ALG("AES-256-SIV", aes256siv_functions),
 #endif /* OPENSSL_NO_SIV */
-    { "AES-256-GCM:id-aes256-GCM", "default=yes", aes256gcm_functions },
-    { "AES-192-GCM:id-aes192-GCM", "default=yes", aes192gcm_functions },
-    { "AES-128-GCM:id-aes128-GCM", "default=yes", aes128gcm_functions },
-    { "AES-256-CCM:id-aes256-CCM", "default=yes", aes256ccm_functions },
-    { "AES-192-CCM:id-aes192-CCM", "default=yes", aes192ccm_functions },
-    { "AES-128-CCM:id-aes128-CCM", "default=yes", aes128ccm_functions },
-    { "AES-256-WRAP:id-aes256-wrap:AES256-WRAP", "default=yes",
-      aes256wrap_functions },
-    { "AES-192-WRAP:id-aes192-wrap:AES192-WRAP", "default=yes",
-      aes192wrap_functions },
-    { "AES-128-WRAP:id-aes128-wrap:AES128-WRAP", "default=yes",
-      aes128wrap_functions },
-    { "AES-256-WRAP-PAD:id-aes256-wrap-pad:AES256-WRAP-PAD", "default=yes",
-      aes256wrappad_functions },
-    { "AES-192-WRAP-PAD:id-aes192-wrap-pad:AES192-WRAP-PAD", "default=yes",
-      aes192wrappad_functions },
-    { "AES-128-WRAP-PAD:id-aes128-wrap-pad:AES128-WRAP-PAD", "default=yes",
-      aes128wrappad_functions },
+    ALG("AES-256-GCM:id-aes256-GCM", aes256gcm_functions),
+    ALG("AES-192-GCM:id-aes192-GCM", aes192gcm_functions),
+    ALG("AES-128-GCM:id-aes128-GCM", aes128gcm_functions),
+    ALG("AES-256-CCM:id-aes256-CCM", aes256ccm_functions),
+    ALG("AES-192-CCM:id-aes192-CCM", aes192ccm_functions),
+    ALG("AES-128-CCM:id-aes128-CCM", aes128ccm_functions),
+    ALG("AES-256-WRAP:id-aes256-wrap:AES256-WRAP", aes256wrap_functions),
+    ALG("AES-192-WRAP:id-aes192-wrap:AES192-WRAP", aes192wrap_functions),
+    ALG("AES-128-WRAP:id-aes128-wrap:AES128-WRAP", aes128wrap_functions),
+    ALG("AES-256-WRAP-PAD:id-aes256-wrap-pad:AES256-WRAP-PAD",
+        aes256wrappad_functions),
+    ALG("AES-192-WRAP-PAD:id-aes192-wrap-pad:AES192-WRAP-PAD",
+        aes192wrappad_functions),
+    ALG("AES-128-WRAP-PAD:id-aes128-wrap-pad:AES128-WRAP-PAD",
+        aes128wrappad_functions),
+    ALGC("AES-128-CBC-HMAC-SHA1", aes128cbc_hmac_sha1_functions,
+         cipher_capable_aes_cbc_hmac_sha1),
+    ALGC("AES-256-CBC-HMAC-SHA1", aes256cbc_hmac_sha1_functions,
+         cipher_capable_aes_cbc_hmac_sha1),
+    ALGC("AES-128-CBC-HMAC-SHA256", aes128cbc_hmac_sha256_functions,
+        cipher_capable_aes_cbc_hmac_sha256),
+    ALGC("AES-256-CBC-HMAC-SHA256", aes256cbc_hmac_sha256_functions,
+         cipher_capable_aes_cbc_hmac_sha256),
 #ifndef OPENSSL_NO_ARIA
-    { "ARIA-256-GCM", "default=yes", aria256gcm_functions },
-    { "ARIA-192-GCM", "default=yes", aria192gcm_functions },
-    { "ARIA-128-GCM", "default=yes", aria128gcm_functions },
-    { "ARIA-256-CCM", "default=yes", aria256ccm_functions },
-    { "ARIA-192-CCM", "default=yes", aria192ccm_functions },
-    { "ARIA-128-CCM", "default=yes", aria128ccm_functions },
-    { "ARIA-256-ECB", "default=yes", aria256ecb_functions },
-    { "ARIA-192-ECB", "default=yes", aria192ecb_functions },
-    { "ARIA-128-ECB", "default=yes", aria128ecb_functions },
-    { "ARIA-256-CBC:ARIA256", "default=yes", aria256cbc_functions },
-    { "ARIA-192-CBC:ARIA192", "default=yes", aria192cbc_functions },
-    { "ARIA-128-CBC:ARIA128", "default=yes", aria128cbc_functions },
-    { "ARIA-256-OFB", "default=yes", aria256ofb_functions },
-    { "ARIA-192-OFB", "default=yes", aria192ofb_functions },
-    { "ARIA-128-OFB", "default=yes", aria128ofb_functions },
-    { "ARIA-256-CFB", "default=yes", aria256cfb_functions },
-    { "ARIA-192-CFB", "default=yes", aria192cfb_functions },
-    { "ARIA-128-CFB", "default=yes", aria128cfb_functions },
-    { "ARIA-256-CFB1", "default=yes", aria256cfb1_functions },
-    { "ARIA-192-CFB1", "default=yes", aria192cfb1_functions },
-    { "ARIA-128-CFB1", "default=yes", aria128cfb1_functions },
-    { "ARIA-256-CFB8", "default=yes", aria256cfb8_functions },
-    { "ARIA-192-CFB8", "default=yes", aria192cfb8_functions },
-    { "ARIA-128-CFB8", "default=yes", aria128cfb8_functions },
-    { "ARIA-256-CTR", "default=yes", aria256ctr_functions },
-    { "ARIA-192-CTR", "default=yes", aria192ctr_functions },
-    { "ARIA-128-CTR", "default=yes", aria128ctr_functions },
+    ALG("ARIA-256-GCM", aria256gcm_functions),
+    ALG("ARIA-192-GCM", aria192gcm_functions),
+    ALG("ARIA-128-GCM", aria128gcm_functions),
+    ALG("ARIA-256-CCM", aria256ccm_functions),
+    ALG("ARIA-192-CCM", aria192ccm_functions),
+    ALG("ARIA-128-CCM", aria128ccm_functions),
+    ALG("ARIA-256-ECB", aria256ecb_functions),
+    ALG("ARIA-192-ECB", aria192ecb_functions),
+    ALG("ARIA-128-ECB", aria128ecb_functions),
+    ALG("ARIA-256-CBC:ARIA256", aria256cbc_functions),
+    ALG("ARIA-192-CBC:ARIA192", aria192cbc_functions),
+    ALG("ARIA-128-CBC:ARIA128", aria128cbc_functions),
+    ALG("ARIA-256-OFB", aria256ofb_functions),
+    ALG("ARIA-192-OFB", aria192ofb_functions),
+    ALG("ARIA-128-OFB", aria128ofb_functions),
+    ALG("ARIA-256-CFB", aria256cfb_functions),
+    ALG("ARIA-192-CFB", aria192cfb_functions),
+    ALG("ARIA-128-CFB", aria128cfb_functions),
+    ALG("ARIA-256-CFB1", aria256cfb1_functions),
+    ALG("ARIA-192-CFB1", aria192cfb1_functions),
+    ALG("ARIA-128-CFB1", aria128cfb1_functions),
+    ALG("ARIA-256-CFB8", aria256cfb8_functions),
+    ALG("ARIA-192-CFB8", aria192cfb8_functions),
+    ALG("ARIA-128-CFB8", aria128cfb8_functions),
+    ALG("ARIA-256-CTR", aria256ctr_functions),
+    ALG("ARIA-192-CTR", aria192ctr_functions),
+    ALG("ARIA-128-CTR", aria128ctr_functions),
 #endif /* OPENSSL_NO_ARIA */
 #ifndef OPENSSL_NO_CAMELLIA
-    { "CAMELLIA-256-ECB", "default=yes", camellia256ecb_functions },
-    { "CAMELLIA-192-ECB", "default=yes", camellia192ecb_functions },
-    { "CAMELLIA-128-ECB", "default=yes", camellia128ecb_functions },
-    { "CAMELLIA-256-CBC:CAMELLIA256", "default=yes", camellia256cbc_functions },
-    { "CAMELLIA-192-CBC:CAMELLIA192", "default=yes", camellia192cbc_functions },
-    { "CAMELLIA-128-CBC:CAMELLIA128", "default=yes", camellia128cbc_functions },
-    { "CAMELLIA-256-OFB", "default=yes", camellia256ofb_functions },
-    { "CAMELLIA-192-OFB", "default=yes", camellia192ofb_functions },
-    { "CAMELLIA-128-OFB", "default=yes", camellia128ofb_functions },
-    { "CAMELLIA-256-CFB", "default=yes", camellia256cfb_functions },
-    { "CAMELLIA-192-CFB", "default=yes", camellia192cfb_functions },
-    { "CAMELLIA-128-CFB", "default=yes", camellia128cfb_functions },
-    { "CAMELLIA-256-CFB1", "default=yes", camellia256cfb1_functions },
-    { "CAMELLIA-192-CFB1", "default=yes", camellia192cfb1_functions },
-    { "CAMELLIA-128-CFB1", "default=yes", camellia128cfb1_functions },
-    { "CAMELLIA-256-CFB8", "default=yes", camellia256cfb8_functions },
-    { "CAMELLIA-192-CFB8", "default=yes", camellia192cfb8_functions },
-    { "CAMELLIA-128-CFB8", "default=yes", camellia128cfb8_functions },
-    { "CAMELLIA-256-CTR", "default=yes", camellia256ctr_functions },
-    { "CAMELLIA-192-CTR", "default=yes", camellia192ctr_functions },
-    { "CAMELLIA-128-CTR", "default=yes", camellia128ctr_functions },
+    ALG("CAMELLIA-256-ECB", camellia256ecb_functions),
+    ALG("CAMELLIA-192-ECB", camellia192ecb_functions),
+    ALG("CAMELLIA-128-ECB", camellia128ecb_functions),
+    ALG("CAMELLIA-256-CBC:CAMELLIA256", camellia256cbc_functions),
+    ALG("CAMELLIA-192-CBC:CAMELLIA192", camellia192cbc_functions),
+    ALG("CAMELLIA-128-CBC:CAMELLIA128", camellia128cbc_functions),
+    ALG("CAMELLIA-256-OFB", camellia256ofb_functions),
+    ALG("CAMELLIA-192-OFB", camellia192ofb_functions),
+    ALG("CAMELLIA-128-OFB", camellia128ofb_functions),
+    ALG("CAMELLIA-256-CFB", camellia256cfb_functions),
+    ALG("CAMELLIA-192-CFB", camellia192cfb_functions),
+    ALG("CAMELLIA-128-CFB", camellia128cfb_functions),
+    ALG("CAMELLIA-256-CFB1", camellia256cfb1_functions),
+    ALG("CAMELLIA-192-CFB1", camellia192cfb1_functions),
+    ALG("CAMELLIA-128-CFB1", camellia128cfb1_functions),
+    ALG("CAMELLIA-256-CFB8", camellia256cfb8_functions),
+    ALG("CAMELLIA-192-CFB8", camellia192cfb8_functions),
+    ALG("CAMELLIA-128-CFB8", camellia128cfb8_functions),
+    ALG("CAMELLIA-256-CTR", camellia256ctr_functions),
+    ALG("CAMELLIA-192-CTR", camellia192ctr_functions),
+    ALG("CAMELLIA-128-CTR", camellia128ctr_functions),
 #endif /* OPENSSL_NO_CAMELLIA */
 #ifndef OPENSSL_NO_DES
-    { "DES-EDE3-ECB:DES-EDE3", "default=yes", tdes_ede3_ecb_functions },
-    { "DES-EDE3-CBC:DES3", "default=yes", tdes_ede3_cbc_functions },
-    { "DES-EDE3-OFB", "default=yes", tdes_ede3_ofb_functions },
-    { "DES-EDE3-CFB", "default=yes", tdes_ede3_cfb_functions },
-    { "DES-EDE3-CFB8", "default=yes", tdes_ede3_cfb8_functions },
-    { "DES-EDE3-CFB1", "default=yes", tdes_ede3_cfb1_functions },
-    { "DES-EDE-ECB:DES-EDE", "default=yes", tdes_ede2_ecb_functions },
-    { "DES-EDE-CBC", "default=yes", tdes_ede2_cbc_functions },
-    { "DES-EDE-OFB", "default=yes", tdes_ede2_ofb_functions },
-    { "DES-EDE-CFB", "default=yes", tdes_ede2_cfb_functions },
-    { "DESX-CBC:DESX", "default=yes", tdes_desx_cbc_functions },
-    { "DES3-WRAP:id-smime-alg-CMS3DESwrap", "default=yes", tdes_wrap_cbc_functions },
-    { "DES-ECB", "default=yes", des_ecb_functions },
-    { "DES-CBC:DES", "default=yes", des_cbc_functions },
-    { "DES-OFB", "default=yes", des_ofb64_functions },
-    { "DES-CFB", "default=yes", des_cfb64_functions },
-    { "DES-CFB1", "default=yes", des_cfb1_functions },
-    { "DES-CFB8", "default=yes", des_cfb8_functions },
+    ALG("DES-EDE3-ECB:DES-EDE3", tdes_ede3_ecb_functions),
+    ALG("DES-EDE3-CBC:DES3", tdes_ede3_cbc_functions),
+    ALG("DES-EDE3-OFB", tdes_ede3_ofb_functions),
+    ALG("DES-EDE3-CFB", tdes_ede3_cfb_functions),
+    ALG("DES-EDE3-CFB8", tdes_ede3_cfb8_functions),
+    ALG("DES-EDE3-CFB1", tdes_ede3_cfb1_functions),
+    ALG("DES-EDE-ECB:DES-EDE", tdes_ede2_ecb_functions),
+    ALG("DES-EDE-CBC", tdes_ede2_cbc_functions),
+    ALG("DES-EDE-OFB", tdes_ede2_ofb_functions),
+    ALG("DES-EDE-CFB", tdes_ede2_cfb_functions),
+    ALG("DESX-CBC:DESX", tdes_desx_cbc_functions),
+    ALG("DES3-WRAP:id-smime-alg-CMS3DESwrap", tdes_wrap_cbc_functions),
+    ALG("DES-ECB", des_ecb_functions),
+    ALG("DES-CBC:DES", des_cbc_functions),
+    ALG("DES-OFB", des_ofb64_functions),
+    ALG("DES-CFB", des_cfb64_functions),
+    ALG("DES-CFB1", des_cfb1_functions),
+    ALG("DES-CFB8", des_cfb8_functions),
 #endif /* OPENSSL_NO_DES */
 #ifndef OPENSSL_NO_BF
-    { "BF-ECB", "default=yes", blowfish128ecb_functions },
-    { "BF-CBC:BF:BLOWFISH", "default=yes", blowfish128cbc_functions },
-    { "BF-OFB", "default=yes", blowfish64ofb64_functions },
-    { "BF-CFB", "default=yes", blowfish64cfb64_functions },
+    ALG("BF-ECB", blowfish128ecb_functions),
+    ALG("BF-CBC:BF:BLOWFISH", blowfish128cbc_functions),
+    ALG("BF-OFB", blowfish64ofb64_functions),
+    ALG("BF-CFB", blowfish64cfb64_functions),
 #endif /* OPENSSL_NO_BF */
 #ifndef OPENSSL_NO_IDEA
-    { "IDEA-ECB", "default=yes", idea128ecb_functions },
-    { "IDEA-CBC:IDEA", "default=yes", idea128cbc_functions },
-    { "IDEA-OFB:IDEA-OFB64", "default=yes", idea128ofb64_functions },
-    { "IDEA-CFB:IDEA-CFB64", "default=yes", idea128cfb64_functions },
+    ALG("IDEA-ECB", idea128ecb_functions),
+    ALG("IDEA-CBC:IDEA", idea128cbc_functions),
+    ALG("IDEA-OFB:IDEA-OFB64", idea128ofb64_functions),
+    ALG("IDEA-CFB:IDEA-CFB64", idea128cfb64_functions),
 #endif /* OPENSSL_NO_IDEA */
 #ifndef OPENSSL_NO_CAST
-    { "CAST5-ECB", "default=yes", cast5128ecb_functions },
-    { "CAST5-CBC:CAST-CBC:CAST", "default=yes", cast5128cbc_functions },
-    { "CAST5-OFB", "default=yes", cast564ofb64_functions },
-    { "CAST5-CFB", "default=yes", cast564cfb64_functions },
+    ALG("CAST5-ECB", cast5128ecb_functions),
+    ALG("CAST5-CBC:CAST-CBC:CAST", cast5128cbc_functions),
+    ALG("CAST5-OFB", cast564ofb64_functions),
+    ALG("CAST5-CFB", cast564cfb64_functions),
 #endif /* OPENSSL_NO_CAST */
 #ifndef OPENSSL_NO_SEED
-    { "SEED-ECB", "default=yes", seed128ecb_functions },
-    { "SEED-CBC:SEED", "default=yes", seed128cbc_functions },
-    { "SEED-OFB:SEED-OFB128", "default=yes", seed128ofb128_functions },
-    { "SEED-CFB:SEED-CFB128", "default=yes", seed128cfb128_functions },
+    ALG("SEED-ECB", seed128ecb_functions),
+    ALG("SEED-CBC:SEED", seed128cbc_functions),
+    ALG("SEED-OFB:SEED-OFB128", seed128ofb128_functions),
+    ALG("SEED-CFB:SEED-CFB128", seed128cfb128_functions),
 #endif /* OPENSSL_NO_SEED */
 #ifndef OPENSSL_NO_SM4
-    { "SM4-ECB", "default=yes", sm4128ecb_functions },
-    { "SM4-CBC:SM4", "default=yes", sm4128cbc_functions },
-    { "SM4-CTR", "default=yes", sm4128ctr_functions },
-    { "SM4-OFB:SM4-OFB128", "default=yes", sm4128ofb128_functions },
-    { "SM4-CFB:SM4-CFB128", "default=yes", sm4128cfb128_functions },
+    ALG("SM4-ECB", sm4128ecb_functions),
+    ALG("SM4-CBC:SM4", sm4128cbc_functions),
+    ALG("SM4-CTR", sm4128ctr_functions),
+    ALG("SM4-OFB:SM4-OFB128", sm4128ofb128_functions),
+    ALG("SM4-CFB:SM4-CFB128", sm4128cfb128_functions),
 #endif /* OPENSSL_NO_SM4 */
 #ifndef OPENSSL_NO_RC4
-    { "RC4", "default=yes", rc4128_functions },
-    { "RC4-40", "default=yes", rc440_functions },
+    ALG("RC4", rc4128_functions),
+    ALG("RC4-40", rc440_functions),
 # ifndef OPENSSL_NO_MD5
-    { "RC4-HMAC-MD5", "default=yes", rc4_hmac_md5_functions },
+    ALG("RC4-HMAC-MD5", rc4_hmac_md5_functions),
 # endif /* OPENSSL_NO_MD5 */
 #endif /* OPENSSL_NO_RC4 */
 #ifndef OPENSSL_NO_RC5
-    { "RC5-ECB", "default=yes", rc5128ecb_functions },
-    { "RC5-CBC", "default=yes", rc5128cbc_functions },
-    { "RC5-OFB", "default=yes", rc5128ofb64_functions },
-    { "RC5-CFB", "default=yes", rc5128cfb64_functions },
+    ALG("RC5-ECB", rc5128ecb_functions),
+    ALG("RC5-CBC", rc5128cbc_functions),
+    ALG("RC5-OFB", rc5128ofb64_functions),
+    ALG("RC5-CFB", rc5128cfb64_functions),
 #endif /* OPENSSL_NO_RC5 */
 #ifndef OPENSSL_NO_RC2
-    { "RC2-ECB", "default=yes", rc2128ecb_functions },
-    { "RC2-CBC", "default=yes", rc2128cbc_functions },
-    { "RC2-40-CBC", "default=yes", rc240cbc_functions },
-    { "RC2-64-CBC", "default=yes", rc264cbc_functions },
-    { "RC2-CFB", "default=yes", rc2128cfb128_functions },
-    { "RC2-OFB", "default=yes", rc2128ofb128_functions },
+    ALG("RC2-ECB", rc2128ecb_functions),
+    ALG("RC2-CBC", rc2128cbc_functions),
+    ALG("RC2-40-CBC", rc240cbc_functions),
+    ALG("RC2-64-CBC", rc264cbc_functions),
+    ALG("RC2-CFB", rc2128cfb128_functions),
+    ALG("RC2-OFB", rc2128ofb128_functions),
 #endif /* OPENSSL_NO_RC2 */
 #ifndef OPENSSL_NO_CHACHA
-    { "ChaCha20", "default=yes", chacha20_functions },
+    ALG("ChaCha20", chacha20_functions),
 # ifndef OPENSSL_NO_POLY1305
-    { "ChaCha20-Poly1305", "default=yes", chacha20_poly1305_functions },
+    ALG("ChaCha20-Poly1305", chacha20_poly1305_functions),
 # endif /* OPENSSL_NO_POLY1305 */
 #endif /* OPENSSL_NO_CHACHA */
-    { NULL, NULL, NULL }
+    { { NULL, NULL, NULL }, NULL }
 };
+static OSSL_ALGORITHM exported_ciphers[OSSL_NELEM(deflt_ciphers)];
 
 static const OSSL_ALGORITHM deflt_macs[] = {
 #ifndef OPENSSL_NO_BLAKE2
@@ -432,7 +444,8 @@ static const OSSL_ALGORITHM *deflt_query(OSSL_PROVIDER *prov,
     case OSSL_OP_DIGEST:
         return deflt_digests;
     case OSSL_OP_CIPHER:
-        return deflt_ciphers;
+        ossl_prov_cache_exported_algorithms(deflt_ciphers, exported_ciphers);
+        return exported_ciphers;
     case OSSL_OP_MAC:
         return deflt_macs;
     case OSSL_OP_KDF:
index 12c471f..7afab78 100644 (file)
 
 #include "internal/cryptlib.h"
 #include "internal/property.h"
+#include "internal/nelem.h"
 #include "crypto/evp.h"
 #include "prov/implementations.h"
 #include "prov/provider_ctx.h"
 #include "prov/providercommon.h"
+#include "prov/provider_util.h"
 #include "selftest.h"
 
+#define ALGC(NAMES, FUNC, CHECK) { { NAMES, "fips=yes", FUNC }, CHECK }
+#define ALG(NAMES, FUNC) ALGC(NAMES, FUNC, NULL)
+
 extern OSSL_core_thread_start_fn *c_thread_start;
 
 /*
@@ -300,6 +305,14 @@ const char *ossl_prov_util_nid_to_name(int nid)
         return "DES-EDE3";
     case NID_des_ede3_cbc:
         return "DES-EDE3-CBC";
+    case NID_aes_256_cbc_hmac_sha256:
+        return "AES-256-CBC-HMAC-SHA256";
+    case NID_aes_128_cbc_hmac_sha256:
+        return "AES-128-CBC-HMAC-SHA256";
+    case NID_aes_256_cbc_hmac_sha1:
+        return "AES-256-CBC-HMAC-SHA1";
+    case NID_aes_128_cbc_hmac_sha1:
+        return "AES-128-CBC-HMAC-SHA1";
     default:
         break;
     }
@@ -358,43 +371,49 @@ static const OSSL_ALGORITHM fips_digests[] = {
     { NULL, NULL, NULL }
 };
 
-static const OSSL_ALGORITHM fips_ciphers[] = {
+static const OSSL_ALGORITHM_CAPABLE fips_ciphers[] = {
     /* Our primary name[:ASN.1 OID name][:our older names] */
-    { "AES-256-ECB", "fips=yes", aes256ecb_functions },
-    { "AES-192-ECB", "fips=yes", aes192ecb_functions },
-    { "AES-128-ECB", "fips=yes", aes128ecb_functions },
-    { "AES-256-CBC", "fips=yes", aes256cbc_functions },
-    { "AES-192-CBC", "fips=yes", aes192cbc_functions },
-    { "AES-128-CBC", "fips=yes", aes128cbc_functions },
-    { "AES-256-CTR", "fips=yes", aes256ctr_functions },
-    { "AES-192-CTR", "fips=yes", aes192ctr_functions },
-    { "AES-128-CTR", "fips=yes", aes128ctr_functions },
-    { "AES-256-XTS", "fips=yes", aes256xts_functions },
-    { "AES-128-XTS", "fips=yes", aes128xts_functions },
-    { "AES-256-GCM:id-aes256-GCM", "fips=yes", aes256gcm_functions },
-    { "AES-192-GCM:id-aes192-GCM", "fips=yes", aes192gcm_functions },
-    { "AES-128-GCM:id-aes128-GCM", "fips=yes", aes128gcm_functions },
-    { "AES-256-CCM:id-aes256-CCM", "fips=yes", aes256ccm_functions },
-    { "AES-192-CCM:id-aes192-CCM", "fips=yes", aes192ccm_functions },
-    { "AES-128-CCM:id-aes128-CCM", "fips=yes", aes128ccm_functions },
-    { "AES-256-WRAP:id-aes256-wrap:AES256-WRAP", "fips=yes",
-      aes256wrap_functions },
-    { "AES-192-WRAP:id-aes192-wrap:AES192-WRAP", "fips=yes",
-      aes192wrap_functions },
-    { "AES-128-WRAP:id-aes128-wrap:AES128-WRAP", "fips=yes",
-      aes128wrap_functions },
-    { "AES-256-WRAP-PAD:id-aes256-wrap-pad:AES256-WRAP-PAD", "fips=yes",
-      aes256wrappad_functions },
-    { "AES-192-WRAP-PAD:id-aes192-wrap-pad:AES192-WRAP-PAD", "fips=yes",
-      aes192wrappad_functions },
-    { "AES-128-WRAP-PAD:id-aes128-wrap-pad:AES128-WRAP-PAD", "fips=yes",
-      aes128wrappad_functions },
+    ALG("AES-256-ECB", aes256ecb_functions),
+    ALG("AES-192-ECB", aes192ecb_functions),
+    ALG("AES-128-ECB", aes128ecb_functions),
+    ALG("AES-256-CBC", aes256cbc_functions),
+    ALG("AES-192-CBC", aes192cbc_functions),
+    ALG("AES-128-CBC", aes128cbc_functions),
+    ALG("AES-256-CTR", aes256ctr_functions),
+    ALG("AES-192-CTR", aes192ctr_functions),
+    ALG("AES-128-CTR", aes128ctr_functions),
+    ALG("AES-256-XTS", aes256xts_functions),
+    ALG("AES-128-XTS", aes128xts_functions),
+    ALG("AES-256-GCM:id-aes256-GCM", aes256gcm_functions),
+    ALG("AES-192-GCM:id-aes192-GCM", aes192gcm_functions),
+    ALG("AES-128-GCM:id-aes128-GCM", aes128gcm_functions),
+    ALG("AES-256-CCM:id-aes256-CCM", aes256ccm_functions),
+    ALG("AES-192-CCM:id-aes192-CCM", aes192ccm_functions),
+    ALG("AES-128-CCM:id-aes128-CCM", aes128ccm_functions),
+    ALG("AES-256-WRAP:id-aes256-wrap:AES256-WRAP", aes256wrap_functions),
+    ALG("AES-192-WRAP:id-aes192-wrap:AES192-WRAP", aes192wrap_functions),
+    ALG("AES-128-WRAP:id-aes128-wrap:AES128-WRAP", aes128wrap_functions),
+    ALG("AES-256-WRAP-PAD:id-aes256-wrap-pad:AES256-WRAP-PAD",
+        aes256wrappad_functions),
+    ALG("AES-192-WRAP-PAD:id-aes192-wrap-pad:AES192-WRAP-PAD",
+        aes192wrappad_functions),
+    ALG("AES-128-WRAP-PAD:id-aes128-wrap-pad:AES128-WRAP-PAD",
+        aes128wrappad_functions),
+    ALGC("AES-128-CBC-HMAC-SHA1", aes128cbc_hmac_sha1_functions,
+         cipher_capable_aes_cbc_hmac_sha1),
+    ALGC("AES-256-CBC-HMAC-SHA1", aes256cbc_hmac_sha1_functions,
+         cipher_capable_aes_cbc_hmac_sha1),
+    ALGC("AES-128-CBC-HMAC-SHA256", aes128cbc_hmac_sha256_functions,
+         cipher_capable_aes_cbc_hmac_sha256),
+    ALGC("AES-256-CBC-HMAC-SHA256", aes256cbc_hmac_sha256_functions,
+         cipher_capable_aes_cbc_hmac_sha256),
 #ifndef OPENSSL_NO_DES
-    { "DES-EDE3-ECB:DES-EDE3", "fips=yes", tdes_ede3_ecb_functions },
-    { "DES-EDE3-CBC:DES3", "fips=yes", tdes_ede3_cbc_functions },
+    ALG("DES-EDE3-ECB:DES-EDE3", tdes_ede3_ecb_functions),
+    ALG("DES-EDE3-CBC:DES3", tdes_ede3_cbc_functions),
 #endif  /* OPENSSL_NO_DES */
-    { NULL, NULL, NULL }
+    { { NULL, NULL, NULL }, NULL }
 };
+static OSSL_ALGORITHM exported_fips_ciphers[OSSL_NELEM(fips_ciphers)];
 
 static const OSSL_ALGORITHM fips_macs[] = {
 #ifndef OPENSSL_NO_CMAC
@@ -416,6 +435,7 @@ static const OSSL_ALGORITHM fips_kdfs[] = {
     { NULL, NULL, NULL }
 };
 
+
 static const OSSL_ALGORITHM *fips_query(OSSL_PROVIDER *prov,
                                          int operation_id,
                                          int *no_cache)
@@ -425,7 +445,8 @@ static const OSSL_ALGORITHM *fips_query(OSSL_PROVIDER *prov,
     case OSSL_OP_DIGEST:
         return fips_digests;
     case OSSL_OP_CIPHER:
-        return fips_ciphers;
+        ossl_prov_cache_exported_algorithms(fips_ciphers, exported_fips_ciphers);
+        return exported_fips_ciphers;
     case OSSL_OP_MAC:
         return fips_macs;
     case OSSL_OP_KDF:
index abc193b..bff5a2d 100644 (file)
@@ -40,7 +40,10 @@ SOURCE[$AES_GOAL]=\
         cipher_aes_xts.c cipher_aes_xts_hw.c \
         cipher_aes_gcm.c cipher_aes_gcm_hw.c \
         cipher_aes_ccm.c cipher_aes_ccm_hw.c \
-        cipher_aes_wrp.c
+        cipher_aes_wrp.c \
+        cipher_aes_cbc_hmac_sha.c \
+        cipher_aes_cbc_hmac_sha256_hw.c cipher_aes_cbc_hmac_sha1_hw.c
+
 # Extra code to satisfy the FIPS and non-FIPS separation.
 # When the AES-xxx-XTS moves to legacy, this can be removed.
 SOURCE[../../libfips.a]=cipher_aes_xts_fips.c
@@ -129,4 +132,3 @@ IF[{- !$disabled{chacha} -}]
       cipher_chacha20_poly1305.c cipher_chacha20_poly1305_hw.c
  ENDIF
 ENDIF
-
diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c
new file mode 100644 (file)
index 0000000..00b46c3
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * 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
+ */
+
+/* Dispatch functions for AES_CBC_HMAC_SHA ciphers */
+
+
+#include "cipher_aes_cbc_hmac_sha.h"
+#include "prov/implementations.h"
+
+#ifndef AES_CBC_HMAC_SHA_CAPABLE
+# define IMPLEMENT_CIPHER(nm, sub, kbits, blkbits, ivbits, flags)              \
+const OSSL_DISPATCH nm##kbits##sub##_functions[] = {                           \
+    { 0, NULL }                                                                \
+};
+#else
+# include "prov/providercommonerr.h"
+
+/* TODO(3.0) Figure out what flags are required */
+# define AES_CBC_HMAC_SHA_FLAGS (EVP_CIPH_CBC_MODE                             \
+                                 | EVP_CIPH_FLAG_DEFAULT_ASN1                  \
+                                 | EVP_CIPH_FLAG_AEAD_CIPHER                   \
+                                 | EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK)
+
+static OSSL_OP_cipher_freectx_fn aes_cbc_hmac_sha1_freectx;
+static OSSL_OP_cipher_freectx_fn aes_cbc_hmac_sha256_freectx;
+static OSSL_OP_cipher_get_ctx_params_fn aes_get_ctx_params;
+static OSSL_OP_cipher_gettable_ctx_params_fn aes_gettable_ctx_params;
+static OSSL_OP_cipher_set_ctx_params_fn aes_set_ctx_params;
+static OSSL_OP_cipher_settable_ctx_params_fn aes_settable_ctx_params;
+# define aes_gettable_params cipher_generic_gettable_params
+# define aes_einit cipher_generic_einit
+# define aes_dinit cipher_generic_dinit
+# define aes_update cipher_generic_stream_update
+# define aes_final cipher_generic_stream_final
+# define aes_cipher cipher_generic_cipher
+
+static const OSSL_PARAM cipher_aes_known_settable_ctx_params[] = {
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_MAC_KEY, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0),
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT, NULL),
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD, NULL),
+    OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, NULL),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN, NULL, 0),
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+    OSSL_PARAM_END
+};
+const OSSL_PARAM *aes_settable_ctx_params(void)
+{
+    return cipher_aes_known_settable_ctx_params;
+}
+
+static int aes_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_CIPHER_HW_AES_HMAC_SHA *hw =
+       (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->hw;
+    EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
+    const OSSL_PARAM *p, *p1, *pin;
+    int ret = 1;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_MAC_KEY);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+        hw->init_mac_key(ctx, p->data, p->data_size);
+    }
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    p = OSSL_PARAM_locate_const(params,
+            OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT);
+    if (p != NULL
+            && !OSSL_PARAM_get_size_t(p, &ctx->multiblock_max_send_fragment)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+        return 0;
+    }
+    /*
+     * The inputs to tls1_multiblock_aad are:
+     *   mb_param->inp
+     *   mb_param->len
+     *   mb_param->interleave
+     * The outputs of tls1_multiblock_aad are written to:
+     *   ctx->multiblock_interleave
+     *   ctx->multiblock_aad_packlen
+     */
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD);
+    if (p != NULL) {
+        p1 = OSSL_PARAM_locate_const(params,
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE);
+        if (p->data_type != OSSL_PARAM_OCTET_STRING
+            || p1 == NULL
+            || !OSSL_PARAM_get_uint(p1, &mb_param.interleave)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+        mb_param.inp = p->data;
+        mb_param.len = p->data_size;
+        if (hw->tls1_multiblock_aad(vctx, &mb_param) <= 0)
+            return 0;
+    }
+
+    /*
+     * The inputs to tls1_multiblock_encrypt are:
+     *   mb_param->inp
+     *   mb_param->len
+     *   mb_param->interleave
+     *   mb_param->out
+     * The outputs of tls1_multiblock_encrypt are:
+     *   ctx->multiblock_encrypt_len
+     */
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC);
+    if (p != NULL) {
+        p1 = OSSL_PARAM_locate_const(params,
+                OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE);
+        pin = OSSL_PARAM_locate_const(params,
+                   OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN);
+        if (p->data_type != OSSL_PARAM_OCTET_STRING
+            || pin == NULL
+            || pin->data_type != OSSL_PARAM_OCTET_STRING
+            || p1 == NULL
+            || !OSSL_PARAM_get_uint(p1, &mb_param.interleave)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+        mb_param.out = p->data;
+        mb_param.inp = pin->data;
+        mb_param.len = pin->data_size;
+        if (hw->tls1_multiblock_encrypt(vctx, &mb_param) <= 0)
+            return 0;
+    }
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+        if (hw->set_tls1_aad(ctx, p->data, p->data_size) <= 0)
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+    if (p != NULL) {
+        size_t keylen;
+
+        if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+        if (ctx->base.keylen != keylen) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+            return 0;
+        }
+    }
+    return ret;
+}
+
+static int aes_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_CIPHER_HW_AES_HMAC_SHA *hw =
+       (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->hw;
+    OSSL_PARAM *p;
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE);
+    if (p != NULL) {
+        size_t len = hw->tls1_multiblock_max_bufsize(ctx);
+
+        if (!OSSL_PARAM_set_size_t(p, len)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+            return 0;
+        }
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE);
+    if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->multiblock_interleave)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN);
+    if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->multiblock_aad_packlen)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN);
+    if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->multiblock_encrypt_len)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+
+    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)) {
+        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->base.keylen)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+    if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.ivlen)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
+    if (p != NULL
+        && !OSSL_PARAM_set_octet_string(p, ctx->base.oiv, ctx->base.ivlen)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
+    return 1;
+}
+
+static const OSSL_PARAM cipher_aes_known_gettable_ctx_params[] = {
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE, NULL),
+    OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, NULL),
+    OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN, NULL),
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN, NULL),
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL),
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+    OSSL_PARAM_END
+};
+const OSSL_PARAM *aes_gettable_ctx_params(void)
+{
+    return cipher_aes_known_gettable_ctx_params;
+}
+
+static void base_init(void *provctx, PROV_AES_HMAC_SHA_CTX *ctx,
+                      const PROV_CIPHER_HW_AES_HMAC_SHA *meths,
+                      size_t kbits, size_t blkbits, size_t ivbits,
+                      uint64_t flags)
+{
+    cipher_generic_initkey(&ctx->base, kbits, blkbits, ivbits,
+                           EVP_CIPH_CBC_MODE, flags,
+                           &meths->base, provctx);
+    ctx->hw = (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->base.hw;
+}
+
+static void *aes_cbc_hmac_sha1_newctx(void *provctx, size_t kbits,
+                                      size_t blkbits, size_t ivbits,
+                                      uint64_t flags)
+{
+    PROV_AES_HMAC_SHA1_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL)
+        base_init(provctx, &ctx->base_ctx,
+                  PROV_CIPHER_HW_aes_cbc_hmac_sha1(), kbits, blkbits,
+                  ivbits, flags);
+    return ctx;
+}
+
+static void aes_cbc_hmac_sha1_freectx(void *vctx)
+{
+    PROV_AES_HMAC_SHA1_CTX *ctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+
+    if (ctx != NULL)
+        OPENSSL_clear_free(ctx, sizeof(ctx));
+}
+
+static void *aes_cbc_hmac_sha256_newctx(void *provctx, size_t kbits,
+                                        size_t blkbits, size_t ivbits,
+                                        uint64_t flags)
+{
+    PROV_AES_HMAC_SHA256_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+    if (ctx != NULL)
+        base_init(provctx, &ctx->base_ctx,
+                  PROV_CIPHER_HW_aes_cbc_hmac_sha256(), kbits, blkbits,
+                  ivbits, flags);
+    return ctx;
+}
+
+static void aes_cbc_hmac_sha256_freectx(void *vctx)
+{
+    PROV_AES_HMAC_SHA256_CTX *ctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+
+    if (ctx != NULL)
+        OPENSSL_clear_free(ctx, sizeof(ctx));
+}
+
+# define IMPLEMENT_CIPHER(nm, sub, kbits, blkbits, ivbits, flags)               \
+static OSSL_OP_cipher_newctx_fn nm##_##kbits##_##sub##_newctx;                 \
+static void *nm##_##kbits##_##sub##_newctx(void *provctx)                      \
+{                                                                              \
+    return nm##_##sub##_newctx(provctx, kbits, blkbits, ivbits, flags);        \
+}                                                                              \
+static OSSL_OP_cipher_get_params_fn nm##_##kbits##_##sub##_get_params;         \
+static int nm##_##kbits##_##sub##_get_params(OSSL_PARAM params[])              \
+{                                                                              \
+    return cipher_generic_get_params(params, EVP_CIPH_CBC_MODE,                \
+                                     flags, kbits, blkbits, ivbits);           \
+}                                                                              \
+const OSSL_DISPATCH nm##kbits##sub##_functions[] = {                           \
+    { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))nm##_##kbits##_##sub##_newctx },\
+    { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))nm##_##sub##_freectx },        \
+    { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))nm##_einit },             \
+    { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))nm##_dinit },             \
+    { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))nm##_update },                  \
+    { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))nm##_final },                    \
+    { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))nm##_cipher },                  \
+    { OSSL_FUNC_CIPHER_GET_PARAMS,                                             \
+        (void (*)(void))nm##_##kbits##_##sub##_get_params },                   \
+    { OSSL_FUNC_CIPHER_GETTABLE_PARAMS,                                        \
+        (void (*)(void))nm##_gettable_params },                                \
+    { OSSL_FUNC_CIPHER_GET_CTX_PARAMS,                                         \
+         (void (*)(void))nm##_get_ctx_params },                                \
+    { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,                                    \
+        (void (*)(void))nm##_gettable_ctx_params },                            \
+    { OSSL_FUNC_CIPHER_SET_CTX_PARAMS,                                         \
+        (void (*)(void))nm##_set_ctx_params },                                 \
+    { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,                                    \
+        (void (*)(void))nm##_settable_ctx_params },                            \
+    { 0, NULL }                                                                \
+};
+
+#endif /* AES_CBC_HMAC_SHA_CAPABLE */
+
+/* aes128cbc_hmac_sha1_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha1, 128, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
+/* aes256cbc_hmac_sha1_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha1, 256, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
+/* aes128cbc_hmac_sha256_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha256, 128, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
+/* aes256cbc_hmac_sha256_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha256, 256, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h
new file mode 100644 (file)
index 0000000..86da791
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+
+int cipher_capable_aes_cbc_hmac_sha1(void);
+int cipher_capable_aes_cbc_hmac_sha256(void);
+
+#ifdef AES_CBC_HMAC_SHA_CAPABLE
+# include <openssl/aes.h>
+# include <openssl/sha.h>
+
+typedef struct prov_cipher_hw_aes_hmac_sha_ctx_st {
+    PROV_CIPHER_HW base; /* must be first */
+    void (*init_mac_key)(void *ctx, const unsigned char *inkey, size_t inlen);
+    int (*set_tls1_aad)(void *ctx, unsigned char *aad_rec, int aad_len);
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    int (*tls1_multiblock_max_bufsize)(void *ctx);
+    int (*tls1_multiblock_aad)(
+        void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param);
+    int (*tls1_multiblock_encrypt)(
+        void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param);
+# endif /* OPENSSL_NO_MULTIBLOCK) */
+} PROV_CIPHER_HW_AES_HMAC_SHA;
+
+typedef struct prov_aes_hmac_sha_ctx_st {
+    PROV_CIPHER_CTX base;
+    AES_KEY ks;
+    size_t payload_length;      /* AAD length in decrypt case */
+    union {
+        unsigned int tls_ver;
+        unsigned char tls_aad[16]; /* 13 used */
+    } aux;
+    const PROV_CIPHER_HW_AES_HMAC_SHA *hw;
+    /* some value that are setup by set methods - that can be retrieved */
+    unsigned int multiblock_interleave;
+    unsigned int multiblock_aad_packlen;
+    size_t multiblock_max_send_fragment;
+    size_t multiblock_encrypt_len;
+    size_t tls_aad_pad;
+} PROV_AES_HMAC_SHA_CTX;
+
+typedef struct prov_aes_hmac_sha1_ctx_st {
+    PROV_AES_HMAC_SHA_CTX base_ctx;
+    SHA_CTX head, tail, md;
+} PROV_AES_HMAC_SHA1_CTX;
+
+typedef struct prov_aes_hmac_sha256_ctx_st {
+    PROV_AES_HMAC_SHA_CTX base_ctx;
+    SHA256_CTX head, tail, md;
+} PROV_AES_HMAC_SHA256_CTX;
+
+# define NO_PAYLOAD_LENGTH ((size_t)-1)
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *PROV_CIPHER_HW_aes_cbc_hmac_sha1(void);
+const PROV_CIPHER_HW_AES_HMAC_SHA *PROV_CIPHER_HW_aes_cbc_hmac_sha256(void);
+
+#endif /* AES_CBC_HMAC_SHA_CAPABLE */
diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c
new file mode 100644 (file)
index 0000000..125369d
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+ * Copyright 2011-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 "cipher_aes_cbc_hmac_sha.h"
+
+#ifndef AES_CBC_HMAC_SHA_CAPABLE
+int cipher_capable_aes_cbc_hmac_sha1(void)
+{
+    return 0;
+}
+#else
+
+# include "crypto/rand.h"
+# include "crypto/evp.h"
+# include "internal/constant_time.h"
+
+void sha1_block_data_order(void *c, const void *p, size_t len);
+void aesni_cbc_sha1_enc(const void *inp, void *out, size_t blocks,
+                        const AES_KEY *key, unsigned char iv[16],
+                        SHA_CTX *ctx, const void *in0);
+
+int cipher_capable_aes_cbc_hmac_sha1(void)
+{
+    return AESNI_CBC_HMAC_SHA_CAPABLE;
+}
+
+static int aesni_cbc_hmac_sha1_init_key(PROV_CIPHER_CTX *vctx,
+                                        const unsigned char *key, size_t keylen)
+{
+    int ret;
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+
+    if (ctx->base.enc)
+        ret = aesni_set_encrypt_key(key, keylen * 8, &ctx->ks);
+    else
+        ret = aesni_set_decrypt_key(key, keylen * 8, &ctx->ks);
+
+    SHA1_Init(&sctx->head);      /* handy when benchmarking */
+    sctx->tail = sctx->head;
+    sctx->md = sctx->head;
+
+    ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+    return ret < 0 ? 0 : 1;
+}
+
+static void sha1_update(SHA_CTX *c, const void *data, size_t len)
+{
+    const unsigned char *ptr = data;
+    size_t res;
+
+    if ((res = c->num)) {
+        res = SHA_CBLOCK - res;
+        if (len < res)
+            res = len;
+        SHA1_Update(c, ptr, res);
+        ptr += res;
+        len -= res;
+    }
+
+    res = len % SHA_CBLOCK;
+    len -= res;
+
+    if (len) {
+        sha1_block_data_order(c, ptr, len / SHA_CBLOCK);
+
+        ptr += len;
+        c->Nh += len >> 29;
+        c->Nl += len <<= 3;
+        if (c->Nl < (unsigned int)len)
+            c->Nh++;
+    }
+
+    if (res)
+        SHA1_Update(c, ptr, res);
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+typedef struct {
+    unsigned int A[8], B[8], C[8], D[8], E[8];
+} SHA1_MB_CTX;
+
+typedef struct {
+    const unsigned char *ptr;
+    int blocks;
+} HASH_DESC;
+
+typedef struct {
+    const unsigned char *inp;
+    unsigned char *out;
+    int blocks;
+    u64 iv[2];
+} CIPH_DESC;
+
+void sha1_multi_block(SHA1_MB_CTX *, const HASH_DESC *, int);
+void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int);
+
+static size_t tls1_multi_block_encrypt(void *vctx,
+                                       unsigned char *out,
+                                       const unsigned char *inp,
+                                       size_t inp_len, int n4x)
+{                               /* n4x is 1 or 2 */
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+    HASH_DESC hash_d[8], edges[8];
+    CIPH_DESC ciph_d[8];
+    unsigned char storage[sizeof(SHA1_MB_CTX) + 32];
+    union {
+        u64 q[16];
+        u32 d[32];
+        u8 c[128];
+    } blocks[8];
+    SHA1_MB_CTX *mctx;
+    unsigned int frag, last, packlen, i;
+    unsigned int x4 = 4 * n4x, minblocks, processed = 0;
+    size_t ret = 0;
+    u8 *IVs;
+#  if defined(BSWAP8)
+    u64 seqnum;
+#  endif
+
+    /* ask for IVs in bulk */
+    if (rand_bytes_ex(ctx->base.libctx, (IVs = blocks[0].c), 16 * x4) <= 0)
+        return 0;
+
+    mctx = (SHA1_MB_CTX *) (storage + 32 - ((size_t)storage % 32)); /* align */
+
+    frag = (unsigned int)inp_len >> (1 + n4x);
+    last = (unsigned int)inp_len + frag - (frag << (1 + n4x));
+    if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) {
+        frag++;
+        last -= x4 - 1;
+    }
+
+    packlen = 5 + 16 + ((frag + 20 + 16) & -16);
+
+    /* populate descriptors with pointers and IVs */
+    hash_d[0].ptr = inp;
+    ciph_d[0].inp = inp;
+    /* 5+16 is place for header and explicit IV */
+    ciph_d[0].out = out + 5 + 16;
+    memcpy(ciph_d[0].out - 16, IVs, 16);
+    memcpy(ciph_d[0].iv, IVs, 16);
+    IVs += 16;
+
+    for (i = 1; i < x4; i++) {
+        ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag;
+        ciph_d[i].out = ciph_d[i - 1].out + packlen;
+        memcpy(ciph_d[i].out - 16, IVs, 16);
+        memcpy(ciph_d[i].iv, IVs, 16);
+        IVs += 16;
+    }
+
+#  if defined(BSWAP8)
+    memcpy(blocks[0].c, sctx->md.data, 8);
+    seqnum = BSWAP8(blocks[0].q[0]);
+#  endif
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag);
+#  if !defined(BSWAP8)
+        unsigned int carry, j;
+#  endif
+
+        mctx->A[i] = sctx->md.h0;
+        mctx->B[i] = sctx->md.h1;
+        mctx->C[i] = sctx->md.h2;
+        mctx->D[i] = sctx->md.h3;
+        mctx->E[i] = sctx->md.h4;
+
+        /* fix seqnum */
+#  if defined(BSWAP8)
+        blocks[i].q[0] = BSWAP8(seqnum + i);
+#  else
+        for (carry = i, j = 8; j--;) {
+            blocks[i].c[j] = ((u8 *)sctx->md.data)[j] + carry;
+            carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1);
+        }
+#  endif
+        blocks[i].c[8] = ((u8 *)sctx->md.data)[8];
+        blocks[i].c[9] = ((u8 *)sctx->md.data)[9];
+        blocks[i].c[10] = ((u8 *)sctx->md.data)[10];
+        /* fix length */
+        blocks[i].c[11] = (u8)(len >> 8);
+        blocks[i].c[12] = (u8)(len);
+
+        memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13);
+        hash_d[i].ptr += 64 - 13;
+        hash_d[i].blocks = (len - (64 - 13)) / 64;
+
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* hash 13-byte headers and first 64-13 bytes of inputs */
+    sha1_multi_block(mctx, edges, n4x);
+    /* hash bulk inputs */
+#  define MAXCHUNKSIZE    2048
+#  if     MAXCHUNKSIZE%64
+#   error  "MAXCHUNKSIZE is not divisible by 64"
+#  elif   MAXCHUNKSIZE
+    /*
+     * goal is to minimize pressure on L1 cache by moving in shorter steps,
+     * so that hashed data is still in the cache by the time we encrypt it
+     */
+    minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64;
+    if (minblocks > MAXCHUNKSIZE / 64) {
+        for (i = 0; i < x4; i++) {
+            edges[i].ptr = hash_d[i].ptr;
+            edges[i].blocks = MAXCHUNKSIZE / 64;
+            ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+        }
+        do {
+            sha1_multi_block(mctx, edges, n4x);
+            aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+            for (i = 0; i < x4; i++) {
+                edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE;
+                hash_d[i].blocks -= MAXCHUNKSIZE / 64;
+                edges[i].blocks = MAXCHUNKSIZE / 64;
+                ciph_d[i].inp += MAXCHUNKSIZE;
+                ciph_d[i].out += MAXCHUNKSIZE;
+                ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+                memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16);
+            }
+            processed += MAXCHUNKSIZE;
+            minblocks -= MAXCHUNKSIZE / 64;
+        } while (minblocks > MAXCHUNKSIZE / 64);
+    }
+#  endif
+#  undef  MAXCHUNKSIZE
+    sha1_multi_block(mctx, hash_d, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag),
+            off = hash_d[i].blocks * 64;
+        const unsigned char *ptr = hash_d[i].ptr + off;
+
+        off = (len - processed) - (64 - 13) - off; /* remainder actually */
+        memcpy(blocks[i].c, ptr, off);
+        blocks[i].c[off] = 0x80;
+        len += 64 + 13;         /* 64 is HMAC header */
+        len *= 8;               /* convert to bits */
+        if (off < (64 - 8)) {
+#  ifdef BSWAP4
+            blocks[i].d[15] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 60, len);
+#  endif
+            edges[i].blocks = 1;
+        } else {
+#  ifdef BSWAP4
+            blocks[i].d[31] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 124, len);
+#  endif
+            edges[i].blocks = 2;
+        }
+        edges[i].ptr = blocks[i].c;
+    }
+
+    /* hash input tails and finalize */
+    sha1_multi_block(mctx, edges, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+#  ifdef BSWAP4
+        blocks[i].d[0] = BSWAP4(mctx->A[i]);
+        mctx->A[i] = sctx->tail.h0;
+        blocks[i].d[1] = BSWAP4(mctx->B[i]);
+        mctx->B[i] = sctx->tail.h1;
+        blocks[i].d[2] = BSWAP4(mctx->C[i]);
+        mctx->C[i] = sctx->tail.h2;
+        blocks[i].d[3] = BSWAP4(mctx->D[i]);
+        mctx->D[i] = sctx->tail.h3;
+        blocks[i].d[4] = BSWAP4(mctx->E[i]);
+        mctx->E[i] = sctx->tail.h4;
+        blocks[i].c[20] = 0x80;
+        blocks[i].d[15] = BSWAP4((64 + 20) * 8);
+#  else
+        PUTU32(blocks[i].c + 0, mctx->A[i]);
+        mctx->A[i] = sctx->tail.h0;
+        PUTU32(blocks[i].c + 4, mctx->B[i]);
+        mctx->B[i] = sctx->tail.h1;
+        PUTU32(blocks[i].c + 8, mctx->C[i]);
+        mctx->C[i] = sctx->tail.h2;
+        PUTU32(blocks[i].c + 12, mctx->D[i]);
+        mctx->D[i] = sctx->tail.h3;
+        PUTU32(blocks[i].c + 16, mctx->E[i]);
+        mctx->E[i] = sctx->tail.h4;
+        blocks[i].c[20] = 0x80;
+        PUTU32(blocks[i].c + 60, (64 + 20) * 8);
+#  endif /* BSWAP */
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* finalize MACs */
+    sha1_multi_block(mctx, edges, n4x);
+
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag), pad, j;
+        unsigned char *out0 = out;
+
+        memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed);
+        ciph_d[i].inp = ciph_d[i].out;
+
+        out += 5 + 16 + len;
+
+        /* write MAC */
+        PUTU32(out + 0, mctx->A[i]);
+        PUTU32(out + 4, mctx->B[i]);
+        PUTU32(out + 8, mctx->C[i]);
+        PUTU32(out + 12, mctx->D[i]);
+        PUTU32(out + 16, mctx->E[i]);
+        out += 20;
+        len += 20;
+
+        /* pad */
+        pad = 15 - len % 16;
+        for (j = 0; j <= pad; j++)
+            *(out++) = pad;
+        len += pad + 1;
+
+        ciph_d[i].blocks = (len - processed) / 16;
+        len += 16;              /* account for explicit iv */
+
+        /* arrange header */
+        out0[0] = ((u8 *)sctx->md.data)[8];
+        out0[1] = ((u8 *)sctx->md.data)[9];
+        out0[2] = ((u8 *)sctx->md.data)[10];
+        out0[3] = (u8)(len >> 8);
+        out0[4] = (u8)(len);
+
+        ret += len + 5;
+        inp += frag;
+    }
+
+    aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+    OPENSSL_cleanse(blocks, sizeof(blocks));
+    OPENSSL_cleanse(mctx, sizeof(*mctx));
+
+    ctx->multiblock_encrypt_len = ret;
+    return ret;
+}
+# endif /* OPENSSL_NO_MULTIBLOCK */
+
+static int aesni_cbc_hmac_sha1_cipher(PROV_CIPHER_CTX *vctx,
+                                      unsigned char *out,
+                                      const unsigned char *in, size_t len)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+    unsigned int l;
+    size_t plen = ctx->payload_length;
+    size_t iv = 0; /* explicit IV in TLS 1.1 and later */
+    size_t aes_off = 0, blocks;
+    size_t sha_off = SHA_CBLOCK - sctx->md.num;
+
+    ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+    if (len % AES_BLOCK_SIZE)
+        return 0;
+
+    if (ctx->base.enc) {
+        if (plen == NO_PAYLOAD_LENGTH)
+            plen = len;
+        else if (len !=
+                 ((plen + SHA_DIGEST_LENGTH +
+                   AES_BLOCK_SIZE) & -AES_BLOCK_SIZE))
+            return 0;
+        else if (ctx->aux.tls_ver >= TLS1_1_VERSION)
+            iv = AES_BLOCK_SIZE;
+
+        if (plen > (sha_off + iv)
+                && (blocks = (plen - (sha_off + iv)) / SHA_CBLOCK)) {
+            sha1_update(&sctx->md, in + iv, sha_off);
+
+            aesni_cbc_sha1_enc(in, out, blocks, &ctx->ks, ctx->base.iv,
+                               &sctx->md, in + iv + sha_off);
+            blocks *= SHA_CBLOCK;
+            aes_off += blocks;
+            sha_off += blocks;
+            sctx->md.Nh += blocks >> 29;
+            sctx->md.Nl += blocks <<= 3;
+            if (sctx->md.Nl < (unsigned int)blocks)
+                sctx->md.Nh++;
+        } else {
+            sha_off = 0;
+        }
+        sha_off += iv;
+        sha1_update(&sctx->md, in + sha_off, plen - sha_off);
+
+        if (plen != len) {      /* "TLS" mode of operation */
+            if (in != out)
+                memcpy(out + aes_off, in + aes_off, plen - aes_off);
+
+            /* calculate HMAC and append it to payload */
+            SHA1_Final(out + plen, &sctx->md);
+            sctx->md = sctx->tail;
+            sha1_update(&sctx->md, out + plen, SHA_DIGEST_LENGTH);
+            SHA1_Final(out + plen, &sctx->md);
+
+            /* pad the payload|hmac */
+            plen += SHA_DIGEST_LENGTH;
+            for (l = len - plen - 1; plen < len; plen++)
+                out[plen] = l;
+            /* encrypt HMAC|padding at once */
+            aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off,
+                              &ctx->ks, ctx->base.iv, 1);
+        } else {
+            aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off,
+                              &ctx->ks, ctx->base.iv, 1);
+        }
+    } else {
+        union {
+            unsigned int u[SHA_DIGEST_LENGTH / sizeof(unsigned int)];
+            unsigned char c[32 + SHA_DIGEST_LENGTH];
+        } mac, *pmac;
+
+        /* arrange cache line alignment */
+        pmac = (void *)(((size_t)mac.c + 31) & ((size_t)0 - 32));
+
+        if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */
+            size_t inp_len, mask, j, i;
+            unsigned int res, maxpad, pad, bitlen;
+            int ret = 1;
+            union {
+                unsigned int u[SHA_LBLOCK];
+                unsigned char c[SHA_CBLOCK];
+            } *data = (void *)sctx->md.data;
+
+            if ((ctx->aux.tls_aad[plen - 4] << 8 | ctx->aux.tls_aad[plen - 3])
+                >= TLS1_1_VERSION) {
+                if (len < (AES_BLOCK_SIZE + SHA_DIGEST_LENGTH + 1))
+                    return 0;
+
+                /* omit explicit iv */
+                memcpy(ctx->base.iv, in, AES_BLOCK_SIZE);
+
+                in += AES_BLOCK_SIZE;
+                out += AES_BLOCK_SIZE;
+                len -= AES_BLOCK_SIZE;
+            } else if (len < (SHA_DIGEST_LENGTH + 1))
+                return 0;
+
+            /* decrypt HMAC|padding at once */
+            aesni_cbc_encrypt(in, out, len, &ctx->ks, ctx->base.iv, 0);
+
+            /* figure out payload length */
+            pad = out[len - 1];
+            maxpad = len - (SHA_DIGEST_LENGTH + 1);
+            maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8);
+            maxpad &= 255;
+
+            mask = constant_time_ge(maxpad, pad);
+            ret &= mask;
+            /*
+             * If pad is invalid then we will fail the above test but we must
+             * continue anyway because we are in constant time code. However,
+             * we'll use the maxpad value instead of the supplied pad to make
+             * sure we perform well defined pointer arithmetic.
+             */
+            pad = constant_time_select(mask, pad, maxpad);
+
+            inp_len = len - (SHA_DIGEST_LENGTH + pad + 1);
+
+            ctx->aux.tls_aad[plen - 2] = inp_len >> 8;
+            ctx->aux.tls_aad[plen - 1] = inp_len;
+
+            /* calculate HMAC */
+            sctx->md = sctx->head;
+            sha1_update(&sctx->md, ctx->aux.tls_aad, plen);
+
+            /* code containing lucky-13 fix */
+            len -= SHA_DIGEST_LENGTH; /* amend mac */
+            if (len >= (256 + SHA_CBLOCK)) {
+                j = (len - (256 + SHA_CBLOCK)) & (0 - SHA_CBLOCK);
+                j += SHA_CBLOCK - sctx->md.num;
+                sha1_update(&sctx->md, out, j);
+                out += j;
+                len -= j;
+                inp_len -= j;
+            }
+
+            /* but pretend as if we hashed padded payload */
+            bitlen = sctx->md.Nl + (inp_len << 3); /* at most 18 bits */
+# ifdef BSWAP4
+            bitlen = BSWAP4(bitlen);
+# else
+            mac.c[0] = 0;
+            mac.c[1] = (unsigned char)(bitlen >> 16);
+            mac.c[2] = (unsigned char)(bitlen >> 8);
+            mac.c[3] = (unsigned char)bitlen;
+            bitlen = mac.u[0];
+# endif /* BSWAP */
+
+            pmac->u[0] = 0;
+            pmac->u[1] = 0;
+            pmac->u[2] = 0;
+            pmac->u[3] = 0;
+            pmac->u[4] = 0;
+
+            for (res = sctx->md.num, j = 0; j < len; j++) {
+                size_t c = out[j];
+                mask = (j - inp_len) >> (sizeof(j) * 8 - 8);
+                c &= mask;
+                c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8));
+                data->c[res++] = (unsigned char)c;
+
+                if (res != SHA_CBLOCK)
+                    continue;
+
+                /* j is not incremented yet */
+                mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha1_block_data_order(&sctx->md, data, 1);
+                mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= sctx->md.h0 & mask;
+                pmac->u[1] |= sctx->md.h1 & mask;
+                pmac->u[2] |= sctx->md.h2 & mask;
+                pmac->u[3] |= sctx->md.h3 & mask;
+                pmac->u[4] |= sctx->md.h4 & mask;
+                res = 0;
+            }
+
+            for (i = res; i < SHA_CBLOCK; i++, j++)
+                data->c[i] = 0;
+
+            if (res > SHA_CBLOCK - 8) {
+                mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha1_block_data_order(&sctx->md, data, 1);
+                mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= sctx->md.h0 & mask;
+                pmac->u[1] |= sctx->md.h1 & mask;
+                pmac->u[2] |= sctx->md.h2 & mask;
+                pmac->u[3] |= sctx->md.h3 & mask;
+                pmac->u[4] |= sctx->md.h4 & mask;
+
+                memset(data, 0, SHA_CBLOCK);
+                j += 64;
+            }
+            data->u[SHA_LBLOCK - 1] = bitlen;
+            sha1_block_data_order(&sctx->md, data, 1);
+            mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+            pmac->u[0] |= sctx->md.h0 & mask;
+            pmac->u[1] |= sctx->md.h1 & mask;
+            pmac->u[2] |= sctx->md.h2 & mask;
+            pmac->u[3] |= sctx->md.h3 & mask;
+            pmac->u[4] |= sctx->md.h4 & mask;
+
+# ifdef BSWAP4
+            pmac->u[0] = BSWAP4(pmac->u[0]);
+            pmac->u[1] = BSWAP4(pmac->u[1]);
+            pmac->u[2] = BSWAP4(pmac->u[2]);
+            pmac->u[3] = BSWAP4(pmac->u[3]);
+            pmac->u[4] = BSWAP4(pmac->u[4]);
+# else
+            for (i = 0; i < 5; i++) {
+                res = pmac->u[i];
+                pmac->c[4 * i + 0] = (unsigned char)(res >> 24);
+                pmac->c[4 * i + 1] = (unsigned char)(res >> 16);
+                pmac->c[4 * i + 2] = (unsigned char)(res >> 8);
+                pmac->c[4 * i + 3] = (unsigned char)res;
+            }
+# endif /* BSWAP4 */
+            len += SHA_DIGEST_LENGTH;
+            sctx->md = sctx->tail;
+            sha1_update(&sctx->md, pmac->c, SHA_DIGEST_LENGTH);
+            SHA1_Final(pmac->c, &sctx->md);
+
+            /* verify HMAC */
+            out += inp_len;
+            len -= inp_len;
+            /* version of code with lucky-13 fix */
+            {
+                unsigned char *p = out + len - 1 - maxpad - SHA_DIGEST_LENGTH;
+                size_t off = out - p;
+                unsigned int c, cmask;
+
+                maxpad += SHA_DIGEST_LENGTH;
+                for (res = 0, i = 0, j = 0; j < maxpad; j++) {
+                    c = p[j];
+                    cmask =
+                        ((int)(j - off - SHA_DIGEST_LENGTH)) >> (sizeof(int) *
+                                                                 8 - 1);
+                    res |= (c ^ pad) & ~cmask; /* ... and padding */
+                    cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1);
+                    res |= (c ^ pmac->c[i]) & cmask;
+                    i += 1 & cmask;
+                }
+                maxpad -= SHA_DIGEST_LENGTH;
+
+                res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+                ret &= (int)~res;
+            }
+            return ret;
+        } else {
+            /* decrypt HMAC|padding at once */
+            aesni_cbc_encrypt(in, out, len, &ctx->ks, ctx->base.iv, 0);
+            sha1_update(&sctx->md, out, len);
+        }
+    }
+
+    return 1;
+}
+
+/* EVP_CTRL_AEAD_SET_MAC_KEY */
+static void aesni_cbc_hmac_sha1_set_mac_key(void *vctx,
+                                            const unsigned char *mac, size_t len)
+{
+    PROV_AES_HMAC_SHA1_CTX *ctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+    unsigned int i;
+    unsigned char hmac_key[64];
+
+    memset(hmac_key, 0, sizeof(hmac_key));
+
+    if (len > (int)sizeof(hmac_key)) {
+        SHA1_Init(&ctx->head);
+        sha1_update(&ctx->head, mac, len);
+        SHA1_Final(hmac_key, &ctx->head);
+    } else {
+        memcpy(hmac_key, mac, len);
+    }
+
+    for (i = 0; i < sizeof(hmac_key); i++)
+        hmac_key[i] ^= 0x36; /* ipad */
+    SHA1_Init(&ctx->head);
+    sha1_update(&ctx->head, hmac_key, sizeof(hmac_key));
+
+    for (i = 0; i < sizeof(hmac_key); i++)
+        hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+    SHA1_Init(&ctx->tail);
+    sha1_update(&ctx->tail, hmac_key, sizeof(hmac_key));
+
+    OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+}
+
+/* EVP_CTRL_AEAD_TLS1_AAD */
+static int aesni_cbc_hmac_sha1_set_tls1_aad(void *vctx,
+                                            unsigned char *aad_rec, int aad_len)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+    unsigned char *p = aad_rec;
+    unsigned int len;
+
+    if (aad_len != EVP_AEAD_TLS1_AAD_LEN)
+        return -1;
+
+    len = p[aad_len - 2] << 8 | p[aad_len - 1];
+
+    if (ctx->base.enc) {
+        ctx->payload_length = len;
+        if ((ctx->aux.tls_ver =
+             p[aad_len - 4] << 8 | p[aad_len - 3]) >= TLS1_1_VERSION) {
+            if (len < AES_BLOCK_SIZE)
+                return 0;
+            len -= AES_BLOCK_SIZE;
+            p[aad_len - 2] = len >> 8;
+            p[aad_len - 1] = len;
+        }
+        sctx->md = sctx->head;
+        sha1_update(&sctx->md, p, aad_len);
+        ctx->tls_aad_pad = (int)(((len + SHA_DIGEST_LENGTH +
+                       AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)
+                     - len);
+        return 1;
+    } else {
+        memcpy(ctx->aux.tls_aad, aad_rec, aad_len);
+        ctx->payload_length = aad_len;
+        ctx->tls_aad_pad = SHA_DIGEST_LENGTH;
+        return 1;
+    }
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE */
+static int aesni_cbc_hmac_sha1_tls1_multiblock_max_bufsize(void *vctx)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+
+    OPENSSL_assert(ctx->multiblock_max_send_fragment != 0);
+    return (int)(5 + 16
+                 + (((int)ctx->multiblock_max_send_fragment + 20 + 16) & -16));
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_AAD */
+static int aesni_cbc_hmac_sha1_tls1_multiblock_aad(
+    void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+    unsigned int n4x = 1, x4;
+    unsigned int frag, last, packlen, inp_len;
+
+    inp_len = param->inp[11] << 8 | param->inp[12];
+    ctx->multiblock_interleave = param->interleave;
+
+    if (ctx->base.enc) {
+        if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION)
+            return -1;
+
+        if (inp_len) {
+            if (inp_len < 4096)
+                return 0; /* too short */
+
+            if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5))
+                n4x = 2; /* AVX2 */
+        } else if ((n4x = param->interleave / 4) && n4x <= 2)
+            inp_len = param->len;
+        else
+            return -1;
+
+        sctx->md = sctx->head;
+        sha1_update(&sctx->md, param->inp, 13);
+
+        x4 = 4 * n4x;
+        n4x += 1;
+
+        frag = inp_len >> n4x;
+        last = inp_len + frag - (frag << n4x);
+        if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) {
+            frag++;
+            last -= x4 - 1;
+        }
+
+        packlen = 5 + 16 + ((frag + 20 + 16) & -16);
+        packlen = (packlen << n4x) - packlen;
+        packlen += 5 + 16 + ((last + 20 + 16) & -16);
+
+        param->interleave = x4;
+        /* The returned values used by get need to be stored */
+        ctx->multiblock_interleave = x4;
+        ctx->multiblock_aad_packlen = packlen;
+        return 1;
+    }
+    return -1;      /* not yet */
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT */
+static int aesni_cbc_hmac_sha1_tls1_multiblock_encrypt(
+    void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+    return (int)tls1_multi_block_encrypt(ctx, param->out,
+                                         param->inp, param->len,
+                                         param->interleave / 4);
+}
+
+#endif /* OPENSSL_NO_MULTIBLOCK */
+
+static const PROV_CIPHER_HW_AES_HMAC_SHA cipher_hw_aes_hmac_sha1 = {
+    {
+      aesni_cbc_hmac_sha1_init_key,
+      aesni_cbc_hmac_sha1_cipher
+    },
+    aesni_cbc_hmac_sha1_set_mac_key,
+    aesni_cbc_hmac_sha1_set_tls1_aad,
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    aesni_cbc_hmac_sha1_tls1_multiblock_max_bufsize,
+    aesni_cbc_hmac_sha1_tls1_multiblock_aad,
+    aesni_cbc_hmac_sha1_tls1_multiblock_encrypt
+# endif
+};
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *PROV_CIPHER_HW_aes_cbc_hmac_sha1(void)
+{
+    return &cipher_hw_aes_hmac_sha1;
+}
+
+#endif /* AES_CBC_HMAC_SHA_CAPABLE */
diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c
new file mode 100644 (file)
index 0000000..4352476
--- /dev/null
@@ -0,0 +1,831 @@
+/*
+ * Copyright 2011-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 "cipher_aes_cbc_hmac_sha.h"
+
+#ifndef AES_CBC_HMAC_SHA_CAPABLE
+int cipher_capable_aes_cbc_hmac_sha256(void)
+{
+    return 0;
+}
+#else
+
+# include "crypto/rand.h"
+# include "crypto/evp.h"
+# include "internal/constant_time.h"
+
+void sha256_block_data_order(void *c, const void *p, size_t len);
+int aesni_cbc_sha256_enc(const void *inp, void *out, size_t blocks,
+                         const AES_KEY *key, unsigned char iv[16],
+                         SHA256_CTX *ctx, const void *in0);
+
+int cipher_capable_aes_cbc_hmac_sha256(void)
+{
+    return AESNI_CBC_HMAC_SHA_CAPABLE
+           && aesni_cbc_sha256_enc(NULL, NULL, 0, NULL, NULL, NULL, NULL);
+}
+
+static int aesni_cbc_hmac_sha256_init_key(PROV_CIPHER_CTX *vctx,
+                                          const unsigned char *key,
+                                          size_t keylen)
+{
+    int ret;
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+
+    if (ctx->base.enc)
+        ret = aesni_set_encrypt_key(key, ctx->base.keylen * 8, &ctx->ks);
+    else
+        ret = aesni_set_decrypt_key(key, ctx->base.keylen * 8, &ctx->ks);
+
+    SHA256_Init(&sctx->head);    /* handy when benchmarking */
+    sctx->tail = sctx->head;
+    sctx->md = sctx->head;
+
+    ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+    return ret < 0 ? 0 : 1;
+}
+
+void sha256_block_data_order(void *c, const void *p, size_t len);
+
+static void sha256_update(SHA256_CTX *c, const void *data, size_t len)
+{
+    const unsigned char *ptr = data;
+    size_t res;
+
+    if ((res = c->num)) {
+        res = SHA256_CBLOCK - res;
+        if (len < res)
+            res = len;
+        SHA256_Update(c, ptr, res);
+        ptr += res;
+        len -= res;
+    }
+
+    res = len % SHA256_CBLOCK;
+    len -= res;
+
+    if (len) {
+        sha256_block_data_order(c, ptr, len / SHA256_CBLOCK);
+
+        ptr += len;
+        c->Nh += len >> 29;
+        c->Nl += len <<= 3;
+        if (c->Nl < (unsigned int)len)
+            c->Nh++;
+    }
+
+    if (res)
+        SHA256_Update(c, ptr, res);
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+typedef struct {
+    unsigned int A[8], B[8], C[8], D[8], E[8], F[8], G[8], H[8];
+} SHA256_MB_CTX;
+
+typedef struct {
+    const unsigned char *ptr;
+    int blocks;
+} HASH_DESC;
+
+typedef struct {
+    const unsigned char *inp;
+    unsigned char *out;
+    int blocks;
+    u64 iv[2];
+} CIPH_DESC;
+
+void sha256_multi_block(SHA256_MB_CTX *, const HASH_DESC *, int);
+void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int);
+
+static size_t tls1_multi_block_encrypt(void *vctx,
+                                       unsigned char *out,
+                                       const unsigned char *inp,
+                                       size_t inp_len, int n4x)
+{                               /* n4x is 1 or 2 */
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+    HASH_DESC hash_d[8], edges[8];
+    CIPH_DESC ciph_d[8];
+    unsigned char storage[sizeof(SHA256_MB_CTX) + 32];
+    union {
+        u64 q[16];
+        u32 d[32];
+        u8 c[128];
+    } blocks[8];
+    SHA256_MB_CTX *mctx;
+    unsigned int frag, last, packlen, i;
+    unsigned int x4 = 4 * n4x, minblocks, processed = 0;
+    size_t ret = 0;
+    u8 *IVs;
+#  if defined(BSWAP8)
+    u64 seqnum;
+#  endif
+
+    /* ask for IVs in bulk */
+    if (rand_bytes_ex(ctx->base.libctx, (IVs = blocks[0].c), 16 * x4) <= 0)
+        return 0;
+
+    mctx = (SHA256_MB_CTX *) (storage + 32 - ((size_t)storage % 32)); /* align */
+
+    frag = (unsigned int)inp_len >> (1 + n4x);
+    last = (unsigned int)inp_len + frag - (frag << (1 + n4x));
+    if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) {
+        frag++;
+        last -= x4 - 1;
+    }
+
+    packlen = 5 + 16 + ((frag + 32 + 16) & -16);
+
+    /* populate descriptors with pointers and IVs */
+    hash_d[0].ptr = inp;
+    ciph_d[0].inp = inp;
+    /* 5+16 is place for header and explicit IV */
+    ciph_d[0].out = out + 5 + 16;
+    memcpy(ciph_d[0].out - 16, IVs, 16);
+    memcpy(ciph_d[0].iv, IVs, 16);
+    IVs += 16;
+
+    for (i = 1; i < x4; i++) {
+        ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag;
+        ciph_d[i].out = ciph_d[i - 1].out + packlen;
+        memcpy(ciph_d[i].out - 16, IVs, 16);
+        memcpy(ciph_d[i].iv, IVs, 16);
+        IVs += 16;
+    }
+
+#  if defined(BSWAP8)
+    memcpy(blocks[0].c, sctx->md.data, 8);
+    seqnum = BSWAP8(blocks[0].q[0]);
+#  endif
+
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag);
+#  if !defined(BSWAP8)
+        unsigned int carry, j;
+#  endif
+
+        mctx->A[i] = sctx->md.h[0];
+        mctx->B[i] = sctx->md.h[1];
+        mctx->C[i] = sctx->md.h[2];
+        mctx->D[i] = sctx->md.h[3];
+        mctx->E[i] = sctx->md.h[4];
+        mctx->F[i] = sctx->md.h[5];
+        mctx->G[i] = sctx->md.h[6];
+        mctx->H[i] = sctx->md.h[7];
+
+        /* fix seqnum */
+#  if defined(BSWAP8)
+        blocks[i].q[0] = BSWAP8(seqnum + i);
+#  else
+        for (carry = i, j = 8; j--;) {
+            blocks[i].c[j] = ((u8 *)sctx->md.data)[j] + carry;
+            carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1);
+        }
+#  endif
+        blocks[i].c[8] = ((u8 *)sctx->md.data)[8];
+        blocks[i].c[9] = ((u8 *)sctx->md.data)[9];
+        blocks[i].c[10] = ((u8 *)sctx->md.data)[10];
+        /* fix length */
+        blocks[i].c[11] = (u8)(len >> 8);
+        blocks[i].c[12] = (u8)(len);
+
+        memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13);
+        hash_d[i].ptr += 64 - 13;
+        hash_d[i].blocks = (len - (64 - 13)) / 64;
+
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* hash 13-byte headers and first 64-13 bytes of inputs */
+    sha256_multi_block(mctx, edges, n4x);
+    /* hash bulk inputs */
+#  define MAXCHUNKSIZE    2048
+#  if     MAXCHUNKSIZE%64
+#   error  "MAXCHUNKSIZE is not divisible by 64"
+#  elif   MAXCHUNKSIZE
+    /*
+     * goal is to minimize pressure on L1 cache by moving in shorter steps,
+     * so that hashed data is still in the cache by the time we encrypt it
+     */
+    minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64;
+    if (minblocks > MAXCHUNKSIZE / 64) {
+        for (i = 0; i < x4; i++) {
+            edges[i].ptr = hash_d[i].ptr;
+            edges[i].blocks = MAXCHUNKSIZE / 64;
+            ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+        }
+        do {
+            sha256_multi_block(mctx, edges, n4x);
+            aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+            for (i = 0; i < x4; i++) {
+                edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE;
+                hash_d[i].blocks -= MAXCHUNKSIZE / 64;
+                edges[i].blocks = MAXCHUNKSIZE / 64;
+                ciph_d[i].inp += MAXCHUNKSIZE;
+                ciph_d[i].out += MAXCHUNKSIZE;
+                ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+                memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16);
+            }
+            processed += MAXCHUNKSIZE;
+            minblocks -= MAXCHUNKSIZE / 64;
+        } while (minblocks > MAXCHUNKSIZE / 64);
+    }
+#  endif
+#  undef  MAXCHUNKSIZE
+    sha256_multi_block(mctx, hash_d, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag),
+            off = hash_d[i].blocks * 64;
+        const unsigned char *ptr = hash_d[i].ptr + off;
+
+        off = (len - processed) - (64 - 13) - off; /* remainder actually */
+        memcpy(blocks[i].c, ptr, off);
+        blocks[i].c[off] = 0x80;
+        len += 64 + 13;         /* 64 is HMAC header */
+        len *= 8;               /* convert to bits */
+        if (off < (64 - 8)) {
+#  ifdef BSWAP4
+            blocks[i].d[15] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 60, len);
+#  endif
+            edges[i].blocks = 1;
+        } else {
+#  ifdef BSWAP4
+            blocks[i].d[31] = BSWAP4(len);
+#  else
+            PUTU32(blocks[i].c + 124, len);
+#  endif
+            edges[i].blocks = 2;
+        }
+        edges[i].ptr = blocks[i].c;
+    }
+
+    /* hash input tails and finalize */
+    sha256_multi_block(mctx, edges, n4x);
+
+    memset(blocks, 0, sizeof(blocks));
+    for (i = 0; i < x4; i++) {
+#  ifdef BSWAP4
+        blocks[i].d[0] = BSWAP4(mctx->A[i]);
+        mctx->A[i] = sctx->tail.h[0];
+        blocks[i].d[1] = BSWAP4(mctx->B[i]);
+        mctx->B[i] = sctx->tail.h[1];
+        blocks[i].d[2] = BSWAP4(mctx->C[i]);
+        mctx->C[i] = sctx->tail.h[2];
+        blocks[i].d[3] = BSWAP4(mctx->D[i]);
+        mctx->D[i] = sctx->tail.h[3];
+        blocks[i].d[4] = BSWAP4(mctx->E[i]);
+        mctx->E[i] = sctx->tail.h[4];
+        blocks[i].d[5] = BSWAP4(mctx->F[i]);
+        mctx->F[i] = sctx->tail.h[5];
+        blocks[i].d[6] = BSWAP4(mctx->G[i]);
+        mctx->G[i] = sctx->tail.h[6];
+        blocks[i].d[7] = BSWAP4(mctx->H[i]);
+        mctx->H[i] = sctx->tail.h[7];
+        blocks[i].c[32] = 0x80;
+        blocks[i].d[15] = BSWAP4((64 + 32) * 8);
+#  else
+        PUTU32(blocks[i].c + 0, mctx->A[i]);
+        mctx->A[i] = sctx->tail.h[0];
+        PUTU32(blocks[i].c + 4, mctx->B[i]);
+        mctx->B[i] = sctx->tail.h[1];
+        PUTU32(blocks[i].c + 8, mctx->C[i]);
+        mctx->C[i] = sctx->tail.h[2];
+        PUTU32(blocks[i].c + 12, mctx->D[i]);
+        mctx->D[i] = sctx->tail.h[3];
+        PUTU32(blocks[i].c + 16, mctx->E[i]);
+        mctx->E[i] = sctx->tail.h[4];
+        PUTU32(blocks[i].c + 20, mctx->F[i]);
+        mctx->F[i] = sctx->tail.h[5];
+        PUTU32(blocks[i].c + 24, mctx->G[i]);
+        mctx->G[i] = sctx->tail.h[6];
+        PUTU32(blocks[i].c + 28, mctx->H[i]);
+        mctx->H[i] = sctx->tail.h[7];
+        blocks[i].c[32] = 0x80;
+        PUTU32(blocks[i].c + 60, (64 + 32) * 8);
+#  endif /* BSWAP */
+        edges[i].ptr = blocks[i].c;
+        edges[i].blocks = 1;
+    }
+
+    /* finalize MACs */
+    sha256_multi_block(mctx, edges, n4x);
+
+    for (i = 0; i < x4; i++) {
+        unsigned int len = (i == (x4 - 1) ? last : frag), pad, j;
+        unsigned char *out0 = out;
+
+        memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed);
+        ciph_d[i].inp = ciph_d[i].out;
+
+        out += 5 + 16 + len;
+
+        /* write MAC */
+        PUTU32(out + 0, mctx->A[i]);
+        PUTU32(out + 4, mctx->B[i]);
+        PUTU32(out + 8, mctx->C[i]);
+        PUTU32(out + 12, mctx->D[i]);
+        PUTU32(out + 16, mctx->E[i]);
+        PUTU32(out + 20, mctx->F[i]);
+        PUTU32(out + 24, mctx->G[i]);
+        PUTU32(out + 28, mctx->H[i]);
+        out += 32;
+        len += 32;
+
+        /* pad */
+        pad = 15 - len % 16;
+        for (j = 0; j <= pad; j++)
+            *(out++) = pad;
+        len += pad + 1;
+
+        ciph_d[i].blocks = (len - processed) / 16;
+        len += 16;              /* account for explicit iv */
+
+        /* arrange header */
+        out0[0] = ((u8 *)sctx->md.data)[8];
+        out0[1] = ((u8 *)sctx->md.data)[9];
+        out0[2] = ((u8 *)sctx->md.data)[10];
+        out0[3] = (u8)(len >> 8);
+        out0[4] = (u8)(len);
+
+        ret += len + 5;
+        inp += frag;
+    }
+
+    aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+    OPENSSL_cleanse(blocks, sizeof(blocks));
+    OPENSSL_cleanse(mctx, sizeof(*mctx));
+
+    ctx->multiblock_encrypt_len = ret;
+    return ret;
+}
+# endif /* !OPENSSL_NO_MULTIBLOCK */
+
+static int aesni_cbc_hmac_sha256_cipher(PROV_CIPHER_CTX *vctx,
+                                        unsigned char *out,
+                                        const unsigned char *in, size_t len)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+    unsigned int l;
+    size_t plen = ctx->payload_length;
+    size_t iv = 0; /* explicit IV in TLS 1.1 and * later */
+    size_t aes_off = 0, blocks;
+    size_t sha_off = SHA256_CBLOCK - sctx->md.num;
+
+    ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+    if (len % AES_BLOCK_SIZE)
+        return 0;
+
+    if (ctx->base.enc) {
+        if (plen == NO_PAYLOAD_LENGTH)
+            plen = len;
+        else if (len !=
+                 ((plen + SHA256_DIGEST_LENGTH +
+                   AES_BLOCK_SIZE) & -AES_BLOCK_SIZE))
+            return 0;
+        else if (ctx->aux.tls_ver >= TLS1_1_VERSION)
+            iv = AES_BLOCK_SIZE;
+
+        /*
+         * Assembly stitch handles AVX-capable processors, but its
+         * performance is not optimal on AMD Jaguar, ~40% worse, for
+         * unknown reasons. Incidentally processor in question supports
+         * AVX, but not AMD-specific XOP extension, which can be used
+         * to identify it and avoid stitch invocation. So that after we
+         * establish that current CPU supports AVX, we even see if it's
+         * either even XOP-capable Bulldozer-based or GenuineIntel one.
+         * But SHAEXT-capable go ahead...
+         */
+        if (((OPENSSL_ia32cap_P[2] & (1 << 29)) ||         /* SHAEXT? */
+             ((OPENSSL_ia32cap_P[1] & (1 << (60 - 32))) && /* AVX? */
+              ((OPENSSL_ia32cap_P[1] & (1 << (43 - 32)))   /* XOP? */
+               | (OPENSSL_ia32cap_P[0] & (1 << 30))))) &&  /* "Intel CPU"? */
+            plen > (sha_off + iv) &&
+            (blocks = (plen - (sha_off + iv)) / SHA256_CBLOCK)) {
+            sha256_update(&sctx->md, in + iv, sha_off);
+
+            (void)aesni_cbc_sha256_enc(in, out, blocks, &ctx->ks,
+                                       ctx->base.iv,
+                                       &sctx->md, in + iv + sha_off);
+            blocks *= SHA256_CBLOCK;
+            aes_off += blocks;
+            sha_off += blocks;
+            sctx->md.Nh += blocks >> 29;
+            sctx->md.Nl += blocks <<= 3;
+            if (sctx->md.Nl < (unsigned int)blocks)
+                sctx->md.Nh++;
+        } else {
+            sha_off = 0;
+        }
+        sha_off += iv;
+        sha256_update(&sctx->md, in + sha_off, plen - sha_off);
+
+        if (plen != len) {      /* "TLS" mode of operation */
+            if (in != out)
+                memcpy(out + aes_off, in + aes_off, plen - aes_off);
+
+            /* calculate HMAC and append it to payload */
+            SHA256_Final(out + plen, &sctx->md);
+            sctx->md = sctx->tail;
+            sha256_update(&sctx->md, out + plen, SHA256_DIGEST_LENGTH);
+            SHA256_Final(out + plen, &sctx->md);
+
+            /* pad the payload|hmac */
+            plen += SHA256_DIGEST_LENGTH;
+            for (l = len - plen - 1; plen < len; plen++)
+                out[plen] = l;
+            /* encrypt HMAC|padding at once */
+            aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off,
+                              &ctx->ks, ctx->base.iv, 1);
+        } else {
+            aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off,
+                              &ctx->ks, ctx->base.iv, 1);
+        }
+    } else {
+        union {
+            unsigned int u[SHA256_DIGEST_LENGTH / sizeof(unsigned int)];
+            unsigned char c[64 + SHA256_DIGEST_LENGTH];
+        } mac, *pmac;
+
+        /* arrange cache line alignment */
+        pmac = (void *)(((size_t)mac.c + 63) & ((size_t)0 - 64));
+
+        /* decrypt HMAC|padding at once */
+        aesni_cbc_encrypt(in, out, len, &ctx->ks,
+                          ctx->base.iv, 0);
+
+        if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */
+            size_t inp_len, mask, j, i;
+            unsigned int res, maxpad, pad, bitlen;
+            int ret = 1;
+            union {
+                unsigned int u[SHA_LBLOCK];
+                unsigned char c[SHA256_CBLOCK];
+            } *data = (void *)sctx->md.data;
+
+            if ((ctx->aux.tls_aad[plen - 4] << 8 | ctx->aux.tls_aad[plen - 3])
+                >= TLS1_1_VERSION)
+                iv = AES_BLOCK_SIZE;
+
+            if (len < (iv + SHA256_DIGEST_LENGTH + 1))
+                return 0;
+
+            /* omit explicit iv */
+            out += iv;
+            len -= iv;
+
+            /* figure out payload length */
+            pad = out[len - 1];
+            maxpad = len - (SHA256_DIGEST_LENGTH + 1);
+            maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8);
+            maxpad &= 255;
+
+            mask = constant_time_ge(maxpad, pad);
+            ret &= mask;
+            /*
+             * If pad is invalid then we will fail the above test but we must
+             * continue anyway because we are in constant time code. However,
+             * we'll use the maxpad value instead of the supplied pad to make
+             * sure we perform well defined pointer arithmetic.
+             */
+            pad = constant_time_select(mask, pad, maxpad);
+
+            inp_len = len - (SHA256_DIGEST_LENGTH + pad + 1);
+
+            ctx->aux.tls_aad[plen - 2] = inp_len >> 8;
+            ctx->aux.tls_aad[plen - 1] = inp_len;
+
+            /* calculate HMAC */
+            sctx->md = sctx->head;
+            sha256_update(&sctx->md, ctx->aux.tls_aad, plen);
+
+            /* code with lucky-13 fix */
+            len -= SHA256_DIGEST_LENGTH; /* amend mac */
+            if (len >= (256 + SHA256_CBLOCK)) {
+                j = (len - (256 + SHA256_CBLOCK)) & (0 - SHA256_CBLOCK);
+                j += SHA256_CBLOCK - sctx->md.num;
+                sha256_update(&sctx->md, out, j);
+                out += j;
+                len -= j;
+                inp_len -= j;
+            }
+
+            /* but pretend as if we hashed padded payload */
+            bitlen = sctx->md.Nl + (inp_len << 3); /* at most 18 bits */
+# ifdef BSWAP4
+            bitlen = BSWAP4(bitlen);
+# else
+            mac.c[0] = 0;
+            mac.c[1] = (unsigned char)(bitlen >> 16);
+            mac.c[2] = (unsigned char)(bitlen >> 8);
+            mac.c[3] = (unsigned char)bitlen;
+            bitlen = mac.u[0];
+# endif /* BSWAP */
+
+            pmac->u[0] = 0;
+            pmac->u[1] = 0;
+            pmac->u[2] = 0;
+            pmac->u[3] = 0;
+            pmac->u[4] = 0;
+            pmac->u[5] = 0;
+            pmac->u[6] = 0;
+            pmac->u[7] = 0;
+
+            for (res = sctx->md.num, j = 0; j < len; j++) {
+                size_t c = out[j];
+                mask = (j - inp_len) >> (sizeof(j) * 8 - 8);
+                c &= mask;
+                c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8));
+                data->c[res++] = (unsigned char)c;
+
+                if (res != SHA256_CBLOCK)
+                    continue;
+
+                /* j is not incremented yet */
+                mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha256_block_data_order(&sctx->md, data, 1);
+                mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= sctx->md.h[0] & mask;
+                pmac->u[1] |= sctx->md.h[1] & mask;
+                pmac->u[2] |= sctx->md.h[2] & mask;
+                pmac->u[3] |= sctx->md.h[3] & mask;
+                pmac->u[4] |= sctx->md.h[4] & mask;
+                pmac->u[5] |= sctx->md.h[5] & mask;
+                pmac->u[6] |= sctx->md.h[6] & mask;
+                pmac->u[7] |= sctx->md.h[7] & mask;
+                res = 0;
+            }
+
+            for (i = res; i < SHA256_CBLOCK; i++, j++)
+                data->c[i] = 0;
+
+            if (res > SHA256_CBLOCK - 8) {
+                mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1));
+                data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+                sha256_block_data_order(&sctx->md, data, 1);
+                mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+                pmac->u[0] |= sctx->md.h[0] & mask;
+                pmac->u[1] |= sctx->md.h[1] & mask;
+                pmac->u[2] |= sctx->md.h[2] & mask;
+                pmac->u[3] |= sctx->md.h[3] & mask;
+                pmac->u[4] |= sctx->md.h[4] & mask;
+                pmac->u[5] |= sctx->md.h[5] & mask;
+                pmac->u[6] |= sctx->md.h[6] & mask;
+                pmac->u[7] |= sctx->md.h[7] & mask;
+
+                memset(data, 0, SHA256_CBLOCK);
+                j += 64;
+            }
+            data->u[SHA_LBLOCK - 1] = bitlen;
+            sha256_block_data_order(&sctx->md, data, 1);
+            mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+            pmac->u[0] |= sctx->md.h[0] & mask;
+            pmac->u[1] |= sctx->md.h[1] & mask;
+            pmac->u[2] |= sctx->md.h[2] & mask;
+            pmac->u[3] |= sctx->md.h[3] & mask;
+            pmac->u[4] |= sctx->md.h[4] & mask;
+            pmac->u[5] |= sctx->md.h[5] & mask;
+            pmac->u[6] |= sctx->md.h[6] & mask;
+            pmac->u[7] |= sctx->md.h[7] & mask;
+
+# ifdef BSWAP4
+            pmac->u[0] = BSWAP4(pmac->u[0]);
+            pmac->u[1] = BSWAP4(pmac->u[1]);
+            pmac->u[2] = BSWAP4(pmac->u[2]);
+            pmac->u[3] = BSWAP4(pmac->u[3]);
+            pmac->u[4] = BSWAP4(pmac->u[4]);
+            pmac->u[5] = BSWAP4(pmac->u[5]);
+            pmac->u[6] = BSWAP4(pmac->u[6]);
+            pmac->u[7] = BSWAP4(pmac->u[7]);
+# else
+            for (i = 0; i < 8; i++) {
+                res = pmac->u[i];
+                pmac->c[4 * i + 0] = (unsigned char)(res >> 24);
+                pmac->c[4 * i + 1] = (unsigned char)(res >> 16);
+                pmac->c[4 * i + 2] = (unsigned char)(res >> 8);
+                pmac->c[4 * i + 3] = (unsigned char)res;
+            }
+# endif /* BSWAP */
+            len += SHA256_DIGEST_LENGTH;
+            sctx->md = sctx->tail;
+            sha256_update(&sctx->md, pmac->c, SHA256_DIGEST_LENGTH);
+            SHA256_Final(pmac->c, &sctx->md);
+
+            /* verify HMAC */
+            out += inp_len;
+            len -= inp_len;
+            /* code containing lucky-13 fix */
+            {
+                unsigned char *p =
+                    out + len - 1 - maxpad - SHA256_DIGEST_LENGTH;
+                size_t off = out - p;
+                unsigned int c, cmask;
+
+                maxpad += SHA256_DIGEST_LENGTH;
+                for (res = 0, i = 0, j = 0; j < maxpad; j++) {
+                    c = p[j];
+                    cmask =
+                        ((int)(j - off - SHA256_DIGEST_LENGTH)) >>
+                        (sizeof(int) * 8 - 1);
+                    res |= (c ^ pad) & ~cmask; /* ... and padding */
+                    cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1);
+                    res |= (c ^ pmac->c[i]) & cmask;
+                    i += 1 & cmask;
+                }
+                maxpad -= SHA256_DIGEST_LENGTH;
+
+                res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+                ret &= (int)~res;
+            }
+            return ret;
+        } else {
+            sha256_update(&sctx->md, out, len);
+        }
+    }
+
+    return 1;
+}
+
+/* EVP_CTRL_AEAD_SET_MAC_KEY */
+static void aesni_cbc_hmac_sha256_set_mac_key(void *vctx,
+                                              const unsigned char *mackey,
+                                              size_t len)
+{
+    PROV_AES_HMAC_SHA256_CTX *ctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+    unsigned int i;
+    unsigned char hmac_key[64];
+
+    memset(hmac_key, 0, sizeof(hmac_key));
+
+    if (len > sizeof(hmac_key)) {
+        SHA256_Init(&ctx->head);
+        sha256_update(&ctx->head, mackey, len);
+        SHA256_Final(hmac_key, &ctx->head);
+    } else {
+        memcpy(hmac_key, mackey, len);
+    }
+
+    for (i = 0; i < sizeof(hmac_key); i++)
+        hmac_key[i] ^= 0x36; /* ipad */
+    SHA256_Init(&ctx->head);
+    sha256_update(&ctx->head, hmac_key, sizeof(hmac_key));
+
+    for (i = 0; i < sizeof(hmac_key); i++)
+        hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+    SHA256_Init(&ctx->tail);
+    sha256_update(&ctx->tail, hmac_key, sizeof(hmac_key));
+
+    OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+}
+
+/* EVP_CTRL_AEAD_TLS1_AAD */
+static int aesni_cbc_hmac_sha256_set_tls1_aad(void *vctx,
+                                              unsigned char *aad_rec, int aad_len)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+    unsigned char *p = aad_rec;
+    unsigned int len;
+
+    if (aad_len != EVP_AEAD_TLS1_AAD_LEN)
+        return -1;
+
+    len = p[aad_len - 2] << 8 | p[aad_len - 1];
+
+    if (ctx->base.enc) {
+        ctx->payload_length = len;
+        if ((ctx->aux.tls_ver =
+             p[aad_len - 4] << 8 | p[aad_len - 3]) >= TLS1_1_VERSION) {
+            if (len < AES_BLOCK_SIZE)
+                return 0;
+            len -= AES_BLOCK_SIZE;
+            p[aad_len] = len >> 8;
+            p[aad_len - 1] = len;
+        }
+        sctx->md = sctx->head;
+        sha256_update(&sctx->md, p, aad_len);
+        ctx->tls_aad_pad = (int)(((len + SHA256_DIGEST_LENGTH +
+                                   AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)
+                                   - len);
+        return 1;
+    } else {
+        memcpy(ctx->aux.tls_aad, p, aad_len);
+        ctx->payload_length = aad_len;
+        ctx->tls_aad_pad = SHA256_DIGEST_LENGTH;
+        return 1;
+    }
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE */
+static int aesni_cbc_hmac_sha256_tls1_multiblock_max_bufsize(
+    void *vctx)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+
+    OPENSSL_assert(ctx->multiblock_max_send_fragment != 0);
+    return (int)(5 + 16
+                 + (((int)ctx->multiblock_max_send_fragment + 32 + 16) & -16));
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_AAD */
+static int aesni_cbc_hmac_sha256_tls1_multiblock_aad(
+    void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+    PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+    PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+    unsigned int n4x = 1, x4;
+    unsigned int frag, last, packlen, inp_len;
+
+    inp_len = param->inp[11] << 8 | param->inp[12];
+
+    if (ctx->base.enc) {
+        if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION)
+            return -1;
+
+        if (inp_len) {
+            if (inp_len < 4096)
+                return 0; /* too short */
+
+            if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5))
+                n4x = 2; /* AVX2 */
+        } else if ((n4x = param->interleave / 4) && n4x <= 2)
+            inp_len = param->len;
+        else
+            return -1;
+
+        sctx->md = sctx->head;
+        sha256_update(&sctx->md, param->inp, 13);
+
+        x4 = 4 * n4x;
+        n4x += 1;
+
+        frag = inp_len >> n4x;
+        last = inp_len + frag - (frag << n4x);
+        if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) {
+            frag++;
+            last -= x4 - 1;
+        }
+
+        packlen = 5 + 16 + ((frag + 32 + 16) & -16);
+        packlen = (packlen << n4x) - packlen;
+        packlen += 5 + 16 + ((last + 32 + 16) & -16);
+
+        param->interleave = x4;
+        /* The returned values used by get need to be stored */
+        ctx->multiblock_interleave = x4;
+        ctx->multiblock_aad_packlen = packlen;
+        return 1;
+    }
+    return -1;      /* not yet */
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT */
+static int aesni_cbc_hmac_sha256_tls1_multiblock_encrypt(
+    void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+    return (int)tls1_multi_block_encrypt(ctx, param->out,
+                                         param->inp, param->len,
+                                         param->interleave / 4);
+}
+#endif
+
+static const PROV_CIPHER_HW_AES_HMAC_SHA cipher_hw_aes_hmac_sha256 = {
+    {
+      aesni_cbc_hmac_sha256_init_key,
+      aesni_cbc_hmac_sha256_cipher
+    },
+    aesni_cbc_hmac_sha256_set_mac_key,
+    aesni_cbc_hmac_sha256_set_tls1_aad,
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+    aesni_cbc_hmac_sha256_tls1_multiblock_max_bufsize,
+    aesni_cbc_hmac_sha256_tls1_multiblock_aad,
+    aesni_cbc_hmac_sha256_tls1_multiblock_encrypt
+# endif
+};
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *PROV_CIPHER_HW_aes_cbc_hmac_sha256(void)
+{
+    return &cipher_hw_aes_hmac_sha256;
+}
+
+#endif /* AES_CBC_HMAC_SHA_CAPABLE */
index 75c20a0..ed44d68 100644 (file)
@@ -75,6 +75,10 @@ extern const OSSL_DISPATCH aes128wrap_functions[];
 extern const OSSL_DISPATCH aes256wrappad_functions[];
 extern const OSSL_DISPATCH aes192wrappad_functions[];
 extern const OSSL_DISPATCH aes128wrappad_functions[];
+extern const OSSL_DISPATCH aes256cbc_hmac_sha1_functions[];
+extern const OSSL_DISPATCH aes128cbc_hmac_sha1_functions[];
+extern const OSSL_DISPATCH aes256cbc_hmac_sha256_functions[];
+extern const OSSL_DISPATCH aes128cbc_hmac_sha256_functions[];
 
 #ifndef OPENSSL_NO_ARIA
 extern const OSSL_DISPATCH aria256gcm_functions[];
index 46e490a..4993f16 100644 (file)
@@ -6820,6 +6820,103 @@ static int test_ca_names(int tst)
     return testresult;
 }
 
+#ifndef OPENSSL_NO_TLS1_2
+static const char *multiblock_cipherlist_data[]=
+{
+    "AES128-SHA",
+    "AES128-SHA256",
+    "AES256-SHA",
+    "AES256-SHA256",
+};
+
+/* Reduce the fragment size - so the multiblock test buffer can be small */
+# define MULTIBLOCK_FRAGSIZE 512
+
+static int test_multiblock_write(int test_index)
+{
+    static const char *fetchable_ciphers[]=
+    {
+        "AES-128-CBC-HMAC-SHA1",
+        "AES-128-CBC-HMAC-SHA256",
+        "AES-256-CBC-HMAC-SHA1",
+        "AES-256-CBC-HMAC-SHA256"
+    };
+    const char *cipherlist = multiblock_cipherlist_data[test_index];
+    const SSL_METHOD *smeth = TLS_server_method();
+    const SSL_METHOD *cmeth = TLS_client_method();
+    int min_version = TLS1_VERSION;
+    int max_version = TLS1_2_VERSION; /* Don't select TLS1_3 */
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+
+    /*
+     * Choose a buffer large enough to perform a multi-block operation
+     * i.e: write_len >= 4 * frag_size
+     * 9 * is chosen so that multiple multiblocks are used + some leftover.
+     */
+    unsigned char msg[MULTIBLOCK_FRAGSIZE * 9];
+    unsigned char buf[sizeof(msg)], *p = buf;
+    size_t readbytes, written, len;
+    EVP_CIPHER *ciph = NULL;
+
+    /*
+     * Check if the cipher exists before attempting to use it since it only has
+     * a hardware specific implementation.
+     */
+    ciph = EVP_CIPHER_fetch(NULL, fetchable_ciphers[test_index], "");
+    if (ciph == NULL) {
+        TEST_skip("Multiblock cipher is not available for %s", cipherlist);
+        return 1;
+    }
+    EVP_CIPHER_free(ciph);
+
+    /* Set up a buffer with some data that will be sent to the client */
+    RAND_bytes(msg, sizeof(msg));
+
+    if (!TEST_true(create_ssl_ctx_pair(smeth, cmeth, min_version, max_version,
+                                       &sctx, &cctx, cert, privkey)))
+        goto end;
+
+    if (!TEST_true(SSL_CTX_set_max_send_fragment(sctx, MULTIBLOCK_FRAGSIZE)))
+        goto end;
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+                                      NULL, NULL)))
+            goto end;
+
+    /* settings to force it to use AES-CBC-HMAC_SHA */
+    SSL_set_options(serverssl, SSL_OP_NO_ENCRYPT_THEN_MAC);
+    if (!TEST_true(SSL_CTX_set_cipher_list(cctx, cipherlist)))
+       goto end;
+
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
+        goto end;
+
+    if (!TEST_true(SSL_write_ex(serverssl, msg, sizeof(msg), &written))
+        || !TEST_size_t_eq(written, sizeof(msg)))
+        goto end;
+
+    len = written;
+    while (len > 0) {
+        if (!TEST_true(SSL_read_ex(clientssl, p, MULTIBLOCK_FRAGSIZE, &readbytes)))
+            goto end;
+        p += readbytes;
+        len -= readbytes;
+    }
+    if (!TEST_mem_eq(msg, sizeof(msg), buf, sizeof(buf)))
+        goto end;
+
+    testresult = 1;
+end:
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+#endif /* OPENSSL_NO_TLS1_2 */
 
 OPT_TEST_DECLARE_USAGE("certfile privkeyfile srpvfile tmpfile\n")
 
@@ -6968,6 +7065,9 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_cert_cb, 6);
     ADD_ALL_TESTS(test_client_cert_cb, 2);
     ADD_ALL_TESTS(test_ca_names, 3);
+#ifndef OPENSSL_NO_TLS1_2
+    ADD_ALL_TESTS(test_multiblock_write, OSSL_NELEM(multiblock_cipherlist_data));
+#endif
     return 1;
 }