ocsp: bind responder authorization to CertID issuerKeyHash

Addresses ZD21675
This commit is contained in:
Juliusz Sosinowicz
2026-04-24 17:36:15 +00:00
parent d7bdfd3e90
commit 0735a0a7b5
4 changed files with 73 additions and 55 deletions
+54 -40
View File
@@ -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;
+16 -14
View File
@@ -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;
+2 -1
View File
@@ -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);
+1
View File
@@ -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 */