EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, enc) ; int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
ENGINE *impl, const unsigned char *key,
const unsigned char *iv, int enc)
{
return evp_cipher_init_internal(ctx, cipher, impl, key, iv, enc, 0, NULL);
} static int evp_cipher_init_internal(EVP_CIPHER_CTX *ctx,
const EVP_CIPHER *cipher,
ENGINE *impl, const unsigned char *key,
const unsigned char *iv, int enc,
uint8_t is_pipeline,
const OSSL_PARAM params[])
{
int n;
#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE)
ENGINE *tmpimpl = NULL;
#endif
/*
* enc == 1 means we are encrypting.
* enc == 0 means we are decrypting.
* enc == -1 means, use the previously initialised value for encrypt/decrypt
*/
if (enc == -1) {
enc = ctx->encrypt;
} else {
if (enc)
enc = 1;
ctx->encrypt = enc;
}
if (cipher == NULL && ctx->cipher == NULL) {
ERR_raise(ERR_LIB_EVP, EVP_R_NO_CIPHER_SET);
return 0;
}
/* Code below to be removed when legacy support is dropped. */
if (is_pipeline)
goto nonlegacy;
#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE)
/*
* Whether it's nice or not, "Inits" can be used on "Final"'d contexts so
* this context may already have an ENGINE! Try to avoid releasing the
* previous handle, re-querying for an ENGINE, and having a
* reinitialisation, when it may all be unnecessary.
*/
if (ctx->engine && ctx->cipher
&& (cipher == NULL || cipher->nid == ctx->cipher->nid))
goto skip_to_init;
if (cipher != NULL && impl == NULL) {
/* Ask if an ENGINE is reserved for this job */
tmpimpl = ENGINE_get_cipher_engine(cipher->nid);
}
#endif
/*
* If there are engines involved then we should use legacy handling for now.
*/
if (ctx->engine != NULL
#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE)
|| tmpimpl != NULL
#endif
|| impl != NULL
|| (cipher != NULL && cipher->origin == EVP_ORIG_METH)
|| (cipher == NULL && ctx->cipher != NULL
&& ctx->cipher->origin == EVP_ORIG_METH)) {
if (ctx->cipher == ctx->fetched_cipher)
ctx->cipher = NULL;
EVP_CIPHER_free(ctx->fetched_cipher);
ctx->fetched_cipher = NULL;
goto legacy;
}
/*
* Ensure a context left lying around from last time is cleared
* (legacy code)
*/
if (cipher != NULL && ctx->cipher != NULL) {
if (ctx->cipher->cleanup != NULL && !ctx->cipher->cleanup(ctx))
return 0;
OPENSSL_clear_free(ctx->cipher_data, ctx->cipher->ctx_size);
ctx->cipher_data = NULL;
}
/* Start of non-legacy code below */
nonlegacy:
/* Ensure a context left lying around from last time is cleared */
if (cipher != NULL && ctx->cipher != NULL) {
unsigned long flags = ctx->flags;
EVP_CIPHER_CTX_reset(ctx);
/* Restore encrypt and flags */
ctx->encrypt = enc;
ctx->flags = flags;
}
if (cipher == NULL)
cipher = ctx->cipher;
if (cipher->prov == NULL) {
#ifdef FIPS_MODULE
/* We only do explicit fetches inside the FIPS module */
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
#else
EVP_CIPHER *provciph =
EVP_CIPHER_fetch(NULL,
cipher->nid == NID_undef ? "NULL"
: OBJ_nid2sn(cipher->nid),
"");
if (provciph == NULL)
return 0;
cipher = provciph;
EVP_CIPHER_free(ctx->fetched_cipher);
ctx->fetched_cipher = provciph;
#endif
}
if (!ossl_assert(cipher->prov != NULL)) {
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
if (cipher != ctx->fetched_cipher) {
if (!EVP_CIPHER_up_ref((EVP_CIPHER *)cipher)) {
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
EVP_CIPHER_free(ctx->fetched_cipher);
/* Coverity false positive, the reference counting is confusing it */
/* coverity[use_after_free] */
ctx->fetched_cipher = (EVP_CIPHER *)cipher;
}
ctx->cipher = cipher;
if (is_pipeline && !EVP_CIPHER_can_pipeline(cipher, enc)) {
ERR_raise(ERR_LIB_EVP, EVP_R_PIPELINE_NOT_SUPPORTED);
return 0;
}
if (ctx->algctx == NULL) {
ctx->algctx = ctx->cipher->newctx(ossl_provider_ctx(cipher->prov));
if (ctx->algctx == NULL) {
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
}
if ((ctx->flags & EVP_CIPH_NO_PADDING) != 0) {
/*
* If this ctx was already set up for no padding then we need to tell
* the new cipher about it.
*/
if (!EVP_CIPHER_CTX_set_padding(ctx, 0))
return 0;
}
#ifndef FIPS_MODULE
/*
* Fix for CVE-2023-5363
* Passing in a size as part of the init call takes effect late
* so, force such to occur before the initialisation.
*
* The FIPS provider's internal library context is used in a manner
* such that this is not an issue.
*/
if (params != NULL) {
OSSL_PARAM param_lens[3] = { OSSL_PARAM_END, OSSL_PARAM_END,
OSSL_PARAM_END };
OSSL_PARAM *q = param_lens;
const OSSL_PARAM *p;
p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
if (p != NULL)
memcpy(q++, p, sizeof(*q));
/*
* Note that OSSL_CIPHER_PARAM_AEAD_IVLEN is a synonym for
* OSSL_CIPHER_PARAM_IVLEN so both are covered here.
*/
p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN);
if (p != NULL)
memcpy(q++, p, sizeof(*q));
if (q != param_lens) {
if (!EVP_CIPHER_CTX_set_params(ctx, param_lens)) {
ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_LENGTH);
return 0;
}
}
}
#endif
if (is_pipeline)
return 1;
if (enc) {
if (ctx->cipher->einit == NULL) {
/*
* We still should be able to set the IV using the new API
* if the key is not specified and old API is not available
*/
if (key == NULL && ctx->cipher->einit_skey != NULL) {
return ctx->cipher->einit_skey(ctx->algctx, NULL,
iv,
iv == NULL ? 0
: EVP_CIPHER_CTX_get_iv_length(ctx),
params);
}
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
return ctx->cipher->einit(ctx->algctx,
key,
key == NULL ? 0
: EVP_CIPHER_CTX_get_key_length(ctx),
iv,
iv == NULL ? 0
: EVP_CIPHER_CTX_get_iv_length(ctx),
params);
}
if (ctx->cipher->dinit == NULL) {
/*
* We still should be able to set the IV using the new API
* if the key is not specified and old API is not available
*/
if (key == NULL && ctx->cipher->dinit_skey != NULL) {
return ctx->cipher->dinit_skey(ctx->algctx, NULL,
iv,
iv == NULL ? 0
: EVP_CIPHER_CTX_get_iv_length(ctx),
params);
}
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
return ctx->cipher->dinit(ctx->algctx,
key,
key == NULL ? 0
: EVP_CIPHER_CTX_get_key_length(ctx),
iv,
iv == NULL ? 0
: EVP_CIPHER_CTX_get_iv_length(ctx),
params);
/* Code below to be removed when legacy support is dropped. */
legacy:
if (cipher != NULL) {
/*
* Ensure a context left lying around from last time is cleared (we
* previously attempted to avoid this if the same ENGINE and
* EVP_CIPHER could be used).
*/
if (ctx->cipher) {
unsigned long flags = ctx->flags;
EVP_CIPHER_CTX_reset(ctx);
/* Restore encrypt and flags */
ctx->encrypt = enc;
ctx->flags = flags;
}
#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE)
if (impl != NULL) {
if (!ENGINE_init(impl)) {
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
} else {
impl = tmpimpl;
}
if (impl != NULL) {
/* There's an ENGINE for this job ... (apparently) */
const EVP_CIPHER *c = ENGINE_get_cipher(impl, cipher->nid);
if (c == NULL) {
/*
* One positive side-effect of US's export control history,
* is that we should at least be able to avoid using US
* misspellings of "initialisation"?
*/
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
/* We'll use the ENGINE's private cipher definition */
cipher = c;
/*
* Store the ENGINE functional reference so we know 'cipher' came
* from an ENGINE and we need to release it when done.
*/
ctx->engine = impl;
} else {
ctx->engine = NULL;
}
#endif
ctx->cipher = cipher;
if (ctx->cipher->ctx_size) {
ctx->cipher_data = OPENSSL_zalloc(ctx->cipher->ctx_size);
if (ctx->cipher_data == NULL) {
ctx->cipher = NULL;
return 0;
}
} else {
ctx->cipher_data = NULL;
}
ctx->key_len = cipher->key_len;
/* Preserve wrap enable flag, zero everything else */
ctx->flags &= EVP_CIPHER_CTX_FLAG_WRAP_ALLOW;
if (ctx->cipher->flags & EVP_CIPH_CTRL_INIT) {
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_INIT, 0, NULL) <= 0) {
ctx->cipher = NULL;
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
return 0;
}
}
}
#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE)
skip_to_init:
#endif
if (ctx->cipher == NULL)
return 0;
/* we assume block size is a power of 2 in *cryptUpdate */
OPENSSL_assert(ctx->cipher->block_size == 1
|| ctx->cipher->block_size == 8
|| ctx->cipher->block_size == 16);
if (!(ctx->flags & EVP_CIPHER_CTX_FLAG_WRAP_ALLOW)
&& EVP_CIPHER_CTX_get_mode(ctx) == EVP_CIPH_WRAP_MODE) {
ERR_raise(ERR_LIB_EVP, EVP_R_WRAP_MODE_NOT_ALLOWED);
return 0;
}
if ((EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(ctx))
& EVP_CIPH_CUSTOM_IV) == 0) {
switch (EVP_CIPHER_CTX_get_mode(ctx)) {
case EVP_CIPH_STREAM_CIPHER:
case EVP_CIPH_ECB_MODE:
break;
case EVP_CIPH_CFB_MODE:
case EVP_CIPH_OFB_MODE:
ctx->num = 0;
/* fall-through */
case EVP_CIPH_CBC_MODE:
n = EVP_CIPHER_CTX_get_iv_length(ctx);
if (n < 0 || n > (int)sizeof(ctx->iv)) {
ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_IV_LENGTH);
return 0;
}
if (iv != NULL)
memcpy(ctx->oiv, iv, n);
memcpy(ctx->iv, ctx->oiv, n);
break;
case EVP_CIPH_CTR_MODE:
ctx->num = 0;
/* Don't reuse IV for CTR mode */
if (iv != NULL) {
n = EVP_CIPHER_CTX_get_iv_length(ctx);
if (n <= 0 || n > (int)sizeof(ctx->iv)) {
ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_IV_LENGTH);
return 0;
}
memcpy(ctx->iv, iv, n);
}
break;
default:
return 0;
}
}
if (key != NULL || (ctx->cipher->flags & EVP_CIPH_ALWAYS_CALL_INIT)) {
if (!ctx->cipher->init(ctx, key, iv, enc))
return 0;
}
ctx->buf_len = 0;
ctx->final_used = 0;
ctx->block_mask = ctx->cipher->block_size - 1;
return 1;
}