Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -17953,6 +17953,17 @@ static int DoCertificateStatus(WOLFSSL* ssl, byte* input, word32* inOutIdx,
|| (response->single->status->status != CERT_GOOD))
ret = BAD_CERTIFICATE_STATUS_ERROR;

/* Bundling more than one SingleResponse inside a single
* stapled BasicOCSPResponse is not supported. The per-cert
* status checked above comes from the first SingleResponse
* in wire order, which is not necessarily the one matching
* the stapled certificate (CompareOcspReqResp() below is
* what locates the match). Rather than risk acting on the
* wrong SingleResponse, reject any response that carries
* multiple singles. */
if (ret == 0 && response->single->next != NULL)
ret = BAD_CERTIFICATE_STATUS_ERROR;

if (ret == 0) {
request = (OcspRequest*)TLSX_CSR2_GetRequest(
ssl->extensions, status_type, idx);
Expand Down
1 change: 1 addition & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -35604,6 +35604,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_ocsp_certid_dup),
TEST_DECL(test_ocsp_resp_find_status_serial_prefix),
TEST_DECL(test_ocsp_tls_cert_cb),
TEST_DECL(test_ocsp_status_request_v2_multi_revoked_single),
TEST_DECL(test_ocsp_cert_unknown_crl_fallback),
TEST_DECL(test_ocsp_cert_unknown_crl_fallback_nonleaf),
TEST_DECL(test_tls13_nonblock_ocsp_low_mfl),
Expand Down
26 changes: 26 additions & 0 deletions tests/api/create_ocsp_test_blobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,32 @@ def create_bad_response(rd: dict) -> bytes:
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-key.pem',
'name': 'resp_server1_cert'
},
{
# A single, validly-signed BasicOCSPResponse that bundles two
# SingleResponses: the first (wire order) is a benign CERT_GOOD entry
# for an unrelated serial, the second is the server1 leaf cert marked
# CERT_REVOKED.
# Signed by intermediate1, the legitimate issuer and
# authorized responder for server1.
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
},
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
'serial': 0x05,
'status': CERT_REVOKED
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-key.pem',
'name': 'resp_server1_revoked_good_first'
},
{
# Ancestor-issued responder; rejected by RFC 6960 4.2.2.2 enforcement
'response_status': 0,
Expand Down
129 changes: 129 additions & 0 deletions tests/api/test_ocsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1218,11 +1218,140 @@ int test_ocsp_tls_cert_cb(void)
return EXPECT_RESULT();
}

#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
/* Stapling for a 3-cert chain. The intermediate and root staples are the
* normal single-SingleResponse CERT_GOOD responses, but the leaf (idx 0)
* staple is resp_server1_revoked_good_first: one validly-signed
* BasicOCSPResponse that bundles a benign CERT_GOOD single FIRST and the
* server1 leaf cert's CERT_REVOKED single SECOND. */
static int test_ocsp_revoked_multi_single_status_cb(WOLFSSL* ssl, void* ioCtx)
{
byte* leaf_resp = NULL;
byte* int_resp = NULL;
byte* root_resp = NULL;
int ret = WOLFSSL_OCSP_STATUS_CB_ALERT_FATAL;
(void)ioCtx;
leaf_resp = (byte*)XMALLOC(sizeof(resp_server1_revoked_good_first), NULL, 0);
int_resp = (byte*)XMALLOC(sizeof(resp_intermediate1_cert), NULL, 0);
root_resp = (byte*)XMALLOC(sizeof(resp_root_ca_cert), NULL, 0);
if (leaf_resp != NULL && int_resp != NULL && root_resp != NULL) {
XMEMCPY(leaf_resp, resp_server1_revoked_good_first,
sizeof(resp_server1_revoked_good_first));
XMEMCPY(int_resp, resp_intermediate1_cert,
sizeof(resp_intermediate1_cert));
XMEMCPY(root_resp, resp_root_ca_cert, sizeof(resp_root_ca_cert));
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, leaf_resp,
sizeof(resp_server1_revoked_good_first), 0) == WOLFSSL_SUCCESS)
leaf_resp = NULL;
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, int_resp,
sizeof(resp_intermediate1_cert), 1) == WOLFSSL_SUCCESS)
int_resp = NULL;
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, root_resp,
sizeof(resp_root_ca_cert), 2) == WOLFSSL_SUCCESS)
root_resp = NULL;
if (leaf_resp == NULL && int_resp == NULL && root_resp == NULL)
ret = WOLFSSL_OCSP_STATUS_CB_OK;
}
XFREE(leaf_resp, NULL, 0);
XFREE(int_resp, NULL, 0);
XFREE(root_resp, NULL, 0);
return ret;
}

/*
* status_request_v2 OCSP-multi must reject a stapled leaf response
* whose MATCHING SingleResponse is CERT_REVOKED, even when an unrelated
* CERT_GOOD SingleResponse appears first in the same BasicOCSPResponse.
*/
int test_ocsp_status_request_v2_multi_revoked_single(void)
{
EXPECT_DECLS;
size_t i;
struct {
method_provider client_meth;
method_provider server_meth;
const char* tls_version;
} params[] = {
#if !defined(WOLFSSL_NO_TLS12)
{ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, "TLSv1_2" },
#ifdef WOLFSSL_DTLS
{ wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLSv1_2" },
#endif
#endif
};

for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
struct test_ssl_memio_ctx test_ctx;
WOLFSSL_ALERT_HISTORY h;

printf("\nTesting %s\n", params[i].tls_version);

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_ctx.c_cb.caPemFile = "";
/* Server cert/key come only from the per-connection cert callback. */
test_ctx.s_cb.certPemFile = "";
test_ctx.s_cb.keyPemFile = "";
test_ctx.c_cb.method = params[i].client_meth;
test_ctx.s_cb.method = params[i].server_meth;

/* Full server1 -> intermediate1 -> root chain so the leaf staple is at
* idx 0. */
test_ocsp_tls_cert_cb_opts.chainLen = 3;
test_ocsp_tls_cert_cb_opts.failStaple = 0;
test_ctx.s_cb.ctx_ready = test_ocsp_tls_cert_cb_ctx_ready;

ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
ExpectIntEQ(wolfSSL_UnloadCertsKeys(test_ctx.s_ssl), WOLFSSL_SUCCESS);

/* server: enable stapling and supply the multi-single leaf */
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.s_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_revoked_multi_single_status_cb), WOLFSSL_SUCCESS);

/* client: verify chain via callback, request status_request_v2 multi.
* Deliberately no ocsp_status_verify_cb: exercise DoCertificateStatus
* in isolation. */
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT,
test_ocsp_tls_cert_cb_verify_cb);
wolfSSL_SetCertCbCtx(test_ctx.c_ssl, test_ctx.c_ssl);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseOCSPStaplingV2(test_ctx.c_ssl,
WOLFSSL_CSR2_OCSP_MULTI, WOLFSSL_CSR2_OCSP_USE_NONCE),
WOLFSSL_SUCCESS);

/* Revoked leaf must abort the handshake. */
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_FAIL);
XMEMSET(&h, 0, sizeof(h));
ExpectIntEQ(wolfSSL_get_alert_history(test_ctx.s_ssl, &h),
WOLFSSL_SUCCESS);
ExpectIntEQ(h.last_rx.level, alert_fatal);
ExpectIntEQ(h.last_rx.code, bad_certificate_status_response);

test_ssl_memio_cleanup(&test_ctx);
}
return EXPECT_RESULT();
}
#else
int test_ocsp_status_request_v2_multi_revoked_single(void)
{
return TEST_SKIPPED;
}
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */

#else /* feature guards */
int test_ocsp_tls_cert_cb(void)
{
return TEST_SKIPPED;
}
int test_ocsp_status_request_v2_multi_revoked_single(void)
{
return TEST_SKIPPED;
}
#endif

/*
Expand Down
1 change: 1 addition & 0 deletions tests/api/test_ocsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ int test_ocsp_basic_verify(void);
int test_ocsp_responder_keyhash_binding(void);
int test_ocsp_response_parsing(void);
int test_ocsp_tls_cert_cb(void);
int test_ocsp_status_request_v2_multi_revoked_single(void);
int test_ocsp_cert_unknown_crl_fallback(void);
int test_ocsp_cert_unknown_crl_fallback_nonleaf(void);
int test_tls13_nonblock_ocsp_low_mfl(void);
Expand Down
60 changes: 60 additions & 0 deletions tests/api/test_ocsp_test_blobs.h
Original file line number Diff line number Diff line change
Expand Up @@ -2415,4 +2415,64 @@ unsigned char resp_bad[] = {
0x63, 0x29, 0xc6, 0xa8, 0x97, 0x4b, 0x14, 0xff, 0xd2,
};

unsigned char resp_server1_revoked_good_first[] = {
0x30, 0x82, 0x02, 0x9e, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x02, 0x97, 0x30,
0x82, 0x02, 0x93, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
0x01, 0x01, 0x04, 0x82, 0x02, 0x84, 0x30, 0x82, 0x02, 0x80, 0x30, 0x82,
0x01, 0x6a, 0xa1, 0x81, 0xa4, 0x30, 0x81, 0xa1, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68,
0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03,
0x55, 0x04, 0x07, 0x0c, 0x07, 0x53, 0x65, 0x61, 0x74, 0x74, 0x6c, 0x65,
0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x77,
0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03,
0x55, 0x04, 0x0b, 0x0c, 0x0b, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x65,
0x72, 0x69, 0x6e, 0x67, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04,
0x03, 0x0c, 0x19, 0x77, 0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x20, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20,
0x43, 0x41, 0x20, 0x31, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e, 0x66,
0x6f, 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f,
0x6d, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31,
0x36, 0x34, 0x32, 0x33, 0x36, 0x5a, 0x30, 0x81, 0xaf, 0x30, 0x4d, 0x30,
0x38, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
0x71, 0x4d, 0x82, 0x23, 0x40, 0x59, 0xc0, 0x96, 0xa1, 0x37, 0x43, 0xfa,
0x31, 0xdb, 0xba, 0xb1, 0x43, 0x18, 0xda, 0x04, 0x04, 0x14, 0x83, 0xc6,
0x3a, 0x89, 0x2c, 0x81, 0xf4, 0x02, 0xd7, 0x9d, 0x4c, 0xe2, 0x2a, 0xc0,
0x71, 0x82, 0x64, 0x44, 0xda, 0x0e, 0x02, 0x01, 0x01, 0x80, 0x00, 0x18,
0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34,
0x32, 0x33, 0x36, 0x5a, 0x30, 0x5e, 0x30, 0x38, 0x30, 0x07, 0x06, 0x05,
0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x71, 0x4d, 0x82, 0x23, 0x40,
0x59, 0xc0, 0x96, 0xa1, 0x37, 0x43, 0xfa, 0x31, 0xdb, 0xba, 0xb1, 0x43,
0x18, 0xda, 0x04, 0x04, 0x14, 0x83, 0xc6, 0x3a, 0x89, 0x2c, 0x81, 0xf4,
0x02, 0xd7, 0x9d, 0x4c, 0xe2, 0x2a, 0xc0, 0x71, 0x82, 0x64, 0x44, 0xda,
0x0e, 0x02, 0x01, 0x05, 0xa1, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x36,
0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34, 0x32, 0x33, 0x36, 0x5a, 0x18,
0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34,
0x32, 0x33, 0x36, 0x5a, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01, 0x00, 0xc4, 0x95,
0xdb, 0x71, 0xca, 0x09, 0x4b, 0x37, 0xf1, 0xe8, 0x25, 0xc4, 0x75, 0x6f,
0x87, 0x22, 0x95, 0x8b, 0x7f, 0x7f, 0x00, 0xf0, 0x75, 0x35, 0x66, 0xea,
0x56, 0x26, 0xc4, 0x9a, 0xe1, 0x40, 0xe9, 0xd2, 0x7c, 0x8c, 0x83, 0x0f,
0x33, 0x33, 0xac, 0x1e, 0xcc, 0x8e, 0x0e, 0x99, 0x43, 0xb8, 0xd6, 0x13,
0xfa, 0xdb, 0xbe, 0xaf, 0x64, 0x6a, 0xbd, 0x92, 0x35, 0xb2, 0x7e, 0xaf,
0xda, 0x49, 0x89, 0xa2, 0x37, 0xcc, 0x50, 0x88, 0xa8, 0x0b, 0xb4, 0xe1,
0x6a, 0xb7, 0x94, 0x06, 0x92, 0xb8, 0x6a, 0xd2, 0x55, 0xfe, 0x7e, 0xb4,
0x5e, 0x12, 0xed, 0x9d, 0xf6, 0x04, 0x49, 0x2c, 0x68, 0xae, 0xc4, 0x21,
0x3c, 0xd4, 0xe4, 0x97, 0x76, 0x2c, 0xdb, 0x78, 0xfb, 0x22, 0xdd, 0x3b,
0xed, 0x03, 0x68, 0xaa, 0x7b, 0x9d, 0xd2, 0x66, 0x9f, 0x53, 0xc8, 0x45,
0xe7, 0x8d, 0xeb, 0x39, 0x05, 0x90, 0xbe, 0x9e, 0xe3, 0x3f, 0xcd, 0x4c,
0x0a, 0x37, 0x3c, 0x18, 0xbb, 0xef, 0x2e, 0xb6, 0x53, 0xda, 0x65, 0xfb,
0xd6, 0xd4, 0xfa, 0x04, 0xcb, 0x93, 0x23, 0x60, 0x5e, 0x62, 0x8b, 0xd2,
0x14, 0xe4, 0x14, 0xd3, 0x3f, 0xdd, 0x8b, 0x65, 0x55, 0x95, 0x27, 0x45,
0xdb, 0x7c, 0x74, 0x88, 0x33, 0x7b, 0x2d, 0x6e, 0x11, 0x41, 0x19, 0xc6,
0xe2, 0x79, 0x3d, 0x27, 0xc1, 0x5e, 0xb1, 0xcc, 0xf9, 0x6a, 0x9c, 0xaf,
0x73, 0x78, 0xc5, 0xeb, 0x1b, 0xfa, 0x6f, 0x92, 0x6b, 0x75, 0xf0, 0x70,
0xc9, 0x44, 0xd2, 0xa8, 0xbe, 0x2e, 0xee, 0x0e, 0x0b, 0xac, 0x7f, 0x99,
0x6d, 0x67, 0x1e, 0x2d, 0x7d, 0x19, 0x3a, 0x66, 0x1b, 0xb2, 0x0f, 0xfe,
0x49, 0x89, 0x24, 0xb2, 0x15, 0xa6, 0xcc, 0xd2, 0xdd, 0xc4, 0x45, 0xfb,
0xd2, 0xe5, 0xeb, 0xe8, 0xd7, 0x09, 0xf1, 0xc0, 0xb8, 0x0d, 0xc8, 0x34,
0xc2, 0x6c,
};

#endif /* OCSP_TEST_BLOBS_H */
Loading