Add provider support for TLS CBC padding and MAC removal
authorMatt Caswell <matt@openssl.org>
Wed, 27 May 2020 16:20:18 +0000 (17:20 +0100)
committerMatt Caswell <matt@openssl.org>
Mon, 6 Jul 2020 08:26:09 +0000 (09:26 +0100)
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@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/12288)

include/openssl/core_names.h
providers/implementations/ciphers/ciphercommon.c
providers/implementations/ciphers/ciphercommon_block.c
providers/implementations/ciphers/ciphercommon_local.h
providers/implementations/include/prov/ciphercommon.h
ssl/build.info

index fe126ccd7aa2393a428112c34761c4215e53bc3c..9ad81337c3eaaa56e69fc1e4817249e331ed4a05 100644 (file)
@@ -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 */
 
index 93bee0dc0fea8104237d6341d0ec6279dfd51e44..0b0219c7adfe92e573174f98c067c5a19d8ce785 100644 (file)
@@ -24,6 +24,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 +70,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)
 
 /*
@@ -178,6 +181,41 @@ 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 == 0
+                || (inl % blksz) != 0
+                || in != out
+                || outsize < inl
+                || !ctx->pad) {
+            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;
+        }
+
+        /* This only fails if padding is publicly invalid */
+        /* TODO(3.0): FIX ME FIX ME - Figure out aead */
+        *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 +276,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);
@@ -375,6 +419,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 +443,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;
index ac792d68d7a3acd331014197de5200ae4071b1ec..ba6f68eeff3a09b17c324fc05509c686b6033e8b 100644 (file)
@@ -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;
+    }
+}
index 1c4716f357ff973052e4f48c408365e12e750e1f..43c1c192af264ede04405390624ff528710ca2a9 100644 (file)
@@ -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);
index fa6eec6a2719473f519d82343ae07ba34b5854a3..18bb5790267262d82e183e6f118e6e6d667baa4b 100644 (file)
@@ -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,14 @@ 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 */
+
     /*
      * num contains the number of bytes of |iv| which are valid for modes that
      * manage partial blocks themselves.
index a66e0d4bdb14d7a6cd028a9b13e83ff653805c39..fd187ac7e54ed6201a1f8967c12e293b2683d1ef 100644 (file)
@@ -29,3 +29,5 @@ SOURCE[../libssl]=\
         record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \
         statem/statem.c record/ssl3_record_tls13.c record/tls_pad.c
 DEFINE[../libssl]=$AESDEF
+
+SOURCE[../providers/libcommon.a]=record/tls_pad.c