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;
}

Reply via email to