From 0735a0a7b511b3ab18e4c4dcf14d62eeac22090d Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 24 Apr 2026 17:36:15 +0000 Subject: [PATCH] ocsp: bind responder authorization to CertID issuerKeyHash Addresses ZD21675 --- src/ocsp.c | 94 +++++++++++++++++++++++------------------ wolfcrypt/src/asn.c | 30 +++++++------ wolfssl/ocsp.h | 3 +- wolfssl/wolfcrypt/asn.h | 1 + 4 files changed, 73 insertions(+), 55 deletions(-) diff --git a/src/ocsp.c b/src/ocsp.c index 2c4887c97f..fefbdc0958 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -585,7 +585,7 @@ int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest, #ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK static int CheckOcspResponderChain(OcspEntry* single, byte* issuerHash, - void* vp, Signer* pendingCAs) { + byte* issuerKeyHash, void* vp, Signer* pendingCAs) { /* Attempt to build a chain up to cert's issuer */ WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; Signer* ca = NULL; @@ -602,54 +602,61 @@ static int CheckOcspResponderChain(OcspEntry* single, byte* issuerHash, * in OCSP request */ - /* Walk up the issuer chain from the responder's direct issuer toward a - * self-signed root, capturing prev before reassigning ca so the - * (ca == prev) self-signed termination check is meaningful. */ - ca = GetCAByName(cm, single->issuerHash); + if (issuerKeyHash == NULL) + return 0; + + /* Select CertID issuer by key hash so a same-DN / different-key trust + * anchor cannot hijack the starting point. */ + ca = GetCAByKeyHash(cm, single->issuerKeyHash); + if (ca != NULL && XMEMCMP(ca->subjectNameHash, single->issuerHash, + OCSP_DIGEST_SIZE) != 0) { + ca = NULL; + } #if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) if (ca == NULL && pendingCAs != NULL) { - ca = findSignerByName(pendingCAs, single->issuerHash); + ca = findSignerByKeyHash(pendingCAs, single->issuerKeyHash); + if (ca != NULL && XMEMCMP(ca->subjectNameHash, single->issuerHash, + OCSP_DIGEST_SIZE) != 0) { + ca = NULL; + } } #else (void)pendingCAs; #endif - while (ca != NULL) { - if (XMEMCMP(issuerHash, ca->issuerNameHash, OCSP_DIGEST_SIZE) == 0) { + for (; ca != NULL && ca != prev; + prev = ca) { + Signer* parent = GetCAByName(cm, ca->issuerNameHash); +#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) + if (parent == NULL && pendingCAs != NULL) { + parent = findSignerByName(pendingCAs, ca->issuerNameHash); + } +#endif + if (parent == NULL || parent == ca) + break; + + if (XMEMCMP(parent->subjectNameHash, issuerHash, + OCSP_DIGEST_SIZE) == 0 && + XMEMCMP(parent->subjectKeyHash, issuerKeyHash, + KEYID_SIZE) == 0) { WOLFSSL_MSG("\tOCSP Response signed by authorized " "responder delegated by issuer " "(found in chain)"); passed = 1; break; } - prev = ca; - ca = GetCAByName(cm, prev->issuerNameHash); -#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) - /* Look up by the in-flight name (prev's issuer), not by - * single->issuerHash; the latter would re-anchor the pendingCAs - * walk to the bottom of the chain on every iteration. */ - if (ca == NULL && pendingCAs != NULL) { - ca = findSignerByName(pendingCAs, prev->issuerNameHash); - } -#endif - if (ca == prev) { - break; - } + ca = parent; } return passed; } #endif -/** - * Enforce https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2 - * @param bs The basic OCSP response to verify - * @param subjectHash The subject key hash of the OCSP responder certificate - * @param extExtKeyUsage The extended key usage bits of the responder certificate - * @param issuerHash The issuer name hash of the OCSP responder certificate - * @param vp Unused (reserved for future use) - * @return 1 if the responder is authorized to sign the response, 0 otherwise - */ +/* Enforce https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2. Both halves + * of CertID (issuerNameHash and issuerKeyHash) must match; name-only matching + * would authorize a same-DN / different-key CA. issuerKeyHash may be NULL when + * unavailable, which disables the delegated branch. */ int CheckOcspResponder(OcspResponse *bs, byte* subjectHash, - byte extExtKeyUsage, byte* issuerHash, void* vp) + byte* subjectKeyHash, byte extExtKeyUsage, byte* issuerHash, + byte* issuerKeyHash, void* vp) { int ret = 0; OcspEntry* single; @@ -667,26 +674,31 @@ int CheckOcspResponder(OcspResponse *bs, byte* subjectHash, if (bs == NULL || subjectHash == NULL || issuerHash == NULL) return BAD_FUNC_ARG; - /* Traverse the list and check that the cert has the authority to provide - * an OCSP response for each entry. */ for (single = bs->single; single != NULL; single = single->next) { int passed = 0; - if (XMEMCMP(subjectHash, single->issuerHash, OCSP_DIGEST_SIZE) == 0) { + if (subjectKeyHash != NULL && + XMEMCMP(subjectHash, single->issuerHash, + OCSP_DIGEST_SIZE) == 0 && + XMEMCMP(subjectKeyHash, single->issuerKeyHash, + OCSP_DIGEST_SIZE) == 0) { WOLFSSL_MSG("\tOCSP Response signed by issuer"); passed = 1; } else if ((extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) != 0) { - if (XMEMCMP(issuerHash, single->issuerHash, OCSP_DIGEST_SIZE) - == 0) { + if (issuerKeyHash != NULL && + XMEMCMP(issuerHash, single->issuerHash, + OCSP_DIGEST_SIZE) == 0 && + XMEMCMP(issuerKeyHash, single->issuerKeyHash, + OCSP_DIGEST_SIZE) == 0) { WOLFSSL_MSG("\tOCSP Response signed by authorized responder " "delegated by issuer"); passed = 1; } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK else if (vp != NULL) { - passed = CheckOcspResponderChain(single, issuerHash, vp, - bs->pendingCAs); + passed = CheckOcspResponderChain(single, issuerHash, + issuerKeyHash, vp, bs->pendingCAs); } #endif } @@ -1090,8 +1102,10 @@ static int OcspVerifySigner(WOLFSSL_OCSP_BASICRESP *resp, DecodedCert *cert, } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK if ((flags & WOLFSSL_OCSP_NOCHECKS) == 0) { - ret = CheckOcspResponder(resp, c->subjectHash, c->extExtKeyUsage, - c->issuerHash, st->cm); + ret = CheckOcspResponder(resp, c->subjectHash, c->subjectKeyHash, + c->extExtKeyUsage, c->issuerHash, + (c->ca != NULL) ? c->ca->subjectKeyHash : NULL, + st->cm); } else { ret = 0; diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 6e36b997c8..0fee3cf800 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -23211,17 +23211,15 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm, } #ifdef HAVE_OCSP - if (type != CA_TYPE && - type != TRUSTED_PEER_TYPE) { - /* Need the CA's public key hash for OCSP */ - if (cert->ca) { - XMEMCPY(cert->issuerKeyHash, cert->ca->subjectKeyHash, - KEYID_SIZE); - } - else if (cert->selfSigned) { - XMEMCPY(cert->issuerKeyHash, cert->subjectKeyHash, - KEYID_SIZE); - } + /* Needed for OCSP requests, and for binding responder authorization + * to CertID's issuerKeyHash when this cert becomes a Signer. */ + if (cert->ca) { + XMEMCPY(cert->issuerKeyHash, cert->ca->subjectKeyHash, + KEYID_SIZE); + } + else if (cert->selfSigned) { + XMEMCPY(cert->issuerKeyHash, cert->subjectKeyHash, + KEYID_SIZE); } #endif /* HAVE_OCSP */ } @@ -23527,6 +23525,8 @@ int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der) #ifdef HAVE_OCSP XMEMCPY(signer->subjectKeyHash, cert->subjectKeyHash, KEYID_SIZE); + XMEMCPY(signer->issuerKeyHash, cert->issuerKeyHash, + KEYID_SIZE); #endif signer->keyUsage = cert->extKeyUsageSet ? cert->extKeyUsage : 0xFFFF; @@ -33617,7 +33617,8 @@ static int OcspRespCheck(OcspResponse *resp, Signer *responder, void* vp) return -1; ret = CheckOcspResponder(resp, responder->subjectNameHash, - responder->extKeyUsage, responder->issuerNameHash, vp); + responder->subjectKeyHash, responder->extKeyUsage, + responder->issuerNameHash, responder->issuerKeyHash, vp); if (ret != 0) return -1; @@ -33705,8 +33706,9 @@ static int OcspCheckCert(OcspResponse *resp, int noVerify, #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK if (ret == 0 && !noVerify) { - ret = CheckOcspResponder(resp, cert->subjectHash, cert->extExtKeyUsage, - cert->issuerHash, cm); + ret = CheckOcspResponder(resp, cert->subjectHash, cert->subjectKeyHash, + cert->extExtKeyUsage, cert->issuerHash, + (cert->ca != NULL) ? cert->ca->subjectKeyHash : NULL, cm); if (ret != 0) { WOLFSSL_MSG("\tOCSP Responder certificate issuer check failed"); goto err; diff --git a/wolfssl/ocsp.h b/wolfssl/ocsp.h index 525e067883..3fd03b72da 100644 --- a/wolfssl/ocsp.h +++ b/wolfssl/ocsp.h @@ -75,7 +75,8 @@ WOLFSSL_LOCAL int CheckOcspResponse(WOLFSSL_OCSP *ocsp, byte *response, int resp void* heap); WOLFSSL_LOCAL int CheckOcspResponder(OcspResponse *bs, byte* subjectHash, - byte extExtKeyUsage, byte* issuerHash, void* vp); + byte* subjectKeyHash, byte extExtKeyUsage, byte* issuerHash, + byte* issuerKeyHash, void* vp); /* Allocates and initializes a WOLFSSL_OCSP object */ WOLFSSL_API WOLFSSL_OCSP* wc_NewOCSP(WOLFSSL_CERT_MANAGER* cm); diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 8e0e1aa821..3dfd7690c2 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2228,6 +2228,7 @@ struct Signer { #endif #ifdef HAVE_OCSP byte subjectKeyHash[KEYID_SIZE]; + byte issuerKeyHash[KEYID_SIZE]; /* key hash of verifying parent CA */ #endif #if defined(WOLFSSL_AKID_NAME) || defined(HAVE_CRL) byte serialHash[SIGNER_DIGEST_SIZE]; /* serial number hash */