diff options
Diffstat (limited to 'crypto/evp/digest.c')
-rw-r--r-- | crypto/evp/digest.c | 903 |
1 files changed, 850 insertions, 53 deletions
diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index 01a6f251f56e..aca05186ec10 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c @@ -1,21 +1,30 @@ /* - * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved. * - * Licensed under the OpenSSL license (the "License"). You may not use + * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ +/* We need to use some engine deprecated APIs */ +#define OPENSSL_SUPPRESS_DEPRECATED + #include <stdio.h> -#include "internal/cryptlib.h" #include <openssl/objects.h> #include <openssl/evp.h> -#include <openssl/engine.h> +#include <openssl/ec.h> +#ifndef FIPS_MODULE +# include <openssl/engine.h> +#endif +#include <openssl/params.h> +#include <openssl/core_names.h> +#include "internal/cryptlib.h" +#include "internal/provider.h" +#include "internal/core.h" #include "crypto/evp.h" #include "evp_local.h" - static void cleanup_old_md_data(EVP_MD_CTX *ctx, int force) { if (ctx->digest != NULL) { @@ -31,32 +40,93 @@ static void cleanup_old_md_data(EVP_MD_CTX *ctx, int force) } } -/* This call frees resources associated with the context */ -int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) +void evp_md_ctx_clear_digest(EVP_MD_CTX *ctx, int force, int keep_fetched) { - if (ctx == NULL) - return 1; + if (ctx->algctx != NULL) { + if (ctx->digest != NULL && ctx->digest->freectx != NULL) + ctx->digest->freectx(ctx->algctx); + ctx->algctx = NULL; + EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_CLEANED); + } + + /* Code below to be removed when legacy support is dropped. */ /* * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because * sometimes only copies of the context are ever finalised. */ - cleanup_old_md_data(ctx, 0); + cleanup_old_md_data(ctx, force); + if (force) + ctx->digest = NULL; + +#if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_ENGINE) + ENGINE_finish(ctx->engine); + ctx->engine = NULL; +#endif + + /* Non legacy code, this has to be later than the ctx->digest cleaning */ + if (!keep_fetched) { + EVP_MD_free(ctx->fetched_digest); + ctx->fetched_digest = NULL; + ctx->reqdigest = NULL; + } +} + +static int evp_md_ctx_reset_ex(EVP_MD_CTX *ctx, int keep_fetched) +{ + if (ctx == NULL) + return 1; +#ifndef FIPS_MODULE /* * pctx should be freed by the user of EVP_MD_CTX * if EVP_MD_CTX_FLAG_KEEP_PKEY_CTX is set */ - if (!EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_KEEP_PKEY_CTX)) + if (!EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_KEEP_PKEY_CTX)) { EVP_PKEY_CTX_free(ctx->pctx); -#ifndef OPENSSL_NO_ENGINE - ENGINE_finish(ctx->engine); + ctx->pctx = NULL; + } #endif - OPENSSL_cleanse(ctx, sizeof(*ctx)); + + evp_md_ctx_clear_digest(ctx, 0, keep_fetched); + if (!keep_fetched) + OPENSSL_cleanse(ctx, sizeof(*ctx)); return 1; } +/* This call frees resources associated with the context */ +int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) +{ + return evp_md_ctx_reset_ex(ctx, 0); +} + +#ifndef FIPS_MODULE +EVP_MD_CTX *evp_md_ctx_new_ex(EVP_PKEY *pkey, const ASN1_OCTET_STRING *id, + OSSL_LIB_CTX *libctx, const char *propq) +{ + EVP_MD_CTX *ctx; + EVP_PKEY_CTX *pctx = NULL; + + if ((ctx = EVP_MD_CTX_new()) == NULL + || (pctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, propq)) == NULL) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (id != NULL && EVP_PKEY_CTX_set1_id(pctx, id->data, id->length) <= 0) + goto err; + + EVP_MD_CTX_set_pkey_ctx(ctx, pctx); + return ctx; + + err: + EVP_PKEY_CTX_free(pctx); + EVP_MD_CTX_free(ctx); + return NULL; +} +#endif + EVP_MD_CTX *EVP_MD_CTX_new(void) { return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); @@ -64,20 +134,63 @@ EVP_MD_CTX *EVP_MD_CTX_new(void) void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { + if (ctx == NULL) + return; + EVP_MD_CTX_reset(ctx); OPENSSL_free(ctx); } -int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type) +static int evp_md_init_internal(EVP_MD_CTX *ctx, const EVP_MD *type, + const OSSL_PARAM params[], ENGINE *impl) { - EVP_MD_CTX_reset(ctx); - return EVP_DigestInit_ex(ctx, type, NULL); -} +#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) + ENGINE *tmpimpl = NULL; +#endif + +#if !defined(FIPS_MODULE) + if (ctx->pctx != NULL + && EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx->pctx) + && ctx->pctx->op.sig.algctx != NULL) { + /* + * Prior to OpenSSL 3.0 calling EVP_DigestInit_ex() on an mdctx + * previously initialised with EVP_DigestSignInit() would retain + * information about the key, and re-initialise for another sign + * operation. So in that case we redirect to EVP_DigestSignInit() + */ + if (ctx->pctx->operation == EVP_PKEY_OP_SIGNCTX) + return EVP_DigestSignInit(ctx, NULL, type, impl, NULL); + if (ctx->pctx->operation == EVP_PKEY_OP_VERIFYCTX) + return EVP_DigestVerifyInit(ctx, NULL, type, impl, NULL); + ERR_raise(ERR_LIB_EVP, EVP_R_UPDATE_ERROR); + return 0; + } +#endif -int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) -{ EVP_MD_CTX_clear_flags(ctx, EVP_MD_CTX_FLAG_CLEANED); -#ifndef OPENSSL_NO_ENGINE + + if (ctx->algctx != NULL) { + if (!ossl_assert(ctx->digest != NULL)) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + return 0; + } + if (ctx->digest->freectx != NULL) + ctx->digest->freectx(ctx->algctx); + ctx->algctx = NULL; + } + + if (type != NULL) { + ctx->reqdigest = type; + } else { + if (ctx->digest == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_NO_DIGEST_SET); + return 0; + } + type = ctx->digest; + } + + /* Code below to be removed when legacy support is dropped. */ +#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 @@ -88,28 +201,114 @@ int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) (type == NULL || (type->type == ctx->digest->type))) goto skip_to_init; - if (type) { + if (type != NULL) { /* * Ensure an ENGINE left lying around from last time is cleared (the * previous check attempted to avoid this if the same ENGINE and * EVP_MD could be used). */ ENGINE_finish(ctx->engine); + ctx->engine = NULL; + } + + if (type != NULL && impl == NULL) + tmpimpl = ENGINE_get_digest_engine(type->type); +#endif + + /* + * If there are engines involved or EVP_MD_CTX_FLAG_NO_INIT is set then we + * should use legacy handling for now. + */ + if (ctx->engine != NULL + || impl != NULL +#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) + || tmpimpl != NULL +#endif + || (ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) != 0 + || (type != NULL && type->origin == EVP_ORIG_METH) + || (type == NULL && ctx->digest != NULL + && ctx->digest->origin == EVP_ORIG_METH)) { + if (ctx->digest == ctx->fetched_digest) + ctx->digest = NULL; + EVP_MD_free(ctx->fetched_digest); + ctx->fetched_digest = NULL; + goto legacy; + } + + cleanup_old_md_data(ctx, 1); + + /* Start of non-legacy code below */ + + if (type->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 + /* The NULL digest is a special case */ + EVP_MD *provmd = EVP_MD_fetch(NULL, + type->type != NID_undef ? OBJ_nid2sn(type->type) + : "NULL", ""); + + if (provmd == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + return 0; + } + type = provmd; + EVP_MD_free(ctx->fetched_digest); + ctx->fetched_digest = provmd; +#endif + } + + if (ctx->algctx != NULL && ctx->digest != NULL && ctx->digest != type) { + if (ctx->digest->freectx != NULL) + ctx->digest->freectx(ctx->algctx); + ctx->algctx = NULL; + } + if (type->prov != NULL && ctx->fetched_digest != type) { + if (!EVP_MD_up_ref((EVP_MD *)type)) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + return 0; + } + EVP_MD_free(ctx->fetched_digest); + ctx->fetched_digest = (EVP_MD *)type; + } + ctx->digest = type; + if (ctx->algctx == NULL) { + ctx->algctx = ctx->digest->newctx(ossl_provider_ctx(type->prov)); + if (ctx->algctx == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + return 0; + } + } + + if (ctx->digest->dinit == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + return 0; + } + + return ctx->digest->dinit(ctx->algctx, params); + + /* Code below to be removed when legacy support is dropped. */ + legacy: + +#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) + if (type) { if (impl != NULL) { if (!ENGINE_init(impl)) { - EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_INITIALIZATION_ERROR); + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); return 0; } } else { /* Ask if an ENGINE is reserved for this job */ - impl = ENGINE_get_digest_engine(type->type); + impl = tmpimpl; } if (impl != NULL) { /* There's an ENGINE for this job ... (apparently) */ const EVP_MD *d = ENGINE_get_digest(impl, type->type); if (d == NULL) { - EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_INITIALIZATION_ERROR); + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); ENGINE_finish(impl); return 0; } @@ -122,12 +321,6 @@ int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) ctx->engine = impl; } else ctx->engine = NULL; - } else { - if (!ctx->digest) { - EVPerr(EVP_F_EVP_DIGESTINIT_EX, EVP_R_NO_DIGEST_SET); - return 0; - } - type = ctx->digest; } #endif if (ctx->digest != type) { @@ -138,32 +331,85 @@ int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) ctx->update = type->update; ctx->md_data = OPENSSL_zalloc(type->ctx_size); if (ctx->md_data == NULL) { - EVPerr(EVP_F_EVP_DIGESTINIT_EX, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE); return 0; } } } -#ifndef OPENSSL_NO_ENGINE +#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) skip_to_init: #endif - if (ctx->pctx) { +#ifndef FIPS_MODULE + if (ctx->pctx != NULL + && (!EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx->pctx) + || ctx->pctx->op.sig.signature == NULL)) { int r; r = EVP_PKEY_CTX_ctrl(ctx->pctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_DIGESTINIT, 0, ctx); if (r <= 0 && (r != -2)) return 0; } +#endif if (ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) return 1; return ctx->digest->init(ctx); } +int EVP_DigestInit_ex2(EVP_MD_CTX *ctx, const EVP_MD *type, + const OSSL_PARAM params[]) +{ + return evp_md_init_internal(ctx, type, params, NULL); +} + +int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type) +{ + EVP_MD_CTX_reset(ctx); + return evp_md_init_internal(ctx, type, NULL, NULL); +} + +int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl) +{ + return evp_md_init_internal(ctx, type, NULL, impl); +} + int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, size_t count) { if (count == 0) return 1; - return ctx->update(ctx, data, count); + if (ctx->pctx != NULL + && EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx->pctx) + && ctx->pctx->op.sig.algctx != NULL) { + /* + * Prior to OpenSSL 3.0 EVP_DigestSignUpdate() and + * EVP_DigestVerifyUpdate() were just macros for EVP_DigestUpdate(). + * Some code calls EVP_DigestUpdate() directly even when initialised + * with EVP_DigestSignInit_ex() or + * EVP_DigestVerifyInit_ex(), so we detect that and redirect to + * the correct EVP_Digest*Update() function + */ + if (ctx->pctx->operation == EVP_PKEY_OP_SIGNCTX) + return EVP_DigestSignUpdate(ctx, data, count); + if (ctx->pctx->operation == EVP_PKEY_OP_VERIFYCTX) + return EVP_DigestVerifyUpdate(ctx, data, count); + ERR_raise(ERR_LIB_EVP, EVP_R_UPDATE_ERROR); + return 0; + } + + if (ctx->digest == NULL + || ctx->digest->prov == NULL + || (ctx->flags & EVP_MD_CTX_FLAG_NO_INIT) != 0) + goto legacy; + + if (ctx->digest->dupdate == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_UPDATE_ERROR); + return 0; + } + return ctx->digest->dupdate(ctx->algctx, data, count); + + /* Code below to be removed when legacy support is dropped. */ + legacy: + return ctx->update != NULL ? ctx->update(ctx, data, count) : 0; } /* The caller can assume that this removes any secret data from the context */ @@ -176,14 +422,46 @@ int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *size) } /* The caller can assume that this removes any secret data from the context */ -int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *size) +int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *isize) { - int ret; + int ret, sz; + size_t size = 0; + size_t mdsize = 0; + + if (ctx->digest == NULL) + return 0; + + sz = EVP_MD_get_size(ctx->digest); + if (sz < 0) + return 0; + mdsize = sz; + if (ctx->digest->prov == NULL) + goto legacy; + + if (ctx->digest->dfinal == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_FINAL_ERROR); + return 0; + } + + ret = ctx->digest->dfinal(ctx->algctx, md, &size, mdsize); + + if (isize != NULL) { + if (size <= UINT_MAX) { + *isize = (unsigned int)size; + } else { + ERR_raise(ERR_LIB_EVP, EVP_R_FINAL_ERROR); + ret = 0; + } + } - OPENSSL_assert(ctx->digest->md_size <= EVP_MAX_MD_SIZE); + return ret; + + /* Code below to be removed when legacy support is dropped. */ + legacy: + OPENSSL_assert(mdsize <= EVP_MAX_MD_SIZE); ret = ctx->digest->final(ctx, md); - if (size != NULL) - *size = ctx->digest->md_size; + if (isize != NULL) + *isize = mdsize; if (ctx->digest->cleanup) { ctx->digest->cleanup(ctx); EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_CLEANED); @@ -195,19 +473,42 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *size) int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t size) { int ret = 0; + OSSL_PARAM params[2]; + size_t i = 0; + if (ctx->digest == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_NULL_ALGORITHM); + return 0; + } + + if (ctx->digest->prov == NULL) + goto legacy; + + if (ctx->digest->dfinal == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_FINAL_ERROR); + return 0; + } + + params[i++] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &size); + params[i++] = OSSL_PARAM_construct_end(); + + if (EVP_MD_CTX_set_params(ctx, params) > 0) + ret = ctx->digest->dfinal(ctx->algctx, md, &size, size); + + return ret; + +legacy: if (ctx->digest->flags & EVP_MD_FLAG_XOF && size <= INT_MAX && ctx->digest->md_ctrl(ctx, EVP_MD_CTRL_XOF_LEN, (int)size, NULL)) { ret = ctx->digest->final(ctx, md); - if (ctx->digest->cleanup != NULL) { ctx->digest->cleanup(ctx); EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_CLEANED); } OPENSSL_cleanse(ctx->md_data, ctx->digest->ctx_size); } else { - EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH); + ERR_raise(ERR_LIB_EVP, EVP_R_NOT_XOF_OR_INVALID_LENGTH); } return ret; @@ -221,15 +522,74 @@ int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in) int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) { + int digest_change = 0; unsigned char *tmp_buf; - if ((in == NULL) || (in->digest == NULL)) { - EVPerr(EVP_F_EVP_MD_CTX_COPY_EX, EVP_R_INPUT_NOT_INITIALIZED); + + if (in == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER); return 0; } -#ifndef OPENSSL_NO_ENGINE + + if (in->digest == NULL) { + /* copying uninitialized digest context */ + EVP_MD_CTX_reset(out); + if (out->fetched_digest != NULL) + EVP_MD_free(out->fetched_digest); + *out = *in; + goto clone_pkey; + } + + if (in->digest->prov == NULL + || (in->flags & EVP_MD_CTX_FLAG_NO_INIT) != 0) + goto legacy; + + if (in->digest->dupctx == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX); + return 0; + } + + evp_md_ctx_reset_ex(out, 1); + digest_change = (out->fetched_digest != in->fetched_digest); + if (digest_change && out->fetched_digest != NULL) + EVP_MD_free(out->fetched_digest); + *out = *in; + /* NULL out pointers in case of error */ + out->pctx = NULL; + out->algctx = NULL; + + if (digest_change && in->fetched_digest != NULL) + EVP_MD_up_ref(in->fetched_digest); + + if (in->algctx != NULL) { + out->algctx = in->digest->dupctx(in->algctx); + if (out->algctx == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX); + return 0; + } + } + + clone_pkey: + /* copied EVP_MD_CTX should free the copied EVP_PKEY_CTX */ + EVP_MD_CTX_clear_flags(out, EVP_MD_CTX_FLAG_KEEP_PKEY_CTX); +#ifndef FIPS_MODULE + if (in->pctx != NULL) { + out->pctx = EVP_PKEY_CTX_dup(in->pctx); + if (out->pctx == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_NOT_ABLE_TO_COPY_CTX); + EVP_MD_CTX_reset(out); + return 0; + } + } +#endif + + return 1; + + /* Code below to be removed when legacy support is dropped. */ + legacy: +#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) /* Make sure it's safe to copy a digest context using an ENGINE */ if (in->engine && !ENGINE_init(in->engine)) { - EVPerr(EVP_F_EVP_MD_CTX_COPY_EX, ERR_R_ENGINE_LIB); + ERR_raise(ERR_LIB_EVP, ERR_R_ENGINE_LIB); return 0; } #endif @@ -258,7 +618,7 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) else { out->md_data = OPENSSL_malloc(out->digest->ctx_size); if (out->md_data == NULL) { - EVPerr(EVP_F_EVP_MD_CTX_COPY_EX, ERR_R_MALLOC_FAILURE); + ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE); return 0; } } @@ -267,6 +627,7 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) out->update = in->update; +#ifndef FIPS_MODULE if (in->pctx) { out->pctx = EVP_PKEY_CTX_dup(in->pctx); if (!out->pctx) { @@ -274,6 +635,7 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) return 0; } } +#endif if (out->digest->copy) return out->digest->copy(out, in); @@ -299,13 +661,448 @@ int EVP_Digest(const void *data, size_t count, return ret; } -int EVP_MD_CTX_ctrl(EVP_MD_CTX *ctx, int cmd, int p1, void *p2) +int EVP_Q_digest(OSSL_LIB_CTX *libctx, const char *name, const char *propq, + const void *data, size_t datalen, + unsigned char *md, size_t *mdlen) { - if (ctx->digest && ctx->digest->md_ctrl) { - int ret = ctx->digest->md_ctrl(ctx, cmd, p1, p2); - if (ret <= 0) - return 0; - return 1; + EVP_MD *digest = EVP_MD_fetch(libctx, name, propq); + unsigned int temp = 0; + int ret = 0; + + if (digest != NULL) { + ret = EVP_Digest(data, datalen, md, &temp, digest, NULL); + EVP_MD_free(digest); + } + if (mdlen != NULL) + *mdlen = temp; + return ret; +} + +int EVP_MD_get_params(const EVP_MD *digest, OSSL_PARAM params[]) +{ + if (digest != NULL && digest->get_params != NULL) + return digest->get_params(params); + return 0; +} + +const OSSL_PARAM *EVP_MD_gettable_params(const EVP_MD *digest) +{ + if (digest != NULL && digest->gettable_params != NULL) + return digest->gettable_params( + ossl_provider_ctx(EVP_MD_get0_provider(digest))); + return NULL; +} + +int EVP_MD_CTX_set_params(EVP_MD_CTX *ctx, const OSSL_PARAM params[]) +{ + EVP_PKEY_CTX *pctx = ctx->pctx; + + /* If we have a pctx then we should try that first */ + if (pctx != NULL + && (pctx->operation == EVP_PKEY_OP_VERIFYCTX + || pctx->operation == EVP_PKEY_OP_SIGNCTX) + && pctx->op.sig.algctx != NULL + && pctx->op.sig.signature->set_ctx_md_params != NULL) + return pctx->op.sig.signature->set_ctx_md_params(pctx->op.sig.algctx, + params); + + if (ctx->digest != NULL && ctx->digest->set_ctx_params != NULL) + return ctx->digest->set_ctx_params(ctx->algctx, params); + + return 0; +} + +const OSSL_PARAM *EVP_MD_settable_ctx_params(const EVP_MD *md) +{ + void *provctx; + + if (md != NULL && md->settable_ctx_params != NULL) { + provctx = ossl_provider_ctx(EVP_MD_get0_provider(md)); + return md->settable_ctx_params(NULL, provctx); + } + return NULL; +} + +const OSSL_PARAM *EVP_MD_CTX_settable_params(EVP_MD_CTX *ctx) +{ + EVP_PKEY_CTX *pctx; + void *alg; + + if (ctx == NULL) + return NULL; + + /* If we have a pctx then we should try that first */ + pctx = ctx->pctx; + if (pctx != NULL + && (pctx->operation == EVP_PKEY_OP_VERIFYCTX + || pctx->operation == EVP_PKEY_OP_SIGNCTX) + && pctx->op.sig.algctx != NULL + && pctx->op.sig.signature->settable_ctx_md_params != NULL) + return pctx->op.sig.signature->settable_ctx_md_params( + pctx->op.sig.algctx); + + if (ctx->digest != NULL && ctx->digest->settable_ctx_params != NULL) { + alg = ossl_provider_ctx(EVP_MD_get0_provider(ctx->digest)); + return ctx->digest->settable_ctx_params(ctx->algctx, alg); } + + return NULL; +} + +int EVP_MD_CTX_get_params(EVP_MD_CTX *ctx, OSSL_PARAM params[]) +{ + EVP_PKEY_CTX *pctx = ctx->pctx; + + /* If we have a pctx then we should try that first */ + if (pctx != NULL + && (pctx->operation == EVP_PKEY_OP_VERIFYCTX + || pctx->operation == EVP_PKEY_OP_SIGNCTX) + && pctx->op.sig.algctx != NULL + && pctx->op.sig.signature->get_ctx_md_params != NULL) + return pctx->op.sig.signature->get_ctx_md_params(pctx->op.sig.algctx, + params); + + if (ctx->digest != NULL && ctx->digest->get_ctx_params != NULL) + return ctx->digest->get_ctx_params(ctx->algctx, params); + return 0; } + +const OSSL_PARAM *EVP_MD_gettable_ctx_params(const EVP_MD *md) +{ + void *provctx; + + if (md != NULL && md->gettable_ctx_params != NULL) { + provctx = ossl_provider_ctx(EVP_MD_get0_provider(md)); + return md->gettable_ctx_params(NULL, provctx); + } + return NULL; +} + +const OSSL_PARAM *EVP_MD_CTX_gettable_params(EVP_MD_CTX *ctx) +{ + EVP_PKEY_CTX *pctx; + void *provctx; + + if (ctx == NULL) + return NULL; + + /* If we have a pctx then we should try that first */ + pctx = ctx->pctx; + if (pctx != NULL + && (pctx->operation == EVP_PKEY_OP_VERIFYCTX + || pctx->operation == EVP_PKEY_OP_SIGNCTX) + && pctx->op.sig.algctx != NULL + && pctx->op.sig.signature->gettable_ctx_md_params != NULL) + return pctx->op.sig.signature->gettable_ctx_md_params( + pctx->op.sig.algctx); + + if (ctx->digest != NULL && ctx->digest->gettable_ctx_params != NULL) { + provctx = ossl_provider_ctx(EVP_MD_get0_provider(ctx->digest)); + return ctx->digest->gettable_ctx_params(ctx->algctx, provctx); + } + return NULL; +} + +int EVP_MD_CTX_ctrl(EVP_MD_CTX *ctx, int cmd, int p1, void *p2) +{ + int ret = EVP_CTRL_RET_UNSUPPORTED; + int set_params = 1; + size_t sz; + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + + if (ctx == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (ctx->digest != NULL && ctx->digest->prov == NULL) + goto legacy; + + switch (cmd) { + case EVP_MD_CTRL_XOF_LEN: + sz = (size_t)p1; + params[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &sz); + break; + case EVP_MD_CTRL_MICALG: + set_params = 0; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_DIGEST_PARAM_MICALG, + p2, p1 ? p1 : 9999); + break; + case EVP_CTRL_SSL3_MASTER_SECRET: + params[0] = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_SSL3_MS, + p2, p1); + break; + default: + goto conclude; + } + + if (set_params) + ret = EVP_MD_CTX_set_params(ctx, params); + else + ret = EVP_MD_CTX_get_params(ctx, params); + goto conclude; + + + /* Code below to be removed when legacy support is dropped. */ + legacy: + if (ctx->digest->md_ctrl == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_CTRL_NOT_IMPLEMENTED); + return 0; + } + + ret = ctx->digest->md_ctrl(ctx, cmd, p1, p2); + conclude: + if (ret <= 0) + return 0; + return ret; +} + +EVP_MD *evp_md_new(void) +{ + EVP_MD *md = OPENSSL_zalloc(sizeof(*md)); + + if (md != NULL) { + md->lock = CRYPTO_THREAD_lock_new(); + if (md->lock == NULL) { + OPENSSL_free(md); + return NULL; + } + md->refcnt = 1; + } + return md; +} + +/* + * 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. + */ +#ifndef FIPS_MODULE +static void set_legacy_nid(const char *name, void *vlegacy_nid) +{ + int nid; + int *legacy_nid = vlegacy_nid; + /* + * We use lowest level function to get the associated method, because + * higher level functions such as EVP_get_digestbyname() have changed + * to look at providers too. + */ + const void *legacy_method = OBJ_NAME_get(name, OBJ_NAME_TYPE_MD_METH); + + if (*legacy_nid == -1) /* We found a clash already */ + return; + + if (legacy_method == NULL) + return; + nid = EVP_MD_nid(legacy_method); + if (*legacy_nid != NID_undef && *legacy_nid != nid) { + *legacy_nid = -1; + return; + } + *legacy_nid = nid; +} +#endif + +static int evp_md_cache_constants(EVP_MD *md) +{ + int ok, xof = 0, algid_absent = 0; + size_t blksz = 0; + size_t mdsize = 0; + OSSL_PARAM params[5]; + + params[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_BLOCK_SIZE, &blksz); + params[1] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_SIZE, &mdsize); + params[2] = OSSL_PARAM_construct_int(OSSL_DIGEST_PARAM_XOF, &xof); + params[3] = OSSL_PARAM_construct_int(OSSL_DIGEST_PARAM_ALGID_ABSENT, + &algid_absent); + params[4] = OSSL_PARAM_construct_end(); + ok = evp_do_md_getparams(md, params) > 0; + if (mdsize > INT_MAX || blksz > INT_MAX) + ok = 0; + if (ok) { + md->block_size = (int)blksz; + md->md_size = (int)mdsize; + if (xof) + md->flags |= EVP_MD_FLAG_XOF; + if (algid_absent) + md->flags |= EVP_MD_FLAG_DIGALGID_ABSENT; + } + return ok; +} + +static void *evp_md_from_algorithm(int name_id, + const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov) +{ + const OSSL_DISPATCH *fns = algodef->implementation; + EVP_MD *md = NULL; + int fncnt = 0; + + /* EVP_MD_fetch() will set the legacy NID if available */ + if ((md = evp_md_new()) == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE); + return NULL; + } + +#ifndef FIPS_MODULE + md->type = NID_undef; + if (!evp_names_do_all(prov, name_id, set_legacy_nid, &md->type) + || md->type == -1) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + EVP_MD_free(md); + return NULL; + } +#endif + + md->name_id = name_id; + if ((md->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL) { + EVP_MD_free(md); + return NULL; + } + md->description = algodef->algorithm_description; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_FUNC_DIGEST_NEWCTX: + if (md->newctx == NULL) { + md->newctx = OSSL_FUNC_digest_newctx(fns); + fncnt++; + } + break; + case OSSL_FUNC_DIGEST_INIT: + if (md->dinit == NULL) { + md->dinit = OSSL_FUNC_digest_init(fns); + fncnt++; + } + break; + case OSSL_FUNC_DIGEST_UPDATE: + if (md->dupdate == NULL) { + md->dupdate = OSSL_FUNC_digest_update(fns); + fncnt++; + } + break; + case OSSL_FUNC_DIGEST_FINAL: + if (md->dfinal == NULL) { + md->dfinal = OSSL_FUNC_digest_final(fns); + fncnt++; + } + break; + case OSSL_FUNC_DIGEST_DIGEST: + if (md->digest == NULL) + md->digest = OSSL_FUNC_digest_digest(fns); + /* We don't increment fnct for this as it is stand alone */ + break; + case OSSL_FUNC_DIGEST_FREECTX: + if (md->freectx == NULL) { + md->freectx = OSSL_FUNC_digest_freectx(fns); + fncnt++; + } + break; + case OSSL_FUNC_DIGEST_DUPCTX: + if (md->dupctx == NULL) + md->dupctx = OSSL_FUNC_digest_dupctx(fns); + break; + case OSSL_FUNC_DIGEST_GET_PARAMS: + if (md->get_params == NULL) + md->get_params = OSSL_FUNC_digest_get_params(fns); + break; + case OSSL_FUNC_DIGEST_SET_CTX_PARAMS: + if (md->set_ctx_params == NULL) + md->set_ctx_params = OSSL_FUNC_digest_set_ctx_params(fns); + break; + case OSSL_FUNC_DIGEST_GET_CTX_PARAMS: + if (md->get_ctx_params == NULL) + md->get_ctx_params = OSSL_FUNC_digest_get_ctx_params(fns); + break; + case OSSL_FUNC_DIGEST_GETTABLE_PARAMS: + if (md->gettable_params == NULL) + md->gettable_params = OSSL_FUNC_digest_gettable_params(fns); + break; + case OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS: + if (md->settable_ctx_params == NULL) + md->settable_ctx_params = + OSSL_FUNC_digest_settable_ctx_params(fns); + break; + case OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS: + if (md->gettable_ctx_params == NULL) + md->gettable_ctx_params = + OSSL_FUNC_digest_gettable_ctx_params(fns); + break; + } + } + if ((fncnt != 0 && fncnt != 5) + || (fncnt == 0 && md->digest == NULL)) { + /* + * In order to be a consistent set of functions we either need the + * whole set of init/update/final etc functions or none of them. + * The "digest" function can standalone. We at least need one way to + * generate digests. + */ + EVP_MD_free(md); + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS); + return NULL; + } + md->prov = prov; + if (prov != NULL) + ossl_provider_up_ref(prov); + + if (!evp_md_cache_constants(md)) { + EVP_MD_free(md); + ERR_raise(ERR_LIB_EVP, EVP_R_CACHE_CONSTANTS_FAILED); + md = NULL; + } + + return md; +} + +static int evp_md_up_ref(void *md) +{ + return EVP_MD_up_ref(md); +} + +static void evp_md_free(void *md) +{ + EVP_MD_free(md); +} + +EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, + const char *properties) +{ + EVP_MD *md = + evp_generic_fetch(ctx, OSSL_OP_DIGEST, algorithm, properties, + evp_md_from_algorithm, evp_md_up_ref, evp_md_free); + + return md; +} + +int EVP_MD_up_ref(EVP_MD *md) +{ + int ref = 0; + + if (md->origin == EVP_ORIG_DYNAMIC) + CRYPTO_UP_REF(&md->refcnt, &ref, md->lock); + return 1; +} + +void EVP_MD_free(EVP_MD *md) +{ + int i; + + if (md == NULL || md->origin != EVP_ORIG_DYNAMIC) + return; + + CRYPTO_DOWN_REF(&md->refcnt, &i, md->lock); + if (i > 0) + return; + evp_md_free_int(md); +} + +void EVP_MD_do_all_provided(OSSL_LIB_CTX *libctx, + void (*fn)(EVP_MD *mac, void *arg), + void *arg) +{ + evp_generic_do_all(libctx, OSSL_OP_DIGEST, + (void (*)(void *, void *))fn, arg, + evp_md_from_algorithm, evp_md_up_ref, evp_md_free); +} |