Merge pull request #8408 from rizlik/ocsp-resp-refactor

OpenSSL Compat Layer: OCSP response improvments
This commit is contained in:
David Garske
2025-02-19 11:20:12 -08:00
committed by GitHub
19 changed files with 3018 additions and 386 deletions

View File

@@ -2510,6 +2510,7 @@ if(WOLFSSL_EXAMPLES)
tests/api/test_ripemd.c
tests/api/test_hash.c
tests/api/test_ascon.c
tests/api/test_ocsp.c
tests/hash.c
tests/srp.c
tests/suites.c

View File

@@ -36,4 +36,5 @@ EXTRA_DIST += \
certs/ocsp/test-response.der \
certs/ocsp/test-response-rsapss.der \
certs/ocsp/test-response-nointern.der \
certs/ocsp/test-multi-response.der
certs/ocsp/test-multi-response.der \
certs/ocsp/test-leaf-response.der

View File

@@ -100,6 +100,16 @@ openssl ocsp -issuer ./root-ca-cert.pem -cert ./intermediate1-ca-cert.pem -cert
kill $PID
wait $PID
# Create a response DER buffer for testing leaf certificate
openssl ocsp -port 22221 -ndays 1000 -index \
./index-intermediate1-ca-issued-certs.txt -rsigner ocsp-responder-cert.pem \
-rkey ocsp-responder-key.pem -CA intermediate1-ca-cert.pem -partial_chain &
PID=$!
sleep 1 # Make sure server is ready
openssl ocsp -issuer ./intermediate1-ca-cert.pem -cert ./server1-cert.pem -url http://localhost:22221/ -respout test-leaf-response.der -noverify
kill $PID
wait $PID
# now start up a responder that signs using rsa-pss
openssl ocsp -port 22221 -ndays 1000 -index index-ca-and-intermediate-cas.txt -rsigner ocsp-responder-cert.pem -rkey ocsp-responder-key.pem -CA root-ca-cert.pem -rsigopt rsa_padding_mode:pss &

Binary file not shown.

View File

@@ -9163,7 +9163,6 @@ then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_PRIORITIZE_PSK"
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_CHECK_ALERT_ON_ERR"
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TICKET_HAVE_ID"
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_OCSP_ISSUER_CHECK"
ENABLED_TRUSTED_PEER_CERT=yes
else
CFLAGS=$(printf "%s" "$CFLAGS" | sed 's/-DOPENSSL_COMPATIBLE_DEFAULTS//g')

View File

@@ -8690,6 +8690,13 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
#ifdef OPENSSL_EXTRA
XFREE(ssl->param, ssl->heap, DYNAMIC_TYPE_OPENSSL);
#endif
#if defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY))
if (ssl->ocspResp) {
XFREE(ssl->ocspResp, NULL, 0);
ssl->ocspResp = NULL;
ssl->ocspRespSz = 0;
}
#endif /* defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)) */
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
while (ssl->certReqCtx != NULL) {
CertReqCtx* curr = ssl->certReqCtx;
@@ -9014,6 +9021,14 @@ void FreeHandshakeResources(WOLFSSL* ssl)
* !WOLFSSL_POST_HANDSHAKE_AUTH */
#endif /* HAVE_TLS_EXTENSIONS && !NO_TLS */
#if defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY))
if (ssl->ocspResp != NULL) {
XFREE(ssl->ocspResp, NULL, 0);
ssl->ocspResp = NULL;
ssl->ocspRespSz = 0;
}
#endif /* defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)) */
#ifdef WOLFSSL_STATIC_MEMORY
/* when done with handshake decrement current handshake count */
if (ssl->heap != NULL) {
@@ -13861,7 +13876,7 @@ static int ProcessCSR_ex(WOLFSSL* ssl, byte* input, word32* inOutIdx,
/* InitOcspResponse sets single and status to response struct. */
InitOcspResponse(response, single, status, input +*inOutIdx, status_length, ssl->heap);
if (OcspResponseDecode(response, SSL_CM(ssl), ssl->heap, 0) != 0)
if (OcspResponseDecode(response, SSL_CM(ssl), ssl->heap, 0, 0) != 0)
ret = BAD_CERTIFICATE_STATUS_ERROR;
else if (CompareOcspReqResp(request, response) != 0)
ret = BAD_CERTIFICATE_STATUS_ERROR;
@@ -16967,7 +16982,7 @@ static int DoCertificateStatus(WOLFSSL* ssl, byte* input, word32* inOutIdx,
status_length, ssl->heap);
response->pendingCAs = pendingCAs;
if ((OcspResponseDecode(response, SSL_CM(ssl), ssl->heap,
0) != 0)
0, 0) != 0)
|| (response->responseStatus != OCSP_SUCCESSFUL)
|| (response->single->status->status != CERT_GOOD))
ret = BAD_CERTIFICATE_STATUS_ERROR;
@@ -24099,7 +24114,7 @@ int CreateOcspRequest(WOLFSSL* ssl, OcspRequest* request,
ret = InitOcspRequest(request, cert, 0, ssl->heap);
if (ret == 0) {
/* make sure ctx OCSP request is updated */
if (!ssl->buffers.weOwnCert) {
if (!ssl->buffers.weOwnCert && SSL_CM(ssl) != NULL) {
wolfSSL_Mutex* ocspLock = &SSL_CM(ssl)->ocsp_stapling->ocspLock;
if (wc_LockMutex(ocspLock) == 0) {
if (ssl->ctx->certOcspRequest == NULL) {
@@ -24840,6 +24855,49 @@ static int BuildCertificateStatus(WOLFSSL* ssl, byte type, buffer* status,
return ret;
}
#endif
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
(defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
defined(WOLFSSL_HAPROXY))
static int BuildCertificateStatusWithStatusCB(WOLFSSL* ssl)
{
WOLFSSL_OCSP *ocsp;
void *ioCtx = NULL;
buffer response;
int ret;
ocsp = SSL_CM(ssl)->ocsp_stapling;
if (ocsp == NULL || ocsp->statusCb == NULL)
return BAD_FUNC_ARG;
ioCtx = (ssl && ssl->ocspIOCtx != NULL) ?
ssl->ocspIOCtx : ocsp->cm->ocspIOCtx;
XMEMSET(&response, 0, sizeof(response));
WOLFSSL_MSG("Calling ocsp->statusCb");
ret = ocsp->statusCb(ssl, ioCtx);
switch (ret) {
case SSL_TLSEXT_ERR_OK:
if (ssl->ocspResp == NULL || ssl->ocspRespSz == 0) {
ret = 0;
break;
}
response.buffer = ssl->ocspResp;
response.length = ssl->ocspRespSz;
ret = BuildCertificateStatus(ssl, WOLFSSL_CSR_OCSP, &response, 1);
break;
case SSL_TLSEXT_ERR_NOACK:
/* No OCSP response to send */
ret = 0;
break;
case SSL_TLSEXT_ERR_ALERT_FATAL:
/* fall through */
default:
ret = WOLFSSL_FATAL_ERROR;
break;
}
return ret;
}
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST && (defined(OPENSSL_ALL) ||
defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)) */
#endif /* NO_WOLFSSL_SERVER */
/* handle generation of certificate_status (22) */
@@ -24860,6 +24918,20 @@ int SendCertificateStatus(WOLFSSL* ssl)
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
status_type = status_type ? status_type : ssl->status_request_v2;
#endif
if (ssl == NULL || SSL_CM(ssl) == NULL) {
WOLFSSL_MSG("SendCertificateStatus bad args");
return BAD_FUNC_ARG;
}
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
(defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \
defined(WOLFSSL_HAPROXY))
if (SSL_CM(ssl)->ocsp_stapling != NULL &&
SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) {
if (ssl->status_request == WOLFSSL_CSR_OCSP)
return BuildCertificateStatusWithStatusCB(ssl);
}
#endif
switch (status_type) {

View File

@@ -333,7 +333,7 @@ int CheckOcspResponse(WOLFSSL_OCSP *ocsp, byte *response, int responseSz,
ocspResponse->pendingCAs = TLSX_CSR2_GetPendingSigners(((WOLFSSL*)ocspRequest->ssl)->extensions);
}
#endif
ret = OcspResponseDecode(ocspResponse, ocsp->cm, ocsp->cm->heap, 0);
ret = OcspResponseDecode(ocspResponse, ocsp->cm, ocsp->cm->heap, 0, 0);
if (ret != 0) {
ocsp->error = ret;
WOLFSSL_LEAVE("OcspResponseDecode failed", ocsp->error);
@@ -480,31 +480,6 @@ int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest,
ioCtx = (ssl && ssl->ocspIOCtx != NULL) ?
ssl->ocspIOCtx : ocsp->cm->ocspIOCtx;
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
if (ocsp->statusCb != NULL && ssl != NULL) {
WOLFSSL_MSG("Calling ocsp->statusCb");
ret = ocsp->statusCb(ssl, ioCtx);
switch (ret) {
case SSL_TLSEXT_ERR_OK:
ret = wolfSSL_get_ocsp_response(ssl, &response);
ret = CheckOcspResponse(ocsp, response, ret, responseBuffer,
status, entry, NULL, heap);
XFREE(response, NULL, DYNAMIC_TYPE_OPENSSL);
break;
case SSL_TLSEXT_ERR_NOACK:
ret = OCSP_LOOKUP_FAIL;
break;
case SSL_TLSEXT_ERR_ALERT_FATAL:
default:
WOLFSSL_LEAVE("CheckOcspRequest", ocsp->error);
ret = WOLFSSL_FATAL_ERROR;
break;
}
WOLFSSL_LEAVE("CheckOcspRequest", ret);
return ret;
}
#endif
if (ocsp->cm->ocspUseOverrideURL) {
url = ocsp->cm->ocspOverrideURL;
if (url != NULL && url[0] != '\0')
@@ -656,9 +631,6 @@ int CheckOcspResponder(OcspResponse *bs, DecodedCert *cert, void* vp)
if (!passed) {
WOLFSSL_MSG("\tOCSP Responder not authorized");
#ifdef OPENSSL_EXTRA
bs->verifyError = OCSP_BAD_ISSUER;
#endif
ret = BAD_OCSP_RESPONDER;
break;
}
@@ -850,79 +822,174 @@ void wolfSSL_OCSP_BASICRESP_free(WOLFSSL_OCSP_BASICRESP* basicResponse)
wolfSSL_OCSP_RESPONSE_free(basicResponse);
}
/* 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)
static int OcspRespIdMatches(OcspResponse* resp, const byte* NameHash,
const byte* keyHash)
{
int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
#ifdef WOLFSSL_SMALL_STACK
DecodedCert *cert;
#else
DecodedCert cert[1];
#endif
byte certInit = 0;
int idx;
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, KEYID_SIZE) == 0;
}
(void)certs;
return 0;
}
if (flags & WOLFSSL_OCSP_NOVERIFY)
return WOLFSSL_SUCCESS;
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;
#ifdef WOLFSSL_SMALL_STACK
cert = (DecodedCert *)
XMALLOC(sizeof(*cert), (st && st->cm) ? st->cm->heap : NULL,
DYNAMIC_TYPE_DCERT);
if (cert == NULL)
return WOLFSSL_FAILURE;
#endif
certDecoded = (DecodedCert *)XMALLOC(sizeof(*certDecoded), resp->heap,
DYNAMIC_TYPE_DCERT);
if (certDecoded == NULL)
return MEMORY_E;
if (bs->verifyError != OCSP_VERIFY_ERROR_NONE)
goto out;
for (i = 0; i < wolfSSL_sk_X509_num(certs); i++) {
signer_x509 = wolfSSL_sk_X509_value(certs, i);
if (signer_x509 == NULL)
continue;
if (flags & WOLFSSL_OCSP_TRUSTOTHER) {
for (idx = 0; idx < wolfSSL_sk_X509_num(certs); idx++) {
WOLFSSL_X509* x = wolfSSL_sk_X509_value(certs, idx);
int derSz = 0;
const byte* der = wolfSSL_X509_get_der(x, &derSz);
if (der != NULL && derSz == (int)bs->certSz &&
XMEMCMP(bs->cert, der, (size_t)derSz) == 0) {
ret = WOLFSSL_SUCCESS;
goto out;
}
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);
InitDecodedCert(cert, bs->cert, bs->certSz, NULL);
certInit = 1;
if (ParseCertRelative(cert, CERT_TYPE, VERIFY, st->cm, NULL) < 0)
goto out;
if (!(flags & WOLFSSL_OCSP_NOCHECKS)) {
if (CheckOcspResponder(bs, cert, st->cm) != 0)
goto out;
}
ret = WOLFSSL_SUCCESS;
out:
if (certInit)
FreeDecodedCert(cert);
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)
{
#ifdef WOLFSSL_SMALL_STACK
XFREE(cert, (st && st->cm) ? st->cm->heap : NULL, DYNAMIC_TYPE_DCERT);
DecodedCert *c = NULL;
#else
DecodedCert c[1];
#endif
int ret = -1;
if (st == NULL)
return ASN_OCSP_CONFIRM_E;
#ifdef WOLFSSL_SMALL_STACK
c = (DecodedCert *)XMALLOC(sizeof(*c), NULL, DYNAMIC_TYPE_DCERT);
if (c == NULL)
return MEMORY_E;
#endif
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, st->cm);
}
else {
ret = 0;
}
#else
(void)resp;
(void)flags;
ret = 0;
#endif
err:
FreeDecodedCert(c);
#ifdef WOLFSSL_SMALL_STACK
XFREE(c, NULL, DYNAMIC_TYPE_DCERT);
#endif
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;
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;
if (response->single != NULL) {
FreeOcspEntry(response->single, NULL);
XFREE(response->single, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
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);
@@ -1045,7 +1112,7 @@ OcspResponse* wolfSSL_d2i_OCSP_RESPONSE(OcspResponse** response,
XMEMCPY(resp->source, *data, (size_t)len);
resp->maxIdx = (word32)len;
ret = OcspResponseDecode(resp, NULL, NULL, 1);
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 */
@@ -1100,27 +1167,9 @@ const char *wolfSSL_OCSP_response_status_str(long s)
WOLFSSL_OCSP_BASICRESP* wolfSSL_OCSP_response_get1_basic(OcspResponse* response)
{
WOLFSSL_OCSP_BASICRESP* bs;
const unsigned char *ptr = response->source;
bs = (WOLFSSL_OCSP_BASICRESP*)XMALLOC(sizeof(WOLFSSL_OCSP_BASICRESP), NULL,
DYNAMIC_TYPE_OCSP_REQUEST);
if (bs == NULL)
return NULL;
XMEMCPY(bs, response, sizeof(OcspResponse));
bs->single = (OcspEntry*)XMALLOC(sizeof(OcspEntry), NULL,
DYNAMIC_TYPE_OCSP_ENTRY);
bs->source = (byte*)XMALLOC(bs->maxIdx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (bs->single == NULL || bs->source == NULL) {
XFREE(bs->single, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
bs->single = NULL;
wolfSSL_OCSP_RESPONSE_free(bs);
bs = NULL;
}
else {
XMEMCPY(bs->single, response->single, sizeof(OcspEntry));
XMEMCPY(bs->source, response->source, response->maxIdx);
bs->single->ownStatus = 0;
}
bs = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, response->maxIdx);
return bs;
}

View File

@@ -17355,7 +17355,7 @@ void wolfSSL_ERR_load_SSL_strings(void)
}
#endif
#ifdef HAVE_OCSP
#if defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY))
long wolfSSL_get_tlsext_status_ocsp_resp(WOLFSSL *s, unsigned char **resp)
{
if (s == NULL || resp == NULL)
@@ -17371,12 +17371,13 @@ long wolfSSL_set_tlsext_status_ocsp_resp(WOLFSSL *s, unsigned char *resp,
if (s == NULL)
return WOLFSSL_FAILURE;
XFREE(s->ocspResp, NULL, 0);
s->ocspResp = resp;
s->ocspRespSz = len;
return WOLFSSL_SUCCESS;
}
#endif /* HAVE_OCSP */
#endif /* defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)) */
#ifdef HAVE_MAX_FRAGMENT
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)

View File

@@ -3238,6 +3238,14 @@ word16 TLSX_CSR_GetSize_ex(CertificateStatusRequest* csr, byte isRequest,
#endif
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER)
if (!isRequest && IsAtLeastTLSv1_3(csr->ssl->version)) {
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
if (csr->ssl != NULL && SSL_CM(csr->ssl) != NULL &&
SSL_CM(csr->ssl)->ocsp_stapling != NULL &&
SSL_CM(csr->ssl)->ocsp_stapling->statusCb != NULL &&
idx == 0) {
return OPAQUE8_LEN + OPAQUE24_LEN + csr->ssl->ocspRespSz;
}
#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */
return (word16)(OPAQUE8_LEN + OPAQUE24_LEN +
csr->responses[idx].length);
}
@@ -3247,6 +3255,70 @@ word16 TLSX_CSR_GetSize_ex(CertificateStatusRequest* csr, byte isRequest,
return size;
}
#if (defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER)) && \
(defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY))
static int TLSX_CSR_SetResponseWithStatusCB(WOLFSSL *ssl)
{
void *ioCtx = NULL;
WOLFSSL_OCSP *ocsp;
int ret;
if (ssl == NULL || SSL_CM(ssl) == NULL)
return BAD_FUNC_ARG;
ocsp = SSL_CM(ssl)->ocsp_stapling;
if (ocsp == NULL || ocsp->statusCb == NULL)
return BAD_FUNC_ARG;
ioCtx = (ssl->ocspIOCtx != NULL) ? ssl->ocspIOCtx : ocsp->cm->ocspIOCtx;
ret = ocsp->statusCb(ssl, ioCtx);
switch (ret) {
case SSL_TLSEXT_ERR_OK:
if (ssl->ocspRespSz > 0) {
/* ack the extension, status cb provided the response in
* ssl->ocspResp */
TLSX_SetResponse(ssl, TLSX_STATUS_REQUEST);
ssl->status_request = WOLFSSL_CSR_OCSP;
}
ret = 0;
break;
case SSL_TLSEXT_ERR_NOACK:
/* suppressing as not critical */
ret = 0;
break;
case SSL_TLSEXT_ERR_ALERT_FATAL:
default:
ret = WOLFSSL_FATAL_ERROR;
break;
}
return ret;
}
static int TLSX_CSR_WriteWithStatusCB(CertificateStatusRequest* csr,
byte* output)
{
WOLFSSL *ssl = csr->ssl;
WOLFSSL_OCSP *ocsp;
word16 offset = 0;
byte *response;
int respSz;
if (ssl == NULL || SSL_CM(ssl) == NULL)
return BAD_FUNC_ARG;
ocsp = SSL_CM(ssl)->ocsp_stapling;
if (ocsp == NULL || ocsp->statusCb == NULL)
return BAD_FUNC_ARG;
response = ssl->ocspResp;
respSz = ssl->ocspRespSz;
if (response == NULL || respSz == 0)
return BAD_FUNC_ARG;
output[offset++] = WOLFSSL_CSR_OCSP;
c32to24(respSz, output + offset);
offset += OPAQUE24_LEN;
XMEMCPY(output + offset, response, respSz);
return offset + respSz;
}
#endif /* (TLS13 && !NO_WOLFSLL_SERVER) && (OPENSSL_ALL || WOLFSSL_NGINX ||
WOLFSSL_HAPROXY) */
static word16 TLSX_CSR_GetSize(CertificateStatusRequest* csr, byte isRequest)
{
return TLSX_CSR_GetSize_ex(csr, isRequest, 0);
@@ -3299,6 +3371,14 @@ int TLSX_CSR_Write_ex(CertificateStatusRequest* csr, byte* output,
#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER)
if (!isRequest && IsAtLeastTLSv1_3(csr->ssl->version)) {
word16 offset = 0;
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
if (csr->ssl != NULL && SSL_CM(csr->ssl) != NULL &&
SSL_CM(csr->ssl)->ocsp_stapling != NULL &&
SSL_CM(csr->ssl)->ocsp_stapling->statusCb != NULL &&
idx == 0) {
return TLSX_CSR_WriteWithStatusCB(csr, output);
}
#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */
output[offset++] = csr->status_type;
c32to24(csr->responses[idx].length, output + offset);
offset += OPAQUE24_LEN;
@@ -3574,7 +3654,13 @@ static int TLSX_CSR_Parse(WOLFSSL* ssl, const byte* input, word16 length,
#if defined(WOLFSSL_TLS13)
if (ssl->options.tls1_3) {
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
if (ssl != NULL && SSL_CM(ssl) != NULL &&
SSL_CM(ssl)->ocsp_stapling != NULL &&
SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) {
return TLSX_CSR_SetResponseWithStatusCB(ssl);
}
#endif /* OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */
if (ssl->buffers.certificate == NULL) {
WOLFSSL_MSG("Certificate buffer not set!");
return BUFFER_ERROR;
@@ -4071,6 +4157,14 @@ static int TLSX_CSR2_Parse(WOLFSSL* ssl, const byte* input, word16 length,
continue;
}
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
/* OpenSSL status CB supports only CERTIFICATE STATUS REQ V1 */
if (ssl != NULL && SSL_CM(ssl) != NULL &&
SSL_CM(ssl)->ocsp_stapling != NULL &&
SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) {
return 0;
}
#endif
/* if using status_request and already sending it, remove it
* and prefer to use the v2 version */
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST

View File

@@ -301,6 +301,7 @@
#include <tests/api/test_hash.h>
#include <tests/api/test_ascon.h>
#include <tests/api/test_dtls.h>
#include <tests/api/test_ocsp.h>
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_TLS) && \
!defined(NO_RSA) && !defined(SINGLE_THREADED) && \
@@ -459,65 +460,6 @@
#endif
#endif /* HAVE_PKCS7 */
typedef int (*ctx_cb)(WOLFSSL_CTX* ctx);
typedef int (*ssl_cb)(WOLFSSL* ssl);
typedef int (*test_cbType)(WOLFSSL_CTX *ctx, WOLFSSL *ssl);
typedef int (*hs_cb)(WOLFSSL_CTX **ctx, WOLFSSL **ssl);
typedef struct test_ssl_cbf {
method_provider method;
ctx_cb ctx_ready;
ssl_cb ssl_ready;
ssl_cb on_result;
ctx_cb on_ctx_cleanup;
ssl_cb on_cleanup;
hs_cb on_handshake;
WOLFSSL_CTX* ctx;
const char* caPemFile;
const char* certPemFile;
const char* keyPemFile;
const char* crlPemFile;
#ifdef WOLFSSL_STATIC_MEMORY
byte* mem;
word32 memSz;
wolfSSL_method_func method_ex;
#endif
int devId;
int return_code;
int last_err;
unsigned char isSharedCtx:1;
unsigned char loadToSSL:1;
unsigned char ticNoInit:1;
unsigned char doUdp:1;
} test_ssl_cbf;
#define TEST_SSL_MEMIO_BUF_SZ (64 * 1024)
typedef struct test_ssl_memio_ctx {
WOLFSSL_CTX* s_ctx;
WOLFSSL_CTX* c_ctx;
WOLFSSL* s_ssl;
WOLFSSL* c_ssl;
const char* c_ciphers;
const char* s_ciphers;
char* c_msg;
int c_msglen;
char* s_msg;
int s_msglen;
test_ssl_cbf s_cb;
test_ssl_cbf c_cb;
byte c_buff[TEST_SSL_MEMIO_BUF_SZ];
int c_len;
byte s_buff[TEST_SSL_MEMIO_BUF_SZ];
int s_len;
} test_ssl_memio_ctx;
int test_wolfSSL_client_server_nofail_memio(test_ssl_cbf* client_cb,
test_ssl_cbf* server_cb, test_cbType client_on_handshake);
#ifdef WOLFSSL_DUMP_MEMIO_STREAM
const char* currentTestName;
char tmpDirName[16];
@@ -4622,7 +4564,11 @@ static int test_wolfSSL_CheckOCSPResponse(void)
wolfSSL_CertManagerFree(cm);
}
#if defined(WC_RSA_PSS)
/* FIPS v2 and below don't support long salts. */
#if defined(WC_RSA_PSS) && \
(!defined(HAVE_FIPS) || (defined(HAVE_FIPS_VERSION) && \
(HAVE_FIPS_VERSION > 2))) && (!defined(HAVE_SELFTEST) || \
(defined(HAVE_SELFTEST_VERSION) && (HAVE_SELFTEST_VERSION > 2)))
{
const char* responsePssFile = "./certs/ocsp/test-response-rsapss.der";
@@ -7261,7 +7207,7 @@ static WC_INLINE int test_ssl_memio_read_cb(WOLFSSL *ssl, char *data, int sz,
return read_sz;
}
static WC_INLINE int test_ssl_memio_setup(test_ssl_memio_ctx *ctx)
int test_ssl_memio_setup(test_ssl_memio_ctx *ctx)
{
EXPECT_DECLS_NO_MSGS(-2000);
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE)
@@ -7461,7 +7407,7 @@ static WC_INLINE int test_ssl_memio_setup(test_ssl_memio_ctx *ctx)
return EXPECT_RESULT();
}
static int test_ssl_memio_do_handshake(test_ssl_memio_ctx* ctx, int max_rounds,
int test_ssl_memio_do_handshake(test_ssl_memio_ctx* ctx, int max_rounds,
int* rounds)
{
int handshake_complete = 0;
@@ -7581,7 +7527,7 @@ static int test_ssl_memio_read_write(test_ssl_memio_ctx* ctx)
return EXPECT_RESULT();
}
static void test_ssl_memio_cleanup(test_ssl_memio_ctx* ctx)
void test_ssl_memio_cleanup(test_ssl_memio_ctx* ctx)
{
ctx->c_cb.last_err = wolfSSL_get_error(ctx->c_ssl, 0);
ctx->s_cb.last_err = wolfSSL_get_error(ctx->s_ssl, 0);
@@ -99372,6 +99318,9 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_SSLDisableRead),
TEST_DECL(test_wolfSSL_inject),
TEST_DECL(test_wolfSSL_dtls_cid_parse),
TEST_DECL(test_ocsp_status_callback),
TEST_DECL(test_ocsp_basic_verify),
TEST_DECL(test_ocsp_response_parsing),
/* This test needs to stay at the end to clean up any caches allocated. */
TEST_DECL(test_wolfSSL_Cleanup)
};

View File

@@ -0,0 +1,453 @@
#!/usr/bin/env python3
"""
This is a simple generator of OCSP responses that will be used to test
wolfSSL OCSP implementation
"""
from pyasn1_modules import rfc6960
from pyasn1.codec.der.encoder import encode
from pyasn1.codec.der.decoder import decode
from pyasn1.type import univ, tag, useful, namedtype
from base64 import b64decode
from hashlib import sha1, sha256
from datetime import datetime
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography import x509
from cryptography.hazmat.backends import default_backend
WOLFSSL_OCSP_CERT_PATH = './certs/ocsp/'
def response_status(value: int) -> rfc6960.OCSPResponseStatus:
return rfc6960.OCSPResponseStatus(value)
def response_type() -> univ.ObjectIdentifier:
return rfc6960.id_pkix_ocsp_basic
sha256WithRSAEncryption = (1, 2, 840, 113549, 1, 1, 11)
sha1_alg_id = (1, 3, 14, 3, 2, 26)
def cert_id_sha1_alg_id() -> rfc6960.AlgorithmIdentifier:
return algorithm(sha1_alg_id)
def signature_algorithm() -> rfc6960.AlgorithmIdentifier:
return algorithm(sha256WithRSAEncryption)
def algorithm(value) -> rfc6960.AlgorithmIdentifier:
ai = rfc6960.AlgorithmIdentifier()
ai['algorithm'] = univ.ObjectIdentifier(value=value)
return ai
def cert_pem_to_der(cert_path: str) -> bytes:
beg_cert = '-----BEGIN CERTIFICATE-----'
end_cert = '-----END CERTIFICATE-----'
with open(cert_path, 'r') as f:
pem = f.read()
cert = pem.split(beg_cert)[1].split(end_cert)[0]
return b64decode(cert)
def certs(cert_path: list[str]) -> univ.SequenceOf | None:
if len(cert_path) == 0:
return None
certs = rfc6960.BasicOCSPResponse()['certs']
for cp in cert_path:
cert_der = cert_pem_to_der(cp)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
certs.append(cert)
return certs
def signature(bitstr: str) -> univ.BitString:
return univ.BitString(hexValue=bitstr)
def resp_id_by_name(cert_path: str) -> rfc6960.ResponderID:
cert_der = cert_pem_to_der(cert_path)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
subj = cert['tbsCertificate']['subject']
rid = rfc6960.ResponderID()
rdi_name = rid['byName']
rdi_name['rdnSequence'] = subj['rdnSequence']
return rid
def resp_id_by_key(cert_path: str) -> rfc6960.ResponderID:
cert_der = cert_pem_to_der(cert_path)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
key = get_key(cert)
key_hash = sha1(key.asOctets()).digest()
rid = rfc6960.ResponderID()
rid['byKey'] = rfc6960.KeyHash(value=key_hash).subtype(explicitTag=
tag.Tag(
tag.tagClassContext,
tag.tagFormatSimple,
2))
return rid
def get_key(cert: rfc6960.Certificate) -> univ.BitString:
return cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
def get_name(cert: rfc6960.Certificate) -> rfc6960.Name:
return cert['tbsCertificate']['subject']
def cert_id_from_hash(issuer_name_hash: bytes, issuer_key_hash: bytes,
serial: int) -> rfc6960.CertID:
cert_id = rfc6960.CertID()
cert_id['hashAlgorithm'] = cert_id_sha1_alg_id()
cert_id['issuerNameHash'] = univ.OctetString(value=issuer_name_hash)
cert_id['issuerKeyHash'] = univ.OctetString(value=issuer_key_hash)
cert_id['serialNumber'] = rfc6960.CertificateSerialNumber(serial)
return cert_id
def cert_id(issuer_cert_path: str, serial: int) -> rfc6960.CertID:
issuer_cert = cert_pem_to_der(issuer_cert_path)
issuer, _ = decode(bytes(issuer_cert), asn1Spec=rfc6960.Certificate())
issuer_name = get_name(issuer)
issuer_key = get_key(issuer)
issuer_name_hash = sha1(encode(issuer_name)).digest()
issuer_key_hash = sha1(issuer_key.asOctets()).digest()
cert_id = rfc6960.CertID()
cert_id['hashAlgorithm'] = cert_id_sha1_alg_id()
cert_id['issuerNameHash'] = univ.OctetString(value=issuer_name_hash)
cert_id['issuerKeyHash'] = univ.OctetString(value=issuer_key_hash)
cert_id['serialNumber'] = rfc6960.CertificateSerialNumber(serial)
return cert_id
CERT_GOOD = 0
CERT_REVOKED = 1
CERT_UNKNOWN = 2
def cert_status(value: int) -> rfc6960.CertStatus:
cs = rfc6960.CertStatus()
if value == CERT_GOOD:
good = univ.Null('').subtype(implicitTag=tag.Tag(tag.tagClassContext,
tag.tagFormatSimple,
0))
cs['good'] = good
elif value == CERT_REVOKED:
revoked = rfc6960.RevokedInfo().subtype(implicitTag=tag.Tag(
tag.tagClassContext, tag.tagFormatSimple, 1))
revoked['revocationTime'] = useful.GeneralizedTime().fromDateTime(
datetime.now())
cs['revoked'] = revoked
return cs
def single_response(issuer_cert_path: str, serial: int,
status: int) -> rfc6960.SingleResponse:
cid = cert_id(issuer_cert_path, serial)
cs = cert_status(status)
sr = rfc6960.SingleResponse().clone()
sr.setComponentByName('certID', cid)
sr['certStatus'] = cs
sr['thisUpdate'] = useful.GeneralizedTime().fromDateTime(datetime.now())
return sr
def response_data(rid: rfc6960.ResponderID | None,
responses: list[rfc6960.SingleResponse]) -> rfc6960.ResponseData:
rd = rfc6960.ResponseData()
rd['version'] = rfc6960.Version('v1').subtype(explicitTag=tag.Tag(
tag.tagClassContext, tag.tagFormatSimple, 0))
if rid:
rd['responderID'] = rid
rd['producedAt'] = useful.GeneralizedTime().fromDateTime(datetime.now())
rs = univ.SequenceOf(componentType=rfc6960.SingleResponse())
rs.extend(responses)
rd['responses'] = rs
return rd
def read_key_der_from_pem(key_path: str) -> bytes:
with open(key_path, 'r') as f:
pem = f.readlines()
pem_start = [i for i, line in enumerate(pem) if '-----BEGIN' in line][0]
pem_end = [i for i, line in enumerate(pem) if '-----END' in line][0]
key = ''.join(pem[pem_start+1:pem_end])
return b64decode(key)
def basic_ocsp_response(rd: rfc6960.ResponseData, sig_alg:
rfc6960.AlgorithmIdentifier, sig: univ.BitString,
certs: univ.SequenceOf|None = None) -> rfc6960.BasicOCSPResponse:
br = rfc6960.BasicOCSPResponse()
br['tbsResponseData'] = rd
br['signatureAlgorithm'] = sig_alg
br['signature'] = sig
if certs is not None:
br['certs'] = certs
return br
def response_bytes(br: rfc6960.BasicOCSPResponse) -> rfc6960.ResponseBytes:
rb = rfc6960.ResponseBytes().subtype(explicitTag=tag.Tag(
tag.tagClassContext, tag.tagFormatConstructed, 0))
rb['responseType'] = response_type()
rb['response'] = encode(br)
return rb
def ocsp_response(status: rfc6960.OCSPResponseStatus,
response_bytes: rfc6960.ResponseBytes) -> rfc6960.OCSPResponse:
orsp = rfc6960.OCSPResponse()
orsp['responseStatus'] = status
orsp['responseBytes'] = response_bytes
return orsp
def get_priv_key(pem_path) -> rsa.RSAPrivateKey:
key_der = read_key_der_from_pem(pem_path)
private_key = serialization.load_der_private_key(
key_der,
password=None,
)
return private_key
def sign_repsonse_data(rd: rfc6960.ResponseData,
key: rsa.RSAPrivateKey) -> univ.BitString:
sig = key.sign(encode(rd), padding.PKCS1v15(), hashes.SHA256())
return univ.BitString(hexValue=sig.hex())
def get_pub_key(cert_path: str) -> rsa.RSAPublicKey:
with open(cert_path, 'rb') as f:
cert = f.read()
cert = x509.load_pem_x509_certificate(cert, default_backend())
return cert.public_key()
def test_signature(ocsp_resp_path: str, key: rsa.RSAPublicKey):
with open(ocsp_resp_path, 'rb') as f:
ocsp_resp = f.read()
ocsp_resp, _ = decode(ocsp_resp, asn1Spec=rfc6960.OCSPResponse())
response = ocsp_resp.getComponentByName(
'responseBytes').getComponentByName('response')
br, _ = decode(response, asn1Spec=rfc6960.BasicOCSPResponse())
rd = br.getComponentByName('tbsResponseData')
rd_hash = sha256(encode(rd)).digest()
di = rfc8017.DigestInfo()
di['digestAlgorithm'] = signature_algorithm()
di['digest'] = univ.OctetString(rd_hash)
sig = br.getComponentByName('signature')
key.verify(sig.asOctets(), encode(rd), padding.PKCS1v15(), hashes.SHA256())
def single_response_from_cert(cert_path: str,
status: int) -> rfc6960.SingleResponse:
cert_der = cert_pem_to_der(cert_path)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
serial = cert['tbsCertificate']['serialNumber']
issuer = cert['tbsCertificate']['issuer']
serialHash = sha1(serial.asOctets()).digest()
issuerHash = sha1(encode(issuer)).digest()
cid = cert_id_from_hash(issuerHash, serialHash, serial)
cs = cert_status(status)
sr = rfc6960.SingleResponse().clone()
sr.setComponentByName('certID', cid)
sr['certStatus'] = cs
sr['thisUpdate'] = useful.GeneralizedTime().fromDateTime(datetime.now())
return sr
RESPONSE_STATUS_GOOD = 0
def write_buffer(name: str, data: bytes, f):
f.write(f"unsigned char {name}[] = {{\n")
for i in range(0, len(data), 12):
f.write(" " + ", ".join(f"0x{b:02x}" for b in data[i:i+12]) + ",\n")
f.write("};\n\n")
def create_response(rd: dict) -> rfc6960.OCSPResponse:
"""create a response using definition in rd"""
cs = response_status(rd.get('response_status', RESPONSE_STATUS_GOOD))
sa = rd.get('signature_algorithm', signature_algorithm())
c = certs(rd.get('certs_path', []))
rid = None
if rd.get('responder_by_name') is not None:
rid = resp_id_by_name(
rd.get(
'responder_cert', WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'))
elif rd.get('responder_by_key', None) is not None:
rid = resp_id_by_key(
rd.get('responder_cert', WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'))
# implement responder byhash
responses = []
for entry in rd.get('responses', []):
if entry.get('certificate'):
sr = single_response_from_cert(entry['certificate'], entry['status'])
else:
sr = single_response(entry['issuer_cert'], entry['serial'], entry['status'])
responses.append(sr)
rd_data = response_data(rid, responses)
k = get_priv_key(rd.get('responder_key', WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem'))
s = sign_repsonse_data(rd_data, k)
br = basic_ocsp_response(rd_data, sa, s, c)
rb = response_bytes(br)
ocspr = ocsp_response(cs, rb)
return ocspr
def create_and_write_response(rd: dict, f):
ocspr = create_response(rd)
encoded_response = encode(ocspr)
write_buffer(rd['name'].replace('-', '_').replace('.', '_'), encoded_response, f)
def add_certificate(cert_path: str, f):
cert_der = cert_pem_to_der(cert_path)
write_buffer(cert_path.split('/')[-1].replace('-', '_').replace('.', '_'), cert_der, f)
class badOCSPResponse(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('responseBytes', rfc6960.ResponseBytes().subtype(
explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
def create_bad_response(rd: dict) -> bytes:
"""Creates a malformed OCSP response by removing the response status field"""
r = create_response(rd)
br = badOCSPResponse()
br['responseBytes'] = r['responseBytes']
return encode(br)
if __name__ == '__main__':
useful.GeneralizedTime._hasSubsecond = False
response_definitions = [
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'certs_path': [WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'],
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem',
'name': 'resp'
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'certs_path': [WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'],
'responder_by_key': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem',
'name': 'resp_rid_bykey',
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem',
'name': 'resp_nocert'
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
},
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x02,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'root-ca-key.pem',
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'name': 'resp_multi'
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
},
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + '../ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'root-ca-key.pem',
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'name': 'resp_bad_noauth'
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
},
],
# unrelated cert
'certs_path' : [WOLFSSL_OCSP_CERT_PATH + 'intermediate2-ca-cert.pem'],
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'root-ca-key.pem',
'name': 'resp_bad_embedded_cert'
},
]
with open('./tests/api/ocsp_test_blobs.h', 'w') as f:
f.write(
"""/*
* This file is generated automatically by running ./tests/api/create_ocsp_test_blobs.py.
*
* ocsp_test_blobs.h
*
* Copyright (C) 2006-2025 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 2 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
*
*/
""")
f.write("#ifndef OCSP_TEST_BLOBS_H\n")
f.write("#define OCSP_TEST_BLOBS_H\n\n")
for rd in response_definitions:
create_and_write_response(rd, f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + '../ca-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + '../server-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem', f)
br = create_bad_response({
'response_status': 0,
'responder_by_key': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'name': 'resp_bad'
})
write_buffer('resp_bad', br, f)
f.write("#endif /* OCSP_TEST_BLOBS_H */\n")

View File

@@ -14,6 +14,7 @@ tests_unit_test_SOURCES += tests/api/test_ripemd.c
tests_unit_test_SOURCES += tests/api/test_hash.c
tests_unit_test_SOURCES += tests/api/test_ascon.c
tests_unit_test_SOURCES += tests/api/test_dtls.c
tests_unit_test_SOURCES += tests/api/test_ocsp.c
endif
EXTRA_DIST += tests/api/api.h
EXTRA_DIST += tests/api/test_md5.h
@@ -29,4 +30,7 @@ EXTRA_DIST += tests/api/test_ascon.h
EXTRA_DIST += tests/api/test_ascon.h
EXTRA_DIST += tests/api/test_ascon_kats.h
EXTRA_DIST += tests/api/test_dtls.h
EXTRA_DIST += tests/api/test_ocsp.h
EXTRA_DIST += tests/api/test_ocsp_test_blobs.h
EXTRA_DIST += tests/api/create_ocsp_test_blobs.py

594
tests/api/test_ocsp.c Normal file
View File

@@ -0,0 +1,594 @@
/* test_ocsp.c
*
* Copyright (C) 2006-2025 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 2 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#if !defined(WOLFSSL_USER_SETTINGS) && !defined(WOLFSSL_NO_OPTIONS_H)
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <tests/api/test_ocsp.h>
#include <tests/api/test_ocsp_test_blobs.h>
#include <tests/unit.h>
#include <wolfssl/internal.h>
#include <wolfssl/ocsp.h>
#include <wolfssl/ssl.h>
#if defined(HAVE_OCSP)
struct ocsp_cb_ctx {
byte* response;
int responseSz;
};
struct test_conf {
unsigned char* resp;
int respSz;
unsigned char* ca0;
int ca0Sz;
unsigned char* ca1;
int ca1Sz;
unsigned char* targetCert;
int targetCertSz;
};
static int ocsp_cb(void* ctx, const char* url, int urlSz, unsigned char* req,
int reqSz, unsigned char** respBuf)
{
struct ocsp_cb_ctx* cb_ctx = (struct ocsp_cb_ctx*)ctx;
(void)url;
(void)urlSz;
(void)req;
(void)reqSz;
*respBuf = cb_ctx->response;
return cb_ctx->responseSz;
}
static int test_ocsp_response_with_cm(struct test_conf* c, int expectedRet)
{
EXPECT_DECLS;
WOLFSSL_CERT_MANAGER* cm = NULL;
struct ocsp_cb_ctx cb_ctx;
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerEnableOCSP(cm,
WOLFSSL_OCSP_URL_OVERRIDE | WOLFSSL_OCSP_NO_NONCE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerSetOCSPOverrideURL(cm, "http://foo.com"),
WOLFSSL_SUCCESS);
cb_ctx.response = (byte*)c->resp;
cb_ctx.responseSz = c->respSz;
ExpectIntEQ(
wolfSSL_CertManagerSetOCSP_Cb(cm, ocsp_cb, NULL, (void*)&cb_ctx),
WOLFSSL_SUCCESS);
/* add ca in cm */
if (c->ca0 != NULL) {
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, c->ca0, c->ca0Sz,
WOLFSSL_FILETYPE_ASN1),
WOLFSSL_SUCCESS);
}
if (c->ca1 != NULL) {
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, c->ca1, c->ca1Sz,
WOLFSSL_FILETYPE_ASN1),
WOLFSSL_SUCCESS);
}
/* check cert */
ExpectIntEQ(
wolfSSL_CertManagerCheckOCSP(cm, c->targetCert, c->targetCertSz),
expectedRet);
if (cm != NULL)
wolfSSL_CertManagerFree(cm);
return EXPECT_RESULT();
}
int test_ocsp_response_parsing(void)
{
EXPECT_DECLS;
struct test_conf conf;
int expectedRet;
conf.resp = (unsigned char*)resp;
conf.respSz = sizeof(resp);
conf.ca0 = root_ca_cert_pem;
conf.ca0Sz = sizeof(root_ca_cert_pem);
conf.ca1 = NULL;
conf.ca1Sz = 0;
conf.targetCert = intermediate1_ca_cert_pem;
conf.targetCertSz = sizeof(intermediate1_ca_cert_pem);
ExpectIntEQ(test_ocsp_response_with_cm(&conf, WOLFSSL_SUCCESS),
TEST_SUCCESS);
conf.resp = (unsigned char*)resp_multi;
conf.respSz = sizeof(resp_multi);
conf.ca0 = root_ca_cert_pem;
conf.ca0Sz = sizeof(root_ca_cert_pem);
conf.ca1 = NULL;
conf.ca1Sz = 0;
conf.targetCert = intermediate1_ca_cert_pem;
conf.targetCertSz = sizeof(intermediate1_ca_cert_pem);
ExpectIntEQ(test_ocsp_response_with_cm(&conf, WOLFSSL_SUCCESS),
TEST_SUCCESS);
conf.resp = (unsigned char*)resp_bad_noauth;
conf.respSz = sizeof(resp_bad_noauth);
conf.ca0 = root_ca_cert_pem;
conf.ca0Sz = sizeof(root_ca_cert_pem);
conf.ca1 = ca_cert_pem;
conf.ca1Sz = sizeof(ca_cert_pem);
conf.targetCert = server_cert_pem;
conf.targetCertSz = sizeof(server_cert_pem);
expectedRet = OCSP_LOOKUP_FAIL;
#ifdef WOLFSSL_NO_OCSP_ISSUER_CHECK
expectedRet = WOLFSSL_SUCCESS;
#endif
ExpectIntEQ(test_ocsp_response_with_cm(&conf, expectedRet), TEST_SUCCESS);
/* Test response with unusable internal cert but that can be verified in CM
*/
conf.resp = (unsigned char*)resp_bad_embedded_cert;
conf.respSz = sizeof(resp_bad_embedded_cert);
conf.ca0 = root_ca_cert_pem;
conf.ca0Sz = sizeof(root_ca_cert_pem);
conf.ca1 = NULL;
conf.ca1Sz = 0;
conf.targetCert = intermediate1_ca_cert_pem;
conf.targetCertSz = sizeof(intermediate1_ca_cert_pem);
ExpectIntEQ(test_ocsp_response_with_cm(&conf, WOLFSSL_SUCCESS),
TEST_SUCCESS);
return EXPECT_SUCCESS();
}
#else /* HAVE_OCSP */
int test_ocsp_response_parsing(void)
{
return TEST_SKIPPED;
}
#endif /* HAVE_OCSP */
#if defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA))
static int test_ocsp_create_x509store(WOLFSSL_X509_STORE** store,
unsigned char* ca, int caSz)
{
EXPECT_DECLS;
WOLFSSL_X509* cert = NULL;
ExpectNotNull(*store = wolfSSL_X509_STORE_new());
ExpectNotNull(cert = wolfSSL_X509_d2i(&cert, ca, caSz));
ExpectIntEQ(wolfSSL_X509_STORE_add_cert(*store, cert), WOLFSSL_SUCCESS);
wolfSSL_X509_free(cert);
return EXPECT_RESULT();
}
static int test_create_stack_of_x509(WOLF_STACK_OF(WOLFSSL_X509) * *certs,
unsigned char* der, int derSz)
{
EXPECT_DECLS;
WOLFSSL_X509* cert = NULL;
ExpectNotNull(*certs = wolfSSL_sk_X509_new_null());
ExpectNotNull(cert = wolfSSL_X509_d2i(&cert, der, derSz));
ExpectIntEQ(wolfSSL_sk_X509_push(*certs, cert), 1);
return EXPECT_RESULT();
}
int test_ocsp_basic_verify(void)
{
EXPECT_DECLS;
WOLF_STACK_OF(WOLFSSL_X509)* certs = NULL;
WOLFSSL_X509_STORE* store = NULL;
const unsigned char* ptr = NULL;
OcspResponse* response = NULL;
DecodedCert cert;
int expectedRet;
wc_InitDecodedCert(&cert, ocsp_responder_cert_pem,
sizeof(ocsp_responder_cert_pem), NULL);
ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, 0, NULL), 0);
/* just decoding */
ptr = (const unsigned char*)resp;
ExpectNotNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp)));
ExpectIntEQ(response->responseStatus, 0);
ExpectIntEQ(response->responderIdType, OCSP_RESPONDER_ID_NAME);
ExpectBufEQ(response->responderId.nameHash, cert.subjectHash,
OCSP_DIGEST_SIZE);
wolfSSL_OCSP_RESPONSE_free(response);
/* responder Id by key hash */
ptr = (const unsigned char*)resp_rid_bykey;
ExpectNotNull(response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr,
sizeof(resp_rid_bykey)));
ExpectIntEQ(response->responseStatus, 0);
ExpectIntEQ(response->responderIdType, OCSP_RESPONDER_ID_KEY);
ExpectBufEQ(response->responderId.keyHash, cert.subjectKeyHash,
OCSP_DIGEST_SIZE);
wolfSSL_OCSP_RESPONSE_free(response);
/* decoding with no embedded certificates */
ptr = (const unsigned char*)resp_nocert;
ExpectNotNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_nocert)));
ExpectIntEQ(response->responseStatus, 0);
wolfSSL_OCSP_RESPONSE_free(response);
/* decoding an invalid response */
ptr = (const unsigned char*)resp_bad;
ExpectNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_bad)));
ptr = (const unsigned char*)resp;
ExpectNotNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp)));
/* no verify signer certificate */
ExpectIntEQ(wolfSSL_OCSP_basic_verify(response, NULL, NULL, OCSP_NOVERIFY),
WOLFSSL_SUCCESS);
/* verify that the signature is checked */
if (EXPECT_SUCCESS()) {
response->sig[0] ^= 0xff;
}
ExpectIntEQ(wolfSSL_OCSP_basic_verify(response, NULL, NULL, OCSP_NOVERIFY),
WOLFSSL_FAILURE);
wolfSSL_OCSP_RESPONSE_free(response);
/* populate a store with root-ca-cert */
ExpectIntEQ(test_ocsp_create_x509store(&store, root_ca_cert_pem,
sizeof(root_ca_cert_pem)),
TEST_SUCCESS);
/* populate a WOLF_STACK_OF(WOLFSSL_X509) with responder certificate */
ExpectIntEQ(test_create_stack_of_x509(&certs, ocsp_responder_cert_pem,
sizeof(ocsp_responder_cert_pem)),
TEST_SUCCESS);
/* cert not embedded, cert in certs, validated using store */
ptr = (const unsigned char*)resp_nocert;
ExpectNotNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_nocert)));
ExpectIntEQ(wolfSSL_OCSP_basic_verify(response, certs, store, 0),
WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
/* cert embedded, verified using store */
ptr = (const unsigned char*)resp;
ExpectNotNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp)));
ExpectIntEQ(wolfSSL_OCSP_basic_verify(response, NULL, store, 0),
WOLFSSL_SUCCESS);
/* make invalid signature */
if (EXPECT_SUCCESS()) {
response->sig[0] ^= 0xff;
}
ExpectIntEQ(wolfSSL_OCSP_basic_verify(response, NULL, store, 0),
WOLFSSL_FAILURE);
if (EXPECT_SUCCESS()) {
response->sig[0] ^= 0xff;
}
/* cert embedded and in certs, no store needed bc OCSP_TRUSTOTHER */
ExpectIntEQ(
wolfSSL_OCSP_basic_verify(response, certs, NULL, OCSP_TRUSTOTHER),
WOLFSSL_SUCCESS);
/* this should also pass */
ExpectIntEQ(
wolfSSL_OCSP_basic_verify(response, certs, store, OCSP_NOINTERN),
WOLFSSL_SUCCESS);
/* this should not */
ExpectIntNE(wolfSSL_OCSP_basic_verify(response, NULL, store, OCSP_NOINTERN),
WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
/* cert not embedded, not certs */
ptr = (const unsigned char*)resp_nocert;
ExpectNotNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_nocert)));
ExpectIntNE(wolfSSL_OCSP_basic_verify(response, NULL, store, 0),
WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
wolfSSL_sk_X509_pop_free(certs, wolfSSL_X509_free);
wolfSSL_X509_STORE_free(store);
ExpectIntEQ(test_ocsp_create_x509store(&store, root_ca_cert_pem,
sizeof(root_ca_cert_pem)),
TEST_SUCCESS);
ExpectIntEQ(test_create_stack_of_x509(&certs, root_ca_cert_pem,
sizeof(root_ca_cert_pem)),
TEST_SUCCESS);
/* multiple responses in a ocsp response */
ptr = (const unsigned char*)resp_multi;
ExpectNotNull(
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_multi)));
ExpectIntEQ(wolfSSL_OCSP_basic_verify(response, certs, store, 0),
WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
/* cert in certs, cert verified on store, not authorized to verify all
* responses */
ptr = (const unsigned char*)resp_bad_noauth;
ExpectNotNull(response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr,
sizeof(resp_bad_noauth)));
expectedRet = WOLFSSL_FAILURE;
#ifdef WOLFSSL_NO_OCSP_ISSUER_CHECK
expectedRet = WOLFSSL_SUCCESS;
#endif
ExpectIntEQ(wolfSSL_OCSP_basic_verify(response, certs, store, 0),
expectedRet);
/* should pass with OCSP_NOCHECKS ...*/
ExpectIntEQ(
wolfSSL_OCSP_basic_verify(response, certs, store, OCSP_NOCHECKS),
WOLFSSL_SUCCESS);
/* or with OSCP_TRUSTOTHER */
ExpectIntEQ(
wolfSSL_OCSP_basic_verify(response, certs, store, OCSP_TRUSTOTHER),
WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
wc_FreeDecodedCert(&cert);
wolfSSL_sk_X509_pop_free(certs, wolfSSL_X509_free);
wolfSSL_X509_STORE_free(store);
return EXPECT_RESULT();
}
#else
int test_ocsp_basic_verify(void)
{
return TEST_SKIPPED;
}
#endif /* HAVE_OCSP && (OPENSSL_ALL || OPENSSL_EXTRA) */
#if defined(HAVE_OCSP) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \
defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(WOLFSSL_NO_TLS12) && \
defined(OPENSSL_ALL)
struct _test_ocsp_status_callback_ctx {
byte* ocsp_resp;
int ocsp_resp_sz;
int invoked;
};
static int test_ocsp_status_callback_cb(WOLFSSL* ssl, void* ctx)
{
struct _test_ocsp_status_callback_ctx* _ctx =
(struct _test_ocsp_status_callback_ctx*)ctx;
byte* allocated;
_ctx->invoked++;
allocated = (byte*)XMALLOC(_ctx->ocsp_resp_sz, NULL, 0);
if (allocated == NULL)
return SSL_TLSEXT_ERR_ALERT_FATAL;
XMEMCPY(allocated, _ctx->ocsp_resp, _ctx->ocsp_resp_sz);
SSL_set_tlsext_status_ocsp_resp(ssl, allocated, _ctx->ocsp_resp_sz);
return SSL_TLSEXT_ERR_OK;
}
static int test_ocsp_status_callback_cb_noack(WOLFSSL* ssl, void* ctx)
{
struct _test_ocsp_status_callback_ctx* _ctx =
(struct _test_ocsp_status_callback_ctx*)ctx;
(void)ssl;
_ctx->invoked++;
return SSL_TLSEXT_ERR_NOACK;
}
static int test_ocsp_status_callback_cb_err(WOLFSSL* ssl, void* ctx)
{
struct _test_ocsp_status_callback_ctx* _ctx =
(struct _test_ocsp_status_callback_ctx*)ctx;
(void)ssl;
_ctx->invoked++;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
static int test_ocsp_status_callback_test_setup(
struct _test_ocsp_status_callback_ctx* cb_ctx,
struct test_ssl_memio_ctx* test_ctx, method_provider cm, method_provider sm)
{
int ret;
cb_ctx->invoked = 0;
XMEMSET(test_ctx, 0, sizeof(*test_ctx));
test_ctx->c_cb.caPemFile = "./certs/ocsp/root-ca-cert.pem";
test_ctx->s_cb.certPemFile = "./certs/ocsp/server1-cert.pem";
test_ctx->s_cb.keyPemFile = "./certs/ocsp/server1-key.pem";
test_ctx->c_cb.method = cm;
test_ctx->s_cb.method = sm;
ret = test_ssl_memio_setup(test_ctx);
wolfSSL_set_verify(test_ctx->c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
return ret;
}
int test_ocsp_status_callback(void)
{
struct test_params {
method_provider c_method;
method_provider s_method;
};
const char* responseFile = "./certs/ocsp/test-leaf-response.der";
struct _test_ocsp_status_callback_ctx cb_ctx;
struct test_ssl_memio_ctx test_ctx;
int enable_client_ocsp;
int enable_must_staple;
XFILE f = XBADFILE;
byte data[4096];
unsigned int i;
EXPECT_DECLS;
struct test_params params[] = {
{wolfTLSv1_2_client_method, wolfTLSv1_2_server_method},
#if defined(WOLFSSL_TLS13)
{wolfTLSv1_3_client_method, wolfTLSv1_3_server_method},
#endif
#if defined(WOLFSSL_DTLS)
{wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method},
#endif
#if defined(WOLFSSL_DTLS13)
{wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method},
#endif
};
XMEMSET(&cb_ctx, 0, sizeof(cb_ctx));
f = XFOPEN(responseFile, "rb");
if (f == XBADFILE)
return -1;
cb_ctx.ocsp_resp_sz = (word32)XFREAD(data, 1, 4096, f);
if (f != XBADFILE) {
XFCLOSE(f);
f = XBADFILE;
}
cb_ctx.ocsp_resp = data;
for (i = 0; i < sizeof(params) / sizeof(params[0]); i++) {
for (enable_client_ocsp = 0; enable_client_ocsp <= 1;
enable_client_ocsp++) {
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
params[i].c_method, params[i].s_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb),
SSL_SUCCESS);
ExpectIntEQ(
SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
if (enable_client_ocsp) {
ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl,
WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
}
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, enable_client_ocsp ? 1 : 0);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
}
}
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
/* test client sending both OCSPv1 and OCSPv2/MultiOCSP */
/* StatusCb only supports OCSPv1 */
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb),
SSL_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(
wolfSSL_UseOCSPStaplingV2(test_ctx.c_ssl, WOLFSSL_CSR2_OCSP_MULTI, 0),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, 1);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
#endif /* defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) */
/* test cb returning NO_ACK, not acking the OCSP */
for (i = 0; i < sizeof(params) / sizeof(params[0]); i++) {
for (enable_must_staple = 0; enable_must_staple <= 1;
enable_must_staple++) {
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
params[i].c_method, params[i].s_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb_noack),
SSL_SUCCESS);
ExpectIntEQ(
SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(
wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
if (enable_must_staple)
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
enable_must_staple ? TEST_FAIL : TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, 1);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
}
}
/* test cb returning err aborting handshake */
for (i = 0; i < sizeof(params) / sizeof(params[0]); i++) {
for (enable_client_ocsp = 0; enable_client_ocsp <= 1;
enable_client_ocsp++) {
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
params[i].c_method, params[i].s_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb_err),
SSL_SUCCESS);
ExpectIntEQ(
SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
if (enable_client_ocsp)
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(
wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
enable_client_ocsp ? TEST_FAIL : TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, enable_client_ocsp ? 1 : 0);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
}
}
return EXPECT_RESULT();
}
#else
int test_ocsp_status_callback(void)
{
return TEST_SKIPPED;
}
#endif /* defined(HAVE_OCSP) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) \
&& defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
!defined(WOLFSSL_NO_TLS12) \
&& defined(OPENSSL_ALL) */

29
tests/api/test_ocsp.h Normal file
View File

@@ -0,0 +1,29 @@
/* test_ocsp.h
*
* Copyright (C) 2006-2025 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 2 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
*/
#ifndef WOLFSSL_TEST_OCSP_H
#define WOLFSSL_TEST_OCSP_H
int test_ocsp_status_callback(void);
int test_ocsp_basic_verify(void);
int test_ocsp_response_parsing(void);
#endif /* WOLFSSL_TEST_OCSP_H */

File diff suppressed because it is too large Load Diff

View File

@@ -340,6 +340,78 @@
#define DoExpectBufEQ(x, y, z) DoExpectBuf(x, y, z, ==, !=)
#define DoExpectBufNE(x, y, z) DoExpectBuf(x, y, z, !=, ==)
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_TLS) && \
!defined(NO_RSA) && \
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
!defined(WOLFSSL_TIRTOS)
#define HAVE_SSL_MEMIO_TESTS_DEPENDENCIES
#endif
#ifdef HAVE_SSL_MEMIO_TESTS_DEPENDENCIES
typedef int (*ctx_cb)(WOLFSSL_CTX* ctx);
typedef int (*ssl_cb)(WOLFSSL* ssl);
typedef int (*test_cbType)(WOLFSSL_CTX *ctx, WOLFSSL *ssl);
typedef int (*hs_cb)(WOLFSSL_CTX **ctx, WOLFSSL **ssl);
typedef struct test_ssl_cbf {
method_provider method;
ctx_cb ctx_ready;
ssl_cb ssl_ready;
ssl_cb on_result;
ctx_cb on_ctx_cleanup;
ssl_cb on_cleanup;
hs_cb on_handshake;
WOLFSSL_CTX* ctx;
const char* caPemFile;
const char* certPemFile;
const char* keyPemFile;
const char* crlPemFile;
#ifdef WOLFSSL_STATIC_MEMORY
byte* mem;
word32 memSz;
wolfSSL_method_func method_ex;
#endif
int devId;
int return_code;
int last_err;
unsigned char isSharedCtx:1;
unsigned char loadToSSL:1;
unsigned char ticNoInit:1;
unsigned char doUdp:1;
} test_ssl_cbf;
#define TEST_SSL_MEMIO_BUF_SZ (64 * 1024)
typedef struct test_ssl_memio_ctx {
WOLFSSL_CTX* s_ctx;
WOLFSSL_CTX* c_ctx;
WOLFSSL* s_ssl;
WOLFSSL* c_ssl;
const char* c_ciphers;
const char* s_ciphers;
char* c_msg;
int c_msglen;
char* s_msg;
int s_msglen;
test_ssl_cbf s_cb;
test_ssl_cbf c_cb;
byte c_buff[TEST_SSL_MEMIO_BUF_SZ];
int c_len;
byte s_buff[TEST_SSL_MEMIO_BUF_SZ];
int s_len;
} test_ssl_memio_ctx;
int test_ssl_memio_setup(test_ssl_memio_ctx *ctx);
int test_ssl_memio_do_handshake(test_ssl_memio_ctx* ctx, int max_rounds,
int* rounds);
void test_ssl_memio_cleanup(test_ssl_memio_ctx* ctx);
int test_wolfSSL_client_server_nofail_memio(test_ssl_cbf* client_cb,
test_ssl_cbf* server_cb, test_cbType client_on_handshake);
#endif /* HAVE_SSL_MEMIO_TESTS_DEPENDENCIES */
void ApiTest_PrintTestCases(void);
int ApiTest_RunIdx(int idx);
int ApiTest_RunName(char* name);

View File

@@ -16822,7 +16822,7 @@ static int HashForSignature(const byte* buf, word32 bufSz, word32 sigOID,
#endif /* !NO_ASN_CRYPT && !NO_HASH_WRAPPER */
/* Return codes: 0=Success, Negative (see error-crypt.h), ASN_SIG_CONFIRM_E */
static int ConfirmSignature(SignatureCtx* sigCtx,
int ConfirmSignature(SignatureCtx* sigCtx,
const byte* buf, word32 bufSz,
const byte* key, word32 keySz, word32 keyOID,
const byte* sig, word32 sigSz, word32 sigOID,
@@ -23634,6 +23634,19 @@ int wc_CertGetPubKey(const byte* cert, word32 certSz,
return ret;
}
#endif
#ifdef HAVE_OCSP
Signer* findSignerByKeyHash(Signer *list, byte *hash)
{
Signer *s;
for (s = list; s != NULL; s = s->next) {
if (XMEMCMP(s->subjectKeyHash, hash, KEYID_SIZE) == 0) {
return s;
}
}
return NULL;
}
#endif /* WOLFSSL_OCSP */
Signer* findSignerByName(Signer *list, byte *hash)
{
Signer *s;
@@ -36864,7 +36877,8 @@ static const ASNItem ocspRespDataASN[] = {
/* byName */
/* BYNAME */ { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 2 },
/* byKey */
/* BYKEY */ { 1, ASN_CONTEXT_SPECIFIC | 2, 1, 0, 2 },
/* BYKEY */ { 1, ASN_CONTEXT_SPECIFIC | 2, 1, 1, 2 },
/* BYKEY_OCT */ { 2, ASN_OCTET_STRING, 0, 0, 0 },
/* producedAt */
/* PA */ { 1, ASN_GENERALIZED_TIME, 0, 0, 0, },
/* responses */
@@ -36878,6 +36892,7 @@ enum {
OCSPRESPDATAASN_IDX_VER,
OCSPRESPDATAASN_IDX_BYNAME,
OCSPRESPDATAASN_IDX_BYKEY,
OCSPRESPDATAASN_IDX_BYKEY_OCT,
OCSPRESPDATAASN_IDX_PA,
OCSPRESPDATAASN_IDX_RESP,
OCSPRESPDATAASN_IDX_RESPEXT,
@@ -36922,16 +36937,40 @@ static int DecodeResponseData(byte* source, word32* ioIndex,
version = 0;
localIdx = idx;
if (GetASNTag(source, &localIdx, &tag, size) == 0 &&
( tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1) ||
tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 2) ))
if (GetASNTag(source, &localIdx, &tag, size) != 0)
return ASN_PARSE_E;
resp->responderIdType = OCSP_RESPONDER_ID_INVALID;
/* parse byName */
if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1))
{
idx++; /* advance past ASN tag */
if (GetLength(source, &idx, &length, size) < 0)
return ASN_PARSE_E;
/* compute the hash of the name */
resp->responderIdType = OCSP_RESPONDER_ID_NAME;
ret = CalcHashId_ex(source + idx, length,
resp->responderId.nameHash, WC_SHA);
if (ret != 0)
return ret;
idx += length;
}
else
else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 2))
{
idx++; /* advance past ASN tag */
if (GetLength(source, &idx, &length, size) < 0)
return ASN_PARSE_E;
if (GetOctetString(source, &idx, &length, size) < 0)
return ASN_PARSE_E;
if (length != KEYID_SIZE)
return ASN_PARSE_E;
resp->responderIdType = OCSP_RESPONDER_ID_KEY;
XMEMCPY(resp->responderId.keyHash, source + idx, length);
idx += length;
}
if (resp->responderIdType == OCSP_RESPONDER_ID_INVALID)
return ASN_PARSE_E;
/* save pointer to the producedAt time */
@@ -36967,6 +37006,7 @@ static int DecodeResponseData(byte* source, word32* ioIndex,
XMEMSET(single->next->status, 0, sizeof(CertStatus));
single->next->isDynamic = 1;
single->next->ownStatus = 1;
single = single->next;
}
@@ -36987,6 +37027,7 @@ static int DecodeResponseData(byte* source, word32* ioIndex,
int ret = 0;
byte version;
word32 dateSz = 0;
word32 responderByKeySz = KEYID_SIZE;
word32 idx = *ioIndex;
OcspEntry* single = NULL;
@@ -37005,6 +37046,8 @@ static int DecodeResponseData(byte* source, word32* ioIndex,
GetASN_Int8Bit(&dataASN[OCSPRESPDATAASN_IDX_VER], &version);
GetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_PA], resp->producedDate,
&dateSz);
GetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_BYKEY_OCT],
resp->responderId.keyHash, &responderByKeySz);
/* Decode the ResponseData. */
ret = GetASN_Items(ocspRespDataASN, dataASN, ocspRespDataASN_Length,
1, source, ioIndex, size);
@@ -37022,7 +37065,22 @@ static int DecodeResponseData(byte* source, word32* ioIndex,
}
}
if (ret == 0) {
/* TODO: use byName/byKey fields. */
if (dataASN[OCSPRESPDATAASN_IDX_BYNAME].tag != 0) {
resp->responderIdType = OCSP_RESPONDER_ID_NAME;
ret = CalcHashId_ex(
dataASN[OCSPRESPDATAASN_IDX_BYNAME].data.ref.data,
dataASN[OCSPRESPDATAASN_IDX_BYNAME].data.ref.length,
resp->responderId.nameHash, WC_SHA);
} else {
resp->responderIdType = OCSP_RESPONDER_ID_KEY;
if (dataASN[OCSPRESPDATAASN_IDX_BYKEY_OCT].length != KEYID_SIZE) {
ret = ASN_PARSE_E;
} else {
resp->responderIdType = OCSP_RESPONDER_ID_KEY;
}
}
}
if (ret == 0) {
/* Store size of response. */
resp->responseSz = *ioIndex - idx;
/* Store date format/tag. */
@@ -37056,6 +37114,7 @@ static int DecodeResponseData(byte* source, word32* ioIndex,
/* Entry to be freed. */
single->next->isDynamic = 1;
single->next->ownStatus = 1;
/* used will be 0 (false) */
single = single->next;
@@ -37164,8 +37223,135 @@ enum {
#define ocspBasicRespASN_Length (sizeof(ocspBasicRespASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */
static int OcspRespIdMatch(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;
return XMEMCMP(keyHash, resp->responderId.keyHash, KEYID_SIZE) == 0;
}
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
static int OcspRespCheck(OcspResponse *resp, Signer *responder)
{
OcspEntry *s;
s = resp->single;
if (s == NULL)
return -1;
/* singles responses must have the same issuer */
for (; s != NULL; s = s->next) {
if (XMEMCMP(s->issuerKeyHash, responder->subjectKeyHash,
KEYID_SIZE) != 0)
return -1;
}
return 0;
}
#endif
static Signer *OcspFindSigner(OcspResponse *resp, WOLFSSL_CERT_MANAGER *cm)
{
Signer *s;
if (cm == NULL)
return NULL;
if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) {
#ifndef NO_SKID
s = GetCAByName(cm, resp->responderId.nameHash);
#else
s = GetCA(cm, resp->responderId.nameHash);
#endif
if (s)
return s;
}
else {
s = GetCAByKeyHash(cm, resp->responderId.keyHash);
if (s)
return s;
}
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
if (resp->pendingCAs == NULL)
return NULL;
if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) {
s = findSignerByName(resp->pendingCAs, resp->responderId.nameHash);
if (s)
return s;
}
else {
s = findSignerByKeyHash(resp->pendingCAs, resp->responderId.keyHash);
if (s)
return s;
}
#endif
return NULL;
}
static int OcspCheckCert(OcspResponse *resp, int noVerify,
int noVerifySignature, WOLFSSL_CERT_MANAGER *cm, void *heap)
{
int ret = 0;
#ifdef WOLFSSL_SMALL_STACK
DecodedCert *cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (cert == NULL)
return MEMORY_E;
#else
DecodedCert cert[1];
#endif
InitDecodedCert(cert, resp->cert, resp->certSz, heap);
ret = ParseCertRelative(cert, CERT_TYPE,
noVerify ? NO_VERIFY : VERIFY_OCSP_CERT,
cm, resp->pendingCAs);
if (ret < 0) {
WOLFSSL_MSG("\tOCSP Responder certificate parsing failed");
}
if (ret == 0 &&
OcspRespIdMatch(resp,
cert->subjectHash, cert->subjectKeyHash) == 0) {
WOLFSSL_MSG("\tInternal check doesn't match responder ID, ignoring\n");
ret = BAD_OCSP_RESPONDER;
goto err;
}
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
if (ret == 0 && !noVerify) {
ret = CheckOcspResponder(resp, cert, cm);
if (ret != 0) {
WOLFSSL_MSG("\tOCSP Responder certificate issuer check failed");
goto err;
}
}
#endif /* WOLFSSL_NO_OCSP_ISSUER_CHECK */
if (ret == 0 && !noVerifySignature) {
ret = ConfirmSignature(
&cert->sigCtx,
resp->response, resp->responseSz,
cert->publicKey, cert->pubKeySize, cert->keyOID,
resp->sig, resp->sigSz, resp->sigOID, resp->sigParams,
resp->sigParamsSz, NULL);
}
err:
FreeDecodedCert(cert);
#ifdef WOLFSSL_SMALL_STACK
if (cert != NULL) {
XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
#endif
return ret;
}
static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
OcspResponse* resp, word32 size, void* cm, void* heap, int noVerify)
OcspResponse* resp, word32 size, void* cm, void* heap, int noVerify,
int noVerifySignature)
{
#ifndef WOLFSSL_ASN_TEMPLATE
int length;
@@ -37175,8 +37361,7 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
#endif
int ret;
int sigLength;
const byte* sigParams = NULL;
word32 sigParamsSz = 0;
int sigValid = 0;
WOLFSSL_ENTER("DecodeBasicOcspResponse");
(void)heap;
@@ -37200,16 +37385,16 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
else if (resp->sigOID == CTC_RSASSAPSS) {
word32 sz;
int len;
const byte* params;
byte* params;
sz = idx;
params = source + idx;
if (GetSequence(source, &idx, &len, size) < 0)
ret = ASN_PARSE_E;
return ASN_PARSE_E;
if (ret == 0) {
idx += len;
sigParams = params;
sigParamsSz = idx - sz;
resp->sigParams = params;
resp->sigParamsSz = idx - sz;
}
}
#endif
@@ -37229,107 +37414,43 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
#ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
if (idx < end_index)
{
int cert_inited = 0;
#ifdef WOLFSSL_SMALL_STACK
DecodedCert *cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (cert == NULL)
return MEMORY_E;
#else
DecodedCert cert[1];
#endif
if (DecodeCerts(source, &idx, resp, size) < 0)
return ASN_PARSE_E;
do {
if (DecodeCerts(source, &idx, resp, size) < 0) {
ret = ASN_PARSE_E;
break;
}
InitDecodedCert(cert, resp->cert, resp->certSz, heap);
cert_inited = 1;
/* Don't verify if we don't have access to Cert Manager. */
ret = ParseCertRelative(cert, CERT_TYPE,
noVerify ? NO_VERIFY : VERIFY_OCSP_CERT,
cm, resp->pendingCAs);
if (ret < 0) {
WOLFSSL_MSG("\tOCSP Responder certificate parsing failed");
break;
}
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
if ((cert->extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) == 0) {
if (XMEMCMP(cert->subjectHash,
resp->single->issuerHash, OCSP_DIGEST_SIZE) == 0) {
WOLFSSL_MSG("\tOCSP Response signed by issuer");
}
else {
WOLFSSL_MSG("\tOCSP Responder key usage check failed");
#ifdef OPENSSL_EXTRA
resp->verifyError = OCSP_BAD_ISSUER;
#else
ret = BAD_OCSP_RESPONDER;
break;
#endif
}
}
#endif
/* ConfirmSignature is blocking here */
ret = ConfirmSignature(
&cert->sigCtx,
resp->response, resp->responseSz,
cert->publicKey, cert->pubKeySize, cert->keyOID,
resp->sig, resp->sigSz, resp->sigOID, sigParams, sigParamsSz,
NULL);
if (ret != 0) {
WOLFSSL_MSG("\tOCSP Confirm signature failed");
ret = ASN_OCSP_CONFIRM_E;
break;
}
} while(0);
if (cert_inited)
FreeDecodedCert(cert);
#ifdef WOLFSSL_SMALL_STACK
XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
if (ret != 0)
return ret;
ret = OcspCheckCert(resp, noVerify, noVerifySignature, cm, heap);
if (ret == 0) {
sigValid = 1;
}
else {
WOLFSSL_MSG("OCSP Internal cert can't verify the response\n");
/* try to verify the OCSP response with CA certs */
ret = 0;
}
}
else
#endif /* WOLFSSL_NO_OCSP_OPTIONAL_CERTS */
{
if (!noVerifySignature && !sigValid) {
Signer* ca;
int sigValid = -1;
SignatureCtx sigCtx;
ca = OcspFindSigner(resp, cm);
if (ca == NULL)
return ASN_NO_SIGNER_E;
#ifndef NO_SKID
ca = GetCAByKeyHash(cm, resp->single->issuerKeyHash);
#else
ca = GetCA(cm, resp->single->issuerHash);
#endif
#if defined(HAVE_CERTIFICATE_STATUS_V2)
if (ca == NULL && resp->pendingCAs != NULL) {
ca = findSignerByName(resp->pendingCAs, resp->single->issuerHash);
}
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
if (OcspRespCheck(resp, ca) != 0)
return BAD_OCSP_RESPONDER;
#endif
if (ca) {
SignatureCtx sigCtx;
InitSignatureCtx(&sigCtx, heap, INVALID_DEVID);
InitSignatureCtx(&sigCtx, heap, INVALID_DEVID);
/* ConfirmSignature is blocking here */
sigValid = ConfirmSignature(&sigCtx, resp->response,
resp->responseSz, ca->publicKey, ca->pubKeySize, ca->keyOID,
resp->sig, resp->sigSz, resp->sigOID, sigParams, sigParamsSz,
NULL);
}
if (ca == NULL || sigValid != 0) {
/* ConfirmSignature is blocking here */
sigValid = ConfirmSignature(&sigCtx, resp->response,
resp->responseSz, ca->publicKey, ca->pubKeySize, ca->keyOID,
resp->sig, resp->sigSz, resp->sigOID, resp->sigParams,
resp->sigParamsSz, NULL);
if (sigValid != 0) {
WOLFSSL_MSG("\tOCSP Confirm signature failed");
return ASN_OCSP_CONFIRM_E;
}
(void)noVerify;
}
@@ -37339,16 +37460,8 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
DECL_ASNGETDATA(dataASN, ocspBasicRespASN_Length);
int ret = 0;
word32 idx = *ioIndex;
const byte* sigParams = NULL;
word32 sigParamsSz = 0;
#ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
#ifdef WOLFSSL_SMALL_STACK
DecodedCert* cert = NULL;
#else
DecodedCert cert[1];
#endif
int certInit = 0;
#endif
Signer* ca = NULL;
int sigValid = 0;
WOLFSSL_ENTER("DecodeBasicOcspResponse");
(void)heap;
@@ -37373,10 +37486,10 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
}
#ifdef WC_RSA_PSS
if (ret == 0 && (dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS].tag != 0)) {
sigParams = GetASNItem_Addr(
resp->sigParams = GetASNItem_Addr(
dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS],
source);
sigParamsSz =
resp->sigParamsSz =
GetASNItem_Length(dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS],
source);
}
@@ -37387,6 +37500,7 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
GetASN_GetRef(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE], &resp->sig,
&resp->sigSz);
}
resp->certSz = 0;
#ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
if ((ret == 0) &&
(dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ].data.ref.data != NULL)) {
@@ -37394,106 +37508,52 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex,
/* Store reference to certificate BER data. */
GetASN_GetRef(&dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ], &resp->cert,
&resp->certSz);
/* Allocate a certificate object to decode cert into. */
#ifdef WOLFSSL_SMALL_STACK
cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (cert == NULL) {
ret = MEMORY_E;
}
}
if ((ret == 0) &&
(dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ].data.ref.data != NULL)) {
#endif
/* Initialize the certificate object. */
InitDecodedCert(cert, resp->cert, resp->certSz, heap);
certInit = 1;
/* Parse the certificate and don't verify if we don't have access to
* Cert Manager. */
ret = ParseCertRelative(cert, CERT_TYPE, noVerify ? NO_VERIFY : VERIFY,
cm, resp->pendingCAs);
if (ret < 0) {
WOLFSSL_MSG("\tOCSP Responder certificate parsing failed");
if ((ret == 0) && resp->certSz > 0) {
ret = OcspCheckCert(resp, noVerify, noVerifySignature,
(WOLFSSL_CERT_MANAGER*)cm, heap);
if (ret == 0) {
sigValid = 1;
}
ret = 0; /* try to verify the OCSP response with CA certs */
}
#endif /* WOLFSSL_NO_OCSP_OPTIONAL_CERTS */
/* try to verify using cm certs */
if (ret == 0 && !noVerifySignature && !sigValid)
{
ca = OcspFindSigner(resp, (WOLFSSL_CERT_MANAGER*)cm);
if (ca == NULL)
ret = ASN_NO_SIGNER_E;
}
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
if ((ret == 0) &&
(dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ].data.ref.data != NULL) &&
!noVerify) {
ret = CheckOcspResponder(resp, cert, cm);
if (ret == 0 && !noVerifySignature && !sigValid) {
if (OcspRespCheck(resp, ca) != 0) {
ret = BAD_OCSP_RESPONDER;
}
}
#endif /* WOLFSSL_NO_OCSP_ISSUER_CHECK */
if ((ret == 0) &&
(dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ].data.ref.data != NULL)) {
#endif
if (ret == 0 && !noVerifySignature && !sigValid) {
SignatureCtx sigCtx;
/* Initialize the signature context. */
InitSignatureCtx(&sigCtx, heap, INVALID_DEVID);
/* TODO: ConfirmSignature is blocking here */
/* Check the signature of the response. */
ret = ConfirmSignature(&cert->sigCtx, resp->response, resp->responseSz,
cert->publicKey, cert->pubKeySize, cert->keyOID, resp->sig,
resp->sigSz, resp->sigOID, NULL, 0, NULL);
if (ret != 0) {
/* Check the signature of the response CA public key. */
sigValid = ConfirmSignature(&sigCtx, resp->response,
resp->responseSz, ca->publicKey, ca->pubKeySize, ca->keyOID,
resp->sig, resp->sigSz, resp->sigOID, resp->sigParams,
resp->sigParamsSz, NULL);
if (sigValid != 0) {
WOLFSSL_MSG("\tOCSP Confirm signature failed");
ret = ASN_OCSP_CONFIRM_E;
}
}
if ((ret == 0) &&
(dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ].data.ref.data == NULL))
#else
if (ret == 0)
#endif /* WOLFSSL_NO_OCSP_OPTIONAL_CERTS */
{
Signer* ca;
int sigValid = -1;
/* Response didn't have a certificate - lookup CA. */
#ifndef NO_SKID
ca = GetCAByKeyHash(cm, resp->single->issuerKeyHash);
#else
ca = GetCA(cm, resp->single->issuerHash);
#endif
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
if (ca == NULL && resp->pendingCAs != NULL) {
ca = findSignerByName(resp->pendingCAs, resp->single->issuerHash);
}
#endif
if (ca) {
SignatureCtx sigCtx;
/* Initialize he signature context. */
InitSignatureCtx(&sigCtx, heap, INVALID_DEVID);
/* TODO: ConfirmSignature is blocking here */
/* Check the signature of the response CA public key. */
sigValid = ConfirmSignature(&sigCtx, resp->response,
resp->responseSz, ca->publicKey, ca->pubKeySize, ca->keyOID,
resp->sig, resp->sigSz, resp->sigOID, sigParams, sigParamsSz,
NULL);
}
if ((ca == NULL) || (sigValid != 0)) {
/* Didn't find certificate or signature verificate failed. */
WOLFSSL_MSG("\tOCSP Confirm signature failed");
ret = ASN_OCSP_CONFIRM_E;
}
}
if (ret == 0) {
/* Update the position to after response data. */
*ioIndex = idx;
}
#ifndef WOLFSSL_NO_OCSP_OPTIONAL_CERTS
if (certInit) {
FreeDecodedCert(cert);
}
#ifdef WOLFSSL_SMALL_STACK
if (cert != NULL) {
/* Dispose of certificate object. */
XFREE(cert, heap, DYNAMIC_TYPE_TMP_BUFFER);
}
#endif
#endif
FREE_ASNGETDATA(dataASN, heap);
return ret;
#endif /* WOLFSSL_ASN_TEMPLATE */
@@ -37516,6 +37576,9 @@ void InitOcspResponse(OcspResponse* resp, OcspEntry* single, CertStatus* status,
resp->maxIdx = inSz;
resp->heap = heap;
resp->pendingCAs = NULL;
resp->sigParams = NULL;
resp->sigParamsSz = 0;
resp->responderIdType = OCSP_RESPONDER_ID_INVALID;
}
void FreeOcspResponse(OcspResponse* resp)
@@ -37569,7 +37632,8 @@ enum {
#define ocspResponseASN_Length (sizeof(ocspResponseASN) / sizeof(ASNItem))
#endif /* WOLFSSL_ASN_TEMPLATE */
int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap, int noVerify)
int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap,
int noVerifyCert, int noVerifySignature)
{
#ifndef WOLFSSL_ASN_TEMPLATE
int ret;
@@ -37638,7 +37702,8 @@ int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap, int noVerify)
return ret;
}
ret = DecodeBasicOcspResponse(source, &idx, resp, size, cm, heap, noVerify);
ret = DecodeBasicOcspResponse(source, &idx, resp, size, cm, heap,
noVerifyCert, noVerifySignature);
if (ret < 0) {
WOLFSSL_LEAVE("OcspResponseDecode", ret);
return ret;
@@ -37678,7 +37743,7 @@ int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap, int noVerify)
idx = 0;
/* Decode BasicOCSPResponse. */
ret = DecodeBasicOcspResponse(basic, &idx, resp, basicSz, cm, heap,
noVerify);
noVerifyCert, noVerifySignature);
}
/* Only support BasicOCSPResponse. */
else {

View File

@@ -6133,12 +6133,10 @@ struct WOLFSSL {
void* ocspIOCtx;
byte ocspProducedDate[MAX_DATE_SZ];
int ocspProducedDateFormat;
#ifdef OPENSSL_EXTRA
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
byte* ocspResp;
int ocspRespSz;
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
char* url;
#endif
char* url;
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST)
word32 response_idx;

View File

@@ -2352,7 +2352,12 @@ WOLFSSL_LOCAL int CheckCertSignaturePubKey(const byte* cert, word32 certSz,
WOLFSSL_LOCAL int wc_CertGetPubKey(const byte* cert, word32 certSz,
const unsigned char** pubKey, word32* pubKeySz);
#endif
WOLFSSL_LOCAL int ConfirmSignature(SignatureCtx* sigCtx,
const byte* buf, word32 bufSz,
const byte* key, word32 keySz, word32 keyOID,
const byte* sig, word32 sigSz, word32 sigOID,
const byte* sigParams, word32 sigParamsSz,
byte* rsaKeyIdx);
#ifdef WOLFSSL_CERT_REQ
WOLFSSL_LOCAL int CheckCSRSignaturePubKey(const byte* cert, word32 certSz,
void* heap, const byte* pubKey, word32 pubKeySz, int pubKeyOID);
@@ -2369,6 +2374,7 @@ WOLFSSL_LOCAL int TryDecodeRPKToKey(DecodedCert* cert);
WOLFSSL_LOCAL int wc_GetPubX509(DecodedCert* cert, int verify, int* badDate);
WOLFSSL_LOCAL const byte* OidFromId(word32 id, word32 type, word32* oidSz);
WOLFSSL_LOCAL Signer* findSignerByKeyHash(Signer *list, byte *hash);
WOLFSSL_LOCAL Signer* findSignerByName(Signer *list, byte *hash);
WOLFSSL_LOCAL int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der);
WOLFSSL_LOCAL Signer* MakeSigner(void* heap);
@@ -2726,6 +2732,11 @@ struct OcspEntry
WC_BITFIELD used:1; /* entry used */
};
enum responderIdType {
OCSP_RESPONDER_ID_INVALID = 0,
OCSP_RESPONDER_ID_NAME = 1,
OCSP_RESPONDER_ID_KEY = 2,
};
/* TODO: Long-term, it would be helpful if we made this struct and other OCSP
structs conform to the ASN spec as described in RFC 6960. It will help
with readability and with implementing OpenSSL compatibility API
@@ -2737,6 +2748,12 @@ struct OcspResponse {
byte* response; /* Pointer to beginning of OCSP Response */
word32 responseSz; /* length of the OCSP Response */
enum responderIdType responderIdType;
union {
byte keyHash[KEYID_SIZE];
byte nameHash[KEYID_SIZE];
} responderId ;
byte producedDate[MAX_DATE_SIZE];
/* Date at which this response was signed */
byte producedDateFormat; /* format of the producedDate */
@@ -2748,6 +2765,9 @@ struct OcspResponse {
word32 sigSz; /* Length in octets for the sig */
word32 sigOID; /* OID for hash used for sig */
byte* sigParams;
word32 sigParamsSz;
OcspEntry* single; /* chain of OCSP single responses */
byte* nonce; /* pointer to nonce inside ASN.1 response */
@@ -2756,9 +2776,6 @@ struct OcspResponse {
byte* source; /* pointer to source buffer, not owned */
word32 maxIdx; /* max offset based on init size */
Signer* pendingCAs;
#ifdef OPENSSL_EXTRA
int verifyError;
#endif
void* heap;
};
@@ -2788,7 +2805,7 @@ WOLFSSL_LOCAL void InitOcspResponse(OcspResponse* resp, OcspEntry* single,
CertStatus* status, byte* source, word32 inSz, void* heap);
WOLFSSL_LOCAL void FreeOcspResponse(OcspResponse* resp);
WOLFSSL_LOCAL int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap,
int noVerify);
int noVerifyCert, int noVerifySignature);
WOLFSSL_LOCAL int InitOcspRequest(OcspRequest* req, DecodedCert* cert,
byte useNonce, void* heap);