[openssl] master update

Richard Levitte levitte at openssl.org
Wed Jul 15 21:12:41 UTC 2020


The branch master has been updated
       via  7cc355c2e4e081dca3c6c345a75a2ab16800c807 (commit)
      from  c35b8535768e22cd3b7743f4887a72e53a621a5f (commit)


- Log -----------------------------------------------------------------
commit 7cc355c2e4e081dca3c6c345a75a2ab16800c807
Author: Shane Lontis <shane.lontis at oracle.com>
Date:   Mon Jun 8 14:33:27 2020 +1000

    Add AES_CBC_CTS ciphers to providers
    
    Added Algorithm names AES-128-CBC-CTS, AES-192-CBC-CTS and AES-256-CBC-CTS.
    CS1, CS2 and CS3 variants are supported.
    Only single shot updates are supported.
    The cipher returns the mode EVP_CIPH_CBC_MODE (Internally it shares the aes_cbc cipher code). This
    would allow existing code that uses AES_CBC to switch to the CTS variant without breaking code that
    tests for this mode. Because it shares the aes_cbc code the cts128.c functions could not be used directly.
    The cipher returns the flag EVP_CIPH_FLAG_CTS.
    EVP_CIPH_FLAG_FIPS & EVP_CIPH_FLAG_NON_FIPS_ALLOW have been deprecated.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/12094)

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

Summary of changes:
 CHANGES.md                                         |   5 +
 doc/man3/EVP_CIPHER_meth_new.pod                   |  12 +-
 doc/man3/EVP_EncryptInit.pod                       |  44 +++
 doc/man7/provider-cipher.pod                       |  32 ++
 include/openssl/core_names.h                       |   6 +
 include/openssl/evp.h                              |  10 +-
 providers/defltprov.c                              |   3 +
 providers/fips/fipsprov.c                          |   3 +
 providers/implementations/ciphers/build.info       |   6 +-
 providers/implementations/ciphers/cipher_aes.c     |   2 +
 .../ciphers/cipher_aes_cts.h}                      |  13 +-
 .../implementations/ciphers/cipher_aes_cts.inc     | 108 ++++++
 .../implementations/ciphers/cipher_aes_cts_fips.c  | 368 +++++++++++++++++++++
 .../implementations/include/prov/ciphercommon.h    |   3 +
 .../implementations/include/prov/implementations.h |   3 +
 test/evp_test.c                                    |  18 +
 test/recipes/30-test_evp.t                         |   3 +-
 test/recipes/30-test_evp_data/evpciph_aes_cts.txt  | 362 ++++++++++++++++++++
 18 files changed, 978 insertions(+), 23 deletions(-)
 copy providers/{common/der/der_ec_gen.c.in => implementations/ciphers/cipher_aes_cts.h} (54%)
 create mode 100644 providers/implementations/ciphers/cipher_aes_cts.inc
 create mode 100644 providers/implementations/ciphers/cipher_aes_cts_fips.c
 create mode 100644 test/recipes/30-test_evp_data/evpciph_aes_cts.txt

diff --git a/CHANGES.md b/CHANGES.md
index 4e0002f668..68d269cb5d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -23,6 +23,11 @@ OpenSSL 3.0
 
 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx]
 
+ * Added ciphertext stealing algorithms AES-128-CBC-CTS, AES-192-CBC-CTS and
+   AES-256-CBC-CTS to the providers. CS1, CS2 and CS3 variants are supported.
+
+   *Shane Lontis*
+
  * 'Configure' has been changed to figure out the configuration target if
    none is given on the command line.  Consequently, the 'config' script is
    now only a mere wrapper.  All documentation is changed to only mention
diff --git a/doc/man3/EVP_CIPHER_meth_new.pod b/doc/man3/EVP_CIPHER_meth_new.pod
index 272e80115c..92ce1d902f 100644
--- a/doc/man3/EVP_CIPHER_meth_new.pod
+++ b/doc/man3/EVP_CIPHER_meth_new.pod
@@ -153,15 +153,11 @@ decryption is to be understood as the number of bits instead of
 bytes for this implementation.
 This is only useful for CFB1 ciphers.
 
-=begin comment
-The FIPS flags seem to be unused, so I'm hiding them until I get an
-explanation or they get removed.  /RL
+=item EVP_CIPH_FLAG_CTS
 
-=item EVP_CIPH_FLAG_FIPS
-
-=item EVP_CIPH_FLAG_NON_FIPS_ALLOW
-
-=end comment
+Indicates that the cipher uses ciphertext stealing. This is currently
+used to indicate that the cipher is a one shot that only allows a single call to
+EVP_CipherUpdate().
 
 =item EVP_CIPH_FLAG_CUSTOM_CIPHER
 
diff --git a/doc/man3/EVP_EncryptInit.pod b/doc/man3/EVP_EncryptInit.pod
index 36efb4090d..d40402ba1d 100644
--- a/doc/man3/EVP_EncryptInit.pod
+++ b/doc/man3/EVP_EncryptInit.pod
@@ -800,6 +800,50 @@ with a 128-bit key:
      return 1;
  }
 
+Encryption using AES-CBC with a 256-bit key with "CS1" ciphertext stealing.
+
+ int encrypt(const unsigned char *key, const unsigned char *iv,
+             const unsigned char *msg, size_t msg_len, unsigned char *out)
+ {
+    /*
+     * This assumes that key size is 32 bytes and the iv is 16 bytes.
+     * For ciphertext stealing mode the length of the ciphertext "out" will be
+     * the same size as the plaintext size "msg_len".
+     * The "msg_len" can be any size >= 16.
+     */
+     int ret = 0, encrypt = 1, outlen, len;
+     EVP_CIPHER_CTX *ctx = NULL;
+     EVP_CIPHER *cipher = NULL;
+     OSSL_PARAM params[2];
+
+     ctx = EVP_CIPHER_CTX_new();
+     cipher = EVP_CIPHER_fetch(NULL, "AES-256-CBC-CTS", NULL);
+     if (ctx == NULL || cipher == NULL)
+         goto err;
+
+     if (!EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, encrypt))
+         goto err;
+     /*
+      * The default is "CS1" so this is not really needed,
+      * but would be needed to set either "CS2" or "CS3".
+      */
+     params[0] = OSSL_PARAM_construct_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE,
+                                                  "CS1", 0);
+     params[1] = OSSL_PARAM_construct_end();
+     if (!EVP_CIPHER_CTX_set_params(ctx, params))
+         goto err;
+
+     /* NOTE: CTS mode does not support multiple calls to EVP_CipherUpdate() */
+     if (!EVP_CipherUpdate(ctx, encrypted, &outlen, msg, msglen))
+         goto err;
+      if (!EVP_CipherFinal_ex(ctx, encrypted + outlen, &len))
+         goto err;
+     ret = 1;
+ err:
+     EVP_CIPHER_free(cipher);
+     EVP_CIPHER_CTX_free(ctx);
+     return ret;
+ }
 
 =head1 SEE ALSO
 
diff --git a/doc/man7/provider-cipher.pod b/doc/man7/provider-cipher.pod
index bb8df17514..83f1768302 100644
--- a/doc/man7/provider-cipher.pod
+++ b/doc/man7/provider-cipher.pod
@@ -410,6 +410,38 @@ Byte 11-12: Input length (Always 0)
 
 Gets the result of running the "tls1multi_aad" operation.
 
+=item "cts_mode" (B<OSSL_CIPHER_PARAM_CTS_MODE>) <utf8 string>
+
+Sets the cipher text stealing mode. For all modes the output size is the same as
+the input size.
+
+Valid values for the mode are:
+
+=over 4
+
+=item "CS1"
+
+The NIST variant of cipher text stealing.
+For message lengths that are multiples of the block size it is equivalent to
+using a "AES-CBC" cipher otherwise the second last cipher text block is a
+partial block.
+
+=item "CS2"
+
+For message lengths that are multiples of the block size it is equivalent to
+using a "AES-CBC" cipher, otherwise it is the same as "CS3".
+
+=item "CS3"
+
+The Kerberos5 variant of cipher text stealing which always swaps the last
+cipher text block with the previous block (which may be a partial or full block
+depending on the input length).
+
+=back
+
+The default is "CS1".
+This is only supported for "AES-128-CBC-CTS", "AES-192-CBC-CTS" and "AES-256-CBC-CTS".
+
 =back
 
 =head1 RETURN VALUES
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 9ad81337c3..702ee6a6ed 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -66,6 +66,7 @@ extern "C" {
 #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_CTS_MODE             "cts_mode"     /* utf8_string */
 /* For passing the AlgorithmIdentifier parameter in DER form */
 #define OSSL_CIPHER_PARAM_ALG_ID               "alg_id_param" /* octet_string */
 
@@ -86,6 +87,11 @@ extern "C" {
 #define OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN                              \
     "tls1multi_enclen"     /* size_t */
 
+/* OSSL_CIPHER_PARAM_CTS_MODE Values */
+#define OSSL_CIPHER_CTS_MODE_CS1 "CS1"
+#define OSSL_CIPHER_CTS_MODE_CS2 "CS2"
+#define OSSL_CIPHER_CTS_MODE_CS3 "CS3"
+
 /* digest parameters */
 #define OSSL_DIGEST_PARAM_XOFLEN     "xoflen"    /* size_t */
 #define OSSL_DIGEST_PARAM_SSL3_MS    "ssl3-ms"   /* octet string */
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 644a214a6e..85a939b5c3 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -287,13 +287,15 @@ int (*EVP_CIPHER_meth_get_ctrl(const EVP_CIPHER *cipher))(EVP_CIPHER_CTX *,
 /* Free:                                         0x1000 */
 /* Buffer length in bits not bytes: CFB1 mode only */
 # define         EVP_CIPH_FLAG_LENGTH_BITS       0x2000
-/* Note if suitable for use in FIPS mode */
-# define         EVP_CIPH_FLAG_FIPS              0x4000
-/* Allow non FIPS cipher in FIPS mode */
-# define         EVP_CIPH_FLAG_NON_FIPS_ALLOW    0x8000
+/* Deprecated FIPS flag: was 0x4000 */
+# define         EVP_CIPH_FLAG_FIPS              0
+/* Deprecated FIPS flag: was 0x8000 */
+# define         EVP_CIPH_FLAG_NON_FIPS_ALLOW    0
+
 /*
  * Cipher handles any and all padding logic as well as finalisation.
  */
+# define         EVP_CIPH_FLAG_CTS               0x4000
 # define         EVP_CIPH_FLAG_CUSTOM_CIPHER     0x100000
 # define         EVP_CIPH_FLAG_AEAD_CIPHER       0x200000
 # define         EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK 0x400000
diff --git a/providers/defltprov.c b/providers/defltprov.c
index c92736e547..d404585afd 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -154,6 +154,9 @@ static const OSSL_ALGORITHM_CAPABLE deflt_ciphers[] = {
     ALG("AES-256-CBC", aes256cbc_functions),
     ALG("AES-192-CBC", aes192cbc_functions),
     ALG("AES-128-CBC", aes128cbc_functions),
+    ALG("AES-128-CBC-CTS", aes128cbc_cts_functions),
+    ALG("AES-192-CBC-CTS", aes192cbc_cts_functions),
+    ALG("AES-256-CBC-CTS", aes256cbc_cts_functions),
     ALG("AES-256-OFB", aes256ofb_functions),
     ALG("AES-192-OFB", aes192ofb_functions),
     ALG("AES-128-OFB", aes128ofb_functions),
diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c
index f7289ad75e..a998e392d7 100644
--- a/providers/fips/fipsprov.c
+++ b/providers/fips/fipsprov.c
@@ -399,6 +399,9 @@ static const OSSL_ALGORITHM_CAPABLE fips_ciphers[] = {
     ALG("AES-256-CBC", aes256cbc_functions),
     ALG("AES-192-CBC", aes192cbc_functions),
     ALG("AES-128-CBC", aes128cbc_functions),
+    ALG("AES-256-CBC-CTS", aes256cbc_cts_functions),
+    ALG("AES-192-CBC-CTS", aes192cbc_cts_functions),
+    ALG("AES-128-CBC-CTS", aes128cbc_cts_functions),
     ALG("AES-256-OFB", aes256ofb_functions),
     ALG("AES-192-OFB", aes192ofb_functions),
     ALG("AES-128-OFB", aes128ofb_functions),
diff --git a/providers/implementations/ciphers/build.info b/providers/implementations/ciphers/build.info
index a952c21638..9199ae0a92 100644
--- a/providers/implementations/ciphers/build.info
+++ b/providers/implementations/ciphers/build.info
@@ -49,9 +49,9 @@ SOURCE[$AES_GOAL]=\
         cipher_aes_cbc_hmac_sha256_hw.c cipher_aes_cbc_hmac_sha1_hw.c
 
 # Extra code to satisfy the FIPS and non-FIPS separation.
-# When the AES-xxx-XTS moves to legacy, this can be removed.
-SOURCE[../../libfips.a]=cipher_aes_xts_fips.c
-SOURCE[../../libnonfips.a]=cipher_aes_xts_fips.c
+# When the AES-xxx-XTS moves to legacy, cipher_aes_xts_fips.c can be removed.
+SOURCE[../../libfips.a]=cipher_aes_xts_fips.c cipher_aes_cts_fips.c
+SOURCE[../../libnonfips.a]=cipher_aes_xts_fips.c cipher_aes_cts_fips.c
 
 IF[{- !$disabled{siv} -}]
   SOURCE[$SIV_GOAL]=\
diff --git a/providers/implementations/ciphers/cipher_aes.c b/providers/implementations/ciphers/cipher_aes.c
index decc27517c..b0c716e3b7 100644
--- a/providers/implementations/ciphers/cipher_aes.c
+++ b/providers/implementations/ciphers/cipher_aes.c
@@ -86,3 +86,5 @@ IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 256, 8, 128, stream)
 IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 192, 8, 128, stream)
 /* aes128ctr_functions */
 IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 128, 8, 128, stream)
+
+#include "cipher_aes_cts.inc"
diff --git a/providers/common/der/der_ec_gen.c.in b/providers/implementations/ciphers/cipher_aes_cts.h
similarity index 54%
copy from providers/common/der/der_ec_gen.c.in
copy to providers/implementations/ciphers/cipher_aes_cts.h
index 40acf9a31c..6b0dfdd2c1 100644
--- a/providers/common/der/der_ec_gen.c.in
+++ b/providers/implementations/ciphers/cipher_aes_cts.h
@@ -7,11 +7,10 @@
  * https://www.openssl.org/source/license.html
  */
 
-#include "prov/der_ec.h"
+#include "crypto/evp.h"
 
-/* Well known OIDs precompiled */
-{-
-    $OUT = oids_to_c::process_leaves('providers/common/der/EC.asn1',
-                                     { dir => $config{sourcedir},
-                                       filter => \&oids_to_c::filter_to_C });
--}
+OSSL_FUNC_cipher_update_fn aes_cbc_cts_block_update;
+OSSL_FUNC_cipher_final_fn aes_cbc_cts_block_final;
+
+const char *aes_cbc_cts_mode_id2name(unsigned int id);
+int aes_cbc_cts_mode_name2id(const char *name);
diff --git a/providers/implementations/ciphers/cipher_aes_cts.inc b/providers/implementations/ciphers/cipher_aes_cts.inc
new file mode 100644
index 0000000000..5b33e972c5
--- /dev/null
+++ b/providers/implementations/ciphers/cipher_aes_cts.inc
@@ -0,0 +1,108 @@
+/*
+ * Copyright 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
+ */
+
+/* Dispatch functions for AES CBC CTS ciphers */
+
+#include "cipher_aes_cts.h"
+#include "prov/providercommonerr.h"
+
+static OSSL_FUNC_cipher_get_ctx_params_fn aes_cbc_cts_get_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn aes_cbc_cts_set_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn aes_cbc_cts_gettable_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn aes_cbc_cts_settable_ctx_params;
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(aes_cbc_cts)
+OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0),
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(aes_cbc_cts)
+
+static int aes_cbc_cts_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+    OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS_MODE);
+    if (p != NULL) {
+        const char *name = aes_cbc_cts_mode_id2name(ctx->cts_mode);
+
+        if (name == NULL || !OSSL_PARAM_set_utf8_string(p, name)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+            return 0;
+        }
+    }
+    return cipher_generic_get_ctx_params(vctx, params);
+}
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(aes_cbc_cts)
+OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0),
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(aes_cbc_cts)
+
+static int aes_cbc_cts_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+    const OSSL_PARAM *p;
+    int id;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_CTS_MODE);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            goto err;
+        id = aes_cbc_cts_mode_name2id(p->data);
+        if (id < 0)
+            goto err;
+
+        ctx->cts_mode = (unsigned int)id;
+    }
+    return cipher_generic_set_ctx_params(vctx, params);
+err:
+    ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+    return 0;
+}
+
+/* NOTE: The underlying block cipher is AES CBC so we reuse most of the code */
+#define IMPLEMENT_cts_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits,         \
+                             blkbits, ivbits, typ)                             \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params;   \
+static int alg##_cts_##kbits##_##lcmode##_get_params(OSSL_PARAM params[])      \
+{                                                                              \
+    return cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, flags,  \
+                                     kbits, blkbits, ivbits);                  \
+}                                                                              \
+const OSSL_DISPATCH alg##kbits##lcmode##_cts_functions[] = {                   \
+    { OSSL_FUNC_CIPHER_NEWCTX,                                                 \
+      (void (*)(void)) alg##_##kbits##_##lcmode##_newctx },                    \
+    { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx },              \
+    { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx },                \
+    { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))cipher_generic_einit },   \
+    { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))cipher_generic_dinit },   \
+    { OSSL_FUNC_CIPHER_UPDATE,                                                 \
+      (void (*)(void)) alg##_##lcmode##_cts_block_update },                    \
+    { OSSL_FUNC_CIPHER_FINAL,                                                  \
+      (void (*)(void)) alg##_##lcmode##_cts_block_final },                     \
+    { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))cipher_generic_cipher },        \
+    { OSSL_FUNC_CIPHER_GET_PARAMS,                                             \
+      (void (*)(void)) alg##_cts_##kbits##_##lcmode##_get_params },            \
+    { OSSL_FUNC_CIPHER_GETTABLE_PARAMS,                                        \
+      (void (*)(void))cipher_generic_gettable_params },                        \
+    { OSSL_FUNC_CIPHER_GET_CTX_PARAMS,                                         \
+      (void (*)(void))aes_cbc_cts_get_ctx_params },                            \
+    { OSSL_FUNC_CIPHER_SET_CTX_PARAMS,                                         \
+      (void (*)(void))aes_cbc_cts_set_ctx_params },                            \
+    { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,                                    \
+      (void (*)(void))aes_cbc_cts_gettable_ctx_params },                       \
+    { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,                                    \
+     (void (*)(void))aes_cbc_cts_settable_ctx_params },                        \
+    { 0, NULL }                                                                \
+};
+
+/* aes256cbc_cts_functions */
+IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, EVP_CIPH_FLAG_CTS, 256, 128, 128, block)
+/* aes192cbc_cts_functions */
+IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, EVP_CIPH_FLAG_CTS, 192, 128, 128, block)
+/* aes128cbc_cts_functions */
+IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, EVP_CIPH_FLAG_CTS, 128, 128, 128, block)
diff --git a/providers/implementations/ciphers/cipher_aes_cts_fips.c b/providers/implementations/ciphers/cipher_aes_cts_fips.c
new file mode 100644
index 0000000000..81e81ad5f2
--- /dev/null
+++ b/providers/implementations/ciphers/cipher_aes_cts_fips.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright 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
+ */
+
+/* Helper functions for AES CBC CTS ciphers related to fips */
+
+/*
+ * Refer to SP800-38A-Addendum
+ *
+ * Ciphertext stealing encrypts plaintext using a block cipher, without padding
+ * the message to a multiple of the block size, so the ciphertext is the same
+ * size as the plaintext.
+ * It does this by altering processing of the last two blocks of the message.
+ * The processing of all but the last two blocks is unchanged, but a portion of
+ * the second-last block's ciphertext is "stolen" to pad the last plaintext
+ * block. The padded final block is then encrypted as usual.
+ * The final ciphertext for the last two blocks, consists of the partial block
+ * (with the "stolen" portion omitted) plus the full final block,
+ * which are the same size as the original plaintext.
+ * Decryption requires decrypting the final block first, then restoring the
+ * stolen ciphertext to the partial block, which can then be decrypted as usual.
+
+ * AES_CBC_CTS has 3 variants:
+ *  (1) CS1 The NIST variant.
+ *      If the length is a multiple of the blocksize it is the same as CBC mode.
+ *      otherwise it produces C1||C2||(C(n-1))*||Cn.
+ *      Where C(n-1)* is a partial block.
+ *  (2) CS2
+ *      If the length is a multiple of the blocksize it is the same as CBC mode.
+ *      otherwise it produces C1||C2||Cn||(C(n-1))*.
+ *      Where C(n-1)* is a partial block.
+ *  (3) CS3 The Kerberos5 variant.
+ *      Produces C1||C2||Cn||(C(n-1))* regardless of the length.
+ *      If the length is a multiple of the blocksize it looks similar to CBC mode
+ *      with the last 2 blocks swapped.
+ *      Otherwise it is the same as CS2.
+ */
+
+#include "e_os.h" /* strcasecmp */
+#include <openssl/core_names.h>
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "internal/nelem.h"
+#include "cipher_aes_cts.h"
+
+/* The value assigned to 0 is the default */
+#define CTS_CS1 0
+#define CTS_CS2 1
+#define CTS_CS3 2
+
+typedef union {
+    size_t align;
+    unsigned char c[AES_BLOCK_SIZE];
+} aligned_16bytes;
+
+typedef struct cts_mode_name2id_st {
+    unsigned int id;
+    const char *name;
+} CTS_MODE_NAME2ID;
+
+static CTS_MODE_NAME2ID cts_modes[] =
+{
+    { CTS_CS1, OSSL_CIPHER_CTS_MODE_CS1 },
+#ifndef FIPS_MODULE
+    { CTS_CS2, OSSL_CIPHER_CTS_MODE_CS2 },
+    { CTS_CS3, OSSL_CIPHER_CTS_MODE_CS3 },
+#endif
+};
+
+const char *aes_cbc_cts_mode_id2name(unsigned int id)
+{
+    size_t i;
+
+    for (i = 0; i < OSSL_NELEM(cts_modes); ++i) {
+        if (cts_modes[i].id == id)
+            return cts_modes[i].name;
+    }
+    return NULL;
+}
+
+int aes_cbc_cts_mode_name2id(const char *name)
+{
+    size_t i;
+
+    for (i = 0; i < OSSL_NELEM(cts_modes); ++i) {
+        if (strcasecmp(name, cts_modes[i].name) == 0)
+            return (int)cts_modes[i].id;
+    }
+    return -1;
+}
+
+static size_t cts128_cs1_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+                                 unsigned char *out, size_t len)
+{
+    aligned_16bytes tmp_in;
+    size_t residue;
+
+    residue = len % AES_BLOCK_SIZE;
+    len -= residue;
+    if (!ctx->hw->cipher(ctx, out, in, len))
+        return 0;
+
+    if (residue == 0)
+        return len;
+
+    in += len;
+    out += len;
+
+    memset(tmp_in.c, 0, sizeof(tmp_in));
+    memcpy(tmp_in.c, in, residue);
+    if (!ctx->hw->cipher(ctx, out - AES_BLOCK_SIZE + residue, tmp_in.c,
+                         AES_BLOCK_SIZE))
+        return 0;
+    return len + residue;
+}
+
+static void do_xor(const unsigned char *in1, const unsigned char *in2,
+                   size_t len, unsigned char *out)
+{
+    size_t i;
+
+    for (i = 0; i < len; ++i)
+        out[i] = in1[i] ^ in2[i];
+}
+
+static size_t cts128_cs1_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+                                 unsigned char *out, size_t len)
+{
+    aligned_16bytes mid_iv, ct_mid, pt_last;
+    size_t residue;
+
+    residue = len % AES_BLOCK_SIZE;
+    if (residue == 0) {
+        /* If there are no partial blocks then it is the same as CBC mode */
+        if (!ctx->hw->cipher(ctx, out, in, len))
+            return 0;
+        return len;
+    }
+    /* Process blocks at the start - but leave the last 2 blocks */
+    len -= AES_BLOCK_SIZE + residue;
+    if (len > 0) {
+        if (!ctx->hw->cipher(ctx, out, in, len))
+            return 0;
+        in += len;
+        out += len;
+    }
+    /* Save the iv that will be used by the second last block */
+    memcpy(mid_iv.c, ctx->iv, AES_BLOCK_SIZE);
+
+    /* Decrypt the last block first using an iv of zero */
+    memset(ctx->iv, 0, AES_BLOCK_SIZE);
+    if (!ctx->hw->cipher(ctx, pt_last.c, in + residue, AES_BLOCK_SIZE))
+        return 0;
+
+    /*
+     * Rebuild the ciphertext of the second last block as a combination of
+     * the decrypted last block + replace the start with the ciphertext bytes
+     * of the partial second last block.
+     */
+    memcpy(ct_mid.c, in, residue);
+    memcpy(ct_mid.c + residue, pt_last.c + residue, AES_BLOCK_SIZE - residue);
+    /*
+     * Restore the last partial ciphertext block.
+     * Now that we have the cipher text of the second last block, apply
+     * that to the partial plaintext end block. We have already decrypted the
+     * block using an IV of zero. For decryption the IV is just XORed after
+     * doing an AES block - so just XOR in the cipher text.
+     */
+    do_xor(ct_mid.c, pt_last.c, residue, out + AES_BLOCK_SIZE);
+
+    /* Restore the iv needed by the second last block */
+    memcpy(ctx->iv, mid_iv.c, AES_BLOCK_SIZE);
+    /*
+     * Decrypt the second last plaintext block now that we have rebuilt the
+     * ciphertext.
+     */
+    if (!ctx->hw->cipher(ctx, out, ct_mid.c, AES_BLOCK_SIZE))
+        return 0;
+
+    return len + AES_BLOCK_SIZE + residue;
+}
+
+#ifndef FIPS_MODULE
+static size_t cts128_cs3_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+                                 unsigned char *out, size_t len)
+{
+    aligned_16bytes tmp_in;
+    size_t residue;
+
+    if (len <= AES_BLOCK_SIZE)  /* CS3 requires 2 blocks */
+        return 0;
+
+    residue = len % AES_BLOCK_SIZE;
+    if (residue == 0)
+        residue = AES_BLOCK_SIZE;
+    len -= residue;
+
+    if (!ctx->hw->cipher(ctx, out, in, len))
+        return 0;
+
+    in += len;
+    out += len;
+
+    memset(tmp_in.c, 0, sizeof(tmp_in));
+    memcpy(tmp_in.c, in, residue);
+    memcpy(out, out - AES_BLOCK_SIZE, residue);
+    if (!ctx->hw->cipher(ctx, out - AES_BLOCK_SIZE, tmp_in.c, AES_BLOCK_SIZE))
+        return 0;
+    return len + residue;
+}
+
+/*
+ * Note:
+ *  The cipher text (in) is of the form C(0), C(1), ., C(n), C(n-1)* where
+ *  C(n) is a full block and C(n-1)* can be a partial block
+ *  (but could be a full block).
+ *  This means that the output plaintext (out) needs to swap the plaintext of
+ *  the last two decoded ciphertext blocks.
+ */
+static size_t cts128_cs3_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+                                 unsigned char *out, size_t len)
+{
+    aligned_16bytes mid_iv, ct_mid, pt_last;
+    size_t residue;
+
+    if (len <= AES_BLOCK_SIZE) /* CS3 requires 2 blocks */
+        return 0;
+
+    /* Process blocks at the start - but leave the last 2 blocks */
+    residue = len % AES_BLOCK_SIZE;
+    if (residue == 0)
+        residue = AES_BLOCK_SIZE;
+    len -= AES_BLOCK_SIZE + residue;
+
+    if (len > 0) {
+        if (!ctx->hw->cipher(ctx, out, in, len))
+            return 0;
+        in += len;
+        out += len;
+    }
+    /* Save the iv that will be used by the second last block */
+    memcpy(mid_iv.c, ctx->iv, AES_BLOCK_SIZE);
+
+    /* Decrypt the Cn block first using an iv of zero */
+    memset(ctx->iv, 0, AES_BLOCK_SIZE);
+    if (!ctx->hw->cipher(ctx, pt_last.c, in, AES_BLOCK_SIZE))
+        return 0;
+
+    /*
+     * Rebuild the ciphertext of C(n-1) as a combination of
+     * the decrypted C(n) block + replace the start with the ciphertext bytes
+     * of the partial last block.
+     */
+    memcpy(ct_mid.c, in + AES_BLOCK_SIZE, residue);
+    if (residue != AES_BLOCK_SIZE)
+        memcpy(ct_mid.c + residue, pt_last.c + residue, AES_BLOCK_SIZE - residue);
+    /*
+     * Restore the last partial ciphertext block.
+     * Now that we have the cipher text of the second last block, apply
+     * that to the partial plaintext end block. We have already decrypted the
+     * block using an IV of zero. For decryption the IV is just XORed after
+     * doing an AES block - so just XOR in the ciphertext.
+     */
+    do_xor(ct_mid.c, pt_last.c, residue, out + AES_BLOCK_SIZE);
+
+    /* Restore the iv needed by the second last block */
+    memcpy(ctx->iv, mid_iv.c, AES_BLOCK_SIZE);
+    /*
+     * Decrypt the second last plaintext block now that we have rebuilt the
+     * ciphertext.
+     */
+    if (!ctx->hw->cipher(ctx, out, ct_mid.c, AES_BLOCK_SIZE))
+        return 0;
+
+    return len + AES_BLOCK_SIZE + residue;
+}
+
+static size_t cts128_cs2_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+                                 unsigned char *out, size_t len)
+{
+    if (len % AES_BLOCK_SIZE == 0) {
+        /* If there are no partial blocks then it is the same as CBC mode */
+        if (!ctx->hw->cipher(ctx, out, in, len))
+            return 0;
+        return len;
+    }
+    /* For partial blocks CS2 is equivalent to CS3 */
+    return cts128_cs3_encrypt(ctx, in, out, len);
+}
+
+static size_t cts128_cs2_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+                                 unsigned char *out, size_t len)
+{
+    if (len % AES_BLOCK_SIZE == 0) {
+        /* If there are no partial blocks then it is the same as CBC mode */
+        if (!ctx->hw->cipher(ctx, out, in, len))
+            return 0;
+        return len;
+    }
+    /* For partial blocks CS2 is equivalent to CS3 */
+    return cts128_cs3_decrypt(ctx, in, out, len);
+}
+#endif
+
+int aes_cbc_cts_block_update(void *vctx, unsigned char *out, size_t *outl,
+                             size_t outsize, const unsigned char *in,
+                             size_t inl)
+{
+    PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+    size_t sz = 0;
+
+    if (inl < AES_BLOCK_SIZE) /* There must be at least one block for CTS mode */
+        return 0;
+    if (outsize < inl)
+        return 0;
+    if (out == NULL) {
+        *outl = inl;
+        return 1;
+    }
+
+    /*
+     * Return an error if the update is called multiple times, only one shot
+     * is supported.
+     */
+    if (ctx->updated == 1)
+        return 0;
+
+    if (ctx->enc) {
+#ifdef FIPS_MODULE
+        sz = cts128_cs1_encrypt(ctx, in, out, inl);
+#else
+        if (ctx->cts_mode == CTS_CS1)
+            sz = cts128_cs1_encrypt(ctx, in, out, inl);
+        else if (ctx->cts_mode == CTS_CS2)
+            sz = cts128_cs2_encrypt(ctx, in, out, inl);
+        else if (ctx->cts_mode == CTS_CS3)
+            sz = cts128_cs3_encrypt(ctx, in, out, inl);
+#endif
+    } else {
+#ifdef FIPS_MODULE
+        sz = cts128_cs1_decrypt(ctx, in, out, inl);
+#else
+        if (ctx->cts_mode == CTS_CS1)
+            sz = cts128_cs1_decrypt(ctx, in, out, inl);
+        else if (ctx->cts_mode == CTS_CS2)
+            sz = cts128_cs2_decrypt(ctx, in, out, inl);
+        else if (ctx->cts_mode == CTS_CS3)
+            sz = cts128_cs3_decrypt(ctx, in, out, inl);
+#endif
+    }
+    if (sz == 0)
+        return 0;
+    ctx->updated = 1; /* Stop multiple updates being allowed */
+    *outl = sz;
+    return 1;
+}
+
+int aes_cbc_cts_block_final(void *vctx, unsigned char *out, size_t *outl,
+                            size_t outsize)
+{
+    *outl = 0;
+    return 1;
+}
diff --git a/providers/implementations/include/prov/ciphercommon.h b/providers/implementations/include/prov/ciphercommon.h
index a5ffbc48a1..7e8143fae0 100644
--- a/providers/implementations/include/prov/ciphercommon.h
+++ b/providers/implementations/include/prov/ciphercommon.h
@@ -47,9 +47,12 @@ struct prov_cipher_ctx_st {
     size_t ivlen;
     size_t blocksize;
     size_t bufsz;            /* Number of bytes in buf */
+    unsigned int cts_mode;   /* Use to set the type for CTS modes */
     unsigned int pad : 1;    /* Whether padding should be used or not */
     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 updated : 1; /* Set to 1 during update for one shot ciphers */
+
 
     unsigned int tlsversion; /* If TLS padding is in use the TLS version number */
     unsigned char *tlsmac;   /* tls MAC extracted from the last record */
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index ee942e94e1..0b32f3727c 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -45,6 +45,9 @@ extern const OSSL_DISPATCH aes128ecb_functions[];
 extern const OSSL_DISPATCH aes256cbc_functions[];
 extern const OSSL_DISPATCH aes192cbc_functions[];
 extern const OSSL_DISPATCH aes128cbc_functions[];
+extern const OSSL_DISPATCH aes256cbc_cts_functions[];
+extern const OSSL_DISPATCH aes192cbc_cts_functions[];
+extern const OSSL_DISPATCH aes128cbc_cts_functions[];
 extern const OSSL_DISPATCH aes256ofb_functions[];
 extern const OSSL_DISPATCH aes192ofb_functions[];
 extern const OSSL_DISPATCH aes128ofb_functions[];
diff --git a/test/evp_test.c b/test/evp_test.c
index c0b7b6f50f..7e93b41f32 100644
--- a/test/evp_test.c
+++ b/test/evp_test.c
@@ -514,6 +514,7 @@ typedef struct cipher_data_st {
     unsigned char *aad[AAD_NUM];
     size_t aad_len[AAD_NUM];
     unsigned char *tag;
+    const char *cts_mode;
     size_t tag_len;
     int tag_late;
 } CIPHER_DATA;
@@ -628,6 +629,10 @@ static int cipher_test_parse(EVP_TEST *t, const char *keyword,
             return -1;
         return 1;
     }
+    if (strcmp(keyword, "CTSMode") == 0) {
+        cdat->cts_mode = value;
+        return 1;
+    }
     return 0;
 }
 
@@ -687,6 +692,18 @@ static int cipher_test_enc(EVP_TEST *t, int enc,
         t->err = "CIPHERINIT_ERROR";
         goto err;
     }
+    if (expected->cts_mode != NULL) {
+        OSSL_PARAM params[2];
+
+        params[0] = OSSL_PARAM_construct_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE,
+                                                     (char *)expected->cts_mode,
+                                                     0);
+        params[1] = OSSL_PARAM_construct_end();
+        if (!EVP_CIPHER_CTX_set_params(ctx_base, params)) {
+            t->err = "INVALID_CTS_MODE";
+            goto err;
+        }
+    }
     if (expected->iv) {
         if (expected->aead) {
             if (!EVP_CIPHER_CTX_ctrl(ctx_base, EVP_CTRL_AEAD_SET_IVLEN,
@@ -939,6 +956,7 @@ static int cipher_test_run(EVP_TEST *t)
              * lengths so we don't fragment for those
              */
             if (cdat->aead == EVP_CIPH_CCM_MODE
+                    || ((EVP_CIPHER_flags(cdat->cipher) & EVP_CIPH_FLAG_CTS) != 0)
                     || EVP_CIPHER_mode(cdat->cipher) == EVP_CIPH_SIV_MODE
                     || EVP_CIPHER_mode(cdat->cipher) == EVP_CIPH_XTS_MODE
                     || EVP_CIPHER_mode(cdat->cipher) == EVP_CIPH_WRAP_MODE)
diff --git a/test/recipes/30-test_evp.t b/test/recipes/30-test_evp.t
index 3855d8a3b9..32639b77a5 100644
--- a/test/recipes/30-test_evp.t
+++ b/test/recipes/30-test_evp.t
@@ -32,7 +32,8 @@ my @configs = ( $defaultcnf );
 push @configs, 'fips.cnf' unless $no_fips;
 
 my @files = qw( evprand.txt evpciph.txt evpdigest.txt evppkey.txt
-    evppkey_ecc.txt );
+    evppkey_ecc.txt evpciph_aes_cts.txt);
+
 my @defltfiles = qw( evpencod.txt evpkdf.txt evppkey_kdf.txt evpmac.txt
     evppbe.txt evpcase.txt evpccmcavs.txt );
 my @ideafiles = qw( evpciph_idea.txt );
diff --git a/test/recipes/30-test_evp_data/evpciph_aes_cts.txt b/test/recipes/30-test_evp_data/evpciph_aes_cts.txt
new file mode 100644
index 0000000000..83bac2c5c8
--- /dev/null
+++ b/test/recipes/30-test_evp_data/evpciph_aes_cts.txt
@@ -0,0 +1,362 @@
+#
+# Copyright 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
+
+# Original test vectors were taken from https://www.ietf.org/rfc/rfc3962.txt for CS3
+# These have an IV of all zeros, for a 128 bit AES key.
+
+# 17 bytes Input
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b652074686520
+Ciphertext = c6353568f2bf8cb4d8a580362da7ff7f97
+
+# 31 bytes input
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320
+Ciphertext = fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5
+
+# 32 bytes input (CS3 always swaps the last 2 byte blocks - so it is not equivalent to CBC for a full block)
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = 39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584
+
+# 47 bytes input
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe584b3fffd940c16a18c1b5549d2f838029e39312523a78662d5be7fcbcc98ebf5
+
+# 48 bytes input
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe5849dad8bbb96c4cdc03bc103e1a194bbd839312523a78662d5be7fcbcc98ebf5a8
+
+# 64 bytes input (CS3 always swaps the last 2 byte blocks - so it is not equivalent to CBC for a full block)
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a84807efe836ee89a526730dbc2f7bc8409dad8bbb96c4cdc03bc103e1a194bbd8
+
+#------------------------------------------------------
+# AES_CBC results for aligned block lengths. (Result should be the same as 32 byte CTS1 & CTS2)
+
+# 32 bytes input
+Cipher = AES-128-CBC
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a8
+
+# 48 bytes input
+Cipher = AES-128-CBC
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a89dad8bbb96c4cdc03bc103e1a194bbd8
+
+# 64 bytes input
+Cipher = AES-128-CBC
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a89dad8bbb96c4cdc03bc103e1a194bbd84807efe836ee89a526730dbc2f7bc840
+
+#------------------------------------------------------
+# Manually edited using the same inputs to also produce CS2 ciphertext
+# where aligned blocks are the same as CBC mode, and partial lengths
+# are the same as CS3.
+
+# 17 bytes Input (For partial blocks the output should match CS3)
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b652074686520
+Ciphertext = c6353568f2bf8cb4d8a580362da7ff7f97
+
+# 31 bytes input (For partial blocks the output should match CS3)
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320
+Ciphertext = fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5
+
+# 32 bytes input (Aligned blocks should match normal CBC mode)
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a8
+
+# 47 bytes input
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe584b3fffd940c16a18c1b5549d2f838029e39312523a78662d5be7fcbcc98ebf5
+
+# 64 bytes input (CS2 is equivalent to CBC when the last block in full)
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a89dad8bbb96c4cdc03bc103e1a194bbd84807efe836ee89a526730dbc2f7bc840
+
+#------------------------------------------------------
+# Manually edited using the same inputs to also produce CS1 ciphertext
+# where aligned blocks are the same as CBC mode, and partial lengths
+# have the last 2 blocks swapped compared to CS3.
+
+# 17 bytes Input((Default is CS1 if CTSMode is not specified)
+Cipher = AES-128-CBC-CTS
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b652074686520
+Ciphertext = 97c6353568f2bf8cb4d8a580362da7ff7f
+
+# 31 bytes input
+Cipher = AES-128-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe5fc00783e0efdb2c1d445d4c8eff7ed22
+
+# 32 bytes input
+Cipher = AES-128-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a8
+
+# 47 bytes input
+Cipher = AES-128-CBC-CTS
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5b3fffd940c16a18c1b5549d2f838029e
+
+# 64 bytes input (CS1 is equivalent to CBC when the last block in full)
+Cipher = AES-128-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e
+Ciphertext = 97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a89dad8bbb96c4cdc03bc103e1a194bbd84807efe836ee89a526730dbc2f7bc840
+
+#-------------------------------------------------------------------------------
+# Generated test values using an IV.
+
+# 47 bytes input
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c
+Ciphertext = 5432a630742dee7beb70f9f1400ee6a0426da5c54a9990f5ae0b7825f51f0060b557cfb581949a4bdf3bb67dedd472
+
+# 47 bytes input
+Cipher = AES-128-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69
+IV =000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c
+Ciphertext = 5432a630742dee7beb70f9f1400ee6a0b557cfb581949a4bdf3bb67dedd472426da5c54a9990f5ae0b7825f51f0060
+
+# 127 bytes
+Cipher = AES-128-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f7570
+Ciphertext = 5432a630742dee7beb70f9f1400ee6a0b557cfb581949a4bdf3bb67dedd472b9fc50e4e7dacf9e3d94b6cc031f9997a22d2fea7e6ef4aba2b717b0fa3f150e5e86e46b9e51c6ea5091a92aa791ce826b2e4fbaaf0e0314939625434b9530ce56f299891a48d26bdc287f54b230340d652a4721bf0f082ede80b6399800a92f
+
+# 129 bytes
+Cipher = AES-128-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e49
+Ciphertext = 5432a630742dee7beb70f9f1400ee6a0b557cfb581949a4bdf3bb67dedd472b9fc50e4e7dacf9e3d94b6cc031f9997a22d2fea7e6ef4aba2b717b0fa3f150e5e86e46b9e51c6ea5091a92aa791ce826b2e4fbaaf0e0314939625434b9530ce56f299891a48d26bdc287f54b230340d14fde9fd1098b9b1db788b5868a8d009eeef
+
+#-------------------------------------------------------------------------------
+# 17 Bytes
+Cipher = AES-192-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV =000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b652074686520
+Ciphertext = de1b402de8f79f947cc6b5880588d9b6e9
+
+# 31 Bytes
+Cipher = AES-192-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320
+Ciphertext = dea2b610546f3b1e1d231821e283e153e9de17d6248fb492bdea1fb2e09c8e
+
+# 32 Bytes
+Cipher = AES-192-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = 31d005cc9fea948fed1ba6308dad9dd1e9de17d6248fb492bdea1fb2e09c8e8e
+
+# 17 Bytes
+Cipher = AES-192-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b652074686520
+Ciphertext = de1b402de8f79f947cc6b5880588d9b6e9
+
+# 31 Bytes
+Cipher = AES-192-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320
+Ciphertext = dea2b610546f3b1e1d231821e283e153e9de17d6248fb492bdea1fb2e09c8e
+
+# 32 Bytes
+Cipher = AES-192-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = e9de17d6248fb492bdea1fb2e09c8e8e31d005cc9fea948fed1ba6308dad9dd1
+
+# 17 Bytes
+Cipher = AES-192-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b652074686520
+Ciphertext = e9de1b402de8f79f947cc6b5880588d9b6
+
+# 31 Bytes
+Cipher = AES-192-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320
+Ciphertext = e9de17d6248fb492bdea1fb2e09c8edea2b610546f3b1e1d231821e283e153
+
+# 32 Bytes
+Cipher = AES-192-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69636869636b656e20
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = e9de17d6248fb492bdea1fb2e09c8e8e31d005cc9fea948fed1ba6308dad9dd1
+
+#-------------------------------------------------------------------------------
+# 17 Bytes
+Cipher = AES-256-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69636869636b656e207465726979616b69
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b652074686520
+Ciphertext = 6b5f5abc21c4d04156c73850da3bba29e9
+
+# 31 Bytes
+Cipher = AES-256-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69636869636b656e207465726979616b69
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c20476175277320
+Ciphertext = f22553af78ee4f468f02fbe6f0f2168ee954e79fae9310dc75b6070e1d6253
+
+# 32 Bytes
+Cipher = AES-256-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69636869636b656e207465726979616b69
+IV = 000102030405060708090A0B0C0D0E0F
+Plaintext = 4920776f756c64206c696b65207468652047656e6572616c2047617527732043
+Ciphertext = 2c0463982174df10baa9d8f782c5a5b3e954e79fae9310dc75b6070e1d625346
+
+#------------------------------------------------------------------------------
+# Failure tests
+
+# 15 bytes should fail for CS1
+Cipher = AES-128-CBC-CTS
+CTSMode = CS1
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 0102030405060708090A0B0C0D0E0F
+Result = CIPHERUPDATE_ERROR
+
+# 15 bytes should fail for CS2
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS2
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 0102030405060708090A0B0C0D0E0F
+Result = CIPHERUPDATE_ERROR
+
+# 15 bytes should fail for CS3
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 0102030405060708090A0B0C0D0E0F
+Result = CIPHERUPDATE_ERROR
+
+# 16 bytes should fail for CS3 (since it always needs 2 blocks).
+Cipher = AES-128-CBC-CTS
+Availablein = default
+CTSMode = CS3
+Key = 636869636b656e207465726979616b69
+IV = 00000000000000000000000000000000
+Plaintext = 0102030405060708090A0B0C0D0E0F00
+Result = CIPHERUPDATE_ERROR


More information about the openssl-commits mailing list