+
+static void *evp_cipher_from_dispatch(const OSSL_DISPATCH *fns,
+ OSSL_PROVIDER *prov)
+{
+ EVP_CIPHER *cipher = NULL;
+ int fnciphcnt = 0, fnctxcnt = 0;
+
+ /*
+ * The legacy NID is set by EVP_CIPHER_fetch() if the name exists in
+ * the object database.
+ */
+ if ((cipher = EVP_CIPHER_meth_new(0, 0, 0)) == NULL)
+ return NULL;
+
+ for (; fns->function_id != 0; fns++) {
+ switch (fns->function_id) {
+ case OSSL_FUNC_CIPHER_NEWCTX:
+ if (cipher->newctx != NULL)
+ break;
+ cipher->newctx = OSSL_get_OP_cipher_newctx(fns);
+ fnctxcnt++;
+ break;
+ case OSSL_FUNC_CIPHER_ENCRYPT_INIT:
+ if (cipher->einit != NULL)
+ break;
+ cipher->einit = OSSL_get_OP_cipher_encrypt_init(fns);
+ fnciphcnt++;
+ break;
+ case OSSL_FUNC_CIPHER_DECRYPT_INIT:
+ if (cipher->dinit != NULL)
+ break;
+ cipher->dinit = OSSL_get_OP_cipher_decrypt_init(fns);
+ fnciphcnt++;
+ break;
+ case OSSL_FUNC_CIPHER_UPDATE:
+ if (cipher->cupdate != NULL)
+ break;
+ cipher->cupdate = OSSL_get_OP_cipher_update(fns);
+ fnciphcnt++;
+ break;
+ case OSSL_FUNC_CIPHER_FINAL:
+ if (cipher->cfinal != NULL)
+ break;
+ cipher->cfinal = OSSL_get_OP_cipher_final(fns);
+ fnciphcnt++;
+ break;
+ case OSSL_FUNC_CIPHER_CIPHER:
+ if (cipher->ccipher != NULL)
+ break;
+ cipher->ccipher = OSSL_get_OP_cipher_cipher(fns);
+ break;
+ case OSSL_FUNC_CIPHER_FREECTX:
+ if (cipher->freectx != NULL)
+ break;
+ cipher->freectx = OSSL_get_OP_cipher_freectx(fns);
+ fnctxcnt++;
+ break;
+ case OSSL_FUNC_CIPHER_DUPCTX:
+ if (cipher->dupctx != NULL)
+ break;
+ cipher->dupctx = OSSL_get_OP_cipher_dupctx(fns);
+ break;
+ case OSSL_FUNC_CIPHER_KEY_LENGTH:
+ if (cipher->key_length != NULL)
+ break;
+ cipher->key_length = OSSL_get_OP_cipher_key_length(fns);
+ break;
+ case OSSL_FUNC_CIPHER_IV_LENGTH:
+ if (cipher->iv_length != NULL)
+ break;
+ cipher->iv_length = OSSL_get_OP_cipher_iv_length(fns);
+ break;
+ case OSSL_FUNC_CIPHER_BLOCK_SIZE:
+ if (cipher->blocksize != NULL)
+ break;
+ cipher->blocksize = OSSL_get_OP_cipher_block_size(fns);
+ break;
+ case OSSL_FUNC_CIPHER_GET_PARAMS:
+ if (cipher->get_params != NULL)
+ break;
+ cipher->get_params = OSSL_get_OP_cipher_get_params(fns);
+ break;
+ case OSSL_FUNC_CIPHER_CTX_GET_PARAMS:
+ if (cipher->ctx_get_params != NULL)
+ break;
+ cipher->ctx_get_params = OSSL_get_OP_cipher_ctx_get_params(fns);
+ break;
+ case OSSL_FUNC_CIPHER_CTX_SET_PARAMS:
+ if (cipher->ctx_set_params != NULL)
+ break;
+ cipher->ctx_set_params = OSSL_get_OP_cipher_ctx_set_params(fns);
+ break;
+ }
+ }
+ if ((fnciphcnt != 0 && fnciphcnt != 3 && fnciphcnt != 4)
+ || (fnciphcnt == 0 && cipher->ccipher == NULL)
+ || fnctxcnt != 2
+ || cipher->blocksize == NULL
+ || cipher->iv_length == NULL
+ || cipher->key_length == NULL) {
+ /*
+ * In order to be a consistent set of functions we must have at least
+ * a complete set of "encrypt" functions, or a complete set of "decrypt"
+ * functions, or a single "cipher" function. In all cases we need a
+ * complete set of context management functions, as well as the
+ * blocksize, iv_length and key_length functions.
+ */
+ EVP_CIPHER_meth_free(cipher);
+ EVPerr(EVP_F_EVP_CIPHER_FROM_DISPATCH, EVP_R_INVALID_PROVIDER_FUNCTIONS);
+ return NULL;
+ }
+ cipher->prov = prov;
+ if (prov != NULL)
+ ossl_provider_up_ref(prov);
+
+ return cipher;
+}
+
+static int evp_cipher_up_ref(void *cipher)
+{
+ return EVP_CIPHER_up_ref(cipher);
+}
+
+static void evp_cipher_free(void *cipher)
+{
+ EVP_CIPHER_meth_free(cipher);
+}
+
+EVP_CIPHER *EVP_CIPHER_fetch(OPENSSL_CTX *ctx, const char *algorithm,
+ const char *properties)
+{
+ EVP_CIPHER *cipher =
+ evp_generic_fetch(ctx, OSSL_OP_CIPHER, algorithm, properties,
+ evp_cipher_from_dispatch, evp_cipher_up_ref,
+ evp_cipher_free);
+
+#ifndef FIPS_MODE
+ /* TODO(3.x) get rid of the need for legacy NIDs */
+ if (cipher != NULL) {
+ /*
+ * FIPS module note: since internal fetches will be entirely
+ * provider based, we know that none of its code depends on legacy
+ * NIDs or any functionality that use them.
+ */
+ cipher->nid = OBJ_sn2nid(algorithm);
+ }
+#endif
+
+ return cipher;
+}