evp enc: cache cipher key length
authorPauli <pauli@openssl.org>
Thu, 27 Jan 2022 02:33:36 +0000 (13:33 +1100)
committerPauli <pauli@openssl.org>
Sun, 6 Feb 2022 22:46:16 +0000 (09:46 +1100)
Instead of doing a heavy params based query every time a context is
asked for its key length, this value is cached in the context and only
queried if it could have been modified.

Fixes #17064

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

crypto/evp/evp_enc.c
crypto/evp/evp_lib.c

index ff315bd922e7829bfa9d01b47881209053e10266..7ae92df98b0e6ef7a98f30702bbf097db1fff093 100644 (file)
@@ -62,7 +62,7 @@ int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx)
     ENGINE_finish(ctx->engine);
 #endif
     memset(ctx, 0, sizeof(*ctx));
-    ctx->iv_len = -1;
+    ctx->iv_len = 0;
     return 1;
 }
 
@@ -984,7 +984,7 @@ int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *c, int keylen)
     if (c->cipher->prov != NULL) {
         int ok;
         OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
-        size_t len = keylen;
+        size_t len;
 
         if (EVP_CIPHER_CTX_get_key_length(c) == keylen)
             return 1;
@@ -997,9 +997,13 @@ int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *c, int keylen)
         }
 
         params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &len);
+        if (!OSSL_PARAM_set_int(params, keylen))
+            return 0;
         ok = evp_do_ciph_ctx_setparams(c->cipher, c->algctx, params);
-
-        return ok > 0 ? 1 : 0;
+        if (ok <= 0)
+            return 0;
+        c->key_len = keylen;
+        return 1;
     }
 
     /* Code below to be removed when legacy support is dropped. */
@@ -1060,6 +1064,7 @@ int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
     switch (type) {
     case EVP_CTRL_SET_KEY_LENGTH:
         params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &sz);
+        ctx->key_len = -1;
         break;
     case EVP_CTRL_RAND_KEY:      /* Used by DES */
         set_params = 0;
@@ -1255,11 +1260,23 @@ int EVP_CIPHER_get_params(EVP_CIPHER *cipher, OSSL_PARAM params[])
 
 int EVP_CIPHER_CTX_set_params(EVP_CIPHER_CTX *ctx, const OSSL_PARAM params[])
 {
+    int r = 0;
+    const OSSL_PARAM *p;
+
     if (ctx->cipher != NULL && ctx->cipher->set_ctx_params != NULL) {
-        ctx->iv_len = -1;
-        return ctx->cipher->set_ctx_params(ctx->algctx, params);
+        r = ctx->cipher->set_ctx_params(ctx->algctx, params);
+        if (r > 0) {
+            p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+            if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->key_len))
+                r = 0;
+        }
+        if (r > 0) {
+            p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN);
+            if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->iv_len))
+                r = 0;
+        }
     }
-    return 0;
+    return r;
 }
 
 int EVP_CIPHER_CTX_get_params(EVP_CIPHER_CTX *ctx, OSSL_PARAM params[])
index 8bac09bba067dde48841bb7c55b1698f27f8e4cb..7e03d03bf652a5d360c3717a133e4c0b40b5eccb 100644 (file)
@@ -647,14 +647,28 @@ int EVP_CIPHER_get_key_length(const EVP_CIPHER *cipher)
 
 int EVP_CIPHER_CTX_get_key_length(const EVP_CIPHER_CTX *ctx)
 {
-    int ok;
-    size_t v = ctx->key_len;
-    OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    if (ctx->key_len <= 0 && ctx->cipher->prov != NULL) {
+        int ok;
+        OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+        size_t len;
 
-    params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &v);
-    ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params);
+        params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_KEYLEN, &len);
+        ok = evp_do_ciph_ctx_getparams(ctx->cipher, ctx->algctx, params);
+        if (ok <= 0)
+            return EVP_CTRL_RET_UNSUPPORTED;
 
-    return ok != 0 ? (int)v : EVP_CTRL_RET_UNSUPPORTED;
+        /*-
+         * The if branch should never be taken since EVP_MAX_KEY_LENGTH is
+         * less than INT_MAX but best to be safe.
+         *
+         * Casting away the const is annoying but required here.  We need to
+         * cache the result for performance reasons.
+         */
+        if (!OSSL_PARAM_get_int(params, &((EVP_CIPHER_CTX *)ctx)->key_len))
+            return -1;
+        ((EVP_CIPHER_CTX *)ctx)->key_len = (int)len;
+    }
+    return ctx->key_len;
 }
 
 int EVP_CIPHER_get_nid(const EVP_CIPHER *cipher)