Skip to content

Commit e8653a8

Browse files
committed
reject status_request_v2 ocsp-multi staples that bundle multiple SingleResponses
So a revoked certificate's non-first single can no longer be accepted
1 parent dd6da70 commit e8653a8

7 files changed

Lines changed: 228 additions & 0 deletions

File tree

doc/dox_comments/header_files/ssl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5681,6 +5681,11 @@ long wolfSSL_set_tlsext_status_ocsp_resp(WOLFSSL *ssl, unsigned char *resp, int
56815681
wolfSSL. The application must not free the buffer after calling this
56825682
function.
56835683

5684+
Each stapled response (one per certificate) must carry exactly one
5685+
SingleResponse. A BasicOCSPResponse bundling more than one SingleResponse
5686+
is rejected by the peer, so responders must supply a separate response per
5687+
certificate rather than combining statuses into a single response.
5688+
56845689
\param ssl The WOLFSSL session.
56855690
\param resp Pointer to the response buffer.
56865691
\param len Length of the response buffer.

src/internal.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17953,6 +17953,11 @@ static int DoCertificateStatus(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1795317953
|| (response->single->status->status != CERT_GOOD))
1795417954
ret = BAD_CERTIFICATE_STATUS_ERROR;
1795517955

17956+
/* Bundling more than one SingleResponse inside a single
17957+
* stapled BasicOCSPResponse is not supported. */
17958+
if (ret == 0 && response->single->next != NULL)
17959+
ret = BAD_CERTIFICATE_STATUS_ERROR;
17960+
1795617961
if (ret == 0) {
1795717962
request = (OcspRequest*)TLSX_CSR2_GetRequest(
1795817963
ssl->extensions, status_type, idx);

tests/api.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35604,6 +35604,7 @@ TEST_CASE testCases[] = {
3560435604
TEST_DECL(test_ocsp_certid_dup),
3560535605
TEST_DECL(test_ocsp_resp_find_status_serial_prefix),
3560635606
TEST_DECL(test_ocsp_tls_cert_cb),
35607+
TEST_DECL(test_ocsp_status_request_v2_multi_revoked_single),
3560735608
TEST_DECL(test_ocsp_cert_unknown_crl_fallback),
3560835609
TEST_DECL(test_ocsp_cert_unknown_crl_fallback_nonleaf),
3560935610
TEST_DECL(test_tls13_nonblock_ocsp_low_mfl),

tests/api/create_ocsp_test_blobs.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,32 @@ def create_bad_response(rd: dict) -> bytes:
436436
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-key.pem',
437437
'name': 'resp_server1_cert'
438438
},
439+
{
440+
# A single, validly-signed BasicOCSPResponse that bundles two
441+
# SingleResponses: the first (wire order) is a benign CERT_GOOD entry
442+
# for an unrelated serial, the second is the server1 leaf cert marked
443+
# CERT_REVOKED.
444+
# Signed by intermediate1, the legitimate issuer and
445+
# authorized responder for server1.
446+
'response_status': 0,
447+
'signature_algorithm': signature_algorithm(),
448+
'responder_by_name': True,
449+
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
450+
'responses': [
451+
{
452+
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
453+
'serial': 0x01,
454+
'status': CERT_GOOD
455+
},
456+
{
457+
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem',
458+
'serial': 0x05,
459+
'status': CERT_REVOKED
460+
}
461+
],
462+
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-key.pem',
463+
'name': 'resp_server1_revoked_good_first'
464+
},
439465
{
440466
# Ancestor-issued responder; rejected by RFC 6960 4.2.2.2 enforcement
441467
'response_status': 0,

tests/api/test_ocsp.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,11 +1218,141 @@ int test_ocsp_tls_cert_cb(void)
12181218
return EXPECT_RESULT();
12191219
}
12201220

1221+
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(WOLFSSL_NO_TLS12)
1222+
/* Stapling for a 3-cert chain. The intermediate and root staples are the
1223+
* normal single-SingleResponse CERT_GOOD responses, but the leaf (idx 0)
1224+
* staple is resp_server1_revoked_good_first: one validly-signed
1225+
* BasicOCSPResponse that bundles a benign CERT_GOOD single FIRST and the
1226+
* server1 leaf cert's CERT_REVOKED single SECOND. */
1227+
static int test_ocsp_revoked_multi_single_status_cb(WOLFSSL* ssl, void* ioCtx)
1228+
{
1229+
byte* leaf_resp = NULL;
1230+
byte* int_resp = NULL;
1231+
byte* root_resp = NULL;
1232+
int ret = WOLFSSL_OCSP_STATUS_CB_ALERT_FATAL;
1233+
(void)ioCtx;
1234+
leaf_resp = (byte*)XMALLOC(sizeof(resp_server1_revoked_good_first), NULL,
1235+
DYNAMIC_TYPE_TMP_BUFFER);
1236+
int_resp = (byte*)XMALLOC(sizeof(resp_intermediate1_cert), NULL,
1237+
DYNAMIC_TYPE_TMP_BUFFER);
1238+
root_resp = (byte*)XMALLOC(sizeof(resp_root_ca_cert), NULL,
1239+
DYNAMIC_TYPE_TMP_BUFFER);
1240+
if (leaf_resp != NULL && int_resp != NULL && root_resp != NULL) {
1241+
XMEMCPY(leaf_resp, resp_server1_revoked_good_first,
1242+
sizeof(resp_server1_revoked_good_first));
1243+
XMEMCPY(int_resp, resp_intermediate1_cert,
1244+
sizeof(resp_intermediate1_cert));
1245+
XMEMCPY(root_resp, resp_root_ca_cert, sizeof(resp_root_ca_cert));
1246+
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, leaf_resp,
1247+
sizeof(resp_server1_revoked_good_first), 0) == WOLFSSL_SUCCESS)
1248+
leaf_resp = NULL;
1249+
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, int_resp,
1250+
sizeof(resp_intermediate1_cert), 1) == WOLFSSL_SUCCESS)
1251+
int_resp = NULL;
1252+
if (wolfSSL_set_tlsext_status_ocsp_resp_multi(ssl, root_resp,
1253+
sizeof(resp_root_ca_cert), 2) == WOLFSSL_SUCCESS)
1254+
root_resp = NULL;
1255+
if (leaf_resp == NULL && int_resp == NULL && root_resp == NULL)
1256+
ret = WOLFSSL_OCSP_STATUS_CB_OK;
1257+
}
1258+
XFREE(leaf_resp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1259+
XFREE(int_resp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1260+
XFREE(root_resp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1261+
return ret;
1262+
}
1263+
1264+
/*
1265+
* status_request_v2 OCSP-multi must reject a stapled leaf response
1266+
* whose MATCHING SingleResponse is CERT_REVOKED, even when an unrelated
1267+
* CERT_GOOD SingleResponse appears first in the same BasicOCSPResponse.
1268+
*/
1269+
int test_ocsp_status_request_v2_multi_revoked_single(void)
1270+
{
1271+
EXPECT_DECLS;
1272+
size_t i;
1273+
struct {
1274+
method_provider client_meth;
1275+
method_provider server_meth;
1276+
const char* tls_version;
1277+
} params[] = {
1278+
{ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, "TLSv1_2" },
1279+
#ifdef WOLFSSL_DTLS
1280+
{ wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLSv1_2" },
1281+
#endif
1282+
};
1283+
1284+
for (i = 0; i < XELEM_CNT(params) && !EXPECT_FAIL(); i++) {
1285+
struct test_ssl_memio_ctx test_ctx;
1286+
WOLFSSL_ALERT_HISTORY h;
1287+
1288+
printf("\nTesting %s\n", params[i].tls_version);
1289+
1290+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1291+
test_ctx.c_cb.caPemFile = "";
1292+
/* Server cert/key come only from the per-connection cert callback. */
1293+
test_ctx.s_cb.certPemFile = "";
1294+
test_ctx.s_cb.keyPemFile = "";
1295+
test_ctx.c_cb.method = params[i].client_meth;
1296+
test_ctx.s_cb.method = params[i].server_meth;
1297+
1298+
/* Full server1 -> intermediate1 -> root chain so the leaf staple is at
1299+
* idx 0. */
1300+
test_ocsp_tls_cert_cb_opts.chainLen = 3;
1301+
test_ocsp_tls_cert_cb_opts.failStaple = 0;
1302+
test_ctx.s_cb.ctx_ready = test_ocsp_tls_cert_cb_ctx_ready;
1303+
1304+
ExpectIntEQ(test_ssl_memio_setup(&test_ctx), TEST_SUCCESS);
1305+
ExpectIntEQ(wolfSSL_UnloadCertsKeys(test_ctx.s_ssl), WOLFSSL_SUCCESS);
1306+
1307+
/* server: enable stapling and supply the multi-single leaf */
1308+
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.s_ctx),
1309+
WOLFSSL_SUCCESS);
1310+
ExpectIntEQ(wolfSSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
1311+
test_ocsp_revoked_multi_single_status_cb), WOLFSSL_SUCCESS);
1312+
1313+
/* client: verify chain via callback, request status_request_v2 multi.
1314+
* Deliberately no ocsp_status_verify_cb: exercise DoCertificateStatus
1315+
* in isolation. */
1316+
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT,
1317+
test_ocsp_tls_cert_cb_verify_cb);
1318+
wolfSSL_SetCertCbCtx(test_ctx.c_ssl, test_ctx.c_ssl);
1319+
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
1320+
WOLFSSL_SUCCESS);
1321+
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
1322+
WOLFSSL_SUCCESS);
1323+
ExpectIntEQ(wolfSSL_UseOCSPStaplingV2(test_ctx.c_ssl,
1324+
WOLFSSL_CSR2_OCSP_MULTI, WOLFSSL_CSR2_OCSP_USE_NONCE),
1325+
WOLFSSL_SUCCESS);
1326+
1327+
/* Revoked leaf must abort the handshake. */
1328+
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
1329+
TEST_FAIL);
1330+
XMEMSET(&h, 0, sizeof(h));
1331+
ExpectIntEQ(wolfSSL_get_alert_history(test_ctx.s_ssl, &h),
1332+
WOLFSSL_SUCCESS);
1333+
ExpectIntEQ(h.last_rx.level, alert_fatal);
1334+
ExpectIntEQ(h.last_rx.code, bad_certificate_status_response);
1335+
1336+
test_ssl_memio_cleanup(&test_ctx);
1337+
}
1338+
return EXPECT_RESULT();
1339+
}
1340+
#else
1341+
int test_ocsp_status_request_v2_multi_revoked_single(void)
1342+
{
1343+
return TEST_SKIPPED;
1344+
}
1345+
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 && !WOLFSSL_NO_TLS12 */
1346+
12211347
#else /* feature guards */
12221348
int test_ocsp_tls_cert_cb(void)
12231349
{
12241350
return TEST_SKIPPED;
12251351
}
1352+
int test_ocsp_status_request_v2_multi_revoked_single(void)
1353+
{
1354+
return TEST_SKIPPED;
1355+
}
12261356
#endif
12271357

12281358
/*

tests/api/test_ocsp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ int test_ocsp_basic_verify(void);
3030
int test_ocsp_responder_keyhash_binding(void);
3131
int test_ocsp_response_parsing(void);
3232
int test_ocsp_tls_cert_cb(void);
33+
int test_ocsp_status_request_v2_multi_revoked_single(void);
3334
int test_ocsp_cert_unknown_crl_fallback(void);
3435
int test_ocsp_cert_unknown_crl_fallback_nonleaf(void);
3536
int test_tls13_nonblock_ocsp_low_mfl(void);

tests/api/test_ocsp_test_blobs.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2415,4 +2415,64 @@ unsigned char resp_bad[] = {
24152415
0x63, 0x29, 0xc6, 0xa8, 0x97, 0x4b, 0x14, 0xff, 0xd2,
24162416
};
24172417

2418+
unsigned char resp_server1_revoked_good_first[] = {
2419+
0x30, 0x82, 0x02, 0x9e, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x02, 0x97, 0x30,
2420+
0x82, 0x02, 0x93, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
2421+
0x01, 0x01, 0x04, 0x82, 0x02, 0x84, 0x30, 0x82, 0x02, 0x80, 0x30, 0x82,
2422+
0x01, 0x6a, 0xa1, 0x81, 0xa4, 0x30, 0x81, 0xa1, 0x31, 0x0b, 0x30, 0x09,
2423+
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30,
2424+
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68,
2425+
0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03,
2426+
0x55, 0x04, 0x07, 0x0c, 0x07, 0x53, 0x65, 0x61, 0x74, 0x74, 0x6c, 0x65,
2427+
0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x77,
2428+
0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03,
2429+
0x55, 0x04, 0x0b, 0x0c, 0x0b, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x65,
2430+
0x72, 0x69, 0x6e, 0x67, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04,
2431+
0x03, 0x0c, 0x19, 0x77, 0x6f, 0x6c, 0x66, 0x53, 0x53, 0x4c, 0x20, 0x69,
2432+
0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20,
2433+
0x43, 0x41, 0x20, 0x31, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86,
2434+
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e, 0x66,
2435+
0x6f, 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f,
2436+
0x6d, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31,
2437+
0x36, 0x34, 0x32, 0x33, 0x36, 0x5a, 0x30, 0x81, 0xaf, 0x30, 0x4d, 0x30,
2438+
0x38, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
2439+
0x71, 0x4d, 0x82, 0x23, 0x40, 0x59, 0xc0, 0x96, 0xa1, 0x37, 0x43, 0xfa,
2440+
0x31, 0xdb, 0xba, 0xb1, 0x43, 0x18, 0xda, 0x04, 0x04, 0x14, 0x83, 0xc6,
2441+
0x3a, 0x89, 0x2c, 0x81, 0xf4, 0x02, 0xd7, 0x9d, 0x4c, 0xe2, 0x2a, 0xc0,
2442+
0x71, 0x82, 0x64, 0x44, 0xda, 0x0e, 0x02, 0x01, 0x01, 0x80, 0x00, 0x18,
2443+
0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34,
2444+
0x32, 0x33, 0x36, 0x5a, 0x30, 0x5e, 0x30, 0x38, 0x30, 0x07, 0x06, 0x05,
2445+
0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x71, 0x4d, 0x82, 0x23, 0x40,
2446+
0x59, 0xc0, 0x96, 0xa1, 0x37, 0x43, 0xfa, 0x31, 0xdb, 0xba, 0xb1, 0x43,
2447+
0x18, 0xda, 0x04, 0x04, 0x14, 0x83, 0xc6, 0x3a, 0x89, 0x2c, 0x81, 0xf4,
2448+
0x02, 0xd7, 0x9d, 0x4c, 0xe2, 0x2a, 0xc0, 0x71, 0x82, 0x64, 0x44, 0xda,
2449+
0x0e, 0x02, 0x01, 0x05, 0xa1, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x36,
2450+
0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34, 0x32, 0x33, 0x36, 0x5a, 0x18,
2451+
0x0f, 0x32, 0x30, 0x32, 0x36, 0x30, 0x36, 0x32, 0x31, 0x31, 0x36, 0x34,
2452+
0x32, 0x33, 0x36, 0x5a, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
2453+
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x03, 0x82, 0x01, 0x01, 0x00, 0xc4, 0x95,
2454+
0xdb, 0x71, 0xca, 0x09, 0x4b, 0x37, 0xf1, 0xe8, 0x25, 0xc4, 0x75, 0x6f,
2455+
0x87, 0x22, 0x95, 0x8b, 0x7f, 0x7f, 0x00, 0xf0, 0x75, 0x35, 0x66, 0xea,
2456+
0x56, 0x26, 0xc4, 0x9a, 0xe1, 0x40, 0xe9, 0xd2, 0x7c, 0x8c, 0x83, 0x0f,
2457+
0x33, 0x33, 0xac, 0x1e, 0xcc, 0x8e, 0x0e, 0x99, 0x43, 0xb8, 0xd6, 0x13,
2458+
0xfa, 0xdb, 0xbe, 0xaf, 0x64, 0x6a, 0xbd, 0x92, 0x35, 0xb2, 0x7e, 0xaf,
2459+
0xda, 0x49, 0x89, 0xa2, 0x37, 0xcc, 0x50, 0x88, 0xa8, 0x0b, 0xb4, 0xe1,
2460+
0x6a, 0xb7, 0x94, 0x06, 0x92, 0xb8, 0x6a, 0xd2, 0x55, 0xfe, 0x7e, 0xb4,
2461+
0x5e, 0x12, 0xed, 0x9d, 0xf6, 0x04, 0x49, 0x2c, 0x68, 0xae, 0xc4, 0x21,
2462+
0x3c, 0xd4, 0xe4, 0x97, 0x76, 0x2c, 0xdb, 0x78, 0xfb, 0x22, 0xdd, 0x3b,
2463+
0xed, 0x03, 0x68, 0xaa, 0x7b, 0x9d, 0xd2, 0x66, 0x9f, 0x53, 0xc8, 0x45,
2464+
0xe7, 0x8d, 0xeb, 0x39, 0x05, 0x90, 0xbe, 0x9e, 0xe3, 0x3f, 0xcd, 0x4c,
2465+
0x0a, 0x37, 0x3c, 0x18, 0xbb, 0xef, 0x2e, 0xb6, 0x53, 0xda, 0x65, 0xfb,
2466+
0xd6, 0xd4, 0xfa, 0x04, 0xcb, 0x93, 0x23, 0x60, 0x5e, 0x62, 0x8b, 0xd2,
2467+
0x14, 0xe4, 0x14, 0xd3, 0x3f, 0xdd, 0x8b, 0x65, 0x55, 0x95, 0x27, 0x45,
2468+
0xdb, 0x7c, 0x74, 0x88, 0x33, 0x7b, 0x2d, 0x6e, 0x11, 0x41, 0x19, 0xc6,
2469+
0xe2, 0x79, 0x3d, 0x27, 0xc1, 0x5e, 0xb1, 0xcc, 0xf9, 0x6a, 0x9c, 0xaf,
2470+
0x73, 0x78, 0xc5, 0xeb, 0x1b, 0xfa, 0x6f, 0x92, 0x6b, 0x75, 0xf0, 0x70,
2471+
0xc9, 0x44, 0xd2, 0xa8, 0xbe, 0x2e, 0xee, 0x0e, 0x0b, 0xac, 0x7f, 0x99,
2472+
0x6d, 0x67, 0x1e, 0x2d, 0x7d, 0x19, 0x3a, 0x66, 0x1b, 0xb2, 0x0f, 0xfe,
2473+
0x49, 0x89, 0x24, 0xb2, 0x15, 0xa6, 0xcc, 0xd2, 0xdd, 0xc4, 0x45, 0xfb,
2474+
0xd2, 0xe5, 0xeb, 0xe8, 0xd7, 0x09, 0xf1, 0xc0, 0xb8, 0x0d, 0xc8, 0x34,
2475+
0xc2, 0x6c,
2476+
};
2477+
24182478
#endif /* OCSP_TEST_BLOBS_H */

0 commit comments

Comments
 (0)