@@ -6189,3 +6189,157 @@ int test_tls13_cipher_fuzz_aes128_ccm_8_sha256(void)
61896189#endif
61906190 return EXPECT_RESULT ();
61916191}
6192+
6193+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6194+ defined(WOLFSSL_TLS13 ) && defined(WOLFSSL_POST_HANDSHAKE_AUTH ) && \
6195+ defined(HAVE_CERTIFICATE_STATUS_REQUEST ) && defined(HAVE_OCSP ) && \
6196+ !defined(NO_CERTS ) && !defined(NO_RSA ) && \
6197+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER )
6198+ /* Mock OCSP I/O callback: returns 0 bytes so the server stapling slot
6199+ * stays empty. This intentionally exercises the "no staple available"
6200+ * path on both peers. */
6201+ static int test_pha_ocsp_io_cb (void * ioCtx , const char * url , int urlSz ,
6202+ unsigned char * req , int reqSz , unsigned char * * resp )
6203+ {
6204+ (void )ioCtx ; (void )url ; (void )urlSz ; (void )req ; (void )reqSz ;
6205+ * resp = NULL ;
6206+ return 0 ;
6207+ }
6208+
6209+ static void test_pha_ocsp_resp_free_cb (void * ioCtx , unsigned char * resp )
6210+ {
6211+ (void )ioCtx ; (void )resp ;
6212+ }
6213+ #endif
6214+
6215+ /* Post-Handshake Authentication combined with OCSP stapling
6216+ * (status_request) on the TLS 1.3 CertificateRequest message.
6217+ *
6218+ * Regression for two related issues:
6219+ * 1. The server's PHA CertificateRequest must include the
6220+ * status_request extension (with an empty body) when the client
6221+ * offered status_request in its ClientHello (RFC 8446 4.2 / 4.3.2).
6222+ * Without the fix the extension was suppressed and the size and
6223+ * write paths disagreed by 4 bytes, causing the client to send a
6224+ * decode_error alert (BUFFER_ERROR / -328).
6225+ * 2. The server-side OCSP-status check in ProcessPeerCerts must run
6226+ * for the PHA-received client Certificate. The check must also
6227+ * tolerate a missing/empty stapled response unless the verifier
6228+ * enforces must-staple, per RFC 8446 4.4.2.1 (client OCSP staple
6229+ * is MAY) and RFC 7633 (must-staple). Without the fix the server
6230+ * either skipped the check entirely or returned
6231+ * BAD_CERTIFICATE_STATUS_ERROR (-406) for the no-staple case.
6232+ *
6233+ * The test exercises a single TLS 1.3 connection: an initial handshake
6234+ * without client authentication, followed by a server-initiated PHA
6235+ * exchange. The server expects to receive (and verify) the client
6236+ * certificate even though no OCSP staple is supplied.
6237+ */
6238+ int test_tls13_pha_status_request (void )
6239+ {
6240+ EXPECT_DECLS ;
6241+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
6242+ defined(WOLFSSL_TLS13 ) && defined(WOLFSSL_POST_HANDSHAKE_AUTH ) && \
6243+ defined(HAVE_CERTIFICATE_STATUS_REQUEST ) && defined(HAVE_OCSP ) && \
6244+ !defined(NO_CERTS ) && !defined(NO_RSA ) && \
6245+ !defined(NO_WOLFSSL_CLIENT ) && !defined(NO_WOLFSSL_SERVER )
6246+ struct test_memio_ctx test_ctx ;
6247+ WOLFSSL_CTX * ctx_c = NULL , * ctx_s = NULL ;
6248+ WOLFSSL * ssl_c = NULL , * ssl_s = NULL ;
6249+ WOLFSSL_X509 * peer = NULL ;
6250+ const char msg [] = "ping" ;
6251+ char buf [8 ];
6252+
6253+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
6254+
6255+ /* --- Client CTX ------------------------------------------------ */
6256+ ExpectNotNull (ctx_c = wolfSSL_CTX_new (wolfTLSv1_3_client_method ()));
6257+ ExpectIntEQ (wolfSSL_CTX_load_verify_locations (ctx_c , caCertFile , 0 ),
6258+ WOLFSSL_SUCCESS );
6259+ ExpectIntEQ (wolfSSL_CTX_use_certificate_file (ctx_c , cliCertFile ,
6260+ WOLFSSL_FILETYPE_PEM ), WOLFSSL_SUCCESS );
6261+ ExpectIntEQ (wolfSSL_CTX_use_PrivateKey_file (ctx_c , cliKeyFile ,
6262+ WOLFSSL_FILETYPE_PEM ), WOLFSSL_SUCCESS );
6263+ /* Must opt in to PHA before wolfSSL_connect to add the
6264+ * post_handshake_auth extension to the ClientHello. */
6265+ ExpectIntEQ (wolfSSL_CTX_allow_post_handshake_auth (ctx_c ), 0 );
6266+ ExpectIntEQ (wolfSSL_CTX_EnableOCSPStapling (ctx_c ), WOLFSSL_SUCCESS );
6267+ wolfSSL_SetIORecv (ctx_c , test_memio_read_cb );
6268+ wolfSSL_SetIOSend (ctx_c , test_memio_write_cb );
6269+
6270+ /* --- Server CTX ------------------------------------------------ */
6271+ ExpectNotNull (ctx_s = wolfSSL_CTX_new (wolfTLSv1_3_server_method ()));
6272+ ExpectIntEQ (wolfSSL_CTX_use_certificate_file (ctx_s , svrCertFile ,
6273+ WOLFSSL_FILETYPE_PEM ), WOLFSSL_SUCCESS );
6274+ ExpectIntEQ (wolfSSL_CTX_use_PrivateKey_file (ctx_s , svrKeyFile ,
6275+ WOLFSSL_FILETYPE_PEM ), WOLFSSL_SUCCESS );
6276+ ExpectIntEQ (wolfSSL_CTX_load_verify_locations (ctx_s , caCertFile , 0 ),
6277+ WOLFSSL_SUCCESS );
6278+ /* Trust the client cert issuer as well, otherwise the PHA
6279+ * Certificate verification would fail with ASN_SELF_SIGNED_E. */
6280+ ExpectIntEQ (wolfSSL_CTX_load_verify_locations (ctx_s ,
6281+ "./certs/client-ca.pem" , 0 ), WOLFSSL_SUCCESS );
6282+ ExpectIntEQ (wolfSSL_CTX_EnableOCSPStapling (ctx_s ), WOLFSSL_SUCCESS );
6283+ /* Mock callback: stapling negotiates but the response is empty. */
6284+ ExpectIntEQ (wolfSSL_CTX_SetOCSP_Cb (ctx_s , test_pha_ocsp_io_cb ,
6285+ test_pha_ocsp_resp_free_cb , NULL ), WOLFSSL_SUCCESS );
6286+ /* Initial handshake: do not request the client certificate yet -
6287+ * the server promotes verification only when triggering PHA. */
6288+ wolfSSL_CTX_set_verify (ctx_s , WOLFSSL_VERIFY_NONE , NULL );
6289+ wolfSSL_SetIORecv (ctx_s , test_memio_read_cb );
6290+ wolfSSL_SetIOSend (ctx_s , test_memio_write_cb );
6291+
6292+ /* --- SSL objects ----------------------------------------------- */
6293+ ExpectNotNull (ssl_c = wolfSSL_new (ctx_c ));
6294+ wolfSSL_SetIOReadCtx (ssl_c , & test_ctx );
6295+ wolfSSL_SetIOWriteCtx (ssl_c , & test_ctx );
6296+ /* Causes status_request in the ClientHello so that the server's
6297+ * PHA CertificateRequest re-emits the same extension. */
6298+ ExpectIntEQ (wolfSSL_UseOCSPStapling (ssl_c , WOLFSSL_CSR_OCSP , 0 ),
6299+ WOLFSSL_SUCCESS );
6300+
6301+ ExpectNotNull (ssl_s = wolfSSL_new (ctx_s ));
6302+ wolfSSL_SetIOReadCtx (ssl_s , & test_ctx );
6303+ wolfSSL_SetIOWriteCtx (ssl_s , & test_ctx );
6304+
6305+ /* --- Initial handshake (no client cert requested) -------------- */
6306+ ExpectIntEQ (test_memio_do_handshake (ssl_c , ssl_s , 10 , NULL ), 0 );
6307+ ExpectNull (wolfSSL_get_peer_certificate (ssl_s ));
6308+
6309+ /* --- Trigger PHA: server now requires the client certificate -- */
6310+ if (EXPECT_SUCCESS ()) {
6311+ wolfSSL_set_verify (ssl_s ,
6312+ WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT ,
6313+ NULL );
6314+ ExpectIntEQ (wolfSSL_request_certificate (ssl_s ), WOLFSSL_SUCCESS );
6315+ }
6316+
6317+ /* The server's wolfSSL_write below carries the PHA
6318+ * CertificateRequest record. The client's wolfSSL_read consumes
6319+ * the request, transmits Certificate/CertificateVerify/Finished
6320+ * and surfaces the application data to us. */
6321+ ExpectIntEQ (wolfSSL_write (ssl_s , msg , (int )sizeof (msg ) - 1 ),
6322+ (int )sizeof (msg ) - 1 );
6323+ ExpectIntEQ (wolfSSL_read (ssl_c , buf , sizeof (buf ) - 1 ),
6324+ (int )sizeof (msg ) - 1 );
6325+
6326+ /* The client's reply lets the server's wolfSSL_read drain the
6327+ * incoming PHA Certificate flight before the application data. */
6328+ ExpectIntEQ (wolfSSL_write (ssl_c , msg , (int )sizeof (msg ) - 1 ),
6329+ (int )sizeof (msg ) - 1 );
6330+ ExpectIntEQ (wolfSSL_read (ssl_s , buf , sizeof (buf ) - 1 ),
6331+ (int )sizeof (msg ) - 1 );
6332+
6333+ /* PHA succeeded: the server now holds the client certificate.
6334+ * Reaching this point also implies the server tolerated the
6335+ * empty OCSP staple instead of failing with -406. */
6336+ ExpectNotNull (peer = wolfSSL_get_peer_certificate (ssl_s ));
6337+ wolfSSL_X509_free (peer );
6338+
6339+ wolfSSL_free (ssl_c );
6340+ wolfSSL_free (ssl_s );
6341+ wolfSSL_CTX_free (ctx_c );
6342+ wolfSSL_CTX_free (ctx_s );
6343+ #endif
6344+ return EXPECT_RESULT ();
6345+ }
0 commit comments