[openssl] master update

Matt Caswell matt at openssl.org
Mon Jul 6 08:37:29 UTC 2020


The branch master has been updated
       via  2d9f56e9992ef3725b87a0a8e6165a18d038b784 (commit)
       via  b5588178232f5cbf32662dfa173c72a001d54aeb (commit)
       via  63ee6ec17714f5446a3656083e438ec941bdd542 (commit)
       via  f29dbb08668318b84d7bca0bd63c585e0169545e (commit)
       via  09ce6e0854b9dee49a25662e1aaaa869b2afc2a1 (commit)
       via  ee0c849e5a1c26ed16c08311efdfd78c8e4c8221 (commit)
       via  978cc3648d02551c6ada328708306dad2d3ce07a (commit)
       via  1ae7354c049cb3e45bfb17c0c1bf3ff04814fa4d (commit)
       via  27d4c840fc399fe0d4550a5b88e91ecca887d1a4 (commit)
       via  524cb684ac00922c4a21235a8ae2c66596893437 (commit)
       via  e71fd827bcff720fb47e39c69cc468da9452935d (commit)
       via  f0237a6c6266535e105d6778ca7c34a080b88e92 (commit)
       via  ebacd57bee1baef6236a518a0eec3135d593f47a (commit)
       via  ec27e619e86c6ce4dfa905044eb4737eeba28a9d (commit)
      from  1b726e9b91a032298dc96ad117b23e18e1583246 (commit)


- Log -----------------------------------------------------------------
commit 2d9f56e9992ef3725b87a0a8e6165a18d038b784
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 26 18:22:18 2020 +0100

    Ensure TLS padding is added during encryption on the provider side
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit b5588178232f5cbf32662dfa173c72a001d54aeb
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 26 13:05:18 2020 +0100

    Convert SSLv3 handling to use provider side CBC/MAC removal
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit 63ee6ec17714f5446a3656083e438ec941bdd542
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Jun 23 16:47:31 2020 +0100

    Ensure any allocated MAC is freed in the provider code
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit f29dbb08668318b84d7bca0bd63c585e0169545e
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Jun 23 14:34:45 2020 +0100

    Decreate the length after decryption for the stitched ciphers
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit 09ce6e0854b9dee49a25662e1aaaa869b2afc2a1
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 22 16:02:12 2020 +0100

    Ensure the sslcorrupttest checks all errors on the queue
    
    sslcorrupttest was looking for a "decryption failed or bad record mac"
    error in the queue. However if there were multiple errors on the queue
    then it would fail to find it. We modify the test to check all errors.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit ee0c849e5a1c26ed16c08311efdfd78c8e4c8221
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 22 16:01:31 2020 +0100

    Ensure GCM "update" failures return 0 on error
    
    EVP_CipherUpdate is supposed to return 1 for success or 0 for error.
    However for GCM ciphers it was sometimes returning -1 for error.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit 978cc3648d02551c6ada328708306dad2d3ce07a
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 22 15:04:50 2020 +0100

    Ensure cipher_generic_initkey gets passed the actual provider ctx
    
    We were not correctly passing the provider ctx down the chain during
    initialisation of a new cipher ctx. Instead the provider ctx got set to
    NULL.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit 1ae7354c049cb3e45bfb17c0c1bf3ff04814fa4d
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jun 22 10:51:48 2020 +0100

    Make the NULL cipher TLS aware
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit 27d4c840fc399fe0d4550a5b88e91ecca887d1a4
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 19 17:26:49 2020 +0100

    Change ChaCha20-Poly1305 to be consistent with out ciphers
    
    Other ciphers return the length of the Payload for TLS as a result of an
    EVP_DecryptUpdate() operation - but  ChaCha20-Poly1305 did not. We change
    it so that it does.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit 524cb684ac00922c4a21235a8ae2c66596893437
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jun 17 17:16:22 2020 +0100

    Make libssl start using the TLS provider CBC support
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit e71fd827bcff720fb47e39c69cc468da9452935d
Author: Matt Caswell <matt at openssl.org>
Date:   Wed May 27 17:20:18 2020 +0100

    Add provider support for TLS CBC padding and MAC removal
    
    The previous commits separated out the TLS CBC padding code in libssl.
    Now we can use that code to directly support TLS CBC padding and MAC
    removal in provided ciphers.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit f0237a6c6266535e105d6778ca7c34a080b88e92
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jun 10 15:34:04 2020 +0100

    Remove SSL dependencies from tls_pad.c
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit ebacd57bee1baef6236a518a0eec3135d593f47a
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jun 10 15:11:28 2020 +0100

    Split the padding/mac removal functions out into a separate file
    
    We split these functions out into a separate file because we are
    preparing to make this file shared between libssl and providers.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

commit ec27e619e86c6ce4dfa905044eb4737eeba28a9d
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jun 3 17:42:01 2020 +0100

    Move MAC removal responsibility to the various protocol "enc" functions
    
    For CBC ciphersuites using Mac-then-encrypt we have to be careful about
    removing the MAC from the record in constant time. Currently that happens
    immediately before MAC verification. Instead we move this responsibility
    to the various protocol "enc" functions so that MAC removal is handled at
    the same time as padding removal.
    
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/12288)

-----------------------------------------------------------------------

Summary of changes:
 include/openssl/core_names.h                       |  43 +-
 providers/implementations/ciphers/cipher_aes.c     |   1 +
 .../ciphers/cipher_aes_cbc_hmac_sha.c              |   8 +-
 .../ciphers/cipher_aes_cbc_hmac_sha1_hw.c          |   2 +
 .../ciphers/cipher_aes_cbc_hmac_sha256_hw.c        |   2 +
 providers/implementations/ciphers/cipher_aes_ocb.c |   1 +
 providers/implementations/ciphers/cipher_aes_wrp.c |   1 +
 providers/implementations/ciphers/cipher_aes_xts.c |   1 +
 providers/implementations/ciphers/cipher_aria.c    |   1 +
 .../implementations/ciphers/cipher_blowfish.c      |   1 +
 .../implementations/ciphers/cipher_camellia.c      |   1 +
 providers/implementations/ciphers/cipher_cast5.c   |   1 +
 .../implementations/ciphers/cipher_chacha20.c      |   1 +
 .../ciphers/cipher_chacha20_poly1305.c             |   5 +-
 .../ciphers/cipher_chacha20_poly1305_hw.c          |   2 +
 providers/implementations/ciphers/cipher_des.c     |   1 +
 providers/implementations/ciphers/cipher_idea.c    |   1 +
 providers/implementations/ciphers/cipher_null.c    |  83 ++-
 providers/implementations/ciphers/cipher_rc2.c     |   1 +
 providers/implementations/ciphers/cipher_rc4.c     |   1 +
 .../implementations/ciphers/cipher_rc4_hmac_md5.c  |   1 +
 providers/implementations/ciphers/cipher_rc5.c     |   1 +
 providers/implementations/ciphers/cipher_seed.c    |   1 +
 providers/implementations/ciphers/cipher_sm4.c     |   1 +
 .../implementations/ciphers/cipher_tdes_common.c   |   1 +
 providers/implementations/ciphers/ciphercommon.c   | 130 ++++
 .../implementations/ciphers/ciphercommon_block.c   |  75 ++
 .../implementations/ciphers/ciphercommon_gcm.c     |   4 +-
 .../implementations/ciphers/ciphercommon_local.h   |   3 +
 .../implementations/include/prov/ciphercommon.h    |  21 +-
 ssl/build.info                                     |   4 +-
 ssl/record/rec_layer_d1.c                          |   2 +-
 ssl/record/rec_layer_s3.c                          |   5 +-
 ssl/record/record.h                                |  15 +-
 ssl/record/record_local.h                          |  22 +-
 ssl/record/ssl3_record.c                           | 809 ++++++++-------------
 ssl/record/ssl3_record_tls13.c                     |  41 +-
 ssl/record/tls_pad.c                               | 319 ++++++++
 ssl/s3_enc.c                                       |   6 +
 ssl/ssl_lib.c                                      |  25 +-
 ssl/ssl_local.h                                    |   5 +-
 ssl/t1_enc.c                                       |  45 ++
 test/sslcorrupttest.c                              |  14 +-
 test/tls13encryptiontest.c                         |   4 +-
 44 files changed, 1124 insertions(+), 588 deletions(-)
 create mode 100644 ssl/record/tls_pad.c

diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index fe126ccd7a..9ad81337c3 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -42,27 +42,30 @@ extern "C" {
 #define OSSL_ALG_PARAM_PROPERTIES   "properties"/* utf8_string */
 
 /* cipher parameters */
-#define OSSL_CIPHER_PARAM_PADDING              "padding"    /* uint */
-#define OSSL_CIPHER_PARAM_MODE                 "mode"       /* uint */
-#define OSSL_CIPHER_PARAM_BLOCK_SIZE           "blocksize" /* size_t */
-#define OSSL_CIPHER_PARAM_FLAGS                "flags"      /* ulong */
-#define OSSL_CIPHER_PARAM_KEYLEN               "keylen"     /* size_t */
-#define OSSL_CIPHER_PARAM_IVLEN                "ivlen"      /* size_t */
-#define OSSL_CIPHER_PARAM_IV                   "iv"         /* octet_string OR octet_ptr */
-#define OSSL_CIPHER_PARAM_NUM                  "num"        /* uint */
-#define OSSL_CIPHER_PARAM_ROUNDS               "rounds"     /* uint */
-#define OSSL_CIPHER_PARAM_AEAD_TAG             "tag"        /* octet_string */
-#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD        "tlsaad"     /* octet_string */
-#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD    "tlsaadpad"  /* size_t */
-#define OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED   "tlsivfixed" /* octet_string */
-#define OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN "tlsivgen" /* octet_string */
-#define OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV "tlsivinv" /* octet_string */
+#define OSSL_CIPHER_PARAM_PADDING              "padding"      /* uint */
+#define OSSL_CIPHER_PARAM_TLS_VERSION          "tls-version"  /* uint */
+#define OSSL_CIPHER_PARAM_TLS_MAC              "tls-mac"      /* octet_ptr */
+#define OSSL_CIPHER_PARAM_TLS_MAC_SIZE         "tls-mac-size" /* size_t */
+#define OSSL_CIPHER_PARAM_MODE                 "mode"         /* uint */
+#define OSSL_CIPHER_PARAM_BLOCK_SIZE           "blocksize"    /* size_t */
+#define OSSL_CIPHER_PARAM_FLAGS                "flags"        /* ulong */
+#define OSSL_CIPHER_PARAM_KEYLEN               "keylen"       /* size_t */
+#define OSSL_CIPHER_PARAM_IVLEN                "ivlen"        /* size_t */
+#define OSSL_CIPHER_PARAM_IV                   "iv"           /* octet_string OR octet_ptr */
+#define OSSL_CIPHER_PARAM_NUM                  "num"          /* uint */
+#define OSSL_CIPHER_PARAM_ROUNDS               "rounds"       /* uint */
+#define OSSL_CIPHER_PARAM_AEAD_TAG             "tag"          /* octet_string */
+#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD        "tlsaad"       /* octet_string */
+#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD    "tlsaadpad"    /* size_t */
+#define OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED   "tlsivfixed"   /* octet_string */
+#define OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN "tlsivgen"     /* octet_string */
+#define OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV "tlsivinv"     /* octet_string */
 #define OSSL_CIPHER_PARAM_AEAD_IVLEN           OSSL_CIPHER_PARAM_IVLEN
-#define OSSL_CIPHER_PARAM_AEAD_TAGLEN          "taglen"     /* size_t */
-#define OSSL_CIPHER_PARAM_AEAD_MAC_KEY         "mackey"     /* octet_string */
-#define OSSL_CIPHER_PARAM_RANDOM_KEY           "randkey"    /* octet_string */
-#define OSSL_CIPHER_PARAM_RC2_KEYBITS          "keybits"    /* size_t */
-#define OSSL_CIPHER_PARAM_SPEED                "speed"      /* uint */
+#define OSSL_CIPHER_PARAM_AEAD_TAGLEN          "taglen"       /* size_t */
+#define OSSL_CIPHER_PARAM_AEAD_MAC_KEY         "mackey"       /* octet_string */
+#define OSSL_CIPHER_PARAM_RANDOM_KEY           "randkey"      /* octet_string */
+#define OSSL_CIPHER_PARAM_RC2_KEYBITS          "keybits"      /* size_t */
+#define OSSL_CIPHER_PARAM_SPEED                "speed"        /* uint */
 /* For passing the AlgorithmIdentifier parameter in DER form */
 #define OSSL_CIPHER_PARAM_ALG_ID               "alg_id_param" /* octet_string */
 
diff --git a/providers/implementations/ciphers/cipher_aes.c b/providers/implementations/ciphers/cipher_aes.c
index ea23e1eed9..decc27517c 100644
--- a/providers/implementations/ciphers/cipher_aes.c
+++ b/providers/implementations/ciphers/cipher_aes.c
@@ -26,6 +26,7 @@ static void aes_freectx(void *vctx)
 {
     PROV_AES_CTX *ctx = (PROV_AES_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c
index d684914c5a..046a66c56d 100644
--- a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c
+++ b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c
@@ -283,8 +283,10 @@ static void aes_cbc_hmac_sha1_freectx(void *vctx)
 {
     PROV_AES_HMAC_SHA1_CTX *ctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
 
-    if (ctx != NULL)
+    if (ctx != NULL) {
+        cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
         OPENSSL_clear_free(ctx, sizeof(*ctx));
+    }
 }
 
 static void *aes_cbc_hmac_sha256_newctx(void *provctx, size_t kbits,
@@ -304,8 +306,10 @@ static void aes_cbc_hmac_sha256_freectx(void *vctx)
 {
     PROV_AES_HMAC_SHA256_CTX *ctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
 
-    if (ctx != NULL)
+    if (ctx != NULL) {
+        cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
         OPENSSL_clear_free(ctx, sizeof(*ctx));
+    }
 }
 
 # define IMPLEMENT_CIPHER(nm, sub, kbits, blkbits, ivbits, flags)              \
diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c
index dc2412c7b5..12644e780f 100644
--- a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c
+++ b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c
@@ -60,6 +60,8 @@ static int aesni_cbc_hmac_sha1_init_key(PROV_CIPHER_CTX *vctx,
 
     ctx->payload_length = NO_PAYLOAD_LENGTH;
 
+    vctx->removetlspad = SHA_DIGEST_LENGTH + AES_BLOCK_SIZE;
+
     return ret < 0 ? 0 : 1;
 }
 
diff --git a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c
index f2a233710c..35106e0171 100644
--- a/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c
+++ b/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c
@@ -62,6 +62,8 @@ static int aesni_cbc_hmac_sha256_init_key(PROV_CIPHER_CTX *vctx,
 
     ctx->payload_length = NO_PAYLOAD_LENGTH;
 
+    vctx->removetlspad = SHA256_DIGEST_LENGTH + AES_BLOCK_SIZE;
+
     return ret < 0 ? 0 : 1;
 }
 
diff --git a/providers/implementations/ciphers/cipher_aes_ocb.c b/providers/implementations/ciphers/cipher_aes_ocb.c
index 09c38b7ef4..2f30b7ffdf 100644
--- a/providers/implementations/ciphers/cipher_aes_ocb.c
+++ b/providers/implementations/ciphers/cipher_aes_ocb.c
@@ -305,6 +305,7 @@ static void aes_ocb_freectx(void *vctx)
 
     if (ctx != NULL) {
         aes_generic_ocb_cleanup(ctx);
+        cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
         OPENSSL_clear_free(ctx,  sizeof(*ctx));
     }
 }
diff --git a/providers/implementations/ciphers/cipher_aes_wrp.c b/providers/implementations/ciphers/cipher_aes_wrp.c
index 9782afa137..5c2ab1c507 100644
--- a/providers/implementations/ciphers/cipher_aes_wrp.c
+++ b/providers/implementations/ciphers/cipher_aes_wrp.c
@@ -64,6 +64,7 @@ static void aes_wrap_freectx(void *vctx)
 {
     PROV_AES_WRAP_CTX *wctx = (PROV_AES_WRAP_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(wctx,  sizeof(*wctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_aes_xts.c b/providers/implementations/ciphers/cipher_aes_xts.c
index 96e885e2ca..f564075abe 100644
--- a/providers/implementations/ciphers/cipher_aes_xts.c
+++ b/providers/implementations/ciphers/cipher_aes_xts.c
@@ -120,6 +120,7 @@ static void aes_xts_freectx(void *vctx)
 {
     PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_aria.c b/providers/implementations/ciphers/cipher_aria.c
index 67dfe0d35f..a079617928 100644
--- a/providers/implementations/ciphers/cipher_aria.c
+++ b/providers/implementations/ciphers/cipher_aria.c
@@ -19,6 +19,7 @@ static void aria_freectx(void *vctx)
 {
     PROV_ARIA_CTX *ctx = (PROV_ARIA_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_blowfish.c b/providers/implementations/ciphers/cipher_blowfish.c
index bb2fa88f6a..3eb4ebead2 100644
--- a/providers/implementations/ciphers/cipher_blowfish.c
+++ b/providers/implementations/ciphers/cipher_blowfish.c
@@ -27,6 +27,7 @@ static void blowfish_freectx(void *vctx)
 {
     PROV_BLOWFISH_CTX *ctx = (PROV_BLOWFISH_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_camellia.c b/providers/implementations/ciphers/cipher_camellia.c
index abb24621a6..ffb23b475a 100644
--- a/providers/implementations/ciphers/cipher_camellia.c
+++ b/providers/implementations/ciphers/cipher_camellia.c
@@ -25,6 +25,7 @@ static void camellia_freectx(void *vctx)
 {
     PROV_CAMELLIA_CTX *ctx = (PROV_CAMELLIA_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_cast5.c b/providers/implementations/ciphers/cipher_cast5.c
index febadfb62b..938b8d2013 100644
--- a/providers/implementations/ciphers/cipher_cast5.c
+++ b/providers/implementations/ciphers/cipher_cast5.c
@@ -28,6 +28,7 @@ static void cast5_freectx(void *vctx)
 {
     PROV_CAST_CTX *ctx = (PROV_CAST_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_chacha20.c b/providers/implementations/ciphers/cipher_chacha20.c
index 45571180c8..6759b0e0f9 100644
--- a/providers/implementations/ciphers/cipher_chacha20.c
+++ b/providers/implementations/ciphers/cipher_chacha20.c
@@ -55,6 +55,7 @@ static void chacha20_freectx(void *vctx)
     PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)vctx;
 
     if (ctx != NULL) {
+        cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
         OPENSSL_clear_free(ctx, sizeof(*ctx));
     }
 }
diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305.c b/providers/implementations/ciphers/cipher_chacha20_poly1305.c
index c34ef320ab..a93f722551 100644
--- a/providers/implementations/ciphers/cipher_chacha20_poly1305.c
+++ b/providers/implementations/ciphers/cipher_chacha20_poly1305.c
@@ -65,8 +65,10 @@ static void chacha20_poly1305_freectx(void *vctx)
 {
     PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx;
 
-    if (ctx != NULL)
+    if (ctx != NULL) {
+        cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
         OPENSSL_clear_free(ctx, sizeof(*ctx));
+    }
 }
 
 static int chacha20_poly1305_get_params(OSSL_PARAM params[])
@@ -275,7 +277,6 @@ static int chacha20_poly1305_cipher(void *vctx, unsigned char *out,
     if (!hw->aead_cipher(ctx, out, outl, in, inl))
         return 0;
 
-    *outl = inl;
     return 1;
 }
 
diff --git a/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c
index 74b8fb6586..70ffaf1588 100644
--- a/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c
+++ b/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c
@@ -252,6 +252,8 @@ static int chacha20_poly1305_tls_cipher(PROV_CIPHER_CTX *bctx,
                        len - POLY1305_BLOCK_SIZE);
             return 0;
         }
+        /* Strip the tag */
+        len -= POLY1305_BLOCK_SIZE;
     }
 
     *out_padlen = len;
diff --git a/providers/implementations/ciphers/cipher_des.c b/providers/implementations/ciphers/cipher_des.c
index 7a7f16e454..7a60e0501c 100644
--- a/providers/implementations/ciphers/cipher_des.c
+++ b/providers/implementations/ciphers/cipher_des.c
@@ -58,6 +58,7 @@ static void des_freectx(void *vctx)
 {
     PROV_DES_CTX *ctx = (PROV_DES_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_idea.c b/providers/implementations/ciphers/cipher_idea.c
index 68cca45f92..7fc5d8403d 100644
--- a/providers/implementations/ciphers/cipher_idea.c
+++ b/providers/implementations/ciphers/cipher_idea.c
@@ -26,6 +26,7 @@ static void idea_freectx(void *vctx)
 {
     PROV_IDEA_CTX *ctx = (PROV_IDEA_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_null.c b/providers/implementations/ciphers/cipher_null.c
index 3018a5b075..713d29e3e8 100644
--- a/providers/implementations/ciphers/cipher_null.c
+++ b/providers/implementations/ciphers/cipher_null.c
@@ -14,22 +14,37 @@
 #include "prov/ciphercommon.h"
 #include "prov/providercommonerr.h"
 
+typedef struct prov_cipher_null_ctx_st {
+    int enc;
+    size_t tlsmacsize;
+    const unsigned char *tlsmac;
+} PROV_CIPHER_NULL_CTX;
+
 static OSSL_FUNC_cipher_newctx_fn null_newctx;
 static void *null_newctx(void *provctx)
 {
-    static int dummy = 0;
-
-    return &dummy;
+    return OPENSSL_zalloc(sizeof(PROV_CIPHER_NULL_CTX));
 }
 
 static OSSL_FUNC_cipher_freectx_fn null_freectx;
 static void null_freectx(void *vctx)
 {
+    OPENSSL_free(vctx);
+}
+
+static OSSL_FUNC_cipher_encrypt_init_fn null_einit;
+static int null_einit(void *vctx, const unsigned char *key, size_t keylen,
+                      const unsigned char *iv, size_t ivlen)
+{
+    PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
+
+    ctx->enc = 1;
+    return 1;
 }
 
-static OSSL_FUNC_cipher_encrypt_init_fn null_init;
-static int null_init(void *vctx, const unsigned char *key, size_t keylen,
-                     const unsigned char *iv, size_t ivlen)
+static OSSL_FUNC_cipher_decrypt_init_fn null_dinit;
+static int null_dinit(void *vctx, const unsigned char *key, size_t keylen,
+                      const unsigned char *iv, size_t ivlen)
 {
     return 1;
 }
@@ -38,6 +53,18 @@ static OSSL_FUNC_cipher_cipher_fn null_cipher;
 static int null_cipher(void *vctx, unsigned char *out, size_t *outl,
                        size_t outsize, const unsigned char *in, size_t inl)
 {
+    PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
+
+    if (!ctx->enc && ctx->tlsmacsize > 0) {
+        /*
+         * TLS NULL cipher as per:
+         * https://tools.ietf.org/html/rfc5246#section-6.2.3.1
+         */
+        if (inl < ctx->tlsmacsize)
+            return 0;
+        ctx->tlsmac = in + inl - ctx->tlsmacsize;
+        inl -= ctx->tlsmacsize;
+    }
     if (outsize < inl)
         return 0;
     if (in != out)
@@ -63,6 +90,7 @@ static int null_get_params(OSSL_PARAM params[])
 static const OSSL_PARAM null_known_gettable_ctx_params[] = {
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+    { OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED },
     OSSL_PARAM_END
 };
 
@@ -75,6 +103,7 @@ static const OSSL_PARAM *null_gettable_ctx_params(void)
 static OSSL_FUNC_cipher_get_ctx_params_fn null_get_ctx_params;
 static int null_get_ctx_params(void *vctx, OSSL_PARAM params[])
 {
+    PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
     OSSL_PARAM *p;
 
     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
@@ -87,6 +116,41 @@ static int null_get_ctx_params(void *vctx, OSSL_PARAM params[])
         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
         return 0;
     }
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC);
+    if (p != NULL
+        && !OSSL_PARAM_set_octet_ptr(p, ctx->tlsmac, ctx->tlsmacsize)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
+    return 1;
+}
+
+static const OSSL_PARAM null_known_settable_ctx_params[] = {
+    OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL),
+    OSSL_PARAM_END
+};
+
+static OSSL_FUNC_cipher_settable_ctx_params_fn null_settable_ctx_params;
+static const OSSL_PARAM *null_settable_ctx_params(void)
+{
+    return null_known_settable_ctx_params;
+}
+
+
+static OSSL_FUNC_cipher_set_ctx_params_fn null_set_ctx_params;
+static int null_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE);
+    if (p != NULL) {
+        if (!OSSL_PARAM_get_size_t(p, &ctx->tlsmacsize)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+    }
+
     return 1;
 }
 
@@ -95,8 +159,8 @@ const OSSL_DISPATCH null_functions[] = {
       (void (*)(void)) null_newctx },
     { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) null_freectx },
     { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) null_newctx },
-    { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))null_init },
-    { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))null_init },
+    { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))null_einit },
+    { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))null_dinit },
     { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))null_cipher },
     { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))null_final },
     { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))null_cipher },
@@ -106,5 +170,8 @@ const OSSL_DISPATCH null_functions[] = {
     { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))null_get_ctx_params },
     { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,
       (void (*)(void))null_gettable_ctx_params },
+    { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))null_set_ctx_params },
+    { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,
+      (void (*)(void))null_settable_ctx_params },
     { 0, NULL }
 };
diff --git a/providers/implementations/ciphers/cipher_rc2.c b/providers/implementations/ciphers/cipher_rc2.c
index f2304b7c0f..d1558be002 100644
--- a/providers/implementations/ciphers/cipher_rc2.c
+++ b/providers/implementations/ciphers/cipher_rc2.c
@@ -32,6 +32,7 @@ static void rc2_freectx(void *vctx)
 {
     PROV_RC2_CTX *ctx = (PROV_RC2_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_rc4.c b/providers/implementations/ciphers/cipher_rc4.c
index 97d66660f0..4660185d45 100644
--- a/providers/implementations/ciphers/cipher_rc4.c
+++ b/providers/implementations/ciphers/cipher_rc4.c
@@ -28,6 +28,7 @@ static void rc4_freectx(void *vctx)
 {
     PROV_RC4_CTX *ctx = (PROV_RC4_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_rc4_hmac_md5.c b/providers/implementations/ciphers/cipher_rc4_hmac_md5.c
index 836274abb0..d9535e23ce 100644
--- a/providers/implementations/ciphers/cipher_rc4_hmac_md5.c
+++ b/providers/implementations/ciphers/cipher_rc4_hmac_md5.c
@@ -62,6 +62,7 @@ static void rc4_hmac_md5_freectx(void *vctx)
 {
     PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_rc5.c b/providers/implementations/ciphers/cipher_rc5.c
index 4d71927914..68ce6fdd91 100644
--- a/providers/implementations/ciphers/cipher_rc5.c
+++ b/providers/implementations/ciphers/cipher_rc5.c
@@ -28,6 +28,7 @@ static void rc5_freectx(void *vctx)
 {
     PROV_RC5_CTX *ctx = (PROV_RC5_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_seed.c b/providers/implementations/ciphers/cipher_seed.c
index 3a3e012fe0..53520b3c4d 100644
--- a/providers/implementations/ciphers/cipher_seed.c
+++ b/providers/implementations/ciphers/cipher_seed.c
@@ -25,6 +25,7 @@ static void seed_freectx(void *vctx)
 {
     PROV_SEED_CTX *ctx = (PROV_SEED_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_sm4.c b/providers/implementations/ciphers/cipher_sm4.c
index e7208ad16c..a5920562fc 100644
--- a/providers/implementations/ciphers/cipher_sm4.c
+++ b/providers/implementations/ciphers/cipher_sm4.c
@@ -19,6 +19,7 @@ static void sm4_freectx(void *vctx)
 {
     PROV_SM4_CTX *ctx = (PROV_SM4_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/cipher_tdes_common.c b/providers/implementations/ciphers/cipher_tdes_common.c
index 4e50450e4d..6cdc88749c 100644
--- a/providers/implementations/ciphers/cipher_tdes_common.c
+++ b/providers/implementations/ciphers/cipher_tdes_common.c
@@ -48,6 +48,7 @@ void tdes_freectx(void *vctx)
 {
     PROV_TDES_CTX *ctx = (PROV_TDES_CTX *)vctx;
 
+    cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
     OPENSSL_clear_free(ctx,  sizeof(*ctx));
 }
 
diff --git a/providers/implementations/ciphers/ciphercommon.c b/providers/implementations/ciphers/ciphercommon.c
index 93bee0dc0f..a8905d1242 100644
--- a/providers/implementations/ciphers/ciphercommon.c
+++ b/providers/implementations/ciphers/ciphercommon.c
@@ -11,6 +11,8 @@
  * Generic dispatch table functions for ciphers.
  */
 
+/* For SSL3_VERSION */
+#include <openssl/ssl.h>
 #include "ciphercommon_local.h"
 #include "prov/provider_ctx.h"
 #include "prov/providercommonerr.h"
@@ -24,6 +26,7 @@ static const OSSL_PARAM cipher_known_gettable_params[] = {
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL),
     OSSL_PARAM_ulong(OSSL_CIPHER_PARAM_FLAGS, NULL),
+    { OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED },
     OSSL_PARAM_END
 };
 const OSSL_PARAM *cipher_generic_gettable_params(void)
@@ -69,6 +72,8 @@ CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(cipher_generic)
 CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(cipher_generic)
 
 CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(cipher_generic)
+OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS_VERSION, NULL),
+OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL),
 CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(cipher_generic)
 
 /*
@@ -130,6 +135,15 @@ const OSSL_PARAM *cipher_aead_settable_ctx_params(void)
     return cipher_aead_known_settable_ctx_params;
 }
 
+void cipher_generic_reset_ctx(PROV_CIPHER_CTX *ctx)
+{
+    if (ctx != NULL && ctx->alloced) {
+        OPENSSL_free(ctx->tlsmac);
+        ctx->alloced = 0;
+        ctx->tlsmac = NULL;
+    }
+}
+
 static int cipher_generic_init_internal(PROV_CIPHER_CTX *ctx,
                                         const unsigned char *key, size_t keylen,
                                         const unsigned char *iv, size_t ivlen,
@@ -169,6 +183,9 @@ int cipher_generic_dinit(void *vctx, const unsigned char *key, size_t keylen,
                                         iv, ivlen, 0);
 }
 
+/* Max padding including padding length byte */
+#define MAX_PADDING 256
+
 int cipher_generic_block_update(void *vctx, unsigned char *out, size_t *outl,
                                 size_t outsize, const unsigned char *in,
                                 size_t inl)
@@ -178,6 +195,81 @@ int cipher_generic_block_update(void *vctx, unsigned char *out, size_t *outl,
     size_t blksz = ctx->blocksize;
     size_t nextblocks;
 
+    if (ctx->tlsversion > 0) {
+        /*
+         * Each update call corresponds to a TLS record and is individually
+         * padded
+         */
+
+        /* Sanity check inputs */
+        if (in == NULL
+                || in != out
+                || outsize < inl
+                || !ctx->pad) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+            return 0;
+        }
+
+        if (ctx->enc) {
+            unsigned char padval;
+            size_t padnum, loop;
+
+            /* Add padding */
+
+            padnum = blksz - (inl % blksz);
+
+            if (outsize < inl + padnum) {
+                ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+                return 0;
+            }
+
+            if (padnum > MAX_PADDING) {
+                ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+                return 0;
+            }
+            padval = (unsigned char)(padnum - 1);
+            if (ctx->tlsversion == SSL3_VERSION) {
+                if (padnum > 1)
+                    memset(out + inl, 0, padnum - 1);
+                *(out + inl + padnum - 1) = padval;
+            } else {
+                /* we need to add 'padnum' padding bytes of value padval */
+                for (loop = inl; loop < inl + padnum; loop++)
+                    out[loop] = padval;
+            }
+            inl += padnum;
+        }
+
+        if ((inl % blksz) != 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+            return 0;
+        }
+
+
+        /* Shouldn't normally fail */
+        if (!ctx->hw->cipher(ctx, out, in, inl)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+            return 0;
+        }
+
+        if (ctx->alloced) {
+            OPENSSL_free(ctx->tlsmac);
+            ctx->alloced = 0;
+            ctx->tlsmac = NULL;
+        }
+
+        /* This only fails if padding is publicly invalid */
+        *outl = inl;
+        if (!ctx->enc
+                && !tlsunpadblock(ctx->libctx, ctx->tlsversion, out, outl,
+                                  blksz, &ctx->tlsmac, &ctx->alloced,
+                                  ctx->tlsmacsize, 0)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+            return 0;
+        }
+        return 1;
+    }
+
     if (ctx->bufsz != 0)
         nextblocks = fillblock(ctx->buf, &ctx->bufsz, blksz, &in, &inl);
     else
@@ -238,6 +330,12 @@ int cipher_generic_block_final(void *vctx, unsigned char *out, size_t *outl,
     PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
     size_t blksz = ctx->blocksize;
 
+    if (ctx->tlsversion > 0) {
+        /* We never finalize TLS, so this is an error */
+        ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+        return 0;
+    }
+
     if (ctx->enc) {
         if (ctx->pad) {
             padblock(ctx->buf, &ctx->bufsz, blksz);
@@ -314,6 +412,18 @@ int cipher_generic_stream_update(void *vctx, unsigned char *out, size_t *outl,
     }
 
     *outl = inl;
+    /*
+     * Remove any TLS padding. Only used by cipher_aes_cbc_hmac_sha1_hw.c and
+     * cipher_aes_cbc_hmac_sha256_hw.c
+     */
+    if (!ctx->enc && ctx->removetlspad > 0) {
+        /* The actual padding length */
+        *outl -= out[inl - 1] + 1;
+
+        /* MAC and explicit IV */
+        *outl -= ctx->removetlspad;
+    }
+
     return 1;
 }
 int cipher_generic_stream_final(void *vctx, unsigned char *out, size_t *outl,
@@ -375,6 +485,12 @@ int cipher_generic_get_ctx_params(void *vctx, OSSL_PARAM params[])
         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
         return 0;
     }
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC);
+    if (p != NULL
+        && !OSSL_PARAM_set_octet_ptr(p, ctx->tlsmac, ctx->tlsmacsize)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
     return 1;
 }
 
@@ -393,6 +509,20 @@ int cipher_generic_set_ctx_params(void *vctx, const OSSL_PARAM params[])
         }
         ctx->pad = pad ? 1 : 0;
     }
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION);
+    if (p != NULL) {
+        if (!OSSL_PARAM_get_uint(p, &ctx->tlsversion)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+    }
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE);
+    if (p != NULL) {
+        if (!OSSL_PARAM_get_size_t(p, &ctx->tlsmacsize)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+            return 0;
+        }
+    }
     p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_NUM);
     if (p != NULL) {
         unsigned int num;
diff --git a/providers/implementations/ciphers/ciphercommon_block.c b/providers/implementations/ciphers/ciphercommon_block.c
index ac792d68d7..ba6f68eeff 100644
--- a/providers/implementations/ciphers/ciphercommon_block.c
+++ b/providers/implementations/ciphers/ciphercommon_block.c
@@ -8,9 +8,31 @@
  */
 
 #include <assert.h>
+/* For SSL3_VERSION, TLS1_VERSION etc */
+#include <openssl/ssl.h>
+#include <openssl/rand.h>
+#include "internal/constant_time.h"
 #include "ciphercommon_local.h"
 #include "prov/providercommonerr.h"
 
+/* Functions defined in ssl/tls_pad.c */
+int ssl3_cbc_remove_padding_and_mac(size_t *reclen,
+                                    size_t origreclen,
+                                    unsigned char *recdata,
+                                    unsigned char **mac,
+                                    int *alloced,
+                                    size_t block_size, size_t mac_size,
+                                    OPENSSL_CTX *libctx);
+
+int tls1_cbc_remove_padding_and_mac(size_t *reclen,
+                                    size_t origreclen,
+                                    unsigned char *recdata,
+                                    unsigned char **mac,
+                                    int *alloced,
+                                    size_t block_size, size_t mac_size,
+                                    int aead,
+                                    OPENSSL_CTX *libctx);
+
 /*
  * Fills a single block of buffered data from the input, and returns the amount
  * of data remaining in the input that is a multiple of the blocksize. The buffer
@@ -110,3 +132,56 @@ int unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize)
     *buflen = len;
     return 1;
 }
+
+/*-
+ * tlsunpadblock removes the CBC padding from the decrypted, TLS, CBC
+ * record in constant time. Also removes the MAC from the record in constant
+ * time.
+ *
+ * libctx: Our library context
+ * tlsversion: The TLS version in use, e.g. SSL3_VERSION, TLS1_VERSION, etc
+ * buf: The decrypted TLS record data
+ * buflen: The length of the decrypted TLS record data. Updated with the new
+ *         length after the padding is removed
+ * block_size: the block size of the cipher used to encrypt the record.
+ * mac: Location to store the pointer to the MAC
+ * alloced: Whether the MAC is stored in a newly allocated buffer, or whether
+ *          *mac points into *buf
+ * macsize: the size of the MAC inside the record (or 0 if there isn't one)
+ * aead: whether this is an aead cipher
+ * returns:
+ *   0: (in non-constant time) if the record is publicly invalid.
+ *   1: (in constant time) Record is publicly valid. If padding is invalid then
+ *      the mac is random
+ */
+int tlsunpadblock(OPENSSL_CTX *libctx, unsigned int tlsversion,
+                  unsigned char *buf, size_t *buflen, size_t blocksize,
+                  unsigned char **mac, int *alloced, size_t macsize, int aead)
+{
+    int ret;
+
+    switch (tlsversion) {
+    case SSL3_VERSION:
+        return ssl3_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac,
+                                               alloced, blocksize, macsize,
+                                               libctx);
+
+    case TLS1_2_VERSION:
+    case DTLS1_2_VERSION:
+    case TLS1_1_VERSION:
+    case DTLS1_VERSION:
+    case DTLS1_BAD_VER:
+        /* Remove the explicit IV */
+        buf += blocksize;
+        *buflen -= blocksize;
+        /* Fall through */
+    case TLS1_VERSION:
+        ret = tls1_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac,
+                                              alloced, blocksize, macsize,
+                                              aead, libctx);
+        return ret;
+
+    default:
+        return 0;
+    }
+}
diff --git a/providers/implementations/ciphers/ciphercommon_gcm.c b/providers/implementations/ciphers/ciphercommon_gcm.c
index 7daa8dce5b..080fcc9bc2 100644
--- a/providers/implementations/ciphers/ciphercommon_gcm.c
+++ b/providers/implementations/ciphers/ciphercommon_gcm.c
@@ -280,12 +280,12 @@ int gcm_stream_update(void *vctx, unsigned char *out, size_t *outl,
 
     if (outsize < inl) {
         ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
-        return -1;
+        return 0;
     }
 
     if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0) {
         ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
-        return -1;
+        return 0;
     }
     return 1;
 }
diff --git a/providers/implementations/ciphers/ciphercommon_local.h b/providers/implementations/ciphers/ciphercommon_local.h
index 1c4716f357..43c1c192af 100644
--- a/providers/implementations/ciphers/ciphercommon_local.h
+++ b/providers/implementations/ciphers/ciphercommon_local.h
@@ -11,3 +11,6 @@
 
 void padblock(unsigned char *buf, size_t *buflen, size_t blocksize);
 int unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize);
+int tlsunpadblock(OPENSSL_CTX *libctx, unsigned int tlsversion,
+                  unsigned char *buf, size_t *buflen, size_t blocksize,
+                  unsigned char **mac, int *alloced, size_t macsize, int aead);
diff --git a/providers/implementations/include/prov/ciphercommon.h b/providers/implementations/include/prov/ciphercommon.h
index fa6eec6a27..a5ffbc48a1 100644
--- a/providers/implementations/include/prov/ciphercommon.h
+++ b/providers/implementations/include/prov/ciphercommon.h
@@ -31,6 +31,9 @@ typedef struct prov_cipher_ctx_st PROV_CIPHER_CTX;
 typedef int (PROV_CIPHER_HW_FN)(PROV_CIPHER_CTX *dat, unsigned char *out,
                                 const unsigned char *in, size_t len);
 
+/* TODO(3.0): VERIFY ME */
+#define MAX_TLS_MAC_SIZE    48
+
 struct prov_cipher_ctx_st {
     block128_f block;
     union {
@@ -48,6 +51,19 @@ struct prov_cipher_ctx_st {
     unsigned int enc : 1;    /* Set to 1 for encrypt, or 0 otherwise */
     unsigned int iv_set : 1; /* Set when the iv is copied to the iv/oiv buffers */
 
+    unsigned int tlsversion; /* If TLS padding is in use the TLS version number */
+    unsigned char *tlsmac;   /* tls MAC extracted from the last record */
+    int alloced;             /*
+                              * Whether the tlsmac data has been allocated or
+                              * points into the user buffer.
+                              */
+    size_t tlsmacsize;       /* Size of the TLS MAC */
+    size_t removetlspad;     /*
+                              * Length of the fixed size data to remove when
+                              * removing TLS padding (equals mac size plus
+                              * IV size if applicable)
+                              */
+
     /*
      * num contains the number of bytes of |iv| which are valid for modes that
      * manage partial blocks themselves.
@@ -71,6 +87,7 @@ struct prov_cipher_hw_st {
     void (*copyctx)(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src);
 };
 
+void cipher_generic_reset_ctx(PROV_CIPHER_CTX *ctx);
 OSSL_FUNC_cipher_encrypt_init_fn cipher_generic_einit;
 OSSL_FUNC_cipher_decrypt_init_fn cipher_generic_dinit;
 OSSL_FUNC_cipher_update_fn cipher_generic_block_update;
@@ -87,6 +104,7 @@ OSSL_FUNC_cipher_set_ctx_params_fn cipher_var_keylen_set_ctx_params;
 OSSL_FUNC_cipher_settable_ctx_params_fn cipher_var_keylen_settable_ctx_params;
 OSSL_FUNC_cipher_gettable_ctx_params_fn cipher_aead_gettable_ctx_params;
 OSSL_FUNC_cipher_settable_ctx_params_fn cipher_aead_settable_ctx_params;
+
 int cipher_generic_get_params(OSSL_PARAM params[], unsigned int md,
                               unsigned long flags,
                               size_t kbits, size_t blkbits, size_t ivbits);
@@ -164,7 +182,8 @@ static void * alg##_##kbits##_##lcmode##_newctx(void *provctx)                 \
      if (ctx != NULL) {                                                        \
          cipher_generic_initkey(ctx, kbits, blkbits, ivbits,                   \
                                 EVP_CIPH_##UCMODE##_MODE, flags,               \
-                                PROV_CIPHER_HW_##alg##_##lcmode(kbits), NULL); \
+                                PROV_CIPHER_HW_##alg##_##lcmode(kbits),        \
+                                provctx);                                      \
      }                                                                         \
      return ctx;                                                               \
 }                                                                              \
diff --git a/ssl/build.info b/ssl/build.info
index 5d70dec676..fd187ac7e5 100644
--- a/ssl/build.info
+++ b/ssl/build.info
@@ -27,5 +27,7 @@ SOURCE[../libssl]=\
         ssl_asn1.c ssl_txt.c ssl_init.c ssl_conf.c  ssl_mcnf.c \
         bio_ssl.c ssl_err.c tls_srp.c t1_trce.c ssl_utst.c \
         record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \
-        statem/statem.c record/ssl3_record_tls13.c
+        statem/statem.c record/ssl3_record_tls13.c record/tls_pad.c
 DEFINE[../libssl]=$AESDEF
+
+SOURCE[../providers/libcommon.a]=record/tls_pad.c
diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c
index 9a82e3ffa2..866ef18381 100644
--- a/ssl/record/rec_layer_d1.c
+++ b/ssl/record/rec_layer_d1.c
@@ -939,7 +939,7 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf,
     if (eivlen)
         SSL3_RECORD_add_length(&wr, eivlen);
 
-    if (s->method->ssl3_enc->enc(s, &wr, 1, 1) < 1) {
+    if (s->method->ssl3_enc->enc(s, &wr, 1, 1, NULL, mac_size) < 1) {
         if (!ossl_statem_in_error(s)) {
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_DTLS1_WRITE,
                      ERR_R_INTERNAL_ERROR);
diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c
index fac3506b19..8ea16672b6 100644
--- a/ssl/record/rec_layer_s3.c
+++ b/ssl/record/rec_layer_s3.c
@@ -1044,7 +1044,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
          * We haven't actually negotiated the version yet, but we're trying to
          * send early data - so we need to use the tls13enc function.
          */
-        if (tls13_enc(s, wr, numpipes, 1) < 1) {
+        if (tls13_enc(s, wr, numpipes, 1, NULL, mac_size) < 1) {
             if (!ossl_statem_in_error(s)) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
                          ERR_R_INTERNAL_ERROR);
@@ -1053,7 +1053,8 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
         }
     } else {
         if (!BIO_get_ktls_send(s->wbio)) {
-            if (s->method->ssl3_enc->enc(s, wr, numpipes, 1) < 1) {
+            if (s->method->ssl3_enc->enc(s, wr, numpipes, 1, NULL,
+                                         mac_size) < 1) {
                 if (!ossl_statem_in_error(s)) {
                     SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DO_SSL3_WRITE,
                             ERR_R_INTERNAL_ERROR);
diff --git a/ssl/record/record.h b/ssl/record/record.h
index 0504a6f959..234656bf93 100644
--- a/ssl/record/record.h
+++ b/ssl/record/record.h
@@ -178,6 +178,12 @@ typedef struct record_layer_st {
  *                                                                           *
  *****************************************************************************/
 
+struct ssl_mac_buf_st {
+    unsigned char *mac;
+    int alloced;
+};
+typedef struct ssl_mac_buf_st SSL_MAC_BUF;
+
 #define MIN_SSL2_RECORD_LEN     9
 
 #define RECORD_LAYER_set_read_ahead(rl, ra)     ((rl)->read_ahead = (ra))
@@ -213,13 +219,16 @@ __owur int ssl3_read_bytes(SSL *s, int type, int *recvd_type,
                            unsigned char *buf, size_t len, int peek,
                            size_t *readbytes);
 __owur int ssl3_setup_buffers(SSL *s);
-__owur int ssl3_enc(SSL *s, SSL3_RECORD *inrecs, size_t n_recs, int send);
+__owur int ssl3_enc(SSL *s, SSL3_RECORD *inrecs, size_t n_recs, int send,
+                    SSL_MAC_BUF *mac, size_t macsize);
 __owur int n_ssl3_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int send);
 __owur int ssl3_write_pending(SSL *s, int type, const unsigned char *buf, size_t len,
                               size_t *written);
-__owur int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int send);
+__owur int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending,
+                    SSL_MAC_BUF *mac, size_t macsize);
 __owur int tls1_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int send);
-__owur int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int send);
+__owur int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int send,
+                     SSL_MAC_BUF *mac, size_t macsize);
 int DTLS_RECORD_LAYER_new(RECORD_LAYER *rl);
 void DTLS_RECORD_LAYER_free(RECORD_LAYER *rl);
 void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl);
diff --git a/ssl/record/record_local.h b/ssl/record/record_local.h
index f7734d832b..9047c23fd5 100644
--- a/ssl/record/record_local.h
+++ b/ssl/record/record_local.h
@@ -107,13 +107,21 @@ void SSL3_RECORD_set_seq_num(SSL3_RECORD *r, const unsigned char *seq_num);
 int ssl3_get_record(SSL *s);
 __owur int ssl3_do_compress(SSL *ssl, SSL3_RECORD *wr);
 __owur int ssl3_do_uncompress(SSL *ssl, SSL3_RECORD *rr);
-int ssl3_cbc_copy_mac(unsigned char *out,
-                       const SSL3_RECORD *rec, size_t md_size);
-__owur int ssl3_cbc_remove_padding(SSL3_RECORD *rec,
-                                   size_t block_size, size_t mac_size);
-__owur int tls1_cbc_remove_padding(const SSL *s,
-                                   SSL3_RECORD *rec,
-                                   size_t block_size, size_t mac_size);
+__owur int ssl3_cbc_remove_padding_and_mac(size_t *reclen,
+                                           size_t origreclen,
+                                           unsigned char *recdata,
+                                           unsigned char **mac,
+                                           int *alloced,
+                                           size_t block_size, size_t mac_size,
+                                           OPENSSL_CTX *libctx);
+__owur int tls1_cbc_remove_padding_and_mac(size_t *reclen,
+                                           size_t origreclen,
+                                           unsigned char *recdata,
+                                           unsigned char **mac,
+                                           int *alloced,
+                                           size_t block_size, size_t mac_size,
+                                           int aead,
+                                           OPENSSL_CTX *libctx);
 int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap);
 __owur int dtls1_get_record(SSL *s);
 int early_data_count_ok(SSL *s, size_t length, size_t overhead, int send);
diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c
index a2f7f848d1..80990e8296 100644
--- a/ssl/record/ssl3_record.c
+++ b/ssl/record/ssl3_record.c
@@ -8,9 +8,9 @@
  */
 
 #include "../ssl_local.h"
-#include "internal/constant_time.h"
 #include <openssl/trace.h>
 #include <openssl/rand.h>
+#include <openssl/core_names.h>
 #include "record_local.h"
 #include "internal/cryptlib.h"
 
@@ -182,12 +182,13 @@ int ssl3_get_record(SSL *s)
     unsigned char *p;
     unsigned char md[EVP_MAX_MD_SIZE];
     unsigned int version;
-    size_t mac_size;
+    size_t mac_size = 0;
     int imac_size;
     size_t num_recs = 0, max_recs, j;
     PACKET pkt, sslv2pkt;
-    size_t first_rec_len;
     int is_ktls_left;
+    SSL_MAC_BUF *macbufs = NULL;
+    int ret = -1;
 
     rr = RECORD_LAYER_get_rrec(&s->rlayer);
     rbuf = RECORD_LAYER_get_rbuf(&s->rlayer);
@@ -526,20 +527,28 @@ int ssl3_get_record(SSL *s)
     if (BIO_get_ktls_recv(s->rbio) && !is_ktls_left)
         goto skip_decryption;
 
+    /* TODO(size_t): convert this to do size_t properly */
+    if (s->read_hash != NULL) {
+        const EVP_MD *tmpmd = EVP_MD_CTX_md(s->read_hash);
+
+        if (tmpmd != NULL) {
+            imac_size = EVP_MD_size(tmpmd);
+            if (!ossl_assert(imac_size >= 0 && imac_size <= EVP_MAX_MD_SIZE)) {
+                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_GET_RECORD,
+                             ERR_LIB_EVP);
+                    return -1;
+            }
+            mac_size = (size_t)imac_size;
+        }
+    }
+
     /*
      * If in encrypt-then-mac mode calculate mac from encrypted record. All
      * the details below are public so no timing details can leak.
      */
     if (SSL_READ_ETM(s) && s->read_hash) {
         unsigned char *mac;
-        /* TODO(size_t): convert this to do size_t properly */
-        imac_size = EVP_MD_CTX_size(s->read_hash);
-        if (!ossl_assert(imac_size >= 0 && imac_size <= EVP_MAX_MD_SIZE)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_GET_RECORD,
-                         ERR_LIB_EVP);
-                return -1;
-        }
-        mac_size = (size_t)imac_size;
+
         for (j = 0; j < num_recs; j++) {
             thisrr = &rr[j];
 
@@ -557,27 +566,39 @@ int ssl3_get_record(SSL *s)
                 return -1;
             }
         }
+        /*
+         * We've handled the mac now - there is no MAC inside the encrypted
+         * record
+         */
+        mac_size = 0;
     }
 
-    first_rec_len = rr[0].length;
+    if (mac_size > 0) {
+        macbufs = OPENSSL_zalloc(sizeof(*macbufs) * num_recs);
+        if (macbufs == NULL) {
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_GET_RECORD,
+                     ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+    }
 
-    enc_err = s->method->ssl3_enc->enc(s, rr, num_recs, 0);
+    enc_err = s->method->ssl3_enc->enc(s, rr, num_recs, 0, macbufs, mac_size);
 
     /*-
      * enc_err is:
-     *    0: (in non-constant time) if the record is publicly invalid.
-     *    1: if the padding is valid
-     *    -1: if the padding is invalid
+     *    0: if the record is publicly invalid, or an internal error, or AEAD
+     *       decryption failed, or ETM decryption failed.
+     *    1: Success or MTE decryption failed (MAC will be randomised)
      */
     if (enc_err == 0) {
         if (ossl_statem_in_error(s)) {
             /* SSLfatal() already got called */
-            return -1;
+            goto end;
         }
         if (num_recs == 1 && ossl_statem_skip_early_data(s)) {
             /*
-             * Valid early_data that we cannot decrypt might fail here as
-             * publicly invalid. We treat it like an empty record.
+             * Valid early_data that we cannot decrypt will fail here. We treat
+             * it like an empty record.
              */
 
             thisrr = &rr[0];
@@ -585,18 +606,19 @@ int ssl3_get_record(SSL *s)
             if (!early_data_count_ok(s, thisrr->length,
                                      EARLY_DATA_CIPHERTEXT_OVERHEAD, 0)) {
                 /* SSLfatal() already called */
-                return -1;
+                goto end;
             }
 
             thisrr->length = 0;
             thisrr->read = 1;
             RECORD_LAYER_set_numrpipes(&s->rlayer, 1);
             RECORD_LAYER_reset_read_sequence(&s->rlayer);
-            return 1;
+            ret = 1;
+            goto end;
         }
         SSLfatal(s, SSL_AD_BAD_RECORD_MAC, SSL_F_SSL3_GET_RECORD,
-                 SSL_R_BLOCK_CIPHER_PAD_IS_WRONG);
-        return -1;
+                 SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
+        goto end;
     }
     OSSL_TRACE_BEGIN(TLS) {
         BIO_printf(trc_out, "dec %lu\n", (unsigned long)rr[0].length);
@@ -608,93 +630,24 @@ int ssl3_get_record(SSL *s)
         (s->enc_read_ctx != NULL) &&
         (!SSL_READ_ETM(s) && EVP_MD_CTX_md(s->read_hash) != NULL)) {
         /* s->read_hash != NULL => mac_size != -1 */
-        unsigned char *mac = NULL;
-        unsigned char mac_tmp[EVP_MAX_MD_SIZE];
-
-        mac_size = EVP_MD_CTX_size(s->read_hash);
-        if (!ossl_assert(mac_size <= EVP_MAX_MD_SIZE)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_GET_RECORD,
-                     ERR_R_INTERNAL_ERROR);
-            return -1;
-        }
 
         for (j = 0; j < num_recs; j++) {
+            SSL_MAC_BUF *thismb = &macbufs[j];
             thisrr = &rr[j];
-            /*
-             * orig_len is the length of the record before any padding was
-             * removed. This is public information, as is the MAC in use,
-             * therefore we can safely process the record in a different amount
-             * of time if it's too short to possibly contain a MAC.
-             */
-            if (thisrr->orig_len < mac_size ||
-                /* CBC records must have a padding length byte too. */
-                (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE &&
-                 thisrr->orig_len < mac_size + 1)) {
-                SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_SSL3_GET_RECORD,
-                         SSL_R_LENGTH_TOO_SHORT);
-                return -1;
-            }
-
-            if (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE) {
-                /*
-                 * We update the length so that the TLS header bytes can be
-                 * constructed correctly but we need to extract the MAC in
-                 * constant time from within the record, without leaking the
-                 * contents of the padding bytes.
-                 */
-                mac = mac_tmp;
-                if (!ssl3_cbc_copy_mac(mac_tmp, thisrr, mac_size)) {
-                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_GET_RECORD,
-                             ERR_R_INTERNAL_ERROR);
-                    return -1;
-                }
-                thisrr->length -= mac_size;
-            } else {
-                /*
-                 * In this case there's no padding, so |rec->orig_len| equals
-                 * |rec->length| and we checked that there's enough bytes for
-                 * |mac_size| above.
-                 */
-                thisrr->length -= mac_size;
-                mac = &thisrr->data[thisrr->length];
-            }
 
             i = s->method->ssl3_enc->mac(s, thisrr, md, 0 /* not send */ );
-            if (i == 0 || mac == NULL
-                || CRYPTO_memcmp(md, mac, (size_t)mac_size) != 0)
-                enc_err = -1;
+            if (i == 0 || thismb == NULL || thismb->mac == NULL
+                || CRYPTO_memcmp(md, thismb->mac, (size_t)mac_size) != 0)
+                enc_err = 0;
             if (thisrr->length > SSL3_RT_MAX_COMPRESSED_LENGTH + mac_size)
-                enc_err = -1;
+                enc_err = 0;
         }
     }
 
-    if (enc_err < 0) {
+    if (enc_err == 0) {
         if (ossl_statem_in_error(s)) {
             /* We already called SSLfatal() */
-            return -1;
-        }
-        if (num_recs == 1 && ossl_statem_skip_early_data(s)) {
-            /*
-             * We assume this is unreadable early_data - we treat it like an
-             * empty record
-             */
-
-            /*
-             * The record length may have been modified by the mac check above
-             * so we use the previously saved value
-             */
-            if (!early_data_count_ok(s, first_rec_len,
-                                     EARLY_DATA_CIPHERTEXT_OVERHEAD, 0)) {
-                /* SSLfatal() already called */
-                return -1;
-            }
-
-            thisrr = &rr[0];
-            thisrr->length = 0;
-            thisrr->read = 1;
-            RECORD_LAYER_set_numrpipes(&s->rlayer, 1);
-            RECORD_LAYER_reset_read_sequence(&s->rlayer);
-            return 1;
+            goto end;
         }
         /*
          * A separate 'decryption_failed' alert was introduced with TLS 1.0,
@@ -705,7 +658,7 @@ int ssl3_get_record(SSL *s)
          */
         SSLfatal(s, SSL_AD_BAD_RECORD_MAC, SSL_F_SSL3_GET_RECORD,
                  SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
-        return -1;
+        goto end;
     }
 
  skip_decryption:
@@ -718,12 +671,12 @@ int ssl3_get_record(SSL *s)
             if (thisrr->length > SSL3_RT_MAX_COMPRESSED_LENGTH) {
                 SSLfatal(s, SSL_AD_RECORD_OVERFLOW, SSL_F_SSL3_GET_RECORD,
                          SSL_R_COMPRESSED_LENGTH_TOO_LONG);
-                return -1;
+                goto end;
             }
             if (!ssl3_do_uncompress(s, thisrr)) {
                 SSLfatal(s, SSL_AD_DECOMPRESSION_FAILURE, SSL_F_SSL3_GET_RECORD,
                          SSL_R_BAD_DECOMPRESSION);
-                return -1;
+                goto end;
             }
         }
 
@@ -736,7 +689,7 @@ int ssl3_get_record(SSL *s)
                     || thisrr->type != SSL3_RT_APPLICATION_DATA) {
                 SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_GET_RECORD,
                          SSL_R_BAD_RECORD_TYPE);
-                return -1;
+                goto end;
             }
 
             /* Strip trailing padding */
@@ -751,7 +704,7 @@ int ssl3_get_record(SSL *s)
                     && thisrr->type != SSL3_RT_HANDSHAKE) {
                 SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_GET_RECORD,
                          SSL_R_BAD_RECORD_TYPE);
-                return -1;
+                goto end;
             }
             if (s->msg_callback)
                 s->msg_callback(0, s->version, SSL3_RT_INNER_CONTENT_TYPE,
@@ -768,13 +721,13 @@ int ssl3_get_record(SSL *s)
                 && thisrr->length == 0) {
             SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_GET_RECORD,
                      SSL_R_BAD_LENGTH);
-            return -1;
+            goto end;
         }
 
         if (thisrr->length > SSL3_RT_MAX_PLAIN_LENGTH && !BIO_get_ktls_recv(s->rbio)) {
             SSLfatal(s, SSL_AD_RECORD_OVERFLOW, SSL_F_SSL3_GET_RECORD,
                      SSL_R_DATA_LENGTH_TOO_LONG);
-            return -1;
+            goto end;
         }
 
         /* If received packet overflows current Max Fragment Length setting */
@@ -783,7 +736,7 @@ int ssl3_get_record(SSL *s)
                 && !BIO_get_ktls_recv(s->rbio)) {
             SSLfatal(s, SSL_AD_RECORD_OVERFLOW, SSL_F_SSL3_GET_RECORD,
                      SSL_R_DATA_LENGTH_TOO_LONG);
-            return -1;
+            goto end;
         }
 
         thisrr->off = 0;
@@ -802,7 +755,7 @@ int ssl3_get_record(SSL *s)
                 > MAX_EMPTY_RECORDS) {
                 SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_GET_RECORD,
                          SSL_R_RECORD_TOO_SMALL);
-                return -1;
+                goto end;
             }
         } else {
             RECORD_LAYER_reset_empty_record_count(&s->rlayer);
@@ -814,12 +767,21 @@ int ssl3_get_record(SSL *s)
         if (thisrr->type == SSL3_RT_APPLICATION_DATA
                 && !early_data_count_ok(s, thisrr->length, 0, 0)) {
             /* SSLfatal already called */
-            return -1;
+            goto end;
         }
     }
 
     RECORD_LAYER_set_numrpipes(&s->rlayer, num_recs);
-    return 1;
+    ret = 1;
+ end:
+    if (macbufs != NULL) {
+        for (j = 0; j < num_recs; j++) {
+            if (macbufs[j].alloced)
+                OPENSSL_free(macbufs[j].mac);
+        }
+        OPENSSL_free(macbufs);
+    }
+    return ret;
 }
 
 int ssl3_do_uncompress(SSL *ssl, SSL3_RECORD *rr)
@@ -866,23 +828,21 @@ int ssl3_do_compress(SSL *ssl, SSL3_RECORD *wr)
 }
 
 /*-
- * ssl3_enc encrypts/decrypts |n_recs| records in |inrecs|.  Will call
- * SSLfatal() for internal errors, but not otherwise.
+ * ssl3_enc encrypts/decrypts |n_recs| records in |inrecs|. Calls SSLfatal on
+ * internal error, but not otherwise. It is the responsibility of the caller to
+ * report a bad_record_mac
  *
  * Returns:
- *   0: (in non-constant time) if the record is publicly invalid (i.e. too
- *       short etc).
- *   1: if the record's padding is valid / the encryption was successful.
- *   -1: if the record's padding is invalid or, if sending, an internal error
- *       occurred.
+ *    0: if the record is publicly invalid, or an internal error
+ *    1: Success or Mac-then-encrypt decryption failed (MAC will be randomised)
  */
-int ssl3_enc(SSL *s, SSL3_RECORD *inrecs, size_t n_recs, int sending)
+int ssl3_enc(SSL *s, SSL3_RECORD *inrecs, size_t n_recs, int sending,
+             SSL_MAC_BUF *mac, size_t macsize)
 {
     SSL3_RECORD *rec;
     EVP_CIPHER_CTX *ds;
     size_t l, i;
-    size_t bs, mac_size = 0;
-    int imac_size;
+    size_t bs;
     const EVP_CIPHER *enc;
 
     rec = inrecs;
@@ -909,13 +869,19 @@ int ssl3_enc(SSL *s, SSL3_RECORD *inrecs, size_t n_recs, int sending)
         memmove(rec->data, rec->input, rec->length);
         rec->input = rec->data;
     } else {
+        int provided = (EVP_CIPHER_provider(enc) != NULL);
+
         l = rec->length;
         /* TODO(size_t): Convert this call */
         bs = EVP_CIPHER_CTX_block_size(ds);
 
         /* COMPRESS */
 
-        if ((bs != 1) && sending) {
+        if ((bs != 1) && sending && !provided) {
+            /*
+             * We only do this for legacy ciphers. Provided ciphers add the
+             * padding on the provider side.
+             */
             i = bs - (l % bs);
 
             /* we need to add 'i-1' padding bytes */
@@ -930,52 +896,82 @@ int ssl3_enc(SSL *s, SSL3_RECORD *inrecs, size_t n_recs, int sending)
         }
 
         if (!sending) {
-            if (l == 0 || l % bs != 0)
+            if (l == 0 || l % bs != 0) {
+                /* Publicly invalid */
                 return 0;
+            }
             /* otherwise, rec->length >= bs */
         }
 
-        /* TODO(size_t): Convert this call */
-        if (EVP_Cipher(ds, rec->data, rec->input, (unsigned int)l) < 1)
-            return -1;
+        if (EVP_CIPHER_provider(enc) != NULL) {
+            int outlen;
 
-        if (EVP_MD_CTX_md(s->read_hash) != NULL) {
-            /* TODO(size_t): convert me */
-            imac_size = EVP_MD_CTX_size(s->read_hash);
-            if (imac_size < 0) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_ENC,
-                         ERR_R_INTERNAL_ERROR);
-                return -1;
+            if (!EVP_CipherUpdate(ds, rec->data, &outlen, rec->input,
+                                  (unsigned int)l))
+                return 0;
+            rec->length = outlen;
+
+            if (!sending && mac != NULL) {
+                /* Now get a pointer to the MAC */
+                OSSL_PARAM params[2], *p = params;
+
+                /* Get the MAC */
+                mac->alloced = 0;
+
+                *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_TLS_MAC,
+                                                      (void **)&mac->mac,
+                                                      macsize);
+                *p = OSSL_PARAM_construct_end();
+
+                if (!EVP_CIPHER_CTX_get_params(ds, params)) {
+                    /* Shouldn't normally happen */
+                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, 0,
+                             ERR_R_INTERNAL_ERROR);
+                    return 0;
+                }
             }
-            mac_size = (size_t)imac_size;
+        } else {
+            /* TODO(size_t): Convert this call */
+            if (EVP_Cipher(ds, rec->data, rec->input, (unsigned int)l) < 1) {
+                /* Shouldn't happen */
+                SSLfatal(s, SSL_AD_BAD_RECORD_MAC, 0, ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
+
+            if (!sending)
+                return ssl3_cbc_remove_padding_and_mac(&rec->length,
+                                           rec->orig_len,
+                                           rec->data,
+                                           (mac != NULL) ? &mac->mac : NULL,
+                                           (mac != NULL) ? &mac->alloced : NULL,
+                                           bs,
+                                           macsize,
+                                           s->ctx->libctx);
         }
-        if ((bs != 1) && !sending)
-            return ssl3_cbc_remove_padding(rec, bs, mac_size);
     }
     return 1;
 }
 
 #define MAX_PADDING 256
 /*-
- * tls1_enc encrypts/decrypts |n_recs| in |recs|.  Will call SSLfatal() for
- * internal errors, but not otherwise.
+ * tls1_enc encrypts/decrypts |n_recs| in |recs|. Calls SSLfatal on internal
+ * error, but not otherwise. It is the responsibility of the caller to report
+ * a bad_record_mac - if appropriate (DTLS just drops the record).
  *
  * Returns:
- *   0: (in non-constant time) if the record is publicly invalid (i.e. too
- *       short etc).
- *   1: if the record's padding is valid / the encryption was successful.
- *   -1: if the record's padding/AEAD-authenticator is invalid or, if sending,
- *       an internal error occurred.
+ *    0: if the record is publicly invalid, or an internal error, or AEAD
+ *       decryption failed, or Encrypt-then-mac decryption failed.
+ *    1: Success or Mac-then-encrypt decryption failed (MAC will be randomised)
  */
-int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
+int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending,
+             SSL_MAC_BUF *macs, size_t macsize)
 {
     EVP_CIPHER_CTX *ds;
     size_t reclen[SSL_MAX_PIPELINES];
     unsigned char buf[SSL_MAX_PIPELINES][EVP_AEAD_TLS1_AAD_LEN];
-    int i, pad = 0, ret, tmpr;
-    size_t bs, mac_size = 0, ctr, padnum, loop;
+    int i, pad = 0, tmpr;
+    size_t bs, ctr, padnum, loop;
     unsigned char padval;
-    int imac_size;
     const EVP_CIPHER *enc;
     int tlstree_enc = sending ? (s->mac_flags & SSL_MAC_FLAG_WRITE_MAC_TLSTREE)
                               : (s->mac_flags & SSL_MAC_FLAG_READ_MAC_TLSTREE);
@@ -992,7 +988,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
             if (!ossl_assert(n >= 0)) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                          ERR_R_INTERNAL_ERROR);
-                return -1;
+                return 0;
             }
         }
         ds = s->enc_write_ctx;
@@ -1016,12 +1012,12 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                          */
                         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                                  ERR_R_INTERNAL_ERROR);
-                        return -1;
+                        return 0;
                     } else if (RAND_bytes_ex(s->ctx->libctx, recs[ctr].input,
                                              ivlen) <= 0) {
                         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                                  ERR_R_INTERNAL_ERROR);
-                        return -1;
+                        return 0;
                     }
                 }
             }
@@ -1032,7 +1028,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
             if (!ossl_assert(n >= 0)) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                          ERR_R_INTERNAL_ERROR);
-                return -1;
+                return 0;
             }
         }
         ds = s->enc_read_ctx;
@@ -1047,8 +1043,9 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
             memmove(recs[ctr].data, recs[ctr].input, recs[ctr].length);
             recs[ctr].input = recs[ctr].data;
         }
-        ret = 1;
     } else {
+        int provided = (EVP_CIPHER_provider(enc) != NULL);
+
         bs = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ds));
 
         if (n_recs > 1) {
@@ -1060,7 +1057,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                  */
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                          SSL_R_PIPELINE_FAILURE);
-                return -1;
+                return 0;
             }
         }
         for (ctr = 0; ctr < n_recs; ctr++) {
@@ -1100,7 +1097,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                 if (pad <= 0) {
                     SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                              ERR_R_INTERNAL_ERROR);
-                    return -1;
+                    return 0;
                 }
 
                 if (sending) {
@@ -1108,7 +1105,11 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                     recs[ctr].length += pad;
                 }
 
-            } else if ((bs != 1) && sending) {
+            } else if ((bs != 1) && sending && !provided) {
+                /*
+                 * We only do this for legacy ciphers. Provided ciphers add the
+                 * padding on the provider side.
+                 */
                 padnum = bs - (reclen[ctr] % bs);
 
                 /* Add weird padding of up to 256 bytes */
@@ -1116,7 +1117,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                 if (padnum > MAX_PADDING) {
                     SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                              ERR_R_INTERNAL_ERROR);
-                    return -1;
+                    return 0;
                 }
                 /* we need to add 'padnum' padding bytes of value padval */
                 padval = (unsigned char)(padnum - 1);
@@ -1127,8 +1128,10 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
             }
 
             if (!sending) {
-                if (reclen[ctr] == 0 || reclen[ctr] % bs != 0)
+                if (reclen[ctr] == 0 || reclen[ctr] % bs != 0) {
+                    /* Publicly invalid */
                     return 0;
+                }
             }
         }
         if (n_recs > 1) {
@@ -1142,7 +1145,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                                     (int)n_recs, data) <= 0) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                          SSL_R_PIPELINE_FAILURE);
-                return -1;
+                return 0;
             }
             /* Set the input buffers */
             for (ctr = 0; ctr < n_recs; ctr++) {
@@ -1154,7 +1157,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                                        (int)n_recs, reclen) <= 0) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                          SSL_R_PIPELINE_FAILURE);
-                return -1;
+                return 0;
             }
         }
 
@@ -1175,67 +1178,129 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
             if (EVP_CIPHER_CTX_ctrl(ds, EVP_CTRL_TLSTREE, decrement_seq, seq) <= 0) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                          ERR_R_INTERNAL_ERROR);
-              return -1;
+                return 0;
             }
         }
 
-        /* TODO(size_t): Convert this call */
-        tmpr = EVP_Cipher(ds, recs[0].data, recs[0].input,
-                          (unsigned int)reclen[0]);
-        if ((EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ds))
-             & EVP_CIPH_FLAG_CUSTOM_CIPHER)
-            ? (tmpr < 0)
-            : (tmpr == 0))
-            return -1;          /* AEAD can fail to verify MAC */
-
-        if (sending == 0) {
-            if (EVP_CIPHER_mode(enc) == EVP_CIPH_GCM_MODE) {
-                for (ctr = 0; ctr < n_recs; ctr++) {
-                    recs[ctr].data += EVP_GCM_TLS_EXPLICIT_IV_LEN;
-                    recs[ctr].input += EVP_GCM_TLS_EXPLICIT_IV_LEN;
-                    recs[ctr].length -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
-                }
-            } else if (EVP_CIPHER_mode(enc) == EVP_CIPH_CCM_MODE) {
-                for (ctr = 0; ctr < n_recs; ctr++) {
-                    recs[ctr].data += EVP_CCM_TLS_EXPLICIT_IV_LEN;
-                    recs[ctr].input += EVP_CCM_TLS_EXPLICIT_IV_LEN;
-                    recs[ctr].length -= EVP_CCM_TLS_EXPLICIT_IV_LEN;
-                }
-            }
-        }
+        if (provided) {
+            int outlen;
 
-        ret = 1;
-        if (!SSL_READ_ETM(s) && EVP_MD_CTX_md(s->read_hash) != NULL) {
-            imac_size = EVP_MD_CTX_size(s->read_hash);
-            if (imac_size < 0) {
+            /* Provided cipher - we do not support pipelining on this path */
+            if (n_recs > 1)  {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
                          ERR_R_INTERNAL_ERROR);
-                return -1;
+                return 0;
             }
-            mac_size = (size_t)imac_size;
-        }
-        if ((bs != 1) && !sending) {
-            int tmpret;
-            for (ctr = 0; ctr < n_recs; ctr++) {
-                tmpret = tls1_cbc_remove_padding(s, &recs[ctr], bs, mac_size);
-                /*
-                 * If tmpret == 0 then this means publicly invalid so we can
-                 * short circuit things here. Otherwise we must respect constant
-                 * time behaviour.
-                 */
-                if (tmpret == 0)
-                    return 0;
-                ret = constant_time_select_int(constant_time_eq_int(tmpret, 1),
-                                               ret, -1);
+
+            if (!EVP_CipherUpdate(ds, recs[0].data, &outlen, recs[0].input,
+                                  (unsigned int)reclen[0]))
+                return 0;
+            recs[0].length = outlen;
+
+            /*
+             * The length returned from EVP_CipherUpdate above is the actual
+             * payload length. We need to adjust the data/input ptr to skip over
+             * any explicit IV
+             */
+            if (!sending) {
+                if (EVP_CIPHER_mode(enc) == EVP_CIPH_GCM_MODE) {
+                        recs[0].data += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+                        recs[0].input += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+                } else if (EVP_CIPHER_mode(enc) == EVP_CIPH_CCM_MODE) {
+                        recs[0].data += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+                        recs[0].input += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+                } else if (bs != 1 && SSL_USE_EXPLICIT_IV(s)) {
+                    recs[0].data += bs;
+                    recs[0].input += bs;
+                    recs[0].orig_len -= bs;
+                }
+
+                /* Now get a pointer to the MAC (if applicable) */
+                if (macs != NULL) {
+                    OSSL_PARAM params[2], *p = params;
+
+                    /* Get the MAC */
+                    macs[0].alloced = 0;
+
+                    *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_TLS_MAC,
+                                                          (void **)&macs[0].mac,
+                                                          macsize);
+                    *p = OSSL_PARAM_construct_end();
+
+                    if (!EVP_CIPHER_CTX_get_params(ds, params)) {
+                        /* Shouldn't normally happen */
+                        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_ENC,
+                                 ERR_R_INTERNAL_ERROR);
+                        return 0;
+                    }
+                }
             }
-        }
-        if (pad && !sending) {
-            for (ctr = 0; ctr < n_recs; ctr++) {
-                recs[ctr].length -= pad;
+        } else {
+            /* Legacy cipher */
+
+            /* TODO(size_t): Convert this call */
+            tmpr = EVP_Cipher(ds, recs[0].data, recs[0].input,
+                              (unsigned int)reclen[0]);
+            if ((EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ds))
+                 & EVP_CIPH_FLAG_CUSTOM_CIPHER)
+                ? (tmpr < 0)
+                : (tmpr == 0)) {
+                /* AEAD can fail to verify MAC */
+                return 0;
+            }
+
+            if (!sending) {
+                /* Adjust the record to remove the explicit IV/MAC/Tag */
+                if (EVP_CIPHER_mode(enc) == EVP_CIPH_GCM_MODE) {
+                    for (ctr = 0; ctr < n_recs; ctr++) {
+                        recs[ctr].data += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+                        recs[ctr].input += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+                        recs[ctr].length -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
+                    }
+                } else if (EVP_CIPHER_mode(enc) == EVP_CIPH_CCM_MODE) {
+                    for (ctr = 0; ctr < n_recs; ctr++) {
+                        recs[ctr].data += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+                        recs[ctr].input += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+                        recs[ctr].length -= EVP_CCM_TLS_EXPLICIT_IV_LEN;
+                    }
+                }
+
+                for (ctr = 0; ctr < n_recs; ctr++) {
+                    if (bs != 1 && SSL_USE_EXPLICIT_IV(s)) {
+                        if (recs[ctr].length < bs)
+                            return 0;
+                        recs[ctr].data += bs;
+                        recs[ctr].input += bs;
+                        recs[ctr].length -= bs;
+                        recs[ctr].orig_len -= bs;
+                    }
+
+                    /*
+                     * If using Mac-then-encrypt, then this will succeed but
+                     * with a random MAC if padding is invalid
+                     */
+                    if (!tls1_cbc_remove_padding_and_mac(&recs[ctr].length,
+                                         recs[ctr].orig_len,
+                                         recs[ctr].data,
+                                         (macs != NULL) ? &macs[ctr].mac : NULL,
+                                         (macs != NULL) ? &macs[ctr].alloced
+                                                        : NULL,
+                                         bs,
+                                         macsize,
+                                         (EVP_CIPHER_flags(enc)
+                                         & EVP_CIPH_FLAG_AEAD_CIPHER) != 0,
+                                         s->ctx->libctx))
+                        return 0;
+                }
+                if (pad) {
+                    for (ctr = 0; ctr < n_recs; ctr++) {
+                        recs[ctr].length -= pad;
+                    }
+                }
             }
         }
     }
-    return ret;
+    return 1;
 }
 
 int n_ssl3_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int sending)
@@ -1446,211 +1511,6 @@ int tls1_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int sending)
     return 1;
 }
 
-/*-
- * ssl3_cbc_remove_padding removes padding from the decrypted, SSLv3, CBC
- * record in |rec| by updating |rec->length| in constant time.
- *
- * block_size: the block size of the cipher used to encrypt the record.
- * returns:
- *   0: (in non-constant time) if the record is publicly invalid.
- *   1: if the padding was valid
- *  -1: otherwise.
- */
-int ssl3_cbc_remove_padding(SSL3_RECORD *rec,
-                            size_t block_size, size_t mac_size)
-{
-    size_t padding_length;
-    size_t good;
-    const size_t overhead = 1 /* padding length byte */  + mac_size;
-
-    /*
-     * These lengths are all public so we can test them in non-constant time.
-     */
-    if (overhead > rec->length)
-        return 0;
-
-    padding_length = rec->data[rec->length - 1];
-    good = constant_time_ge_s(rec->length, padding_length + overhead);
-    /* SSLv3 requires that the padding is minimal. */
-    good &= constant_time_ge_s(block_size, padding_length + 1);
-    rec->length -= good & (padding_length + 1);
-    return constant_time_select_int_s(good, 1, -1);
-}
-
-/*-
- * tls1_cbc_remove_padding removes the CBC padding from the decrypted, TLS, CBC
- * record in |rec| in constant time and returns 1 if the padding is valid and
- * -1 otherwise. It also removes any explicit IV from the start of the record
- * without leaking any timing about whether there was enough space after the
- * padding was removed.
- *
- * block_size: the block size of the cipher used to encrypt the record.
- * returns:
- *   0: (in non-constant time) if the record is publicly invalid.
- *   1: if the padding was valid
- *  -1: otherwise.
- */
-int tls1_cbc_remove_padding(const SSL *s,
-                            SSL3_RECORD *rec,
-                            size_t block_size, size_t mac_size)
-{
-    size_t good;
-    size_t padding_length, to_check, i;
-    const size_t overhead = 1 /* padding length byte */  + mac_size;
-    /* Check if version requires explicit IV */
-    if (SSL_USE_EXPLICIT_IV(s)) {
-        /*
-         * These lengths are all public so we can test them in non-constant
-         * time.
-         */
-        if (overhead + block_size > rec->length)
-            return 0;
-        /* We can now safely skip explicit IV */
-        rec->data += block_size;
-        rec->input += block_size;
-        rec->length -= block_size;
-        rec->orig_len -= block_size;
-    } else if (overhead > rec->length)
-        return 0;
-
-    padding_length = rec->data[rec->length - 1];
-
-    if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(s->enc_read_ctx)) &
-        EVP_CIPH_FLAG_AEAD_CIPHER) {
-        /* padding is already verified */
-        rec->length -= padding_length + 1;
-        return 1;
-    }
-
-    good = constant_time_ge_s(rec->length, overhead + padding_length);
-    /*
-     * The padding consists of a length byte at the end of the record and
-     * then that many bytes of padding, all with the same value as the length
-     * byte. Thus, with the length byte included, there are i+1 bytes of
-     * padding. We can't check just |padding_length+1| bytes because that
-     * leaks decrypted information. Therefore we always have to check the
-     * maximum amount of padding possible. (Again, the length of the record
-     * is public information so we can use it.)
-     */
-    to_check = 256;            /* maximum amount of padding, inc length byte. */
-    if (to_check > rec->length)
-        to_check = rec->length;
-
-    for (i = 0; i < to_check; i++) {
-        unsigned char mask = constant_time_ge_8_s(padding_length, i);
-        unsigned char b = rec->data[rec->length - 1 - i];
-        /*
-         * The final |padding_length+1| bytes should all have the value
-         * |padding_length|. Therefore the XOR should be zero.
-         */
-        good &= ~(mask & (padding_length ^ b));
-    }
-
-    /*
-     * If any of the final |padding_length+1| bytes had the wrong value, one
-     * or more of the lower eight bits of |good| will be cleared.
-     */
-    good = constant_time_eq_s(0xff, good & 0xff);
-    rec->length -= good & (padding_length + 1);
-
-    return constant_time_select_int_s(good, 1, -1);
-}
-
-/*-
- * ssl3_cbc_copy_mac copies |md_size| bytes from the end of |rec| to |out| in
- * constant time (independent of the concrete value of rec->length, which may
- * vary within a 256-byte window).
- *
- * ssl3_cbc_remove_padding or tls1_cbc_remove_padding must be called prior to
- * this function.
- *
- * On entry:
- *   rec->orig_len >= md_size
- *   md_size <= EVP_MAX_MD_SIZE
- *
- * If CBC_MAC_ROTATE_IN_PLACE is defined then the rotation is performed with
- * variable accesses in a 64-byte-aligned buffer. Assuming that this fits into
- * a single or pair of cache-lines, then the variable memory accesses don't
- * actually affect the timing. CPUs with smaller cache-lines [if any] are
- * not multi-core and are not considered vulnerable to cache-timing attacks.
- */
-#define CBC_MAC_ROTATE_IN_PLACE
-
-int ssl3_cbc_copy_mac(unsigned char *out,
-                       const SSL3_RECORD *rec, size_t md_size)
-{
-#if defined(CBC_MAC_ROTATE_IN_PLACE)
-    unsigned char rotated_mac_buf[64 + EVP_MAX_MD_SIZE];
-    unsigned char *rotated_mac;
-#else
-    unsigned char rotated_mac[EVP_MAX_MD_SIZE];
-#endif
-
-    /*
-     * mac_end is the index of |rec->data| just after the end of the MAC.
-     */
-    size_t mac_end = rec->length;
-    size_t mac_start = mac_end - md_size;
-    size_t in_mac;
-    /*
-     * scan_start contains the number of bytes that we can ignore because the
-     * MAC's position can only vary by 255 bytes.
-     */
-    size_t scan_start = 0;
-    size_t i, j;
-    size_t rotate_offset;
-
-    if (!ossl_assert(rec->orig_len >= md_size
-                     && md_size <= EVP_MAX_MD_SIZE))
-        return 0;
-
-#if defined(CBC_MAC_ROTATE_IN_PLACE)
-    rotated_mac = rotated_mac_buf + ((0 - (size_t)rotated_mac_buf) & 63);
-#endif
-
-    /* This information is public so it's safe to branch based on it. */
-    if (rec->orig_len > md_size + 255 + 1)
-        scan_start = rec->orig_len - (md_size + 255 + 1);
-
-    in_mac = 0;
-    rotate_offset = 0;
-    memset(rotated_mac, 0, md_size);
-    for (i = scan_start, j = 0; i < rec->orig_len; i++) {
-        size_t mac_started = constant_time_eq_s(i, mac_start);
-        size_t mac_ended = constant_time_lt_s(i, mac_end);
-        unsigned char b = rec->data[i];
-
-        in_mac |= mac_started;
-        in_mac &= mac_ended;
-        rotate_offset |= j & mac_started;
-        rotated_mac[j++] |= b & in_mac;
-        j &= constant_time_lt_s(j, md_size);
-    }
-
-    /* Now rotate the MAC */
-#if defined(CBC_MAC_ROTATE_IN_PLACE)
-    j = 0;
-    for (i = 0; i < md_size; i++) {
-        /* in case cache-line is 32 bytes, touch second line */
-        ((volatile unsigned char *)rotated_mac)[rotate_offset ^ 32];
-        out[j++] = rotated_mac[rotate_offset++];
-        rotate_offset &= constant_time_lt_s(rotate_offset, md_size);
-    }
-#else
-    memset(out, 0, md_size);
-    rotate_offset = md_size - rotate_offset;
-    rotate_offset &= constant_time_lt_s(rotate_offset, md_size);
-    for (i = 0; i < md_size; i++) {
-        for (j = 0; j < md_size; j++)
-            out[j] |= rotated_mac[i] & constant_time_eq_8_s(j, rotate_offset);
-        rotate_offset++;
-        rotate_offset &= constant_time_lt_s(rotate_offset, md_size);
-    }
-#endif
-
-    return 1;
-}
-
 int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
 {
     int i;
@@ -1658,9 +1518,11 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
     SSL_SESSION *sess;
     SSL3_RECORD *rr;
     int imac_size;
-    size_t mac_size;
+    size_t mac_size = 0;
     unsigned char md[EVP_MAX_MD_SIZE];
     size_t max_plain_length = SSL3_RT_MAX_PLAIN_LENGTH;
+    SSL_MAC_BUF macbuf = { NULL, 0 };
+    int ret = 0;
 
     rr = RECORD_LAYER_get_rrec(&s->rlayer);
     sess = s->session;
@@ -1694,14 +1556,24 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
     rr->data = rr->input;
     rr->orig_len = rr->length;
 
+    /* TODO(size_t): convert this to do size_t properly */
+    if (s->read_hash != NULL) {
+        const EVP_MD *tmpmd = EVP_MD_CTX_md(s->read_hash);
+
+        if (tmpmd != NULL) {
+            imac_size = EVP_MD_size(tmpmd);
+            if (!ossl_assert(imac_size >= 0 && imac_size <= EVP_MAX_MD_SIZE)) {
+                    SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_SSL3_GET_RECORD,
+                             ERR_LIB_EVP);
+                    return -1;
+            }
+            mac_size = (size_t)imac_size;
+        }
+    }
+
     if (SSL_READ_ETM(s) && s->read_hash) {
         unsigned char *mac;
-        mac_size = EVP_MD_CTX_size(s->read_hash);
-        if (!ossl_assert(mac_size <= EVP_MAX_MD_SIZE)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DTLS1_PROCESS_RECORD,
-                     ERR_R_INTERNAL_ERROR);
-            return 0;
-        }
+
         if (rr->orig_len < mac_size) {
             SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_DTLS1_PROCESS_RECORD,
                      SSL_R_LENGTH_TOO_SHORT);
@@ -1715,24 +1587,30 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
                    SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
             return 0;
         }
+        /*
+         * We've handled the mac now - there is no MAC inside the encrypted
+         * record
+         */
+        mac_size = 0;
     }
 
-    enc_err = s->method->ssl3_enc->enc(s, rr, 1, 0);
+    enc_err = s->method->ssl3_enc->enc(s, rr, 1, 0, &macbuf, mac_size);
+
     /*-
      * enc_err is:
-     *    0: (in non-constant time) if the record is publicly invalid.
-     *    1: if the padding is valid
-     *   -1: if the padding is invalid
+     *    0: if the record is publicly invalid, or an internal error, or AEAD
+     *       decryption failed, or ETM decryption failed.
+     *    1: Success or MTE decryption failed (MAC will be randomised)
      */
     if (enc_err == 0) {
         if (ossl_statem_in_error(s)) {
             /* SSLfatal() got called */
-            return 0;
+            goto end;
         }
         /* For DTLS we simply ignore bad packets. */
         rr->length = 0;
         RECORD_LAYER_reset_packet_length(&s->rlayer);
-        return 0;
+        goto end;
     }
     OSSL_TRACE_BEGIN(TLS) {
         BIO_printf(trc_out, "dec %zd\n", rr->length);
@@ -1743,75 +1621,20 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
     if ((sess != NULL) && !SSL_READ_ETM(s) &&
         (s->enc_read_ctx != NULL) && (EVP_MD_CTX_md(s->read_hash) != NULL)) {
         /* s->read_hash != NULL => mac_size != -1 */
-        unsigned char *mac = NULL;
-        unsigned char mac_tmp[EVP_MAX_MD_SIZE];
-
-        /* TODO(size_t): Convert this to do size_t properly */
-        imac_size = EVP_MD_CTX_size(s->read_hash);
-        if (imac_size < 0) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DTLS1_PROCESS_RECORD,
-                     ERR_LIB_EVP);
-            return 0;
-        }
-        mac_size = (size_t)imac_size;
-        if (!ossl_assert(mac_size <= EVP_MAX_MD_SIZE)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DTLS1_PROCESS_RECORD,
-                     ERR_R_INTERNAL_ERROR);
-            return 0;
-        }
-
-        /*
-         * orig_len is the length of the record before any padding was
-         * removed. This is public information, as is the MAC in use,
-         * therefore we can safely process the record in a different amount
-         * of time if it's too short to possibly contain a MAC.
-         */
-        if (rr->orig_len < mac_size ||
-            /* CBC records must have a padding length byte too. */
-            (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE &&
-             rr->orig_len < mac_size + 1)) {
-            SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_DTLS1_PROCESS_RECORD,
-                     SSL_R_LENGTH_TOO_SHORT);
-            return 0;
-        }
-
-        if (EVP_CIPHER_CTX_mode(s->enc_read_ctx) == EVP_CIPH_CBC_MODE) {
-            /*
-             * We update the length so that the TLS header bytes can be
-             * constructed correctly but we need to extract the MAC in
-             * constant time from within the record, without leaking the
-             * contents of the padding bytes.
-             */
-            mac = mac_tmp;
-            if (!ssl3_cbc_copy_mac(mac_tmp, rr, mac_size)) {
-                SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_DTLS1_PROCESS_RECORD,
-                         ERR_R_INTERNAL_ERROR);
-                return 0;
-            }
-            rr->length -= mac_size;
-        } else {
-            /*
-             * In this case there's no padding, so |rec->orig_len| equals
-             * |rec->length| and we checked that there's enough bytes for
-             * |mac_size| above.
-             */
-            rr->length -= mac_size;
-            mac = &rr->data[rr->length];
-        }
 
         i = s->method->ssl3_enc->mac(s, rr, md, 0 /* not send */ );
-        if (i == 0 || mac == NULL
-            || CRYPTO_memcmp(md, mac, mac_size) != 0)
-            enc_err = -1;
+        if (i == 0 || macbuf.mac == NULL
+            || CRYPTO_memcmp(md, macbuf.mac, mac_size) != 0)
+            enc_err = 0;
         if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH + mac_size)
-            enc_err = -1;
+            enc_err = 0;
     }
 
-    if (enc_err < 0) {
+    if (enc_err == 0) {
         /* decryption failed, silently discard message */
         rr->length = 0;
         RECORD_LAYER_reset_packet_length(&s->rlayer);
-        return 0;
+        goto end;
     }
 
     /* r->length is now just compressed */
@@ -1819,12 +1642,12 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
         if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH) {
             SSLfatal(s, SSL_AD_RECORD_OVERFLOW, SSL_F_DTLS1_PROCESS_RECORD,
                      SSL_R_COMPRESSED_LENGTH_TOO_LONG);
-            return 0;
+            goto end;
         }
         if (!ssl3_do_uncompress(s, rr)) {
             SSLfatal(s, SSL_AD_DECOMPRESSION_FAILURE,
                      SSL_F_DTLS1_PROCESS_RECORD, SSL_R_BAD_DECOMPRESSION);
-            return 0;
+            goto end;
         }
     }
 
@@ -1836,7 +1659,7 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
     if (rr->length > max_plain_length) {
         SSLfatal(s, SSL_AD_RECORD_OVERFLOW, SSL_F_DTLS1_PROCESS_RECORD,
                  SSL_R_DATA_LENGTH_TOO_LONG);
-        return 0;
+        goto end;
     }
 
     rr->off = 0;
@@ -1855,7 +1678,11 @@ int dtls1_process_record(SSL *s, DTLS1_BITMAP *bitmap)
     /* Mark receipt of record. */
     dtls1_record_bitmap_update(s, bitmap);
 
-    return 1;
+    ret = 1;
+ end:
+    if (macbuf.alloced)
+        OPENSSL_free(macbuf.mac);
+    return ret;
 }
 
 /*
diff --git a/ssl/record/ssl3_record_tls13.c b/ssl/record/ssl3_record_tls13.c
index f18da2db74..910b6a5862 100644
--- a/ssl/record/ssl3_record_tls13.c
+++ b/ssl/record/ssl3_record_tls13.c
@@ -12,17 +12,16 @@
 #include "internal/cryptlib.h"
 
 /*-
- * tls13_enc encrypts/decrypts |n_recs| in |recs|. Will call SSLfatal() for
- * internal errors, but not otherwise.
+ * tls13_enc encrypts/decrypts |n_recs| in |recs|. Calls SSLfatal on internal
+ * error, but not otherwise. It is the responsibility of the caller to report
+ * a bad_record_mac.
  *
  * Returns:
- *    0: (in non-constant time) if the record is publicly invalid (i.e. too
- *        short etc).
- *    1: if the record encryption was successful.
- *   -1: if the record's AEAD-authenticator is invalid or, if sending,
- *       an internal error occurred.
+ *    0: On failure
+ *    1: if the record encryption/decryption was successful.
  */
-int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
+int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending,
+              SSL_MAC_BUF *mac, size_t macsize)
 {
     EVP_CIPHER_CTX *ctx;
     unsigned char iv[EVP_MAX_IV_LENGTH], recheader[SSL3_RT_HEADER_LENGTH];
@@ -39,7 +38,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
         /* TODO(TLS1.3): Support pipelining */
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
                  ERR_R_INTERNAL_ERROR);
-        return -1;
+        return 0;
     }
 
     if (sending) {
@@ -75,7 +74,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                              && s->psksession->ext.max_early_data > 0)) {
                 SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
                          ERR_R_INTERNAL_ERROR);
-                return -1;
+                return 0;
             }
             alg_enc = s->psksession->cipher->algorithm_enc;
         }
@@ -87,7 +86,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
         if (!ossl_assert(s->s3.tmp.new_cipher != NULL)) {
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
                      ERR_R_INTERNAL_ERROR);
-            return -1;
+            return 0;
         }
         alg_enc = s->s3.tmp.new_cipher->algorithm_enc;
     }
@@ -101,7 +100,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                                          NULL) <= 0) {
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
                      ERR_R_INTERNAL_ERROR);
-            return -1;
+            return 0;
         }
     } else if (alg_enc & SSL_AESGCM) {
         taglen = EVP_GCM_TLS_TAG_LEN;
@@ -110,7 +109,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
     } else {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
                  ERR_R_INTERNAL_ERROR);
-        return -1;
+        return 0;
     }
 
     if (!sending) {
@@ -128,7 +127,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
         /* Should not happen */
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
                  ERR_R_INTERNAL_ERROR);
-        return -1;
+        return 0;
     }
     offset = ivlen - SEQ_NUM_SIZE;
     memcpy(iv, staticiv, offset);
@@ -143,7 +142,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
     }
     if (loop == 0) {
         /* Sequence has wrapped */
-        return -1;
+        return 0;
     }
 
     /* TODO(size_t): lenu/lenf should be a size_t but EVP doesn't support it */
@@ -151,7 +150,9 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
             || (!sending && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
                                              taglen,
                                              rec->data + rec->length) <= 0)) {
-        return -1;
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
     }
 
     /* Set up the AAD */
@@ -162,8 +163,10 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
             || !WPACKET_get_total_written(&wpkt, &hdrlen)
             || hdrlen != SSL3_RT_HEADER_LENGTH
             || !WPACKET_finish(&wpkt)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
+                 ERR_R_INTERNAL_ERROR);
         WPACKET_cleanup(&wpkt);
-        return -1;
+        return 0;
     }
 
     /*
@@ -179,7 +182,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                                 (unsigned int)rec->length) <= 0
             || EVP_CipherFinal_ex(ctx, rec->data + lenu, &lenf) <= 0
             || (size_t)(lenu + lenf) != rec->length) {
-        return -1;
+        return 0;
     }
     if (sending) {
         /* Add the tag */
@@ -187,7 +190,7 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int sending)
                                 rec->data + rec->length) <= 0) {
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS13_ENC,
                      ERR_R_INTERNAL_ERROR);
-            return -1;
+            return 0;
         }
         rec->length += taglen;
     }
diff --git a/ssl/record/tls_pad.c b/ssl/record/tls_pad.c
new file mode 100644
index 0000000000..9f698483f1
--- /dev/null
+++ b/ssl/record/tls_pad.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include "internal/constant_time.h"
+#include "internal/cryptlib.h"
+
+/*
+ * This file has no dependencies on the rest of libssl because it is shared
+ * with the providers. It contains functions for low level CBC TLS padding
+ * removal. Responsibility for this lies with the cipher implementations in the
+ * providers. However there are legacy code paths in libssl which also need to
+ * do this. In time those legacy code paths can be removed and this file can be
+ * moved out of libssl.
+ */
+
+static int ssl3_cbc_copy_mac(size_t *reclen,
+                             size_t origreclen,
+                             unsigned char *recdata,
+                             unsigned char **mac,
+                             int *alloced,
+                             size_t block_size,
+                             size_t mac_size,
+                             size_t good,
+                             OPENSSL_CTX *libctx);
+
+int ssl3_cbc_remove_padding_and_mac(size_t *reclen,
+                                    size_t origreclen,
+                                    unsigned char *recdata,
+                                    unsigned char **mac,
+                                    int *alloced,
+                                    size_t block_size, size_t mac_size,
+                                    OPENSSL_CTX *libctx);
+
+int tls1_cbc_remove_padding_and_mac(size_t *reclen,
+                                    size_t origreclen,
+                                    unsigned char *recdata,
+                                    unsigned char **mac,
+                                    int *alloced,
+                                    size_t block_size, size_t mac_size,
+                                    int aead,
+                                    OPENSSL_CTX *libctx);
+
+/*-
+ * ssl3_cbc_remove_padding removes padding from the decrypted, SSLv3, CBC
+ * record in |recdata| by updating |reclen| in constant time. It also extracts
+ * the MAC from the underlying record and places a pointer to it in |mac|. The
+ * MAC data can either be newly allocated memory, or a pointer inside the
+ * |recdata| buffer. If allocated then |*alloced| is set to 1, otherwise it is
+ * set to 0.
+ *
+ * origreclen: the original record length before any changes were made
+ * block_size: the block size of the cipher used to encrypt the record.
+ * mac_size: the size of the MAC to be extracted
+ * aead: 1 if an AEAD cipher is in use, or 0 otherwise
+ * returns:
+ *   0: if the record is publicly invalid.
+ *   1: if the record is publicly valid. If the padding removal fails then the
+ *      MAC returned is random.
+ */
+int ssl3_cbc_remove_padding_and_mac(size_t *reclen,
+                                    size_t origreclen,
+                                    unsigned char *recdata,
+                                    unsigned char **mac,
+                                    int *alloced,
+                                    size_t block_size, size_t mac_size,
+                                    OPENSSL_CTX *libctx)
+{
+    size_t padding_length;
+    size_t good;
+    const size_t overhead = 1 /* padding length byte */  + mac_size;
+
+    /*
+     * These lengths are all public so we can test them in non-constant time.
+     */
+    if (overhead > *reclen)
+        return 0;
+
+    padding_length = recdata[*reclen - 1];
+    good = constant_time_ge_s(*reclen, padding_length + overhead);
+    /* SSLv3 requires that the padding is minimal. */
+    good &= constant_time_ge_s(block_size, padding_length + 1);
+    *reclen -= good & (padding_length + 1);
+
+    return ssl3_cbc_copy_mac(reclen, origreclen, recdata, mac, alloced,
+                             block_size, mac_size, good, libctx);
+}
+
+/*-
+ * tls1_cbc_remove_padding_and_mac removes padding from the decrypted, TLS, CBC
+ * record in |recdata| by updating |reclen| in constant time. It also extracts
+ * the MAC from the underlying record and places a pointer to it in |mac|. The
+ * MAC data can either be newly allocated memory, or a pointer inside the
+ * |recdata| buffer. If allocated then |*alloced| is set to 1, otherwise it is
+ * set to 0.
+ *
+ * origreclen: the original record length before any changes were made
+ * block_size: the block size of the cipher used to encrypt the record.
+ * mac_size: the size of the MAC to be extracted
+ * aead: 1 if an AEAD cipher is in use, or 0 otherwise
+ * returns:
+ *   0: if the record is publicly invalid.
+ *   1: if the record is publicly valid. If the padding removal fails then the
+ *      MAC returned is random.
+ */
+int tls1_cbc_remove_padding_and_mac(size_t *reclen,
+                                    size_t origreclen,
+                                    unsigned char *recdata,
+                                    unsigned char **mac,
+                                    int *alloced,
+                                    size_t block_size, size_t mac_size,
+                                    int aead,
+                                    OPENSSL_CTX *libctx)
+{
+    size_t good = -1;
+    size_t padding_length, to_check, i;
+    size_t overhead = ((block_size == 1) ? 0 : 1) /* padding length byte */
+                      + mac_size;
+
+    /*
+     * These lengths are all public so we can test them in non-constant
+     * time.
+     */
+    if (overhead > *reclen)
+        return 0;
+
+    if (block_size != 1) {
+
+        padding_length = recdata[*reclen - 1];
+
+        if (aead) {
+            /* padding is already verified and we don't need to check the MAC */
+            *reclen -= padding_length + 1 + mac_size;
+            *mac = NULL;
+            *alloced = 0;
+            return 1;
+        }
+
+        good = constant_time_ge_s(*reclen, overhead + padding_length);
+        /*
+         * The padding consists of a length byte at the end of the record and
+         * then that many bytes of padding, all with the same value as the
+         * length byte. Thus, with the length byte included, there are i+1 bytes
+         * of padding. We can't check just |padding_length+1| bytes because that
+         * leaks decrypted information. Therefore we always have to check the
+         * maximum amount of padding possible. (Again, the length of the record
+         * is public information so we can use it.)
+         */
+        to_check = 256;        /* maximum amount of padding, inc length byte. */
+        if (to_check > *reclen)
+            to_check = *reclen;
+
+        for (i = 0; i < to_check; i++) {
+            unsigned char mask = constant_time_ge_8_s(padding_length, i);
+            unsigned char b = recdata[*reclen - 1 - i];
+            /*
+             * The final |padding_length+1| bytes should all have the value
+             * |padding_length|. Therefore the XOR should be zero.
+             */
+            good &= ~(mask & (padding_length ^ b));
+        }
+
+        /*
+         * If any of the final |padding_length+1| bytes had the wrong value, one
+         * or more of the lower eight bits of |good| will be cleared.
+         */
+        good = constant_time_eq_s(0xff, good & 0xff);
+        *reclen -= good & (padding_length + 1);
+    }
+
+    return ssl3_cbc_copy_mac(reclen, origreclen, recdata, mac, alloced,
+                             block_size, mac_size, good, libctx);
+}
+
+/*-
+ * ssl3_cbc_copy_mac copies |md_size| bytes from the end of the record in
+ * |recdata| to |*mac| in constant time (independent of the concrete value of
+ * the record length |reclen|, which may vary within a 256-byte window).
+ *
+ * On entry:
+ *   origreclen >= mac_size
+ *   mac_size <= EVP_MAX_MD_SIZE
+ *
+ * If CBC_MAC_ROTATE_IN_PLACE is defined then the rotation is performed with
+ * variable accesses in a 64-byte-aligned buffer. Assuming that this fits into
+ * a single or pair of cache-lines, then the variable memory accesses don't
+ * actually affect the timing. CPUs with smaller cache-lines [if any] are
+ * not multi-core and are not considered vulnerable to cache-timing attacks.
+ */
+#define CBC_MAC_ROTATE_IN_PLACE
+
+static int ssl3_cbc_copy_mac(size_t *reclen,
+                             size_t origreclen,
+                             unsigned char *recdata,
+                             unsigned char **mac,
+                             int *alloced,
+                             size_t block_size,
+                             size_t mac_size,
+                             size_t good,
+                             OPENSSL_CTX *libctx)
+{
+#if defined(CBC_MAC_ROTATE_IN_PLACE)
+    unsigned char rotated_mac_buf[64 + EVP_MAX_MD_SIZE];
+    unsigned char *rotated_mac;
+#else
+    unsigned char rotated_mac[EVP_MAX_MD_SIZE];
+#endif
+    unsigned char randmac[EVP_MAX_MD_SIZE];
+    unsigned char *out;
+
+    /*
+     * mac_end is the index of |recdata| just after the end of the MAC.
+     */
+    size_t mac_end = *reclen;
+    size_t mac_start = mac_end - mac_size;
+    size_t in_mac;
+    /*
+     * scan_start contains the number of bytes that we can ignore because the
+     * MAC's position can only vary by 255 bytes.
+     */
+    size_t scan_start = 0;
+    size_t i, j;
+    size_t rotate_offset;
+
+    if (!ossl_assert(origreclen >= mac_size
+                     && mac_size <= EVP_MAX_MD_SIZE))
+        return 0;
+
+    /* If no MAC then nothing to be done */
+    if (mac_size == 0) {
+        /* No MAC so we can do this in non-constant time */
+        if (good == 0)
+            return 0;
+        return 1;
+    }
+
+    *reclen -= mac_size;
+
+    if (block_size == 1) {
+        /* There's no padding so the position of the MAC is fixed */
+        if (mac != NULL)
+            *mac = &recdata[*reclen];
+        if (alloced != NULL)
+            *alloced = 0;
+        return 1;
+    }
+
+    /* Create the random MAC we will emit if padding is bad */
+    if (!RAND_bytes_ex(libctx, randmac, mac_size))
+        return 0;
+
+    if (!ossl_assert(mac != NULL && alloced != NULL))
+        return 0;
+    *mac = out = OPENSSL_malloc(mac_size);
+    if (*mac == NULL)
+        return 0;
+    *alloced = 1;
+
+#if defined(CBC_MAC_ROTATE_IN_PLACE)
+    rotated_mac = rotated_mac_buf + ((0 - (size_t)rotated_mac_buf) & 63);
+#endif
+
+    /* This information is public so it's safe to branch based on it. */
+    if (origreclen > mac_size + 255 + 1)
+        scan_start = origreclen - (mac_size + 255 + 1);
+
+    in_mac = 0;
+    rotate_offset = 0;
+    memset(rotated_mac, 0, mac_size);
+    for (i = scan_start, j = 0; i < origreclen; i++) {
+        size_t mac_started = constant_time_eq_s(i, mac_start);
+        size_t mac_ended = constant_time_lt_s(i, mac_end);
+        unsigned char b = recdata[i];
+
+        in_mac |= mac_started;
+        in_mac &= mac_ended;
+        rotate_offset |= j & mac_started;
+        rotated_mac[j++] |= b & in_mac;
+        j &= constant_time_lt_s(j, mac_size);
+    }
+
+    /* Now rotate the MAC */
+#if defined(CBC_MAC_ROTATE_IN_PLACE)
+    j = 0;
+    for (i = 0; i < mac_size; i++) {
+        /* in case cache-line is 32 bytes, touch second line */
+        ((volatile unsigned char *)rotated_mac)[rotate_offset ^ 32];
+
+        /* If the padding wasn't good we emit a random MAC */
+        out[j++] = constant_time_select_8((unsigned char)(good & 0xff),
+                                          rotated_mac[rotate_offset++],
+                                          randmac[i]);
+        rotate_offset &= constant_time_lt_s(rotate_offset, mac_size);
+    }
+#else
+    memset(out, 0, mac_size);
+    rotate_offset = mac_size - rotate_offset;
+    rotate_offset &= constant_time_lt_s(rotate_offset, mac_size);
+    for (i = 0; i < mac_size; i++) {
+        for (j = 0; j < mac_size; j++)
+            out[j] |= rotated_mac[i] & constant_time_eq_8_s(j, rotate_offset);
+        rotate_offset++;
+        rotate_offset &= constant_time_lt_s(rotate_offset, mac_size);
+
+        /* If the padding wasn't good we emit a random MAC */
+        out[i] = constant_time_select_8((unsigned char)(good & 0xff), out[i],
+                                        randmac[i]);
+    }
+#endif
+
+    return 1;
+}
diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c
index 5a807d6d57..36b7c7616e 100644
--- a/ssl/s3_enc.c
+++ b/ssl/s3_enc.c
@@ -241,6 +241,12 @@ int ssl3_change_cipher_state(SSL *s, int which)
         goto err;
     }
 
+    if (EVP_CIPHER_provider(c) != NULL
+            && !tls_provider_set_tls_params(s, dd, c, m)) {
+        /* SSLfatal already called */
+        goto err;
+    }
+
     s->statem.enc_write_state = ENC_WRITE_STATE_VALID;
     return 1;
  err:
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index a252761ca4..c3174a7c91 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -34,51 +34,37 @@ DEFINE_STACK_OF(OCSP_RESPID)
 DEFINE_STACK_OF(SRTP_PROTECTION_PROFILE)
 DEFINE_STACK_OF(SCT)
 
-static int ssl_undefined_function_1(SSL *ssl, SSL3_RECORD *r, size_t s, int t)
+static int ssl_undefined_function_1(SSL *ssl, SSL3_RECORD *r, size_t s, int t,
+                                    SSL_MAC_BUF *mac, size_t macsize)
 {
-    (void)r;
-    (void)s;
-    (void)t;
     return ssl_undefined_function(ssl);
 }
 
 static int ssl_undefined_function_2(SSL *ssl, SSL3_RECORD *r, unsigned char *s,
                                     int t)
 {
-    (void)r;
-    (void)s;
-    (void)t;
     return ssl_undefined_function(ssl);
 }
 
 static int ssl_undefined_function_3(SSL *ssl, unsigned char *r,
                                     unsigned char *s, size_t t, size_t *u)
 {
-    (void)r;
-    (void)s;
-    (void)t;
-    (void)u;
     return ssl_undefined_function(ssl);
 }
 
 static int ssl_undefined_function_4(SSL *ssl, int r)
 {
-    (void)r;
     return ssl_undefined_function(ssl);
 }
 
 static size_t ssl_undefined_function_5(SSL *ssl, const char *r, size_t s,
                                        unsigned char *t)
 {
-    (void)r;
-    (void)s;
-    (void)t;
     return ssl_undefined_function(ssl);
 }
 
 static int ssl_undefined_function_6(int r)
 {
-    (void)r;
     return ssl_undefined_function(NULL);
 }
 
@@ -86,13 +72,6 @@ static int ssl_undefined_function_7(SSL *ssl, unsigned char *r, size_t s,
                                     const char *t, size_t u,
                                     const unsigned char *v, size_t w, int x)
 {
-    (void)r;
-    (void)s;
-    (void)t;
-    (void)u;
-    (void)v;
-    (void)w;
-    (void)x;
     return ssl_undefined_function(ssl);
 }
 
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index 58bc1f99c4..250098600f 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -2069,7 +2069,7 @@ typedef struct cert_st {
  * of a mess of functions, but hell, think of it as an opaque structure :-)
  */
 typedef struct ssl3_enc_method {
-    int (*enc) (SSL *, SSL3_RECORD *, size_t, int);
+    int (*enc) (SSL *, SSL3_RECORD *, size_t, int, SSL_MAC_BUF *, size_t);
     int (*mac) (SSL *, SSL3_RECORD *, unsigned char *, int);
     int (*setup_key_block) (SSL *);
     int (*generate_master_secret) (SSL *, unsigned char *, unsigned char *,
@@ -2804,6 +2804,9 @@ const EVP_MD *ssl_evp_md_fetch(OPENSSL_CTX *libctx,
 int ssl_evp_md_up_ref(const EVP_MD *md);
 void ssl_evp_md_free(const EVP_MD *md);
 
+int tls_provider_set_tls_params(SSL *s, EVP_CIPHER_CTX *ctx,
+                                const EVP_CIPHER *ciph,
+                                const EVP_MD *md);
 
 # else /* OPENSSL_UNIT_TEST */
 
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index e929121cd2..7c0b3e9d65 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -136,6 +136,45 @@ static int count_unprocessed_records(SSL *s)
 # endif
 #endif
 
+
+int tls_provider_set_tls_params(SSL *s, EVP_CIPHER_CTX *ctx,
+                                const EVP_CIPHER *ciph,
+                                const EVP_MD *md)
+{
+    /*
+     * Provided cipher, the TLS padding/MAC removal is performed provider
+     * side so we need to tell the ctx about our TLS version and mac size
+     */
+    OSSL_PARAM params[3], *pprm = params;
+    size_t macsize = 0;
+    int imacsize = -1;
+
+    if ((EVP_CIPHER_flags(ciph) & EVP_CIPH_FLAG_AEAD_CIPHER) == 0
+               /*
+                * We look at s->ext.use_etm instead of SSL_READ_ETM() or
+                * SSL_WRITE_ETM() because this test applies to both reading
+                * and writing.
+                */
+            && !s->ext.use_etm)
+        imacsize = EVP_MD_size(md);
+    if (imacsize >= 0)
+        macsize = (size_t)imacsize;
+
+    *pprm++ = OSSL_PARAM_construct_int(OSSL_CIPHER_PARAM_TLS_VERSION,
+                                       &s->version);
+    *pprm++ = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE,
+                                          &macsize);
+    *pprm = OSSL_PARAM_construct_end();
+
+    if (!EVP_CIPHER_CTX_set_params(ctx, params)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS1_CHANGE_CIPHER_STATE,
+                 ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return 1;
+}
+
 int tls1_change_cipher_state(SSL *s, int which)
 {
     unsigned char *p, *mac_secret;
@@ -396,6 +435,12 @@ int tls1_change_cipher_state(SSL *s, int which)
                  ERR_R_INTERNAL_ERROR);
         goto err;
     }
+    if (EVP_CIPHER_provider(c) != NULL
+            && !tls_provider_set_tls_params(s, dd, c, m)) {
+        /* SSLfatal already called */
+        goto err;
+    }
+
 #ifndef OPENSSL_NO_KTLS
     if (s->compress)
         goto skip_ktls;
diff --git a/test/sslcorrupttest.c b/test/sslcorrupttest.c
index 476a1758ad..641ecf331d 100644
--- a/test/sslcorrupttest.c
+++ b/test/sslcorrupttest.c
@@ -190,9 +190,12 @@ static int test_ssl_corrupt(int testidx)
     int testresult = 0;
     STACK_OF(SSL_CIPHER) *ciphers;
     const SSL_CIPHER *currcipher;
+    int err;
 
     docorrupt = 0;
 
+    ERR_clear_error();
+
     TEST_info("Starting #%d, %s", testidx, cipher_list[testidx]);
 
     if (!TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(),
@@ -234,9 +237,14 @@ static int test_ssl_corrupt(int testidx)
     if (!TEST_int_lt(SSL_read(server, junk, sizeof(junk)), 0))
         goto end;
 
-    if (!TEST_int_eq(ERR_GET_REASON(ERR_peek_error()),
-                     SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC))
-        goto end;
+    do {
+        err = ERR_get_error();
+
+        if (err == 0) {
+            TEST_error("Decryption failed or bad record MAC not seen");
+            goto end;
+        }
+    } while (ERR_GET_REASON(err) != SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
 
     testresult = 1;
  end:
diff --git a/test/tls13encryptiontest.c b/test/tls13encryptiontest.c
index a49fbc0013..3bb189f0b5 100644
--- a/test/tls13encryptiontest.c
+++ b/test/tls13encryptiontest.c
@@ -368,7 +368,7 @@ static int test_tls13_encryption(void)
         }
 
         /* Encrypt it */
-        if (!TEST_size_t_eq(tls13_enc(s, &rec, 1, 1), 1)) {
+        if (!TEST_size_t_eq(tls13_enc(s, &rec, 1, 1, NULL, 0), 1)) {
             TEST_info("Failed to encrypt record %zu", ctr);
             goto err;
         }
@@ -378,7 +378,7 @@ static int test_tls13_encryption(void)
         }
 
         /* Decrypt it */
-        if (!TEST_int_eq(tls13_enc(s, &rec, 1, 0), 1)) {
+        if (!TEST_int_eq(tls13_enc(s, &rec, 1, 0, NULL, 0), 1)) {
             TEST_info("Failed to decrypt record %zu", ctr);
             goto err;
         }


More information about the openssl-commits mailing list