Support cipher provider "iv state"
authorBenjamin Kaduk <bkaduk@akamai.com>
Fri, 19 Jun 2020 20:50:22 +0000 (13:50 -0700)
committerBenjamin Kaduk <bkaduk@akamai.com>
Tue, 11 Aug 2020 14:07:37 +0000 (07:07 -0700)
Some modes (e.g., CBC and OFB) update the effective IV with each
block-cipher invocation, making the "IV" stored in the (historically)
EVP_CIPHER_CTX or (current) PROV_CIPHER_CTX distinct from the initial
IV passed in at cipher initialization time.  The latter is stored in
the "oiv" (original IV) field, and has historically been accessible
via the EVP_CIPHER_CTX_original_iv() API.  The "effective IV" has
also historically been accessible, via both EVP_CIPHER_CTX_iv()
and EVP_CIPHER_CTX_iv_noconst(), the latter of which allows for
*write* access to the internal cipher state.  This is particularly
problematic given that provider-internal cipher state need not, in
general, even be accessible from the same address space as libcrypto,
so these APIs are not sustainable in the long term.  However, it still
remains necessary to provide access to the contents of the "IV state"
(e.g., when serializing cipher state for in-kernel TLS); a subsequent
reinitialization of a cipher context using the "IV state" as the
input IV will be able to resume processing of data in a compatible
manner.

This problem was introduced in commit
089cb623be76b88a1eea6fcd135101037661bbc3, which effectively caused
all IV queries to return the "original IV", removing access to the
current IV state of the cipher.

These functions for accessing the (even the "original") IV had remained
undocumented for quite some time, presumably due to unease about
exposing the internals of the cipher state in such a manner.

Note that this also as a side effect "fixes" some "bugs" where things
had been referring to the 'iv' field that should have been using the
'oiv' field.  It also fixes the EVP_CTRL_GET_IV cipher control,
which was clearly intended to expose the non-original IV, for
use exporting the cipher state into the kernel for kTLS.

Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/12233)

crypto/evp/evp_enc.c
crypto/evp/evp_lib.c
doc/man7/provider-cipher.pod
include/openssl/core_names.h
providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c
providers/implementations/ciphers/cipher_aes_ocb.c
providers/implementations/ciphers/ciphercommon.c
providers/implementations/ciphers/ciphercommon_ccm.c
providers/implementations/ciphers/ciphercommon_gcm.c
providers/implementations/include/prov/ciphercommon.h

index 74d4afdac4c79833d4f5894f0da1e7fe33227211..44108db30bc2fa037e1ee9cd4afdb88c90b2f560 100644 (file)
@@ -973,8 +973,9 @@ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
         goto end;
     case EVP_CTRL_GET_IV:
         set_params = 0;
-        params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_IV,
-                                                      ptr, sz);
+        params[0] =
+            OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_IV_STATE, ptr,
+                                              sz);
         break;
     case EVP_CTRL_AEAD_SET_IVLEN:
         if (arg < 0)
index 9f2165dc59bcb3e7618d8f0e63f0347ef52822f6..e6902ac6f1cf5f012f01c1bbe24d40b993fc5ff8 100644 (file)
@@ -460,7 +460,7 @@ const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx)
     OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
 
     params[0] =
-        OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_IV, (void **)&v,
+        OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_IV_STATE, (void **)&v,
                                        sizeof(ctx->iv));
     ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->provctx, params);
 
@@ -474,7 +474,7 @@ unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx)
     OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
 
     params[0] =
-        OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_IV, (void **)&v,
+        OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_IV_STATE, (void **)&v,
                                        sizeof(ctx->iv));
     ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->provctx, params);
 
index ee159ff7eb13d3813032d3ad509b1941aad221e7..d6d544c0ba8bbdc0bd3d8b3d688d8921b8cb7287 100644 (file)
@@ -240,7 +240,14 @@ The length of the "ivlen" parameter should not exceed that of a B<size_t>.
 
 =item "iv" (B<OSSL_CIPHER_PARAM_IV>) <octet string OR octet ptr>
 
-Gets the IV for the associated cipher ctx.
+Gets the IV used to initialize the associated cipher ctx.
+
+=item "iv-state" (B<OSSL_CIPHER_PARAM_IV_STATE>) <octet string OR octet ptr>
+
+Gets the current pseudo-IV state for the associated cipher ctx, e.g.,
+the previous ciphertext block for CBC mode or the iteratively encrypted IV
+value for OFB mode.  Note that octet pointer access is deprecated and is
+provided only for backwards compatibility with historical libcrypto APIs.
 
 =item "num" (B<OSSL_CIPHER_PARAM_NUM>) <unsigned integer>
 
index b511571fb3077888050e21862f9c79029db3c179..53e68e778b7ed3be52fe98bc95bc49aba83c425c 100644 (file)
@@ -53,6 +53,7 @@ extern "C" {
 #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_IV_STATE             "iv-state"     /* 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 */
index 6cf6a1b1118c1df504def592c8d305334f6c3158..8f731228d94f19b2515fe6f0aba75aa79f12a986 100644 (file)
@@ -234,6 +234,12 @@ static int aes_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_IV_STATE);
+    if (p != NULL
+        && !OSSL_PARAM_set_octet_string(p, ctx->base.iv, ctx->base.ivlen)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
     return 1;
 }
 
@@ -248,6 +254,7 @@ static const OSSL_PARAM cipher_aes_known_gettable_ctx_params[] = {
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
     OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV_STATE, NULL, 0),
     OSSL_PARAM_END
 };
 const OSSL_PARAM *aes_gettable_ctx_params(void *provctx)
index 162945f922ac6e0e61a9782cca35f9c6eea19575..7be5c7f5e8556a6d1518cef2af4e733e7e888e0c 100644 (file)
@@ -416,6 +416,18 @@ static int aes_ocb_get_ctx_params(void *vctx, OSSL_PARAM params[])
             return 0;
         }
     }
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV_STATE);
+    if (p != NULL) {
+        if (ctx->base.ivlen > p->data_size) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+            return 0;
+        }
+        if (!OSSL_PARAM_set_octet_string(p, ctx->base.iv, ctx->base.ivlen)
+            && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.iv, ctx->base.ivlen)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+            return 0;
+        }
+    }
     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
     if (p != NULL) {
         if (p->data_type != OSSL_PARAM_OCTET_STRING) {
@@ -436,6 +448,7 @@ static const OSSL_PARAM cipher_ocb_known_gettable_ctx_params[] = {
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
     OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV_STATE, NULL, 0),
     OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
     OSSL_PARAM_END
 };
index dd25f00db471887c410f35f83e33ff182010b133..a5de18ab3ba0a55b4c39c6b2ae89760c1887a476 100644 (file)
@@ -112,6 +112,7 @@ static const OSSL_PARAM cipher_aead_known_gettable_ctx_params[] = {
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
     OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV_STATE, NULL, 0),
     OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL),
     OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN, NULL, 0),
@@ -478,6 +479,13 @@ 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_IV_STATE);
+    if (p != NULL
+        && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen)
+        && !OSSL_PARAM_set_octet_string(p, &ctx->iv, ctx->ivlen)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+        return 0;
+    }
     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_NUM);
     if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->num)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
index 2b9a0687e3c947008762b93cd1200cb58dd0cd81..bdbfa74d400a6f57116aec4e46cf2fb73a0211bd 100644 (file)
@@ -171,6 +171,19 @@ int ccm_get_ctx_params(void *vctx, OSSL_PARAM params[])
         }
     }
 
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV_STATE);
+    if (p != NULL) {
+        if (ccm_get_ivlen(ctx) > p->data_size) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IVLEN);
+            return 0;
+        }
+        if (!OSSL_PARAM_set_octet_string(p, ctx->iv, p->data_size)
+            && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, p->data_size)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+            return 0;
+        }
+    }
+
     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
     if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
index 080fcc9bc23bb5450e723222cb7d8d3f73289e6d..415483cf2b6bcad125c16884791fc183e04abbdf 100644 (file)
@@ -167,6 +167,21 @@ int gcm_get_ctx_params(void *vctx, OSSL_PARAM params[])
         }
     }
 
+    p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV_STATE);
+    if (p != NULL) {
+        if (ctx->iv_gen != 1 && ctx->iv_gen_rand != 1)
+            return 0;
+        if (ctx->ivlen > p->data_size) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+            return 0;
+        }
+        if (!OSSL_PARAM_set_octet_string(p, ctx->iv, ctx->ivlen)
+            && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen)) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+            return 0;
+        }
+    }
+
     p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD);
     if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
index 43cec3cc2b66c67a54a359f1bc36abdbf65f2010..90f6d39d394d067fc8820cb1b8e426c5bb1eb4f9 100644 (file)
@@ -314,7 +314,8 @@ static const OSSL_PARAM name##_known_gettable_ctx_params[] = {                 \
     OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),                          \
     OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL),                          \
     OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL),                              \
-    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),                    \
+    OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV_STATE, NULL, 0),
 
 #define CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(name)                           \
     OSSL_PARAM_END                                                             \