Correction: my rationale for this patch was wrong, and so was the probe I
attached.
Implicit static digests DO reach providers. evp_md_init_internal() sees
type->prov == NULL and re-fetches the MD by name, so EVP_sha256() passed to
EVP_DigestInit_ex ends up provider-backed. My probe missed that because
EVP_MD_CTX_get0_md() returns ctx->reqdigest, the MD passed in, not the one
used. Please disregard provider_probe.c.
The corrected probe is attached. With the default property query set to
provider=legacy, which has no SHA256, EVP_DigestInit_ex(ctx, EVP_sha256())
fails. It could not if that path were served by a built-in.
So this patch is not a bypass fix. What survives: the internal fetch
hardcodes libctx=NULL and propq="", so a non-default OSSL_LIB_CTX gets no
provider control and no property query is expressible, and a registered ENGINE
takes the legacy branch and bypasses providers outright. Control and clarity,
not bypass.
Patch 1 (HMAC via EVP_MAC) is unaffected. HMAC_CTX does the ipad/opad
construction in OpenSSL's own code and delegates only the digest, so a
provider's HMAC is genuinely never consulted.
Repost on the narrower basis, or withdraw? The same correction applies to the
channel binding patch [1].
[1]
https://postgr.es/m/178596055550.1584328.5465570319340175126%40reviewcommit.com
--
Mark
/* Does EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) reach a provider on 3.0?
* Discriminator: constrain the DEFAULT PROPERTIES to a provider that lacks
* SHA256. If the implicit path is provider-mediated, init must FAIL. */
#include <stdio.h>
#include <openssl/evp.h>
#include <openssl/provider.h>
static const char *pn(const OSSL_PROVIDER *p)
{ return p ? OSSL_PROVIDER_get0_name(p) : "NO PROVIDER"; }
static int try_init(void)
{
EVP_MD_CTX *c = EVP_MD_CTX_new();
int ok = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
EVP_MD_CTX_free(c);
return ok;
}
int main(void)
{
EVP_MD *f;
EVP_MD_CTX *c;
printf("Runtime: %s\n\n", OpenSSL_version(OPENSSL_VERSION));
printf("A. baseline DigestInit_ex(EVP_sha256()) : %s\n",
try_init() ? "OK" : "FAILED");
/* The measurement artifact our original probe hit. */
c = EVP_MD_CTX_new();
EVP_DigestInit_ex(c, EVP_sha256(), NULL);
printf("B. get0_md provider after that init : %s\n",
pn(EVP_MD_get0_provider(EVP_MD_CTX_get0_md(c))));
printf(" (get0_md returns ctx->reqdigest = the STATIC md we passed,\n"
" NOT what OpenSSL actually used -- so B proves nothing)\n");
EVP_MD_CTX_free(c);
/* Now constrain default properties to the legacy provider. */
if (!OSSL_PROVIDER_load(NULL, "legacy"))
printf(" (warning: legacy provider failed to load)\n");
if (!EVP_set_default_properties(NULL, "provider=legacy"))
printf(" (warning: could not set default properties)\n");
f = EVP_MD_fetch(NULL, "SHA256", NULL);
printf("C. explicit EVP_MD_fetch under provider=legacy: %s\n",
f ? "OK" : "FAILED (legacy has no SHA256)");
EVP_MD_free(f);
printf("D. DigestInit_ex(EVP_sha256()) under same : %s\n",
try_init() ? "OK -> implicit path IGNORES providers"
: "FAILED -> implicit path IS provider-mediated");
return 0;
}