/* ocsp.c * * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include /* Name change compatibility layer no longer needs to be included here */ /* * WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK: * Disable looking for an authorized responder in the verification path of * the issuer. This will make the authorized responder only look at the * OCSP response signer and direct issuer. */ /* * OCSP responder missing features: * - Support for multiple requests and responses in a single OCSP exchange * - Support name-based responder ID * - Support for singleExtensions */ #ifndef WOLFCRYPT_ONLY #ifdef HAVE_OCSP #include #include #include #ifdef NO_INLINE #include #else #define WOLFSSL_MISC_INCLUDED #include #endif /* Allocates and initializes a WOLFSSL_OCSP object. Returns pointer on success, NULL on failure. */ WOLFSSL_OCSP* wc_NewOCSP(WOLFSSL_CERT_MANAGER* cm) { WOLFSSL_OCSP* ocsp = NULL; ocsp = (WOLFSSL_OCSP*)XMALLOC(sizeof(WOLFSSL_OCSP), cm ? cm->heap : NULL, DYNAMIC_TYPE_OCSP); if (ocsp == NULL) return NULL; if (InitOCSP(ocsp, cm) != 0) { XFREE(ocsp, cm ? cm->heap : NULL, DYNAMIC_TYPE_OCSP); return NULL; } return ocsp; } /* Frees a WOLFSSL_OCSP object allocated by wc_NewOCSP. */ void wc_FreeOCSP(WOLFSSL_OCSP* ocsp) { if (ocsp) { FreeOCSP(ocsp, 1); } } int wc_CheckCertOcspResponse(WOLFSSL_OCSP *ocsp, DecodedCert *cert, byte *response, int responseSz, void* heap) { int ret = WC_NO_ERR_TRACE(ASN_OCSP_CONFIRM_E); #ifdef WOLFSSL_SMALL_STACK OcspRequest* ocspRequest; #else OcspRequest ocspRequest[1]; #endif #ifdef WOLFSSL_SMALL_STACK ocspRequest = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ocspRequest == NULL) { WOLFSSL_LEAVE("wc_CheckCertOcspResponse", MEMORY_ERROR); return MEMORY_E; } #endif if (InitOcspRequest(ocspRequest, cert, ocsp->cm->ocspSendNonce, ocsp->cm->heap) == 0) { ret = CheckOcspResponse(ocsp, response, responseSz, NULL, NULL, NULL, ocspRequest, heap); FreeOcspRequest(ocspRequest); } #ifdef WOLFSSL_SMALL_STACK XFREE(ocspRequest, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif WOLFSSL_LEAVE("wc_CheckCertOcspResponse", ret); return ret; } int InitOCSP(WOLFSSL_OCSP* ocsp, WOLFSSL_CERT_MANAGER* cm) { WOLFSSL_ENTER("InitOCSP"); ForceZero(ocsp, sizeof(WOLFSSL_OCSP)); if (wc_InitMutex(&ocsp->ocspLock) != 0) return BAD_MUTEX_E; ocsp->cm = cm; return 0; } static int InitOcspEntry(OcspEntry* entry, OcspRequest* request) { WOLFSSL_ENTER("InitOcspEntry"); ForceZero(entry, sizeof(OcspEntry)); XMEMCPY(entry->issuerHash, request->issuerHash, OCSP_DIGEST_SIZE); XMEMCPY(entry->issuerKeyHash, request->issuerKeyHash, OCSP_DIGEST_SIZE); return 0; } static void FreeOcspEntry(OcspEntry* entry, void* heap) { CertStatus *status, *next; if (entry == NULL || !entry->ownStatus) return; WOLFSSL_ENTER("FreeOcspEntry"); for (status = entry->status; status; status = next) { next = status->next; XFREE(status->rawOcspResponse, heap, DYNAMIC_TYPE_OCSP_STATUS); #ifdef OPENSSL_EXTRA if (status->serialInt) { if (status->serialInt->isDynamic) { XFREE(status->serialInt->data, NULL, DYNAMIC_TYPE_OPENSSL); } XFREE(status->serialInt, NULL, DYNAMIC_TYPE_OPENSSL); } status->serialInt = NULL; #endif XFREE(status, heap, DYNAMIC_TYPE_OCSP_STATUS); } (void)heap; } void FreeOCSP(WOLFSSL_OCSP* ocsp, int dynamic) { OcspEntry *entry, *next; void* heap = (ocsp->cm != NULL) ? ocsp->cm->heap : NULL; WOLFSSL_ENTER("FreeOCSP"); for (entry = ocsp->ocspList; entry; entry = next) { next = entry->next; FreeOcspEntry(entry, heap); XFREE(entry, heap, DYNAMIC_TYPE_OCSP_ENTRY); } wc_FreeMutex(&ocsp->ocspLock); if (dynamic) XFREE(ocsp, heap, DYNAMIC_TYPE_OCSP); } static int xstat2err(int st) { switch (st) { case CERT_GOOD: return 0; case CERT_REVOKED: return OCSP_CERT_REVOKED; default: return OCSP_CERT_UNKNOWN; } } int CheckCertOCSP_ex(WOLFSSL_OCSP* ocsp, DecodedCert* cert, WOLFSSL* ssl) { int ret = WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL); WC_DECLARE_VAR(ocspRequest, OcspRequest, 1, 0); WOLFSSL_ENTER("CheckCertOCSP"); #ifdef WOLFSSL_SMALL_STACK ocspRequest = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ocspRequest == NULL) { WOLFSSL_LEAVE("CheckCertOCSP", MEMORY_ERROR); return MEMORY_E; } #endif if (InitOcspRequest(ocspRequest, cert, ocsp->cm->ocspSendNonce, ocsp->cm->heap) == 0) { ocspRequest->ssl = ssl; ret = CheckOcspRequest(ocsp, ocspRequest, NULL, NULL); FreeOcspRequest(ocspRequest); } WC_FREE_VAR_EX(ocspRequest, NULL, DYNAMIC_TYPE_TMP_BUFFER); WOLFSSL_LEAVE("CheckCertOCSP", ret); return ret; } int CheckCertOCSP(WOLFSSL_OCSP* ocsp, DecodedCert* cert) { return CheckCertOCSP_ex(ocsp, cert, NULL); } static int GetOcspEntry(WOLFSSL_OCSP* ocsp, OcspRequest* request, OcspEntry** entry) { WOLFSSL_ENTER("GetOcspEntry"); *entry = NULL; if (wc_LockMutex(&ocsp->ocspLock) != 0) { WOLFSSL_LEAVE("GetOcspEntry", BAD_MUTEX_E); return BAD_MUTEX_E; } for (*entry = ocsp->ocspList; *entry; *entry = (*entry)->next) if (XMEMCMP((*entry)->issuerHash, request->issuerHash, OCSP_DIGEST_SIZE) == 0 && XMEMCMP((*entry)->issuerKeyHash, request->issuerKeyHash, OCSP_DIGEST_SIZE) == 0) break; if (*entry == NULL) { *entry = (OcspEntry*)XMALLOC(sizeof(OcspEntry), ocsp->cm->heap, DYNAMIC_TYPE_OCSP_ENTRY); if (*entry) { InitOcspEntry(*entry, request); (*entry)->next = ocsp->ocspList; ocsp->ocspList = *entry; } } wc_UnLockMutex(&ocsp->ocspLock); return *entry ? 0 : MEMORY_ERROR; } /* Mallocs responseBuffer->buffer and is up to caller to free on success * * Returns OCSP status */ static int GetOcspStatus(WOLFSSL_OCSP* ocsp, OcspRequest* request, OcspEntry* entry, CertStatus** status, buffer* responseBuffer, void* heap) { int ret = WC_NO_ERR_TRACE(OCSP_INVALID_STATUS); WOLFSSL_ENTER("GetOcspStatus"); (void)heap; *status = NULL; if (wc_LockMutex(&ocsp->ocspLock) != 0) { WOLFSSL_LEAVE("GetOcspStatus", BAD_MUTEX_E); return BAD_MUTEX_E; } for (*status = entry->status; *status; *status = (*status)->next) if ((*status)->serialSz == request->serialSz && !XMEMCMP((*status)->serial, request->serial, (size_t)(*status)->serialSz)) break; if (responseBuffer && *status && !(*status)->rawOcspResponse) { /* force fetching again */ ret = OCSP_INVALID_STATUS; } else if (*status) { #ifndef NO_ASN_TIME if (XVALIDATE_DATE((*status)->thisDate, (*status)->thisDateFormat, ASN_BEFORE, MAX_DATE_SIZE) && ((*status)->nextDate[0] != 0) && XVALIDATE_DATE((*status)->nextDate, (*status)->nextDateFormat, ASN_AFTER, MAX_DATE_SIZE)) #endif { ret = xstat2err((*status)->status); if (responseBuffer) { responseBuffer->buffer = (byte*)XMALLOC( (*status)->rawOcspResponseSz, heap, DYNAMIC_TYPE_TMP_BUFFER); if (responseBuffer->buffer) { responseBuffer->length = (*status)->rawOcspResponseSz; XMEMCPY(responseBuffer->buffer, (*status)->rawOcspResponse, (*status)->rawOcspResponseSz); } } } } wc_UnLockMutex(&ocsp->ocspLock); return ret; } /* Check that the response for validity. Store result in status. * * ocsp Context object for OCSP status. * response OCSP response message data. * responseSz Length of OCSP response message data. * responseBuffer Buffer object to return the response with. * status The certificate status object. * entry The OCSP entry for this certificate. * ocspRequest Request corresponding to response. * heap Heap hint used for responseBuffer * returns OCSP_LOOKUP_FAIL when the response is bad and 0 otherwise. */ int CheckOcspResponse(WOLFSSL_OCSP *ocsp, byte *response, int responseSz, WOLFSSL_BUFFER_INFO *responseBuffer, CertStatus *status, OcspEntry *entry, OcspRequest *ocspRequest, void* heap) { #ifdef WOLFSSL_SMALL_STACK CertStatus* newStatus; OcspEntry* newSingle; OcspResponse* ocspResponse; #else CertStatus newStatus[1]; OcspEntry newSingle[1]; OcspResponse ocspResponse[1]; #endif int ret; int validated = 0; /* ocsp validation flag */ (void)heap; #ifdef WOLFSSL_SMALL_STACK newStatus = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL, DYNAMIC_TYPE_OCSP_STATUS); newSingle = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL, DYNAMIC_TYPE_OCSP_ENTRY); ocspResponse = (OcspResponse*)XMALLOC(sizeof(OcspResponse), NULL, DYNAMIC_TYPE_OCSP_REQUEST); if (newStatus == NULL || newSingle == NULL || ocspResponse == NULL) { XFREE(newStatus, NULL, DYNAMIC_TYPE_OCSP_STATUS); XFREE(newSingle, NULL, DYNAMIC_TYPE_OCSP_ENTRY); XFREE(ocspResponse, NULL, DYNAMIC_TYPE_OCSP_REQUEST); WOLFSSL_LEAVE("CheckOcspResponse", MEMORY_ERROR); return MEMORY_E; } #endif InitOcspResponse(ocspResponse, newSingle, newStatus, response, (word32)responseSz, ocsp->cm->heap); #if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(NO_TLS) if (ocspRequest != NULL && ocspRequest->ssl != NULL && TLSX_CSR2_IsMulti(((WOLFSSL*)ocspRequest->ssl)->extensions)) { ocspResponse->pendingCAs = TLSX_CSR2_GetPendingSigners(((WOLFSSL*)ocspRequest->ssl)->extensions); } #endif ret = OcspResponseDecode(ocspResponse, ocsp->cm, ocsp->cm->heap, 0, 0); if (ret != 0) { ocsp->error = ret; WOLFSSL_LEAVE("OcspResponseDecode failed", ocsp->error); goto end; } if (ocspResponse->responseStatus != OCSP_SUCCESSFUL) { WOLFSSL_MSG("OcspResponse status bad"); goto end; } if (ocspRequest != NULL) { /* Has the chance to bubble up response changing ocspResponse->single to no longer be pointing at newSingle */ ret = CompareOcspReqResp(ocspRequest, ocspResponse); if (ret != 0) { goto end; } } if (responseBuffer) { responseBuffer->buffer = (byte*)XMALLOC((size_t)responseSz, heap, DYNAMIC_TYPE_TMP_BUFFER); if (responseBuffer->buffer) { responseBuffer->length = (unsigned int)responseSz; XMEMCPY(responseBuffer->buffer, response, (size_t)responseSz); } } ret = xstat2err(ocspResponse->single->status->status); if (ret == 0) { validated = 1; } if (wc_LockMutex(&ocsp->ocspLock) != 0) { ret = BAD_MUTEX_E; goto end; } if (status != NULL) { XFREE(status->rawOcspResponse, ocsp->cm->heap, DYNAMIC_TYPE_OCSP_STATUS); /* Replace existing certificate entry with updated */ ocspResponse->single->status->next = status->next; XMEMCPY(status, ocspResponse->single->status, sizeof(CertStatus)); } else if (entry != NULL) { /* Save new certificate entry */ status = (CertStatus*)XMALLOC(sizeof(CertStatus), ocsp->cm->heap, DYNAMIC_TYPE_OCSP_STATUS); if (status != NULL) { XMEMCPY(status, ocspResponse->single->status, sizeof(CertStatus)); status->next = entry->status; entry->status = status; entry->ownStatus = 1; entry->totalStatus++; } } if (status && responseBuffer && responseBuffer->buffer) { status->rawOcspResponse = (byte*)XMALLOC(responseBuffer->length, ocsp->cm->heap, DYNAMIC_TYPE_OCSP_STATUS); if (status->rawOcspResponse) { status->rawOcspResponseSz = responseBuffer->length; XMEMCPY(status->rawOcspResponse, responseBuffer->buffer, responseBuffer->length); } } wc_UnLockMutex(&ocsp->ocspLock); end: if (ret == 0 && validated == 1) { WOLFSSL_MSG("New OcspResponse validated"); } else if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED)) { WOLFSSL_MSG("OCSP revoked"); } else if (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN)) { WOLFSSL_MSG("OCSP unknown"); } else { WOLFSSL_MSG("OCSP lookup failure"); ret = OCSP_LOOKUP_FAIL; } FreeOcspResponse(ocspResponse); WC_FREE_VAR_EX(newStatus, NULL, DYNAMIC_TYPE_OCSP_STATUS); WC_FREE_VAR_EX(newSingle, NULL, DYNAMIC_TYPE_OCSP_ENTRY); WC_FREE_VAR_EX(ocspResponse, NULL, DYNAMIC_TYPE_OCSP_REQUEST); return ret; } /* 0 on success */ /* allow user to override the maximum request size at build-time */ #ifndef OCSP_MAX_REQUEST_SZ #define OCSP_MAX_REQUEST_SZ 2048 #endif int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest, buffer* responseBuffer, void* heap) { OcspEntry* entry = NULL; CertStatus* status = NULL; byte* request = NULL; int requestSz = OCSP_MAX_REQUEST_SZ; int responseSz = 0; byte* response = NULL; const char* url = NULL; int urlSz = 0; int ret = -1; WOLFSSL* ssl; void* ioCtx; WOLFSSL_ENTER("CheckOcspRequest"); if (ocsp == NULL || ocspRequest == NULL) return BAD_FUNC_ARG; if (responseBuffer) { responseBuffer->buffer = NULL; responseBuffer->length = 0; } ret = GetOcspEntry(ocsp, ocspRequest, &entry); if (ret != 0) return ret; ret = GetOcspStatus(ocsp, ocspRequest, entry, &status, responseBuffer, heap); if (ret != WC_NO_ERR_TRACE(OCSP_INVALID_STATUS)) return ret; if (responseBuffer) { XFREE(responseBuffer->buffer, heap, DYNAMIC_TYPE_TMP_BUFFER); responseBuffer->buffer = NULL; } /* get SSL and IOCtx */ ssl = (WOLFSSL*)ocspRequest->ssl; ioCtx = (ssl && ssl->ocspIOCtx != NULL) ? ssl->ocspIOCtx : ocsp->cm->ocspIOCtx; if (ocsp->cm->ocspUseOverrideURL) { url = ocsp->cm->ocspOverrideURL; if (url != NULL && url[0] != '\0') urlSz = (int)XSTRLEN(url); else return OCSP_NEED_URL; } else if (ocspRequest->urlSz != 0 && ocspRequest->url != NULL) { url = (const char *)ocspRequest->url; urlSz = ocspRequest->urlSz; } else { /* cert doesn't have extAuthInfo, assuming CERT_GOOD */ WOLFSSL_MSG("Cert has no OCSP URL, assuming CERT_GOOD"); return 0; } request = (byte*)XMALLOC((size_t)requestSz, ocsp->cm->heap, DYNAMIC_TYPE_OCSP); if (request == NULL) { WOLFSSL_LEAVE("CheckOcspRequest", MEMORY_ERROR); return MEMORY_ERROR; } requestSz = EncodeOcspRequest(ocspRequest, request, (word32)requestSz); if (requestSz > 0 && ocsp->cm->ocspIOCb) { responseSz = ocsp->cm->ocspIOCb(ioCtx, url, urlSz, request, requestSz, &response); } if (responseSz == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ)) { ret = OCSP_WANT_READ; } else if (responseSz == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_TIMEOUT)){ ret = HTTP_TIMEOUT; } XFREE(request, ocsp->cm->heap, DYNAMIC_TYPE_OCSP); if (responseSz >= 0 && response) { ret = CheckOcspResponse(ocsp, response, responseSz, responseBuffer, status, entry, ocspRequest, heap); } if (response != NULL && ocsp->cm->ocspRespFreeCb) ocsp->cm->ocspRespFreeCb(ioCtx, response); /* Keep responseBuffer in the case of getting to response check. Caller * should free responseBuffer after checking OCSP return value in "ret" */ WOLFSSL_LEAVE("CheckOcspRequest", ret); return ret; } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK static int CheckOcspResponderChain(OcspEntry* single, byte* issuerNameHash, 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; Signer* prev = NULL; int passed = 0; /* * Relation between certs: * CA * / \ * intermediate(s) cert in OCSP response * | with OCSP key usage ext * issuer of cert * in OCSP request */ 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 = findSignerByKeyHash(pendingCAs, single->issuerKeyHash); if (ca != NULL && XMEMCMP(ca->subjectNameHash, single->issuerHash, OCSP_DIGEST_SIZE) != 0) { ca = NULL; } } #else (void)pendingCAs; #endif 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, issuerNameHash, 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; } ca = parent; } return passed; } #endif /* 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* subjectNameHash, byte* subjectKeyHash, byte extExtKeyUsage, byte* issuerNameHash, byte* issuerKeyHash, void* vp) { int ret = 0; OcspEntry* single; /* Both evaluate to enum values so can't use a pre-processor check */ WOLFSSL_ASSERT_EQ(OCSP_DIGEST_SIZE, SIGNER_DIGEST_SIZE); (void)vp; WOLFSSL_ENTER("CheckOcspResponder"); /* In the future if this API is used more then it could be beneficial to * implement calling InitDecodedCert and ParseCertRelative here * automatically when cert == NULL. */ if (bs == NULL || subjectNameHash == NULL || issuerNameHash == NULL) return BAD_FUNC_ARG; for (single = bs->single; single != NULL; single = single->next) { int passed = 0; if (subjectKeyHash != NULL && XMEMCMP(subjectNameHash, single->issuerHash, OCSP_DIGEST_SIZE) == 0 && XMEMCMP(subjectKeyHash, single->issuerKeyHash, KEYID_SIZE) == 0) { WOLFSSL_MSG("\tOCSP Response signed by issuer"); passed = 1; } else if ((extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) != 0) { if (issuerKeyHash != NULL && XMEMCMP(issuerNameHash, single->issuerHash, OCSP_DIGEST_SIZE) == 0 && XMEMCMP(issuerKeyHash, single->issuerKeyHash, KEYID_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, issuerNameHash, issuerKeyHash, vp, bs->pendingCAs); } #endif } if (!passed) { WOLFSSL_MSG("\tOCSP Responder not authorized"); ret = BAD_OCSP_RESPONDER; break; } } return ret; } OcspRequest* wc_OcspRequest_new(void* heap) { OcspRequest* request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), heap, DYNAMIC_TYPE_OCSP_REQUEST); if (request != NULL) { XMEMSET(request, 0, sizeof(OcspRequest)); request->heap = heap; } return request; } void wc_OcspRequest_free(OcspRequest* request) { if (request != NULL) { FreeOcspRequest(request); XFREE(request, request->heap, DYNAMIC_TYPE_OCSP_REQUEST); } } OcspResponse* wc_OcspResponse_new(void* heap) { OcspResponse* response = (OcspResponse*)XMALLOC(sizeof(OcspResponse), heap, DYNAMIC_TYPE_OCSP_RESPONSE); if (response != NULL) { XMEMSET(response, 0, sizeof(OcspResponse)); response->heap = heap; } return response; } void wc_OcspResponse_free(OcspResponse* response) { if (response != NULL) { FreeOcspResponse(response); XFREE(response, response->heap, DYNAMIC_TYPE_OCSP_RESPONSE); } } /* compatibility layer OCSP functions */ #ifdef OPENSSL_EXTRA int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs, WOLFSSL_OCSP_CERTID* id, int* status, int* reason, WOLFSSL_ASN1_TIME** revtime, WOLFSSL_ASN1_TIME** thisupd, WOLFSSL_ASN1_TIME** nextupd) { WOLFSSL_OCSP_SINGLERESP* single; if (bs == NULL || id == NULL) return WOLFSSL_FAILURE; single = bs->single; while (single != NULL) { if (single->status != NULL && id->status != NULL && (XMEMCMP(single->status->serial, id->status->serial, (size_t)single->status->serialSz) == 0) && (XMEMCMP(single->issuerHash, id->issuerHash, OCSP_DIGEST_SIZE) == 0) && (XMEMCMP(single->issuerKeyHash, id->issuerKeyHash, OCSP_DIGEST_SIZE) == 0)) { break; } single = single->next; } if (single == NULL) return WOLFSSL_FAILURE; if (status != NULL) *status = single->status->status; #ifdef WOLFSSL_OCSP_PARSE_STATUS if (thisupd != NULL) *thisupd = &single->status->thisDateParsed; if (nextupd != NULL) *nextupd = &single->status->nextDateParsed; #else if (thisupd != NULL) *thisupd = NULL; if (nextupd != NULL) *nextupd = NULL; #endif /* TODO: Not needed for Nginx or httpd */ if (reason != NULL) *reason = 0; if (revtime != NULL) *revtime = NULL; return WOLFSSL_SUCCESS; } const char *wolfSSL_OCSP_cert_status_str(long s) { switch (s) { case CERT_GOOD: return "good"; case CERT_REVOKED: return "revoked"; case CERT_UNKNOWN: return "unknown"; default: return "(UNKNOWN)"; } } int wolfSSL_OCSP_check_validity(WOLFSSL_ASN1_TIME* thisupd, WOLFSSL_ASN1_TIME* nextupd, long sec, long maxsec) { (void)thisupd; (void)nextupd; (void)sec; (void)maxsec; /* Dates validated in DecodeSingleResponse. */ return WOLFSSL_SUCCESS; } void wolfSSL_OCSP_CERTID_free(WOLFSSL_OCSP_CERTID* certId) { FreeOcspEntry(certId, NULL); XFREE(certId, NULL, DYNAMIC_TYPE_OPENSSL); } WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_cert_to_id( const WOLFSSL_EVP_MD *dgst, const WOLFSSL_X509 *subject, const WOLFSSL_X509 *issuer) { WOLFSSL_OCSP_CERTID* certId = NULL; CertStatus* certStatus = NULL; WOLFSSL_CERT_MANAGER* cm = NULL; int ret = -1; DerBuffer* derCert = NULL; int dgstType; WC_DECLARE_VAR(cert, DecodedCert, 1, 0); if (dgst == NULL) { dgstType = WC_HASH_TYPE_SHA; } else if (wolfSSL_EVP_get_hashinfo(dgst, &dgstType, NULL) != WOLFSSL_SUCCESS) { return NULL; } if (dgstType != OCSP_DIGEST) return NULL; cm = wolfSSL_CertManagerNew(); if (cm == NULL || subject == NULL || subject->derCert == NULL || issuer == NULL || issuer->derCert == NULL) goto out; WC_ALLOC_VAR_EX(cert, DecodedCert, 1, cm->heap, DYNAMIC_TYPE_DCERT, goto out); ret = AllocDer(&derCert, issuer->derCert->length, issuer->derCert->type, NULL); if (ret == 0) { /* AddCA() frees the buffer. */ XMEMCPY(derCert->buffer, issuer->derCert->buffer, issuer->derCert->length); ret = AddCA(cm, &derCert, WOLFSSL_USER_CA, 1); if (ret != WOLFSSL_SUCCESS) { goto out; } derCert = NULL; } ret = -1; certId = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID), cm->heap, DYNAMIC_TYPE_OPENSSL); if (certId == NULL) goto out; certStatus = (CertStatus*)XMALLOC(sizeof(CertStatus), cm->heap, DYNAMIC_TYPE_OPENSSL); if (certStatus == NULL) goto out; XMEMSET(certId, 0, sizeof(WOLFSSL_OCSP_CERTID)); XMEMSET(certStatus, 0, sizeof(CertStatus)); certId->status = certStatus; certId->ownStatus = 1; InitDecodedCert(cert, subject->derCert->buffer, subject->derCert->length, NULL); if (ParseCertRelative(cert, CERT_TYPE, VERIFY_OCSP, cm, NULL) != 0) { FreeDecodedCert(cert); goto out; } else { certId->hashAlgoOID = wc_HashGetOID(OCSP_DIGEST); XMEMCPY(certId->issuerHash, cert->issuerHash, OCSP_DIGEST_SIZE); XMEMCPY(certId->issuerKeyHash, cert->issuerKeyHash, OCSP_DIGEST_SIZE); XMEMCPY(certId->status->serial, cert->serial, (size_t)cert->serialSz); certId->status->serialSz = cert->serialSz; FreeDecodedCert(cert); } ret = 0; out: if (ret != 0) { if (derCert != NULL) FreeDer(&derCert); if (cm != NULL) { XFREE(certId, cm->heap, DYNAMIC_TYPE_OPENSSL); certId = NULL; XFREE(certStatus, cm->heap, DYNAMIC_TYPE_OPENSSL); } } #ifdef WOLFSSL_SMALL_STACK if (cm != NULL) XFREE(cert, cm->heap, DYNAMIC_TYPE_DCERT); #endif if (cm != NULL) wolfSSL_CertManagerFree(cm); return certId; } void wolfSSL_OCSP_BASICRESP_free(WOLFSSL_OCSP_BASICRESP* basicResponse) { wolfSSL_OCSP_RESPONSE_free(basicResponse); } /* Calculate ancode CertID DER encoding following RFC 6960: CertID ::= SEQUENCE { hashAlgorithm AlgorithmIdentifier, issuerNameHash OCTET STRING, issuerKeyHash OCTET STRING, serialNumber CertificateSerialNumber } */ static int OcspEncodeCertID(WOLFSSL_OCSP_CERTID* id, byte* output, word32* totalSz, word32* intSize) { word32 idx = 0; int ret; if (id == NULL || totalSz == NULL || intSize == NULL || (output != NULL && (*totalSz == 0 || *totalSz <= *intSize))) return BAD_FUNC_ARG; if (output != NULL) { ret = SetSequence(*intSize, output); if (ret < 0) return ret; idx += ret; } ret = SetAlgoID(id->hashAlgoOID, ((output != NULL) ? output + idx : output), oidHashType, 0); if (ret <= 0) return -1; idx += ret; /* issuerNameHash */ ret = SetOctetString(OCSP_DIGEST_SIZE, ((output != NULL) ? output + idx : output)); if (ret < 0) return ret; idx += ret; if (output != NULL) XMEMCPY(output + idx, id->issuerHash, OCSP_DIGEST_SIZE); idx += OCSP_DIGEST_SIZE; /* issuerKeyHash */ ret = SetOctetString(OCSP_DIGEST_SIZE, ((output != NULL) ? output + idx : output)); if (ret < 0) return ret; idx += ret; if (output != NULL) XMEMCPY(output + idx, id->issuerKeyHash, OCSP_DIGEST_SIZE); idx += OCSP_DIGEST_SIZE; /* serialNumber */ ret = SetASNInt(id->status->serialSz, id->status->serial[0], ((output != NULL) ? output + idx : output)); if (ret < 0) return ret; idx += ret; if (output != NULL) XMEMCPY(output + idx, id->status->serial, id->status->serialSz); idx += id->status->serialSz; if (output == NULL) { *intSize = idx; ret = SetSequence(idx, NULL); if (ret < 0) return ret; idx += ret; *totalSz = idx; } else if (idx != *totalSz) { return BUFFER_E; } return 0; } static int OcspRespIdMatches(OcspResponse* resp, const byte* NameHash, const byte* keyHash) { if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) { return XMEMCMP(NameHash, resp->responderId.nameHash, SIGNER_DIGEST_SIZE) == 0; } else if (resp->responderIdType == OCSP_RESPONDER_ID_KEY) { return XMEMCMP(keyHash, resp->responderId.keyHash, OCSP_RESPONDER_ID_KEY_SZ) == 0; } return 0; } static int OcspFindSigner(WOLFSSL_OCSP_BASICRESP *resp, WOLF_STACK_OF(WOLFSSL_X509) *certs, DecodedCert **signer, int *embedded, unsigned long flags) { WOLFSSL_X509 *signer_x509 = NULL; DecodedCert *certDecoded; int i; certDecoded = (DecodedCert *)XMALLOC(sizeof(*certDecoded), resp->heap, DYNAMIC_TYPE_DCERT); if (certDecoded == NULL) return MEMORY_E; for (i = 0; i < wolfSSL_sk_X509_num(certs); i++) { signer_x509 = wolfSSL_sk_X509_value(certs, i); if (signer_x509 == NULL) continue; InitDecodedCert(certDecoded, signer_x509->derCert->buffer, signer_x509->derCert->length, resp->heap); if (ParseCertRelative(certDecoded, CERT_TYPE, NO_VERIFY, NULL, NULL) == 0) { if (OcspRespIdMatches(resp, certDecoded->subjectHash, certDecoded->subjectKeyHash)) { *signer = certDecoded; *embedded = 0; return 0; } } FreeDecodedCert(certDecoded); } if (flags & WOLFSSL_OCSP_NOINTERN) { XFREE(certDecoded, resp->heap, DYNAMIC_TYPE_DCERT); return ASN_NO_SIGNER_E; } /* not found in certs, search the cert embedded in the response */ InitDecodedCert(certDecoded, resp->cert, resp->certSz, resp->heap); if (ParseCertRelative(certDecoded, CERT_TYPE, NO_VERIFY, NULL, NULL) == 0) { if (OcspRespIdMatches(resp, certDecoded->subjectHash, certDecoded->subjectKeyHash)) { *signer = certDecoded; *embedded = 1; return 0; } } FreeDecodedCert(certDecoded); XFREE(certDecoded, resp->heap, DYNAMIC_TYPE_DCERT); return ASN_NO_SIGNER_E; } static int OcspVerifySigner(WOLFSSL_OCSP_BASICRESP *resp, DecodedCert *cert, WOLFSSL_X509_STORE *st, unsigned long flags) { WC_DECLARE_VAR(c, DecodedCert, 1, 0); int ret = -1; if (st == NULL) return ASN_OCSP_CONFIRM_E; WC_ALLOC_VAR_EX(c, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT, return MEMORY_E); InitDecodedCert(c, cert->source, cert->maxIdx, NULL); if (ParseCertRelative(c, CERT_TYPE, VERIFY, st->cm, NULL) != 0) { ret = ASN_OCSP_CONFIRM_E; goto err; } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK if ((flags & WOLFSSL_OCSP_NOCHECKS) == 0) { ret = CheckOcspResponder(resp, c->subjectHash, c->subjectKeyHash, c->extExtKeyUsage, c->issuerHash, (c->ca != NULL) ? c->ca->subjectKeyHash : NULL, st->cm); } else { ret = 0; } #else (void)resp; (void)flags; ret = 0; #endif err: FreeDecodedCert(c); WC_FREE_VAR_EX(c, NULL, DYNAMIC_TYPE_DCERT); return ret; } /* Signature verified in DecodeBasicOcspResponse. * But no store available to verify certificate. */ int wolfSSL_OCSP_basic_verify(WOLFSSL_OCSP_BASICRESP* bs, WOLF_STACK_OF(WOLFSSL_X509) * certs, WOLFSSL_X509_STORE* st, unsigned long flags) { int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); int embedded; DecodedCert *cert = NULL; if (bs == NULL) return WOLFSSL_FAILURE; ret = OcspFindSigner(bs, certs, &cert, &embedded, flags); if (ret != 0) { WOLFSSL_MSG("OCSP no signer found"); return WOLFSSL_FAILURE; } /* skip certificate verification if cert in certs and TRUST_OTHER is true */ if (!embedded && (flags & WOLFSSL_OCSP_TRUSTOTHER) != 0) flags |= WOLFSSL_OCSP_NOVERIFY; /* verify response signature */ ret = ConfirmSignature( &cert->sigCtx, bs->response, bs->responseSz, cert->publicKey, cert->pubKeySize, cert->keyOID, bs->sig, bs->sigSz, bs->sigOID, bs->sigParams, bs->sigParamsSz, NULL); if (ret != 0) { WOLFSSL_MSG("OCSP signature verification failed"); ret = -1; goto err; } if ((flags & WOLFSSL_OCSP_NOVERIFY) == 0) { ret = OcspVerifySigner(bs, cert, st, flags); } err: FreeDecodedCert(cert); XFREE(cert, NULL, DYNAMIC_TYPE_DCERT); return ret == 0 ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } void wolfSSL_OCSP_RESPONSE_free(OcspResponse* response) { OcspEntry *s, *sNext; if (response == NULL) return; s = response->single; while (s != NULL) { sNext = s->next; FreeOcspEntry(s, NULL); XFREE(s, NULL, DYNAMIC_TYPE_OCSP_ENTRY); s = sNext; } XFREE(response->source, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(response, NULL, DYNAMIC_TYPE_OCSP_REQUEST); } #ifndef NO_BIO OcspResponse* wolfSSL_d2i_OCSP_RESPONSE_bio(WOLFSSL_BIO* bio, OcspResponse** response) { byte* data; byte* p; int len; int dataAlloced = 0; OcspResponse* ret = NULL; if (bio == NULL) return NULL; if (bio->type == WOLFSSL_BIO_MEMORY) { len = wolfSSL_BIO_get_mem_data(bio, &data); if (len <= 0 || data == NULL) { return NULL; } } #ifndef NO_FILESYSTEM else if (bio->type == WOLFSSL_BIO_FILE) { long fcur; long flen; if (bio->ptr.fh == NULL) return NULL; fcur = XFTELL(bio->ptr.fh); if (fcur < 0) return NULL; if(XFSEEK(bio->ptr.fh, 0, SEEK_END) != 0) return NULL; flen = XFTELL(bio->ptr.fh); if (flen < 0) return NULL; if (XFSEEK(bio->ptr.fh, fcur, SEEK_SET) != 0) return NULL; /* check calculated length */ fcur = flen - fcur; if (fcur > MAX_WOLFSSL_FILE_SIZE || fcur <= 0) return NULL; data = (byte*)XMALLOC((size_t)fcur, 0, DYNAMIC_TYPE_TMP_BUFFER); if (data == NULL) return NULL; dataAlloced = 1; len = wolfSSL_BIO_read(bio, (char *)data, (int)flen); } #endif else return NULL; if (len > 0) { p = data; ret = wolfSSL_d2i_OCSP_RESPONSE(response, (const unsigned char **)&p, len); } if (dataAlloced) XFREE(data, 0, DYNAMIC_TYPE_TMP_BUFFER); return ret; } #endif /* !NO_BIO */ OcspResponse* wolfSSL_d2i_OCSP_RESPONSE(OcspResponse** response, const unsigned char** data, int len) { OcspResponse *resp = NULL; word32 idx = 0; int length = 0; int ret; if (data == NULL) return NULL; if (response != NULL) resp = *response; if (resp == NULL) { resp = (OcspResponse*)XMALLOC(sizeof(OcspResponse), NULL, DYNAMIC_TYPE_OCSP_REQUEST); if (resp == NULL) return NULL; XMEMSET(resp, 0, sizeof(OcspResponse)); } resp->source = (byte*)XMALLOC((size_t)len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (resp->source == NULL) { XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST); return NULL; } resp->single = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL, DYNAMIC_TYPE_OCSP_ENTRY); if (resp->single == NULL) { XFREE(resp->source, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST); return NULL; } XMEMSET(resp->single, 0, sizeof(OcspEntry)); resp->single->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL, DYNAMIC_TYPE_OCSP_STATUS); resp->single->ownStatus = 1; if (resp->single->status == NULL) { XFREE(resp->source, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(resp->single, NULL, DYNAMIC_TYPE_OCSP_ENTRY); XFREE(resp, NULL, DYNAMIC_TYPE_OCSP_REQUEST); return NULL; } XMEMSET(resp->single->status, 0, sizeof(CertStatus)); XMEMCPY(resp->source, *data, (size_t)len); resp->maxIdx = (word32)len; ret = OcspResponseDecode(resp, NULL, NULL, 1, 1); if (ret != 0 && ret != WC_NO_ERR_TRACE(ASN_OCSP_CONFIRM_E)) { /* for just converting from a DER to an internal structure the CA may * not yet be known to this function for signature verification */ wolfSSL_OCSP_RESPONSE_free(resp); return NULL; } if (GetSequence(*data, &idx, &length, (word32)len) >= 0) (*data) += idx + length; if (response != NULL && *response == NULL) *response = resp; return resp; } int wolfSSL_i2d_OCSP_RESPONSE(OcspResponse* response, unsigned char** data) { if (response == NULL) return BAD_FUNC_ARG; if (response->source == NULL) return BAD_FUNC_ARG; if (data == NULL) return (int)response->maxIdx; if (*data == NULL) { *data = (unsigned char*)XMALLOC(response->maxIdx, NULL, DYNAMIC_TYPE_OPENSSL); if (*data == NULL) return -1; XMEMCPY(*data, response->source, response->maxIdx); } else { XMEMCPY(*data, response->source, response->maxIdx); *data += response->maxIdx; } return (int)response->maxIdx; } int wolfSSL_OCSP_response_status(OcspResponse *response) { if (response == NULL) return -1; return response->responseStatus; } const char *wolfSSL_OCSP_response_status_str(long s) { switch (s) { case OCSP_SUCCESSFUL: return "successful"; case OCSP_MALFORMED_REQUEST: return "malformedrequest"; case OCSP_INTERNAL_ERROR: return "internalerror"; case OCSP_TRY_LATER: return "trylater"; case OCSP_SIG_REQUIRED: return "sigrequired"; case OCSP_UNAUTHORIZED: return "unauthorized"; default: return "(UNKNOWN)"; } } WOLFSSL_OCSP_BASICRESP* wolfSSL_OCSP_response_get1_basic(OcspResponse* response) { WOLFSSL_OCSP_BASICRESP* bs; const unsigned char *ptr; if (response == NULL || response->source == NULL) return NULL; ptr = response->source; bs = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, response->maxIdx); return bs; } OcspRequest* wolfSSL_OCSP_REQUEST_new(void) { OcspRequest* request; request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), NULL, DYNAMIC_TYPE_OPENSSL); if (request != NULL) XMEMSET(request, 0, sizeof(OcspRequest)); return request; } void wolfSSL_OCSP_REQUEST_free(OcspRequest* request) { FreeOcspRequest(request); XFREE(request, NULL, DYNAMIC_TYPE_OPENSSL); } int wolfSSL_i2d_OCSP_REQUEST(OcspRequest* request, unsigned char** data) { int size; if (request == NULL) return BAD_FUNC_ARG; size = EncodeOcspRequest(request, NULL, 0); if (size <= 0 || data == NULL) return size; size = EncodeOcspRequest(request, *data, (word32) size); if (size > 0) *data += size; return size; } WOLFSSL_OCSP_ONEREQ* wolfSSL_OCSP_request_add0_id(OcspRequest *req, WOLFSSL_OCSP_CERTID *cid) { if (req == NULL || cid == NULL || cid->status == NULL) return NULL; if (req->cid != NULL) wolfSSL_OCSP_CERTID_free((WOLFSSL_OCSP_CERTID*)req->cid); /* Keep to free */ req->cid = (void*)cid; XMEMCPY(req->issuerHash, cid->issuerHash, WC_MAX_DIGEST_SIZE); XMEMCPY(req->issuerKeyHash, cid->issuerKeyHash, WC_MAX_DIGEST_SIZE); req->hashAlg = (int)cid->hashAlgoOID; if (cid->status->serialSz > req->serialSz) { XFREE(req->serial, req->heap, DYNAMIC_TYPE_OCSP); req->serial = (byte*)XMALLOC((size_t)cid->status->serialSz, req->heap, DYNAMIC_TYPE_OCSP_REQUEST); if (req->serial == NULL) return NULL; } XMEMCPY(req->serial, cid->status->serial, (size_t)cid->status->serialSz); req->serialSz = cid->status->serialSz; return req; } WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_CERTID_dup(WOLFSSL_OCSP_CERTID* id) { WOLFSSL_OCSP_CERTID* certId; if (id == NULL) return NULL; certId = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID), NULL, DYNAMIC_TYPE_OPENSSL); if (certId == NULL) return NULL; XMEMCPY(certId, id, sizeof(WOLFSSL_OCSP_CERTID)); certId->next = NULL; certId->rawCertId = NULL; certId->rawCertIdSize = 0; /* Deep-copy the status to avoid double-free */ if (id->status != NULL) { certId->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL, DYNAMIC_TYPE_OCSP_STATUS); if (certId->status == NULL) { XFREE(certId, NULL, DYNAMIC_TYPE_OPENSSL); return NULL; } XMEMCPY(certId->status, id->status, sizeof(CertStatus)); certId->status->next = NULL; /* Don't share dynamically allocated fields */ certId->status->rawOcspResponse = NULL; certId->status->rawOcspResponseSz = 0; certId->status->serialInt = NULL; #ifdef WOLFSSL_OCSP_PARSE_STATUS certId->status->thisDateAsn = NULL; certId->status->nextDateAsn = NULL; #endif } certId->ownStatus = 1; return certId; } #ifndef NO_BIO int wolfSSL_i2d_OCSP_REQUEST_bio(WOLFSSL_BIO* out, WOLFSSL_OCSP_REQUEST *req) { int size = -1; unsigned char* data = NULL; WOLFSSL_ENTER("wolfSSL_i2d_OCSP_REQUEST_bio"); if (out == NULL || req == NULL) return WOLFSSL_FAILURE; size = wolfSSL_i2d_OCSP_REQUEST(req, NULL); if (size > 0) { data = (unsigned char*) XMALLOC((size_t)size, out->heap, DYNAMIC_TYPE_TMP_BUFFER); } if (data != NULL) { unsigned char* dataOrig = data; size = wolfSSL_i2d_OCSP_REQUEST(req, &data); data = dataOrig; } if (size <= 0) { XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER); return WOLFSSL_FAILURE; } if (wolfSSL_BIO_write(out, data, size) == (int)size) { XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER); return WOLFSSL_SUCCESS; } XFREE(data, out->heap, DYNAMIC_TYPE_TMP_BUFFER); return WOLFSSL_FAILURE; } #endif /* !NO_BIO */ int wolfSSL_i2d_OCSP_CERTID(WOLFSSL_OCSP_CERTID* id, unsigned char** data) { int allocated = 0; word32 derSz = 0; word32 intSz = 0; int ret; WOLFSSL_ENTER("wolfSSL_i2d_OCSP_CERTID"); if (id == NULL) return -1; if (id->rawCertId != NULL) { derSz = id->rawCertIdSize; } else { ret = OcspEncodeCertID(id, NULL, &derSz, &intSz); if (ret != 0) { WOLFSSL_MSG("Failed to calculate CertID size"); return -1; } } if (data == NULL) { return derSz; } if (*data == NULL) { /* Allocate buffer for DER encoding */ *data = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_OPENSSL); if (*data == NULL) { WOLFSSL_MSG("Failed to allocate memory for CertID DER encoding"); return -1; } allocated = 1; } if (id->rawCertId != NULL) { XMEMCPY(*data, id->rawCertId, id->rawCertIdSize); } else { ret = OcspEncodeCertID(id, *data, &derSz, &intSz); if (ret < 0) { WOLFSSL_MSG("Failed to encode CertID"); if (allocated) { XFREE(*data, NULL, DYNAMIC_TYPE_OPENSSL); *data = NULL; } return -1; } } if (!allocated) *data += derSz; return derSz; } WOLFSSL_OCSP_CERTID* wolfSSL_d2i_OCSP_CERTID(WOLFSSL_OCSP_CERTID** cidOut, const unsigned char** derIn, int length) { WOLFSSL_OCSP_CERTID *cid = NULL; int isAllocated = 0; word32 idx = 0; int ret; if (derIn == NULL || *derIn == NULL || length <= 0) return NULL; if (cidOut != NULL && *cidOut != NULL) { cid = *cidOut; FreeOcspEntry(cid, NULL); } else { cid = (WOLFSSL_OCSP_CERTID*)XMALLOC(sizeof(WOLFSSL_OCSP_CERTID), NULL, DYNAMIC_TYPE_OPENSSL); if (cid == NULL) return NULL; isAllocated = 1; } XMEMSET(cid, 0, sizeof(WOLFSSL_OCSP_CERTID)); cid->status = (CertStatus*)XMALLOC(sizeof(CertStatus), NULL, DYNAMIC_TYPE_OCSP_STATUS); if (cid->status == NULL) { XFREE(cid, NULL, DYNAMIC_TYPE_OPENSSL); return NULL; } XMEMSET(cid->status, 0, sizeof(CertStatus)); cid->ownStatus = 1; ret = OcspDecodeCertID(*derIn, &idx, length, cid); if (ret != 0) { FreeOcspEntry(cid, NULL); if (isAllocated) { XFREE(cid, NULL, DYNAMIC_TYPE_OPENSSL); } return NULL; } *derIn += idx; if (isAllocated && cidOut != NULL) *cidOut = cid; return cid; } const WOLFSSL_OCSP_CERTID* wolfSSL_OCSP_SINGLERESP_get0_id( const WOLFSSL_OCSP_SINGLERESP *single) { return single; } /** * Compare two WOLFSSL_OCSP_CERTID objects * @param a * @param b * @return 0 on success and when objects have the same id otherwise either * the id's don't match or an error occurred */ int wolfSSL_OCSP_id_cmp(WOLFSSL_OCSP_CERTID *a, WOLFSSL_OCSP_CERTID *b) { int ret = 0; if (a == NULL || b == NULL) return WOLFSSL_FATAL_ERROR; ret = a->hashAlgoOID != b->hashAlgoOID; if (ret == 0) ret = XMEMCMP(a->issuerHash, b->issuerHash, OCSP_DIGEST_SIZE); if (ret == 0) ret = XMEMCMP(a->issuerKeyHash, b->issuerKeyHash, OCSP_DIGEST_SIZE); if (ret == 0) { if (a->status != NULL && b->status != NULL) { if (a->status->serialSz == b->status->serialSz) ret = XMEMCMP(a->status->serial, b->status->serial, (size_t)a->status->serialSz); else ret = -1; } else if (a->status != b->status) { /* If either is not null then return non-zero */ ret = -1; } } return ret; } int wolfSSL_OCSP_single_get0_status(WOLFSSL_OCSP_SINGLERESP *single, int *reason, WOLFSSL_ASN1_TIME **revtime, WOLFSSL_ASN1_TIME **thisupd, WOLFSSL_ASN1_TIME **nextupd) { if (single == NULL || single->status == NULL) return -1; #ifdef WOLFSSL_OCSP_PARSE_STATUS if (thisupd != NULL) *thisupd = &single->status->thisDateParsed; if (nextupd != NULL) *nextupd = &single->status->nextDateParsed; #else if (thisupd != NULL) *thisupd = NULL; if (nextupd != NULL) *nextupd = NULL; #endif if (reason != NULL) *reason = 0; if (revtime != NULL) *revtime = NULL; return single->status->status; } int wolfSSL_OCSP_resp_count(WOLFSSL_OCSP_BASICRESP *bs) { WOLFSSL_OCSP_SINGLERESP* single; int count = 0; if (bs == NULL) return WOLFSSL_FAILURE; single = bs->single; while(single != NULL) { ++count; single = single->next; } return count; } WOLFSSL_OCSP_SINGLERESP* wolfSSL_OCSP_resp_get0(WOLFSSL_OCSP_BASICRESP *bs, int idx) { WOLFSSL_OCSP_SINGLERESP* single; int currIdx = 0; if (bs == NULL) return NULL; single = bs->single; while(single != NULL && currIdx != idx) { single = single->next; ++currIdx; } return single; } #endif /* OPENSSL_EXTRA */ #ifdef OPENSSL_ALL /******************************************************************************* * START OF WOLFSSL_OCSP_REQ_CTX API ******************************************************************************/ enum ocspReqStates { ORS_INVALID = 0, ORS_HEADER_ADDED, ORS_REQ_DONE }; enum ocspReqIOStates { ORIOS_INVALID = 0, ORIOS_WRITE, ORIOS_READ }; WOLFSSL_OCSP_REQ_CTX* wolfSSL_OCSP_REQ_CTX_new(WOLFSSL_BIO *bio, int maxline) { WOLFSSL_OCSP_REQ_CTX* ret = NULL; WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_new"); if (maxline <= 0) maxline = OCSP_MAX_REQUEST_SZ; ret = (WOLFSSL_OCSP_REQ_CTX*)XMALLOC(sizeof(*ret), NULL, DYNAMIC_TYPE_OPENSSL); if (ret != NULL) { XMEMSET(ret, 0, sizeof(*ret)); ret->buf = (byte*)XMALLOC((word32)maxline, NULL, DYNAMIC_TYPE_OPENSSL); if (ret->buf == NULL) goto error; ret->reqResp = wolfSSL_BIO_new(wolfSSL_BIO_s_mem()); ret->bufLen = maxline; ret->bio = bio; ret->ioState = ORIOS_WRITE; } return ret; error: wolfSSL_OCSP_REQ_CTX_free(ret); return NULL; } void wolfSSL_OCSP_REQ_CTX_free(WOLFSSL_OCSP_REQ_CTX *ctx) { WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_free"); if (ctx != NULL) { if (ctx->buf != NULL) XFREE(ctx->buf, NULL, DYNAMIC_TYPE_OPENSSL); if (ctx->reqResp != NULL) wolfSSL_BIO_free(ctx->reqResp); XFREE(ctx, NULL, DYNAMIC_TYPE_OPENSSL); } } WOLFSSL_OCSP_REQ_CTX* wolfSSL_OCSP_sendreq_new(WOLFSSL_BIO *bio, const char *path, OcspRequest *req, int maxline) { WOLFSSL_OCSP_REQ_CTX* ret = NULL; WOLFSSL_ENTER("wolfSSL_OCSP_sendreq_new"); ret = wolfSSL_OCSP_REQ_CTX_new(bio, maxline); if (ret == NULL) return NULL; if (wolfSSL_OCSP_REQ_CTX_http(ret, "POST", path) != WOLFSSL_SUCCESS) goto error; if (req != NULL && wolfSSL_OCSP_REQ_CTX_set1_req(ret, req) != WOLFSSL_SUCCESS) goto error; return ret; error: wolfSSL_OCSP_REQ_CTX_free(ret); return NULL; } int wolfSSL_OCSP_REQ_CTX_add1_header(WOLFSSL_OCSP_REQ_CTX *ctx, const char *name, const char *value) { WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_add1_header"); if (ctx == NULL || name == NULL) { WOLFSSL_MSG("Bad parameter"); return WOLFSSL_FAILURE; } if (wolfSSL_BIO_puts(ctx->reqResp, name) <= 0) { WOLFSSL_MSG("wolfSSL_BIO_puts error"); return WOLFSSL_FAILURE; } if (value != NULL) { if (wolfSSL_BIO_write(ctx->reqResp, ": ", 2) != 2) { WOLFSSL_MSG("wolfSSL_BIO_write error"); return WOLFSSL_FAILURE; } if (wolfSSL_BIO_puts(ctx->reqResp, value) <= 0) { WOLFSSL_MSG("wolfSSL_BIO_puts error"); return WOLFSSL_FAILURE; } } if (wolfSSL_BIO_write(ctx->reqResp, "\r\n", 2) != 2) { WOLFSSL_MSG("wolfSSL_BIO_write error"); return WOLFSSL_FAILURE; } ctx->state = ORS_HEADER_ADDED; return WOLFSSL_SUCCESS; } int wolfSSL_OCSP_REQ_CTX_http(WOLFSSL_OCSP_REQ_CTX *ctx, const char *op, const char *path) { static const char http_hdr[] = "%s %s HTTP/1.0\r\n"; WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_http"); if (ctx == NULL || op == NULL) { WOLFSSL_MSG("Bad parameter"); return WOLFSSL_FAILURE; } if (path == NULL) path = "/"; if (wolfSSL_BIO_printf(ctx->reqResp, http_hdr, op, path) <= 0) { WOLFSSL_MSG("WOLFSSL_OCSP_REQ_CTX: wolfSSL_BIO_printf error"); return WOLFSSL_FAILURE; } ctx->state = ORS_HEADER_ADDED; return WOLFSSL_SUCCESS; } int wolfSSL_OCSP_REQ_CTX_set1_req(WOLFSSL_OCSP_REQ_CTX *ctx, OcspRequest *req) { static const char req_hdr[] = "Content-Type: application/ocsp-request\r\n" "Content-Length: %d\r\n\r\n"; /* Should be enough to hold Content-Length */ char req_hdr_buf[sizeof(req_hdr) + 10]; int req_hdr_buf_len; int req_len = wolfSSL_i2d_OCSP_REQUEST(req, NULL); WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_set1_req"); if (ctx == NULL || req == NULL) { WOLFSSL_MSG("Bad parameters"); return WOLFSSL_FAILURE; } if (req_len <= 0) { WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request len error"); return WOLFSSL_FAILURE; } req_hdr_buf_len = XSNPRINTF(req_hdr_buf, sizeof(req_hdr_buf), req_hdr, req_len); if (req_hdr_buf_len >= (int)sizeof(req_hdr_buf)) { WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request too long"); return WOLFSSL_FAILURE; } if (wolfSSL_BIO_write(ctx->reqResp, req_hdr_buf, req_hdr_buf_len) <= 0) { WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: wolfSSL_BIO_write error"); return WOLFSSL_FAILURE; } if (wolfSSL_i2d_OCSP_REQUEST_bio(ctx->reqResp, req) <= 0) { WOLFSSL_MSG("wolfSSL_OCSP_REQ_CTX_set1_req: request i2d error"); return WOLFSSL_FAILURE; } ctx->state = ORS_REQ_DONE; return WOLFSSL_SUCCESS; } static int OCSP_REQ_CTX_bio_cb(char *buf, int sz, void *ctx) { return BioReceiveInternal((WOLFSSL_BIO*)ctx, NULL, buf, sz); } int wolfSSL_OCSP_REQ_CTX_nbio(WOLFSSL_OCSP_REQ_CTX *ctx) { WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_nbio"); if (ctx == NULL) { WOLFSSL_MSG("Bad parameters"); return WOLFSSL_FAILURE; } switch ((enum ocspReqIOStates)ctx->ioState) { case ORIOS_WRITE: case ORIOS_READ: break; case ORIOS_INVALID: default: WOLFSSL_MSG("Invalid ctx->ioState state"); return WOLFSSL_FAILURE; } if (ctx->ioState == ORIOS_WRITE) { switch ((enum ocspReqStates)ctx->state) { case ORS_HEADER_ADDED: /* Write final new line to complete http header */ if (wolfSSL_BIO_write(ctx->reqResp, "\r\n", 2) != 2) { WOLFSSL_MSG("wolfSSL_BIO_write error"); return WOLFSSL_FAILURE; } break; case ORS_REQ_DONE: break; case ORS_INVALID: default: WOLFSSL_MSG("Invalid WOLFSSL_OCSP_REQ_CTX state"); return WOLFSSL_FAILURE; } } switch ((enum ocspReqIOStates)ctx->ioState) { case ORIOS_WRITE: { const unsigned char *req; int reqLen = wolfSSL_BIO_get_mem_data(ctx->reqResp, (void*)&req); if (reqLen <= 0) { WOLFSSL_MSG("wolfSSL_BIO_get_mem_data error"); return WOLFSSL_FAILURE; } while (ctx->sent < reqLen) { int sent = wolfSSL_BIO_write(ctx->bio, req + ctx->sent, reqLen - ctx->sent); if (sent <= 0) { if (wolfSSL_BIO_should_retry(ctx->bio)) return WOLFSSL_FATAL_ERROR; WOLFSSL_MSG("wolfSSL_BIO_write error"); ctx->ioState = ORIOS_INVALID; return 0; } ctx->sent += sent; } ctx->sent = 0; ctx->ioState = ORIOS_READ; (void)wolfSSL_BIO_reset(ctx->reqResp); FALL_THROUGH; } case ORIOS_READ: { byte* resp = NULL; int respLen; int ret; if (ctx->buf == NULL) /* Should be allocated in new call */ return WOLFSSL_FAILURE; ret = wolfIO_HttpProcessResponseOcspGenericIO(OCSP_REQ_CTX_bio_cb, ctx->bio, &resp, ctx->buf, ctx->bufLen, NULL); if (ret <= 0) { if (resp != NULL) XFREE(resp, NULL, DYNAMIC_TYPE_OCSP); if (ret == WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ) || ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) { return WOLFSSL_FATAL_ERROR; } return WOLFSSL_FAILURE; } respLen = ret; ret = wolfSSL_BIO_write(ctx->reqResp, resp, respLen); XFREE(resp, NULL, DYNAMIC_TYPE_OCSP); if (ret != respLen) { WOLFSSL_MSG("wolfSSL_BIO_write error"); return WOLFSSL_FAILURE; } break; } case ORIOS_INVALID: default: WOLFSSL_MSG("Invalid ctx->ioState state"); return WOLFSSL_FAILURE; } return WOLFSSL_SUCCESS; } int wolfSSL_OCSP_sendreq_nbio(OcspResponse **presp, WOLFSSL_OCSP_REQ_CTX *ctx) { int ret; int len; const unsigned char *resp = NULL; WOLFSSL_ENTER("wolfSSL_OCSP_sendreq_nbio"); if (presp == NULL) return WOLFSSL_FAILURE; ret = wolfSSL_OCSP_REQ_CTX_nbio(ctx); if (ret != WOLFSSL_SUCCESS) return ret; len = wolfSSL_BIO_get_mem_data(ctx->reqResp, (void*)&resp); if (len <= 0) return WOLFSSL_FAILURE; return wolfSSL_d2i_OCSP_RESPONSE(presp, &resp, len) != NULL ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE; } /******************************************************************************* * END OF WOLFSSL_OCSP_REQ_CTX API ******************************************************************************/ #ifndef NO_WOLFSSL_STUB int wolfSSL_OCSP_REQUEST_add_ext(OcspRequest* req, WOLFSSL_X509_EXTENSION* ext, int idx) { WOLFSSL_STUB("wolfSSL_OCSP_REQUEST_add_ext"); (void)req; (void)ext; (void)idx; return WOLFSSL_FATAL_ERROR; } #endif #ifndef NO_WOLFSSL_STUB OcspResponse* wolfSSL_OCSP_response_create(int status, WOLFSSL_OCSP_BASICRESP* bs) { WOLFSSL_STUB("wolfSSL_OCSP_response_create"); (void)status; (void)bs; return NULL; } #endif #ifndef NO_WOLFSSL_STUB const char* wolfSSL_OCSP_crl_reason_str(long s) { WOLFSSL_STUB("wolfSSL_OCSP_crl_reason_str"); (void)s; return NULL; } #endif /* Returns elements of an OCSP_CERTID struct. Currently only supports * returning the serial number, and returns an error if user requests * any of name, pmd, and/or keyHash. * Return 1 on success, 0 on failure */ int wolfSSL_OCSP_id_get0_info(WOLFSSL_ASN1_STRING **name, WOLFSSL_ASN1_OBJECT **pmd, WOLFSSL_ASN1_STRING **keyHash, WOLFSSL_ASN1_INTEGER **serial, WOLFSSL_OCSP_CERTID *cid) { WOLFSSL_ENTER("wolfSSL_OCSP_id_get0_info"); if (cid == NULL) return 0; /* build up ASN1_INTEGER for serial */ if (serial != NULL) { int i = 0; WOLFSSL_ASN1_INTEGER* ser; ser = wolfSSL_ASN1_INTEGER_new(); if (ser == NULL) return 0; if (cid->status->serialSz > (WOLFSSL_ASN1_INTEGER_MAX - 2)) { /* allocate data buffer, +2 for type and length */ ser->data = (unsigned char*)XMALLOC((size_t)cid->status->serialSz + 2, NULL, DYNAMIC_TYPE_OPENSSL); if (ser->data == NULL) { wolfSSL_ASN1_INTEGER_free(ser); return 0; } ser->dataMax = (unsigned int)cid->status->serialSz + 2; ser->isDynamic = 1; } else { /* Use array instead of dynamic memory */ ser->data = ser->intData; ser->dataMax = WOLFSSL_ASN1_INTEGER_MAX; } #if defined(WOLFSSL_QT) || defined(WOLFSSL_HAPROXY) /* Serial number starts at 0 index of ser->data */ XMEMCPY(&ser->data[i], cid->status->serial, (size_t)cid->status->serialSz); ser->length = cid->status->serialSz; #else ser->data[i++] = ASN_INTEGER; i += SetLength(cid->status->serialSz, ser->data + i); XMEMCPY(&ser->data[i], cid->status->serial, (size_t)cid->status->serialSz); ser->length = i + cid->status->serialSz; #endif cid->status->serialInt = ser; *serial = ser; } /* Not needed for Apache, return error if user is requesting */ if (name != NULL || pmd != NULL || keyHash != NULL) { if (name != NULL) *name = NULL; if (pmd != NULL) *pmd = NULL; if (keyHash != NULL) *keyHash = NULL; return 0; } return 1; } int wolfSSL_OCSP_request_add1_nonce(OcspRequest* req, unsigned char* val, int sz) { WC_RNG rng; WOLFSSL_ENTER("wolfSSL_OCSP_request_add1_nonce"); if (req == NULL || sz > MAX_OCSP_NONCE_SZ) { WOLFSSL_MSG("Bad parameter"); return WOLFSSL_FAILURE; } if (sz <= 0) sz = MAX_OCSP_NONCE_SZ; if (val != NULL) { XMEMCPY(req->nonce, val, (size_t)sz); } else { if ( #ifndef HAVE_FIPS wc_InitRng_ex(&rng, req->heap, INVALID_DEVID) #else wc_InitRng(&rng) #endif != 0) { WOLFSSL_MSG("RNG init failed"); return WOLFSSL_FAILURE; } if (wc_RNG_GenerateBlock(&rng, req->nonce, (word32)sz) != 0) { WOLFSSL_MSG("wc_RNG_GenerateBlock failed"); wc_FreeRng(&rng); return WOLFSSL_FAILURE; } wc_FreeRng(&rng); } req->nonceSz = sz; return WOLFSSL_SUCCESS; } /* Returns result of OCSP nonce comparison. Return values: * 1 - nonces are both present and equal * 2 - both nonces are absent * 3 - nonce only present in response * -1 - nonce only present in request * 0 - both nonces present and equal */ int wolfSSL_OCSP_check_nonce(OcspRequest* req, WOLFSSL_OCSP_BASICRESP* bs) { const byte* reqNonce = NULL; const byte* rspNonce = NULL; int reqNonceSz = 0; int rspNonceSz = 0; WOLFSSL_ENTER("wolfSSL_OCSP_check_nonce"); if (req != NULL) { reqNonce = req->nonce; reqNonceSz = req->nonceSz; } if (bs != NULL) { rspNonce = bs->nonce; rspNonceSz = bs->nonceSz; } /* nonce absent in both req and rsp */ if (reqNonce == NULL && rspNonce == NULL) return 2; /* nonce present in rsp only */ if (reqNonce == NULL && rspNonce != NULL) return 3; /* nonce present in req only */ if (reqNonce != NULL && rspNonce == NULL) return WOLFSSL_FATAL_ERROR; /* nonces are present and equal, return 1. Extra NULL check for fixing scan-build warning. */ if (reqNonceSz == rspNonceSz && reqNonce && rspNonce) { if (XMEMCMP(reqNonce, rspNonce, (size_t)reqNonceSz) == 0) return 1; } /* nonces are present but not equal */ return 0; } #endif /* OPENSSL_ALL */ #ifdef HAVE_OCSP_RESPONDER /* Free a CA entry and all its resources */ static void FreeOcspResponderCa(OcspResponderCa* ca, void* heap) { OcspResponderCertStatus* status; OcspResponderCertStatus* nextStatus; if (ca == NULL) return; /* Free private key */ #ifndef NO_RSA if (ca->keyType == RSAk) { wc_FreeRsaKey(&ca->key.rsa); } #endif #ifdef HAVE_ECC if (ca->keyType == ECDSAk) { wc_ecc_free(&ca->key.ecc); } #endif /* Free certificate DER if allocated */ if (ca->certDer != NULL) { XFREE(ca->certDer, heap, DYNAMIC_TYPE_OCSP); } /* Free certificate status list */ status = ca->statuses; while (status != NULL) { nextStatus = status->next; XFREE(status, heap, DYNAMIC_TYPE_OCSP); status = nextStatus; } XFREE(ca, heap, DYNAMIC_TYPE_OCSP); } /* Allocate and initialize an OCSP Responder */ OcspResponder* wc_OcspResponder_new(void* heap, int sendCerts) { OcspResponder* responder; WOLFSSL_ENTER("wc_OcspResponder_new"); responder = (OcspResponder*)XMALLOC(sizeof(OcspResponder), heap, DYNAMIC_TYPE_OCSP); if (responder != NULL) { XMEMSET(responder, 0, sizeof(OcspResponder)); responder->heap = heap; responder->sendCerts = sendCerts ? 1 : 0; if (wc_InitRng(&responder->rng) != 0) { XFREE(responder, heap, DYNAMIC_TYPE_OCSP); responder = NULL; } } return responder; } /* Free an OCSP Responder */ void wc_OcspResponder_free(OcspResponder* responder) { WOLFSSL_ENTER("wc_OcspResponder_free"); if (responder != NULL) { OcspResponderCa* ca; OcspResponderCa* nextCa; /* Free all CAs */ ca = responder->caList; while (ca != NULL) { nextCa = ca->next; FreeOcspResponderCa(ca, responder->heap); ca = nextCa; } wc_FreeRng(&responder->rng); XFREE(responder, responder->heap, DYNAMIC_TYPE_OCSP); } } int wc_OcspResponder_AddSigner(OcspResponder* responder, const byte* signerDer, word32 signerDerSz, const byte* keyDer, word32 keyDerSz, /* Necessary when adding an authorized responder */ const byte* issuerCertDer, word32 issuerCertDerSz) { int ret = -1; OcspResponderCa* ca = NULL; DecodedCert* decoded = NULL; word32 keyOID = 0; WOLFSSL_ENTER("wc_OcspResponder_AddSigner"); if (responder == NULL || signerDer == NULL || signerDerSz == 0 || keyDer == NULL || keyDerSz == 0 || (issuerCertDerSz != 0 && issuerCertDer == NULL)) return BAD_FUNC_ARG; /* Allocate CA structure */ ca = (OcspResponderCa*)XMALLOC(sizeof(OcspResponderCa), responder->heap, DYNAMIC_TYPE_OCSP); if (ca == NULL) { ret = MEMORY_E; goto out; } XMEMSET(ca, 0, sizeof(OcspResponderCa)); /* Parse certificate */ decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), responder->heap, DYNAMIC_TYPE_OCSP); if (decoded == NULL) { ret = MEMORY_E; goto out; } wc_InitDecodedCert(decoded, signerDer, signerDerSz, responder->heap); ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL); if (ret != 0) goto out; ret = wc_CheckPrivateKeyCert(keyDer, keyDerSz, decoded, 1, responder->heap); if (ret != 1) { ret = (ret == 0) ? BAD_FUNC_ARG : ret; goto out; } keyOID = decoded->keyOID; ret = wc_ShaHash(decoded->publicKeyForHash, decoded->pubKeyForHashSize, ca->responderKeyHash); if (ret != 0) goto out; /* Now place the real issuer into decoded and perform cert can actually * sign for issuer. */ if (issuerCertDer != NULL && issuerCertDerSz > 0) { char issuer[WC_ASN_NAME_MAX]; if (signerDerSz == issuerCertDerSz && XMEMCMP(signerDer, issuerCertDer, signerDerSz) == 0) { /* API misuse */ ret = BAD_FUNC_ARG; goto out; } if ((decoded->extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) == 0) { /* Cert is not authorized for OCSP signing */ ret = BAD_FUNC_ARG; goto out; } XMEMCPY(issuer, decoded->issuer, WC_ASN_NAME_MAX); /* Use allocated DecodedCert */ wc_FreeDecodedCert(decoded); wc_InitDecodedCert(decoded, issuerCertDer, issuerCertDerSz, responder->heap); ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL); if (ret != 0) goto out; if (XSTRNCMP(issuer, decoded->subject, WC_ASN_NAME_MAX) != 0) { /* Issuer name in responder cert does not match subject of issuer cert */ ret = BAD_FUNC_ARG; goto out; } ca->authResp = 1; } /* Use responder cert's subject and public key for hashes */ if (decoded->subjectRawForHash == NULL || decoded->subjectRawForHashLen <= 0) { ret = BAD_FUNC_ARG; goto out; } ret = AsnHashesHash(&ca->issuerHashes, decoded->subjectRawForHash, (word32)decoded->subjectRawForHashLen); if (ret != 0) goto out; if (decoded->publicKeyForHash == NULL || decoded->pubKeyForHashSize == 0) { ret = BAD_FUNC_ARG; goto out; } ret = AsnHashesHash(&ca->issuerKeyHashes, decoded->publicKeyForHash, decoded->pubKeyForHashSize); if (ret != 0) goto out; /* Extract necessary info from decoded cert */ XMEMCPY(ca->subject, decoded->subject, WC_ASN_NAME_MAX); /* Store raw certificate DER if sendCerts is enabled */ if (responder->sendCerts) { ca->certDer = (byte*)XMALLOC(signerDerSz, responder->heap, DYNAMIC_TYPE_OCSP); if (ca->certDer == NULL) { ret = MEMORY_E; goto out; } XMEMCPY(ca->certDer, signerDer, signerDerSz); ca->certDerSz = signerDerSz; } /* Load the private key */ #ifndef NO_RSA if (keyOID == RSAk) { ret = wc_InitRsaKey(&ca->key.rsa, responder->heap); if (ret == 0) { word32 idx = 0; ca->keyType = RSAk; ret = wc_RsaPrivateKeyDecode(keyDer, &idx, &ca->key.rsa, keyDerSz); } if (ret != 0) goto out; } else #endif #ifdef HAVE_ECC if (keyOID == ECDSAk) { ret = wc_ecc_init_ex(&ca->key.ecc, responder->heap, INVALID_DEVID); if (ret == 0) { word32 idx = 0; ca->keyType = ECDSAk; ret = wc_EccPrivateKeyDecode(keyDer, &idx, &ca->key.ecc, keyDerSz); } if (ret != 0) goto out; } else #endif { ret = NOT_COMPILED_IN; goto out; } /* Check for duplicates before adding */ { OcspResponderCa* existing = responder->caList; while (existing != NULL) { if (XMEMCMP(&existing->issuerHashes, &ca->issuerHashes, sizeof(AsnHashes)) == 0 && XMEMCMP(&existing->issuerKeyHashes, &ca->issuerKeyHashes, sizeof(AsnHashes)) == 0) { ret = DUPE_ENTRY_E; goto out; } existing = existing->next; } } /* Add CA to list */ ca->next = responder->caList; responder->caList = ca; ca = NULL; /* Ownership transferred */ ret = 0; out: if (decoded != NULL) { wc_FreeDecodedCert(decoded); XFREE(decoded, responder->heap, DYNAMIC_TYPE_OCSP); } if (ca != NULL) FreeOcspResponderCa(ca, responder->heap); return ret; } /* Find Auth CA by issuer hashes from request */ static OcspResponderCa* FindCaByHashes(OcspResponder* responder, const byte* issuerHash, const byte* issuerKeyHash, int hashAlg) { OcspResponderCa* ca = responder->caList; while (ca != NULL) { int hashSz = 0; const byte* caIssuerHash = AsnHashesGetHash(&ca->issuerHashes, hashAlg, &hashSz); const byte* caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes, hashAlg, &hashSz); if (caIssuerHash != NULL && caKeyHash != NULL && hashSz > 0 && XMEMCMP(caIssuerHash, issuerHash, (size_t)hashSz) == 0 && XMEMCMP(caKeyHash, issuerKeyHash, (size_t)hashSz) == 0) { return ca; } ca = ca->next; } return NULL; } /* Find certificate status in a CA */ static OcspResponderCertStatus* FindCertStatus(OcspResponderCa* ca, const byte* serial, word32 serialSz) { OcspResponderCertStatus* status = ca->statuses; while (status != NULL) { if (status->serialSz == (int)serialSz && XMEMCMP(status->serial, serial, serialSz) == 0) { return status; } status = status->next; } return NULL; } /* Find Auth CA by subject string */ static OcspResponderCa* FindCaBySubject(OcspResponder* responder, const char* caSubject, word32 caSubjectSz) { OcspResponderCa* ca = responder->caList; while (ca != NULL) { word32 subjectLen = (word32)XSTRLEN(ca->subject); if (subjectLen == caSubjectSz && XMEMCMP(ca->subject, caSubject, caSubjectSz) == 0) { return ca; } ca = ca->next; } return NULL; } /* Add a certificate status for a specific CA */ int wc_OcspResponder_SetCertStatus(OcspResponder* responder, const char* caSubject, word32 caSubjectSz, const byte* serial, word32 serialSz, enum Ocsp_Cert_Status status, time_t revocationTime, enum WC_CRL_Reason revocationReason, word32 validityPeriod) { OcspResponderCa* ca = NULL; OcspResponderCertStatus* certStatus = NULL; int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); int isNew = 0; WOLFSSL_ENTER("wc_OcspResponder_SetCertStatus"); if (responder == NULL || caSubject == NULL || caSubjectSz == 0 || serial == NULL || serialSz == 0 || serialSz > EXTERNAL_SERIAL_SIZE) goto out; if (status != CERT_GOOD && status != CERT_REVOKED && status != CERT_UNKNOWN) goto out; /* Validate revocation parameters when status is REVOKED */ if (status == CERT_REVOKED) { if (revocationTime <= 0) goto out; if (revocationReason < WC_CRL_REASON_UNSPECIFIED || revocationReason > WC_CRL_REASON_AA_COMPROMISE) goto out; /* Skip value 7 which is not used */ if (revocationReason == 7) goto out; } if (status == CERT_GOOD && validityPeriod == 0) goto out; if (status != CERT_GOOD && validityPeriod != 0) goto out; /* Find the CA */ ca = FindCaBySubject(responder, caSubject, caSubjectSz); if (ca == NULL) { ret = ASN_NO_SIGNER_E; goto out; } /* Check if status already exists for this serial */ certStatus = FindCertStatus(ca, serial, serialSz); if (certStatus == NULL) { /* Allocate new status entry */ certStatus = (OcspResponderCertStatus*)XMALLOC( sizeof(OcspResponderCertStatus), responder->heap, DYNAMIC_TYPE_OCSP); if (certStatus == NULL) { ret = MEMORY_E; goto out; } XMEMSET(certStatus, 0, sizeof(OcspResponderCertStatus)); XMEMCPY(certStatus->serial, serial, serialSz); certStatus->serialSz = serialSz; isNew = 1; } certStatus->status = status; certStatus->validityPeriod = 0; if (status == CERT_REVOKED) { ret = GetFormattedTime_ex(&revocationTime, certStatus->revocationDate, sizeof(certStatus->revocationDate), ASN_GENERALIZED_TIME); if (ret <= 0) { WOLFSSL_MSG("Failed to format revocation time"); goto out; } certStatus->revocationDateSz = (word32)ret; certStatus->revocationReason = revocationReason; } else if (status == CERT_GOOD) { certStatus->validityPeriod = validityPeriod; } if (isNew) { /* Add to CA's status list */ certStatus->next = ca->statuses; ca->statuses = certStatus; certStatus = NULL; /* Ownership transferred */ } ret = 0; out: if (isNew && certStatus != NULL) XFREE(certStatus, responder->heap, DYNAMIC_TYPE_OCSP); return ret; } static int OcspResponse_WriteResponse(OcspResponder* responder, byte* response, word32* responseSz, OcspResponderCa* ca, OcspResponderCertStatus* certStatus, OcspRequest* req) { OcspResponse resp; CertStatus status; OcspEntry entry; int ret = 0; time_t now; RsaKey* rsaKey = NULL; ecc_key* eccKey = NULL; int respInited = 0; int hashSz = 0; const byte* caIssuerHash = NULL; const byte* caKeyHash = NULL; int hashAlg = -1; WOLFSSL_ASSERT_SIZEOF_EQ(resp.responderId.keyHash, ca->responderKeyHash); WOLFSSL_ENTER("OcspResponse_WriteResponse"); if (responseSz == NULL || ca == NULL || certStatus == NULL || certStatus->serialSz > EXTERNAL_SERIAL_SIZE) { ret = BAD_FUNC_ARG; goto out; } InitOcspResponse(&resp, &entry, &status, NULL, 0, responder->heap); respInited = 1; resp.responseStatus = OCSP_SUCCESSFUL; status.status = certStatus->status; XMEMCPY(status.serial, certStatus->serial, certStatus->serialSz); status.serialSz = (byte)certStatus->serialSz; /* Copy revocation information if status is REVOKED */ if (certStatus->status == CERT_REVOKED) { XMEMCPY(status.revocationDate, certStatus->revocationDate, certStatus->revocationDateSz); status.revocationDateSz = certStatus->revocationDateSz; status.revocationReason = (byte)certStatus->revocationReason; } now = wc_Time(NULL); ret = GetFormattedTime_ex(&now, status.thisDate, sizeof(status.thisDate), ASN_GENERALIZED_TIME); if (ret <= 0) { WOLFSSL_MSG("Failed to format thisUpdate time"); goto out; } XMEMCPY(resp.producedDate, status.thisDate, ret); resp.producedDateSz = status.thisDateSz = (byte)(ret); resp.producedDateFormat = status.thisDateFormat = ASN_GENERALIZED_TIME; /* Set nextUpdate if validity period is specified (for CERT_GOOD) */ if (certStatus->status == CERT_GOOD && certStatus->validityPeriod > 0) { time_t nextTime = now + (time_t)certStatus->validityPeriod; ret = GetFormattedTime_ex(&nextTime, status.nextDate, sizeof(status.nextDate), ASN_GENERALIZED_TIME); if (ret <= 0) { WOLFSSL_MSG("Failed to format nextUpdate time"); goto out; } status.nextDateSz = (byte)ret; status.nextDateFormat = ASN_GENERALIZED_TIME; } /* Set certificate if sendCerts is enabled */ if (responder->sendCerts && ca->certDer != NULL) { resp.cert = ca->certDer; resp.certSz = ca->certDerSz; } /* Copy nonce from request to response if present */ if (req != NULL && req->nonceSz > 0) { resp.nonce = req->nonce; resp.nonceSz = req->nonceSz; } /* Echo the hash algorithm from the request */ if (req == NULL) { hashAlg = SHAh; /* Fall back to SHA-1 as its required for OCSP */ } else { hashAlg = req->hashAlg; } caIssuerHash = AsnHashesGetHash(&ca->issuerHashes, hashAlg, &hashSz); caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes, hashAlg, &hashSz); if (caIssuerHash == NULL || caKeyHash == NULL || hashSz <= 0) { ret = ASN_SIG_HASH_E; goto out; } entry.hashAlgoOID = (word32)hashAlg; XMEMCPY(entry.issuerHash, caIssuerHash, (size_t)hashSz); XMEMCPY(entry.issuerKeyHash, caKeyHash, (size_t)hashSz); resp.responderIdType = OCSP_RESPONDER_ID_KEY; XMEMCPY(resp.responderId.keyHash, ca->responderKeyHash, WC_SHA_DIGEST_SIZE); /* TODO allow user to set algo */ if (ca->keyType == RSAk) { rsaKey = &ca->key.rsa; resp.sigOID = CTC_SHA256wRSA; } else if (ca->keyType == ECDSAk) { eccKey = &ca->key.ecc; resp.sigOID = CTC_SHA256wECDSA; } else { ret = NOT_COMPILED_IN; goto out; } ret = OcspResponseEncode(&resp, response, responseSz, rsaKey, eccKey, &responder->rng); if (ret != 0) { WOLFSSL_MSG("Failed to encode OCSP response"); goto out; } ret = 0; out: if (respInited) FreeOcspResponse(&resp); return ret; } /* Generate OCSP response for a request */ int wc_OcspResponder_WriteResponse(OcspResponder* responder, const byte* request, word32 requestSz, byte* response, word32* responseSz) { int ret = 0; OcspRequest req; OcspResponderCa* ca = NULL; OcspResponderCertStatus* certStatus = NULL; int reqInited = 0; WOLFSSL_ENTER("wc_OcspResponder_WriteResponse"); if (responder == NULL || request == NULL || requestSz == 0) { ret = BAD_FUNC_ARG; goto out; } XMEMSET(&req, 0, sizeof(OcspRequest)); /* Decode the OCSP request */ ret = DecodeOcspRequest(&req, request, requestSz); if (ret != 0) { WOLFSSL_MSG("Failed to decode OCSP request"); goto out; } reqInited = 1; /* Find the CA by issuer hashes */ ca = FindCaByHashes(responder, req.issuerHash, req.issuerKeyHash, req.hashAlg); if (ca == NULL) { WOLFSSL_MSG("No matching CA found for request"); ret = ASN_NO_SIGNER_E; goto out; } /* Find the certificate status */ certStatus = FindCertStatus(ca, req.serial, req.serialSz); if (certStatus == NULL) { /* RFC 6960: 'unknown' is a per-certificate status inside a successful * OCSPResponse, not an error response. Generate a successful response * with CERT_UNKNOWN so clients can distinguish it from UNAUTHORIZED. */ OcspResponderCertStatus unknownStatus; WOLFSSL_MSG("No status for requested certificate, responding unknown"); if (req.serialSz > EXTERNAL_SERIAL_SIZE) { ret = BUFFER_E; goto out; } XMEMSET(&unknownStatus, 0, sizeof(unknownStatus)); XMEMCPY(unknownStatus.serial, req.serial, req.serialSz); unknownStatus.serialSz = req.serialSz; unknownStatus.status = CERT_UNKNOWN; ret = OcspResponse_WriteResponse(responder, response, responseSz, ca, &unknownStatus, &req); } else { WOLFSSL_MSG("Found CA and certificate status"); ret = OcspResponse_WriteResponse(responder, response, responseSz, ca, certStatus, &req); } out: if (reqInited) FreeOcspRequest(&req); WOLFSSL_LEAVE("wc_OcspResponder_WriteResponse", ret); return ret; } int wc_OcspResponder_WriteErrorResponse(enum Ocsp_Response_Status status, byte* response, word32* responseSz) { int ret = 0; OcspResponse resp; int respInited = 0; WOLFSSL_ENTER("wc_OcspResponder_WriteErrorResponse"); if (responseSz == NULL) { ret = BAD_FUNC_ARG; goto out; } /* Validate status - OCSP_SUCCESSFUL is not allowed for error responses */ if (status == OCSP_SUCCESSFUL) { WOLFSSL_MSG("OCSP_SUCCESSFUL is not a valid error status"); ret = BAD_FUNC_ARG; goto out; } /* Validate that status is a known enumeration value */ if (status != OCSP_MALFORMED_REQUEST && status != OCSP_INTERNAL_ERROR && status != OCSP_TRY_LATER && status != OCSP_SIG_REQUIRED && status != OCSP_UNAUTHORIZED) { WOLFSSL_MSG("Invalid OCSP response status"); ret = BAD_FUNC_ARG; goto out; } /* Initialize a minimal OCSP response structure */ InitOcspResponse(&resp, NULL, NULL, NULL, 0, NULL); respInited = 1; /* Set the error status */ resp.responseStatus = (byte)status; /* Encode the error response (no responseBytes, just status) */ ret = OcspResponseEncode(&resp, response, responseSz, NULL, NULL, NULL); if (ret != 0) { WOLFSSL_MSG("Failed to encode OCSP error response"); goto out; } ret = 0; out: if (respInited) FreeOcspResponse(&resp); return ret; } #endif /* HAVE_OCSP_RESPONDER */ /* Helper functions for testing */ int wc_InitOcspRequest(OcspRequest* req, DecodedCert* cert, byte useNonce, void* heap) { return InitOcspRequest(req, cert, useNonce, heap); } int wc_EncodeOcspRequest(OcspRequest* req, byte* output, word32 size) { return EncodeOcspRequest(req, output, size); } #else /* HAVE_OCSP */ #ifdef _MSC_VER /* 4206 warning for blank file */ #pragma warning(disable: 4206) #endif #endif /* HAVE_OCSP */ #endif /* WOLFCRYPT_ONLY */