Skip to content

Commit 1410138

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 1410138

6 files changed

Lines changed: 223 additions & 0 deletions

File tree

src/internal.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17953,6 +17953,17 @@ 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. The per-cert
17958+
* status checked above comes from the first SingleResponse
17959+
* in wire order, which is not necessarily the one matching
17960+
* the stapled certificate (CompareOcspReqResp() below is
17961+
* what locates the match). Rather than risk acting on the
17962+
* wrong SingleResponse, reject any response that carries
17963+
* multiple singles. */
17964+
if (ret == 0 && response->single->next != NULL)
17965+
ret = BAD_CERTIFICATE_STATUS_ERROR;
17966+
1795617967
if (ret == 0) {
1795717968
request = (OcspRequest*)TLSX_CSR2_GetRequest(
1795817969
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: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,11 +1218,135 @@ int test_ocsp_tls_cert_cb(void)
12181218
return EXPECT_RESULT();
12191219
}
12201220

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

12281352
/*

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)