Fix EVP_Cipher() for provided cipher implementations
authorRichard Levitte <levitte@openssl.org>
Thu, 10 Oct 2019 16:04:06 +0000 (18:04 +0200)
committerRichard Levitte <levitte@openssl.org>
Fri, 11 Oct 2019 13:55:36 +0000 (15:55 +0200)
EVP_Cipher() would return whatever ctx->cipher->ccipher() returned
with no regard for historical semantics.

We change this to first look if there is a ctx->cipher->ccipher(), and
in that case we treat the implementation as one with a custom cipher,
and "translate" it's return value like this: 0 => -1, 1 => outl, where
|outl| is the output length.

If there is no ctx->cipher->ccipher, we treat the implementation as
one without a custom cipher, call ctx->cipher->cupdate or
ctx->cipher->cfinal depending on input, and return whatever they
return (0 or 1).

Furthermore, we add a small hack in EVP_CIPHER_flags() to check if the
cipher is a provided one, and add EVP_CIPH_FLAG_CUSTOM_CIPHER to the
flags to be returned if there is a cipher->ccipher.  That way,
provided implementations never have to set that flag themselves, all
they need to do is to include a OSSL_FUNC_CIPHER_CIPHER function.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/10137)

crypto/evp/evp_lib.c
doc/man3/EVP_EncryptInit.pod

index 3a3eec615fc4527ecf56971f48271a9c70026a7a..c567b2efee40d1083902e1d5bf2ea20ff9a2e850 100644 (file)
@@ -298,15 +298,31 @@ int EVP_Cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
                const unsigned char *in, unsigned int inl)
 {
     if (ctx->cipher->prov != NULL) {
-        size_t outl = 0;         /* ignored */
-        int blocksize = EVP_CIPHER_CTX_block_size(ctx);
+        /*
+         * If the provided implementation has a ccipher function, we use it,
+         * and translate its return value like this: 0 => -1, 1 => outlen
+         *
+         * Otherwise, we call the cupdate function if in != NULL, or cfinal
+         * if in == NULL.  Regardless of which, we return what we got.
+         */
+        int ret = -1;
+        size_t outl = 0;
+        size_t blocksize = EVP_CIPHER_CTX_block_size(ctx);
 
         if (ctx->cipher->ccipher != NULL)
-            return
-                ctx->cipher->ccipher(ctx->provctx, out, &outl,
-                                     inl + (blocksize == 1 ? 0 : blocksize),
-                                     in, (size_t)inl);
-        return 0;
+            ret =  ctx->cipher->ccipher(ctx->provctx, out, &outl,
+                                        inl + (blocksize == 1 ? 0 : blocksize),
+                                        in, (size_t)inl)
+                ? (int)outl : -1;
+        else if (in != NULL)
+            ret = ctx->cipher->cupdate(ctx->provctx, out, &outl,
+                                       inl + (blocksize == 1 ? 0 : blocksize),
+                                       in, (size_t)inl);
+        else
+            ret = ctx->cipher->cfinal(ctx->provctx, out, &outl,
+                                      blocksize == 1 ? 0 : blocksize);
+
+        return ret;
     }
 
     return ctx->cipher->do_cipher(ctx, out, in, inl);
@@ -331,6 +347,10 @@ unsigned long EVP_CIPHER_flags(const EVP_CIPHER *cipher)
     params[0] = OSSL_PARAM_construct_ulong(OSSL_CIPHER_PARAM_FLAGS, &v);
     ok = evp_do_ciph_getparams(cipher, params);
 
+    /* Provided implementations may have a custom cipher_cipher */
+    if (cipher->prov != NULL && cipher->ccipher != NULL)
+        v |= EVP_CIPH_FLAG_CUSTOM_CIPHER;
+
     return ok != 0 ? v : 0;
 }
 
index 3e668206b1bbf4cee5bbaad822677495540c17ad..722a8e3d361a70c7091bce30e97ff62f3fb30af6 100644 (file)
@@ -25,6 +25,7 @@ EVP_DecryptInit,
 EVP_DecryptFinal,
 EVP_CipherInit,
 EVP_CipherFinal,
+EVP_Cipher,
 EVP_get_cipherbyname,
 EVP_get_cipherbynid,
 EVP_get_cipherbyobj,
@@ -107,6 +108,9 @@ EVP_CIPHER_do_all_ex
                     const unsigned char *key, const unsigned char *iv, int enc);
  int EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl);
 
+ int EVP_Cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                const unsigned char *in, unsigned int inl);
+
  int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *x, int padding);
  int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen);
  int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr);
@@ -251,6 +255,15 @@ EVP_CipherFinal_ex(). In previous releases they also cleaned up
 the B<ctx>, but this is no longer done and EVP_CIPHER_CTX_clean()
 must be called to free any context resources.
 
+EVP_Cipher() encrypts or decrypts a maximum I<inl> amount of bytes from
+I<in> and leaves the result in I<out>.
+If the cipher doesn't have the flag B<EVP_CIPH_FLAG_CUSTOM_CIPHER> set,
+then I<inl> must be a multiple of EVP_CIPHER_block_size().  If it isn't,
+the result is undefined.  If the cipher has that flag set, then I<inl>
+can be any size.
+This function is historic and shouldn't be used in an application, please
+consider using EVP_CipherUpdate() and EVP_CipherFinal_ex instead.
+
 EVP_get_cipherbyname(), EVP_get_cipherbynid() and EVP_get_cipherbyobj()
 return an EVP_CIPHER structure when passed a cipher name, a NID or an
 ASN1_OBJECT structure.
@@ -388,6 +401,11 @@ EVP_DecryptFinal_ex() returns 0 if the decrypt failed or 1 for success.
 EVP_CipherInit_ex() and EVP_CipherUpdate() return 1 for success and 0 for failure.
 EVP_CipherFinal_ex() returns 0 for a decryption failure or 1 for success.
 
+EVP_Cipher() returns the amount of encrypted / decrypted bytes, or -1
+on failure, if the flag B<EVP_CIPH_FLAG_CUSTOM_CIPHER> is set for the
+cipher.  EVP_Cipher() returns 1 on success or 0 on failure, if the flag
+B<EVP_CIPH_FLAG_CUSTOM_CIPHER> is not set for the cipher.
+
 EVP_CIPHER_CTX_reset() returns 1 for success and 0 for failure.
 
 EVP_get_cipherbyname(), EVP_get_cipherbynid() and EVP_get_cipherbyobj()