diff --git a/src/internal.c b/src/internal.c index 5dcaf668e65..240cbe4761e 100644 --- a/src/internal.c +++ b/src/internal.c @@ -19116,10 +19116,10 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif /* HAVE_RPK && WOLFSSL_TLS13 */ if (args->count == 0) { - /* Empty certificate message. */ - if ((ssl->options.side == WOLFSSL_SERVER_END) && - (ssl->options.mutualAuth || (ssl->options.failNoCert && - IsAtLeastTLSv1_2(ssl)))) { + /* Empty certificate message. Only mutualAuth fails here + * before TLS 1.2; the later check catches the rest. */ + if (ClientAuthRequired(ssl, ssl->options.usingPSK_cipher) && + (ssl->options.mutualAuth || IsAtLeastTLSv1_2(ssl))) { WOLFSSL_MSG("No peer cert from Client"); ret = NO_PEER_CERT; WOLFSSL_ERROR_VERBOSE(ret); @@ -40363,6 +40363,26 @@ const byte* MaskCurve25519PeerKey(const byte* pub, word32 pubSz, return MATCH_SUITE_ERROR; } +/* Does a full handshake on this connection require a client certificate? + * usingPsk is whether the suite in question authenticates with a PSK, which + * only the caller knows. verifyPostHandshake is deliberately absent: it defers + * the demand, not the policy, so the callers that care apply it. */ +int ClientAuthRequired(const WOLFSSL* ssl, int usingPsk) +{ +#if !defined(NO_CERTS) && !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) + if (ssl->options.side != WOLFSSL_SERVER_END) + return 0; + if (ssl->options.verifyNone) + return 0; + return ssl->options.mutualAuth || ssl->options.failNoCert || + (ssl->options.failNoCertxPSK && !usingPsk); +#else + /* Only a TLS server asks for a certificate, so only it can require one. */ + (void)ssl; + (void)usingPsk; + return 0; +#endif +} #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) @@ -42658,7 +42678,15 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (!session) { WOLFSSL_MSG("Session lookup for resume failed"); ssl->options.resuming = 0; - } else { + } + else if (ClientAuthRequired(ssl, CipherRequires( + session->cipherSuite0, session->cipherSuite, + REQUIRES_PSK)) && !session->peerAuthOk) { + WOLFSSL_MSG("Session lacks client auth, do full handshake"); + ssl->options.resuming = 0; + ssl->options.peerAuthGood = 0; + } + else { if (MatchSuite(ssl, &clSuites) < 0) { WOLFSSL_MSG("Unsupported cipher suite, OldClientHello"); return UNSUPPORTED_SUITE; @@ -42742,6 +42770,16 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #endif ) { int secretSz = SECRET_LEN; + /* The secret comes from the callback, and nothing records an auth + * outcome for it. The suite is not chosen yet, so no PSK exception + * applies. */ + if (ClientAuthRequired(ssl, 0)) { + WOLFSSL_MSG("Session secret callback cannot assert client auth," + " do full handshake"); + ssl->options.resuming = 0; + ssl->options.peerAuthGood = 0; + return ret; + } WOLFSSL_MSG("Calling session secret callback"); ret = wc_RNG_GenerateBlock(ssl->rng, ssl->arrays->serverRandom, RAN_LEN); @@ -42789,6 +42827,14 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) ssl->options.resuming = 0; return ret; } + if (ClientAuthRequired(ssl, CipherRequires(session->cipherSuite0, + session->cipherSuite, REQUIRES_PSK)) && + !session->peerAuthOk) { + WOLFSSL_MSG("Session lacks client auth, do full handshake"); + ssl->options.resuming = 0; + ssl->options.peerAuthGood = 0; + return ret; + } #if defined(HAVE_SESSION_TICKET) && \ (defined(HAVE_SNI) || defined(HAVE_ALPN)) /* Do not resume session if sniHash/alpnHash do not match. */ @@ -42991,6 +43037,15 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) /* Reset to sane value for SCR */ ssl->options.resuming = 0; ssl->arrays->sessionIDSz = 0; +#ifdef HAVE_SESSION_TICKET + /* Only this ClientHello may set them, in TLSX_SessionTicket_Parse(), + * which ignores the extension during SCR - so an SCR hello inherits + * the connection's. */ + if (!IsSCR(ssl)) { + ssl->options.useTicket = 0; + ssl->options.createTicket = 0; + } +#endif /* protocol version, random and session id length check */ if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz) @@ -44477,6 +44532,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) it->suite[0] = ssl->options.cipherSuite0; it->suite[1] = ssl->options.cipherSuite; + it->peerAuthOk = (byte)SSL_PEER_CERT_VERIFIED(ssl); + it->isPSK = (byte)ssl->options.usingPSK_cipher; + #ifdef WOLFSSL_EARLY_DATA c32toa(ssl->options.maxEarlyDataSz, it->maxEarlyDataSz); #endif @@ -45050,6 +45108,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #ifdef HAVE_ALPN XMEMCPY(ssl->session->alpnHash, it->alpnHash, TICKET_BINDING_HASH_SZ); #endif + /* After the DupSession above, which would otherwise overwrite it. */ + ssl->session->peerAuthOk = it->peerAuthOk; + ssl->session->isPSK = it->isPSK; if (!IsAtLeastTLSv1_3(ssl->version)) { if (ssl->arrays == NULL) @@ -45139,6 +45200,8 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) c32toa((word32)milliBornOn , it->timestamp + OPAQUE32_LEN); #endif it->haveEMS = (byte)sess->haveEMS; + it->peerAuthOk = sess->peerAuthOk; + it->isPSK = sess->isPSK; c32toa(sess->ticketAdd, it->ageAdd); c16toa(sess->namedGroup, it->namedGroup); if (sess->ticketNonce.len <= MAX_TICKET_NONCE_STATIC_SZ) { @@ -46652,20 +46715,10 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], } #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) - if (ssl->options.verifyPeer && - (ssl->options.mutualAuth || ssl->options.failNoCert)) { - if (!ssl->options.havePeerCert) { - WOLFSSL_MSG("client didn't present peer cert"); - ERROR_OUT(NO_PEER_CERT, exit_dcke); - } - } - - if (ssl->options.verifyPeer && ssl->options.failNoCertxPSK) { - if (!ssl->options.havePeerCert && - !ssl->options.usingPSK_cipher) { - WOLFSSL_MSG("client didn't present peer cert"); - ERROR_OUT(NO_PEER_CERT, exit_dcke); - } + if (ClientAuthRequired(ssl, ssl->options.usingPSK_cipher) && + !ssl->options.havePeerCert) { + WOLFSSL_MSG("client didn't present peer cert"); + ERROR_OUT(NO_PEER_CERT, exit_dcke); } #endif /* !NO_CERTS && !WOLFSSL_NO_CLIENT_AUTH */ diff --git a/src/keys.c b/src/keys.c index d1ab48e4f63..96f6920eafa 100644 --- a/src/keys.c +++ b/src/keys.c @@ -46,9 +46,13 @@ int SetCipherSpecs(WOLFSSL* ssl) { - int ret = GetCipherSpec(ssl->options.side, ssl->options.cipherSuite0, - ssl->options.cipherSuite, &ssl->specs, - &ssl->options); + int ret; + + /* GetCipherSpec only sets these, for the suite being set. */ + ssl->options.usingPSK_cipher = 0; + ssl->options.usingAnon_cipher = 0; + ret = GetCipherSpec(ssl->options.side, ssl->options.cipherSuite0, + ssl->options.cipherSuite, &ssl->specs, &ssl->options); if (ret == 0) { #ifdef WOLFSSL_TLS13 /* The client only checks that the ServerHello suite was offered, and diff --git a/src/ssl.c b/src/ssl.c index d4dfe1afee7..7d0cb3cc46d 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -5793,14 +5793,30 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, return WOLFSSL_FAILURE; } - if (!ssl->options.handShakeDone) { - /* Only reset the session if we didn't complete a handshake */ + /* A client keeps the session it may resume. A server's is per + * connection: take a new one when the application holds a reference, + * else reset ours, as wolfSSL_get_session() hands out a pointer + * without taking one. */ + if (!ssl->options.handShakeDone || + ((ssl->options.side == WOLFSSL_SERVER_END) && + (wolfSSL_RefCur(ssl->session->ref) > 1))) { wolfSSL_FreeSession(ssl->ctx, ssl->session); ssl->session = wolfSSL_NewSession(ssl->heap); if (ssl->session == NULL) { return WOLFSSL_FAILURE; } } + else if (ssl->options.side == WOLFSSL_SERVER_END) { + ssl->session->haveAltSessionID = 0; + ForceZero(ssl->session->altSessionID, ID_LEN); +#ifndef NO_WOLFSSL_SERVER + ssl->session->peerAuthOk = 0; + ssl->session->isPSK = 0; +#endif + #if defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TLS13) + ssl->session->ticketNonce.len = 0; + #endif + } /* reset error */ ssl->error = 0; @@ -5811,6 +5827,9 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, ssl->options.connReset = 0; ssl->options.sentNotify = 0; ssl->options.closeNotify = 0; + /* Per-connection budgets, not cumulative across a reused object. */ + ssl->options.alertCount = 0; + ssl->options.emptyRecordCount = 0; ssl->options.sendVerify = 0; ssl->options.serverState = NULL_STATE; ssl->options.clientState = NULL_STATE; @@ -5834,12 +5853,18 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, DYNAMIC_TYPE_TMP_BUFFER); ssl->buffers.certVerifyMsg.buffer = NULL; ssl->buffers.certVerifyMsg.length = 0; - ssl->fragOffset = 0; #endif + ssl->fragOffset = 0; + ssl->options.buildingMsg = 0; ssl->options.processReply = 0; /* doProcessInit */ ssl->options.havePeerVerify = 0; ssl->options.havePeerCert = 0; ssl->options.peerAuthGood = 0; + ssl->options.usingPSK_cipher = 0; + ssl->options.usingAnon_cipher = 0; +#if defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)) + ssl->options.pskNegotiated = 0; +#endif ssl->options.tls1_3 = 0; ssl->options.haveSessionId = 0; ssl->options.tls = 0; @@ -5939,10 +5964,6 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #ifdef HAVE_SESSION_TICKET #ifdef WOLFSSL_TLS13 ssl->options.ticketsSent = 0; - #ifndef NO_WOLFSSL_SERVER - if (ssl->options.side == WOLFSSL_SERVER_END) - ssl->session->ticketNonce.len = 0; - #endif #if !defined(NO_WOLFSSL_SERVER) && \ defined(WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES) /* Recorded from the ClientHello, so it must not carry into the next @@ -5955,6 +5976,8 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #endif #endif ssl->options.rejectTicket = 0; + ssl->options.useTicket = 0; + ssl->options.createTicket = 0; #endif #ifdef WOLFSSL_EARLY_DATA ssl->earlyData = no_early_data; diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index 62e8f653d56..beb194edd6b 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -47,6 +47,11 @@ int wolfSSL_CTX_mutual_auth(WOLFSSL_CTX* ctx, int req) return SIDE_ERROR; ctx->mutualAuth = (byte)req; + /* A certificate has to be asked for to be required. */ + if (req) { + ctx->verifyNone = 0; + ctx->verifyPeer = 1; + } return 0; } @@ -69,6 +74,11 @@ int wolfSSL_mutual_auth(WOLFSSL* ssl, int req) return SIDE_ERROR; ssl->options.mutualAuth = (word16)req; + /* A certificate has to be asked for to be required. */ + if (req) { + ssl->options.verifyNone = 0; + ssl->options.verifyPeer = 1; + } return 0; } @@ -587,6 +597,11 @@ static SetVerifyOptions ModeToVerifyOptions(int mode) (mode & WOLFSSL_VERIFY_FAIL_EXCEPT_PSK) != 0; opts.failNoCert = (mode & WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT) != 0; + /* Both only fail a certificate that was asked for, so they imply + * asking. */ + if (opts.failNoCert || opts.failNoCertxPSK) { + opts.verifyPeer = 1; + } #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) opts.verifyPostHandshake = (mode & WOLFSSL_VERIFY_POST_HANDSHAKE) != 0; diff --git a/src/ssl_api_hs.c b/src/ssl_api_hs.c index 5feaff1d091..521bc38b8a5 100644 --- a/src/ssl_api_hs.c +++ b/src/ssl_api_hs.c @@ -1102,11 +1102,11 @@ int wolfSSL_accept(WOLFSSL* ssl) case ACCEPT_SECOND_REPLY_DONE : #ifndef NO_CERTS /* SERVER: When not resuming and verifying peer but no certificate - * received and not failing when not received then peer auth good. + * received and none was required then peer auth good. */ if ((!ssl->options.resuming) && (ssl->options.verifyPeer) && (!ssl->options.havePeerCert) && - (!ssl->options.failNoCert)) { + (!ClientAuthRequired(ssl, ssl->options.usingPSK_cipher))) { ssl->options.peerAuthGood = 1; } #endif /* !NO_CERTS */ diff --git a/src/ssl_sess.c b/src/ssl_sess.c index ba42dea2168..341ccd77afc 100644 --- a/src/ssl_sess.c +++ b/src/ssl_sess.c @@ -2952,6 +2952,10 @@ int wolfSSL_i2d_SSL_SESSION(WOLFSSL_SESSION* sess, unsigned char** p) size += TICKET_BINDING_HASH_SZ; #endif #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ +#endif +#ifndef NO_WOLFSSL_SERVER + /* peerAuthOk and isPSK */ + size += OPAQUE8_LEN; #endif if (p != NULL) { @@ -3047,6 +3051,9 @@ int wolfSSL_i2d_SSL_SESSION(WOLFSSL_SESSION* sess, unsigned char** p) idx += TICKET_BINDING_HASH_SZ; #endif #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ +#endif +#ifndef NO_WOLFSSL_SERVER + data[idx++] = (byte)(sess->peerAuthOk | (sess->isPSK << 1)); #endif } #endif @@ -3068,6 +3075,11 @@ int wolfSSL_i2d_SSL_SESSION(WOLFSSL_SESSION* sess, unsigned char** p) * enabled with the wolfSSL library exporting a session then it is * expected to be turned on with the wolfSSL library importing the * session. + * + * i is how many bytes are readable at *p, not necessarily the size of the + * session: trailing bytes are ignored, and *p is advanced to the end of the + * session decoded, so sessions stored back to back decode with repeated + * calls. Passing in too few bytes will cause an error. */ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, const unsigned char** p, long i) @@ -3354,7 +3366,15 @@ WOLFSSL_SESSION* wolfSSL_d2i_SSL_SESSION(WOLFSSL_SESSION** sess, #endif #endif /* !NO_WOLFSSL_SERVER && !NO_TLS */ #endif - (void)idx; +#ifndef NO_WOLFSSL_SERVER + if (i - idx < OPAQUE8_LEN) { + ret = BUFFER_ERROR; + goto end; + } + s->peerAuthOk = (data[idx] & 0x01) != 0; + s->isPSK = (data[idx] & 0x02) != 0; + idx++; +#endif if (sess != NULL) { wolfSSL_FreeSession(NULL, *sess); @@ -3928,6 +3948,24 @@ void SetupSession(WOLFSSL* ssl) session->haveEMS = 1; else session->haveEMS = ssl->options.haveEMS; + /* Server only: on a client these flags describe the server certificate, + * a different statement that nothing reads. A full handshake records its + * own outcome; a resumed one keeps what it inherited unless post-handshake + * auth added a certificate. */ +#ifndef NO_WOLFSSL_SERVER + if (ssl->options.side == WOLFSSL_SERVER_END) { + if (!ssl->options.resuming) { + session->peerAuthOk = 0; + session->isPSK = (byte)ssl->options.usingPSK_cipher; + } + if (SSL_PEER_CERT_VERIFIED(ssl)) + session->peerAuthOk = 1; + } + else { + session->peerAuthOk = 0; + session->isPSK = 0; + } +#endif #ifdef WOLFSSL_SESSION_ID_CTX /* If using compatibility layer then check for and copy over session context * id. */ diff --git a/src/tls13.c b/src/tls13.c index 437218fefa0..7cf731a3d2f 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6904,11 +6904,6 @@ static int FindPsk(WOLFSSL* ssl, PreSharedKey* psk, const byte* suite, int* err) psk->resumption = 0; /* Default to ciphersuite if cb doesn't specify. */ ssl->options.resuming = 0; - /* Don't send certificate request when using PSK. */ -#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK - if (!ssl->options.certWithExternPsk) -#endif - ssl->options.verifyPeer = 0; /* obfuscated_ticket_age is not checked: RFC 8446 Section 4.2.11 * requires servers to ignore it for an external identity. */ @@ -6922,13 +6917,16 @@ static int FindPsk(WOLFSSL* ssl, PreSharedKey* psk, const byte* suite, int* err) } if (ret == 0) { /* PSK negotiation has succeeded */ - ssl->options.isPSK = 1; #ifdef WOLFSSL_CERT_WITH_EXTERN_PSK - if (!ssl->options.certWithExternPsk) + /* RFC 9973 applies only if the client sent the extension; the + * server opting in is not enough. */ + if (TLSX_Find(ssl->extensions, TLSX_CERT_WITH_EXTERN_PSK) == NULL) #endif { - /* SERVER: using PSK for peer authentication. */ + /* SERVER: using PSK for peer authentication, so no + * certificate is requested below. */ ssl->options.peerAuthGood = 1; + ssl->options.usingPSK_cipher = 1; } } } @@ -7096,6 +7094,17 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz, #endif } #endif + /* failNoCertxPSK excuses only a ticket from a PSK session. Before + * Finalize, so a declined ticket leaves nothing on the session. */ + if (ret == 0 && ClientAuthRequired(ssl, current->it->isPSK) && + #ifdef WOLFSSL_POST_HANDSHAKE_AUTH + /* Deferred, so this handshake asks for nothing. */ + !ssl->options.verifyPostHandshake && + #endif + !current->it->peerAuthOk) { + WOLFSSL_MSG("Ticket session lacks client auth, skipping PSK"); + ret = WOLFSSL_FATAL_ERROR; + } if (ret == 0) DoClientTicketFinalize(ssl, current->it, current->sess); if (current->sess_free_cb != NULL) { @@ -7157,6 +7166,8 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz, ret = SetCipherSpecs(ssl); if (ret != 0) goto cleanup; + /* TLS 1.3 suites do not record it, so restore it from the ticket. */ + ssl->options.usingPSK_cipher = ssl->session->isPSK; /* Resumption PSK is resumption master secret. */ ssl->arrays->psk_keySz = ssl->specs.hash_size; @@ -13708,13 +13719,13 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, WOLFSSL_START(WC_FUNC_FINISHED_DO); WOLFSSL_ENTER("DoTls13Finished"); -#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) +#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) && \ + !defined(NO_WOLFSSL_SERVER) /* verify the client sent certificate if required */ - if (ssl->options.side == WOLFSSL_SERVER_END && - (!ssl->options.resuming || TLS13_AFTER_HANDSHAKE(ssl)) && - (ssl->options.mutualAuth || ssl->options.failNoCert)) { + if ((!ssl->options.resuming || TLS13_AFTER_HANDSHAKE(ssl)) && + ClientAuthRequired(ssl, ssl->options.usingPSK_cipher)) { #ifdef OPENSSL_COMPATIBLE_DEFAULTS - if (ssl->options.isPSK && !TLS13_AFTER_HANDSHAKE(ssl)) { + if (ssl->options.usingPSK_cipher && !TLS13_AFTER_HANDSHAKE(ssl)) { WOLFSSL_MSG("TLS v1.3 client used PSK but cert required. Allowing " "for OpenSSL compatibility"); } @@ -18260,7 +18271,7 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) case TLS13_SERVER_EXTENSIONS_SENT : #ifndef NO_CERTS if (!ssl->options.resuming) { - if (ssl->options.verifyPeer + if (ssl->options.verifyPeer && !ssl->options.usingPSK_cipher #ifdef WOLFSSL_POST_HANDSHAKE_AUTH && !ssl->options.verifyPostHandshake #endif @@ -18329,7 +18340,8 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) #endif #ifdef HAVE_SESSION_TICKET #ifdef WOLFSSL_TLS13_TICKET_BEFORE_FINISHED - if (!ssl->options.verifyPeer && !ssl->options.noTicketTls13 && + if ((!ssl->options.verifyPeer || ssl->options.usingPSK_cipher) && + !ssl->options.noTicketTls13 && ssl->ctx->ticketEncCb != NULL && ssl->options.maxTicketTls13 > 0) { if ((ssl->error = SendTls13NewSessionTicket(ssl)) != 0) { @@ -18378,13 +18390,14 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl) case TLS13_ACCEPT_FINISHED_DONE : /* SERVER: When not resuming and verifying peer but no certificate - * received and not failing when not received then peer auth good. + * received and none was required then peer auth good. */ if (!ssl->options.resuming && ssl->options.verifyPeer && #ifdef WOLFSSL_POST_HANDSHAKE_AUTH !ssl->options.verifyPostHandshake && #endif - !ssl->options.havePeerCert && !ssl->options.failNoCert) { + !ssl->options.havePeerCert && + !ClientAuthRequired(ssl, ssl->options.usingPSK_cipher)) { ssl->options.peerAuthGood = 1; } /* SERVER: check peer authentication. */ diff --git a/tests/api.c b/tests/api.c index 182c5ab7996..f9ad8cff0de 100644 --- a/tests/api.c +++ b/tests/api.c @@ -23089,6 +23089,94 @@ static int test_wolfSSL_sigalg_info(void) return EXPECT_RESULT(); } +/* peerAuthOk is the last field, so a blob written before it existed is simply + * short and an older reader ignores the trailing byte. */ +static int test_wolfSSL_i2d_SSL_SESSION_peer_auth(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_EXT_CACHE) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_SESSION* sess = NULL; + WOLFSSL_SESSION* restored = NULL; + unsigned char* der = NULL; + const unsigned char* ptr = NULL; + unsigned char* pp = NULL; + int sz = 0; + + ExpectNotNull(sess = wolfSSL_SESSION_new()); + if (sess != NULL) { + sess->peerAuthOk = 1; + sess->isSetup = 1; + } + ExpectIntGT((sz = wolfSSL_i2d_SSL_SESSION(sess, NULL)), 0); + /* Two sessions back to back, then slack, to exercise the length contract. */ + ExpectNotNull(der = (unsigned char*)XMALLOC((size_t)(2 * sz) + 8, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + if (der != NULL) { + XMEMSET(der, 0xAA, (size_t)(2 * sz) + 8); + pp = der; + ExpectIntGT(wolfSSL_i2d_SSL_SESSION(sess, &pp), 0); + /* i2d does not advance *p, so place the second one by hand. */ + pp = der + sz; + ExpectIntGT(wolfSSL_i2d_SSL_SESSION(sess, &pp), 0); + } + + /* Round trip keeps it, and consumes exactly the session. */ + ptr = der; + ExpectNotNull(restored = wolfSSL_d2i_SSL_SESSION(NULL, &ptr, (long)sz)); + if (restored != NULL) { + ExpectIntEQ(restored->peerAuthOk, 1); + ExpectIntEQ(restored->isPSK, 0); + } + ExpectPtrEq(ptr, der + sz); + wolfSSL_SESSION_free(restored); + restored = NULL; + + /* Too few bytes is an error, not a partial import. */ + ptr = der; + ExpectNull(wolfSSL_d2i_SSL_SESSION(NULL, &ptr, (long)sz - 1)); + + /* More bytes than the session is allowed: the extra is ignored, the field + * survives and *p stops at the end of the session. */ + ptr = der; + ExpectNotNull(restored = wolfSSL_d2i_SSL_SESSION(NULL, &ptr, (long)sz + 8)); + if (restored != NULL) + ExpectIntEQ(restored->peerAuthOk, 1); + ExpectPtrEq(ptr, der + sz); + wolfSSL_SESSION_free(restored); + restored = NULL; + + /* So the second of two stored back to back decodes from where the first + * one left off. */ + ExpectNotNull(restored = wolfSSL_d2i_SSL_SESSION(NULL, &ptr, + (long)(der + (2 * sz) - ptr))); + if (restored != NULL) + ExpectIntEQ(restored->peerAuthOk, 1); + ExpectPtrEq(ptr, der + (2 * sz)); + wolfSSL_SESSION_free(restored); + restored = NULL; + + /* isPSK shares the byte, and each flag decodes on its own. */ + if (sess != NULL) { + sess->peerAuthOk = 0; + sess->isPSK = 1; + } + pp = der; + ExpectIntEQ(wolfSSL_i2d_SSL_SESSION(sess, &pp), sz); + ptr = der; + ExpectNotNull(restored = wolfSSL_d2i_SSL_SESSION(NULL, &ptr, (long)sz)); + if (restored != NULL) { + ExpectIntEQ(restored->peerAuthOk, 0); + ExpectIntEQ(restored->isPSK, 1); + } + wolfSSL_SESSION_free(restored); + + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wolfSSL_SESSION_free(sess); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_d2i_SSL_SESSION_bounds_check(void) { EXPECT_DECLS; @@ -24886,11 +24974,14 @@ static int test_wolfSSL_verify_mode(void) wolfSSL_set_verify(ssl, SSL_VERIFY_NONE, 0); ExpectIntEQ(SSL_get_verify_mode(ssl), SSL_VERIFY_NONE); + /* Either fail-on-no-cert flag implies SSL_VERIFY_PEER. */ wolfSSL_set_verify(ssl, SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); - ExpectIntEQ(SSL_get_verify_mode(ssl), SSL_VERIFY_FAIL_IF_NO_PEER_CERT); + ExpectIntEQ(SSL_get_verify_mode(ssl), + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT); wolfSSL_set_verify(ssl, SSL_VERIFY_FAIL_EXCEPT_PSK, 0); - ExpectIntEQ(SSL_get_verify_mode(ssl), SSL_VERIFY_FAIL_EXCEPT_PSK); + ExpectIntEQ(SSL_get_verify_mode(ssl), + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_EXCEPT_PSK); #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) wolfSSL_set_verify(ssl, SSL_VERIFY_POST_HANDSHAKE, 0); @@ -46170,6 +46261,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_ciphersuite_auth), TEST_DECL(test_wolfSSL_sigalg_info), /* Can't memory test as tcp_connect aborts. */ + TEST_DECL(test_wolfSSL_i2d_SSL_SESSION_peer_auth), TEST_DECL(test_wolfSSL_d2i_SSL_SESSION_bounds_check), TEST_DECL(test_wolfSSL_sk_GENERAL_NAME), TEST_DECL(test_wolfSSL_sk_GENERAL_NAME_new_null), diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index 68dcedca73a..51d37f9001c 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -1030,9 +1030,20 @@ int test_wolfSSL_mutual_auth(void) ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 1), 0); ExpectIntEQ(wolfSSL_mutual_auth(ssl, 1), 0); + /* Requiring the certificate asks for it and sets nothing else. */ + ExpectIntEQ(ctx->mutualAuth, 1); + ExpectIntEQ(ctx->verifyPeer, 1); + ExpectIntEQ(ctx->verifyNone, 0); + ExpectIntEQ(ctx->failNoCert, 0); + ExpectIntEQ(ssl->options.mutualAuth, 1); + ExpectIntEQ(ssl->options.verifyPeer, 1); + ExpectIntEQ(ssl->options.verifyNone, 0); + ExpectIntEQ(ssl->options.failNoCert, 0); /* The setting can be turned back off. */ ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 0), 0); ExpectIntEQ(wolfSSL_mutual_auth(ssl, 0), 0); + ExpectIntEQ(ctx->mutualAuth, 0); + ExpectIntEQ(ssl->options.mutualAuth, 0); wolfSSL_free(ssl); wolfSSL_CTX_free(ctx); @@ -1773,10 +1784,12 @@ static const VerifyModeCase verifyModeCases[] = { WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT | WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, 0, 1, 1, 1 }, - /* FAIL_IF_NO_PEER_CERT on its own is recorded even though a server that - * never requests a certificate cannot act on it. */ + /* Either fail-on-no-cert flag on its own turns verifyPeer on: failing + * without a certificate only means anything when one was asked for. */ { "FAIL_IF_NO_PEER_CERT", WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, - 0, 0, 1, 0 }, + 0, 1, 1, 0 }, + { "FAIL_EXCEPT_PSK", WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, + 0, 1, 0, 1 }, /* VERIFY_DEFAULT clears every option instead of setting one. That is * why it does not round-trip through get_verify_mode(). */ { "DEFAULT", WOLFSSL_VERIFY_DEFAULT, 0, 0, 0, 0 }, @@ -2337,9 +2350,8 @@ int test_wolfSSL_verify_none_server_no_request(void) * without WOLFSSL_VERIFY_PEER still refuses a client that has no * certificate. * - * The CertificateRequest is sent only for WOLFSSL_VERIFY_PEER, so nothing - * asks the client for a certificate and nothing notices it is missing until - * DoTls13Finished checks at the end of the handshake. + * The flag implies WOLFSSL_VERIFY_PEER, so the CertificateRequest is sent and + * the client answers with an empty Certificate, which is then refused. * * @return TEST_SUCCESS on success. */ @@ -2357,15 +2369,16 @@ int test_wolfSSL_verify_tls13_failnocert_only(void) wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); - ExpectIntEQ(ssl_s->options.verifyPeer, 0); + ExpectIntEQ(ssl_s->options.verifyPeer, 1); ExpectIntEQ(ssl_s->options.failNoCert, 1); ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WC_NO_ERR_TRACE(NO_PEER_CERT)); - /* The client was never asked, so it never sent one. */ - ExpectIntEQ(ssl_s->msgsReceived.got_certificate, 0); + /* The client was asked, and answered with an empty Certificate. */ + ExpectIntEQ(ssl_s->msgsReceived.got_certificate, 1); + ExpectIntEQ(ssl_s->options.havePeerCert, 0); wolfSSL_free(ssl_c); wolfSSL_free(ssl_s); diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c index 24792031363..6ead5e411cf 100644 --- a/tests/api/test_tls.c +++ b/tests/api/test_tls.c @@ -3417,6 +3417,1571 @@ int test_tls12_resume_ticket_wrong_suite(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_PSK) && \ + (defined(BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) || \ + defined(WOLFSSL_TLS13)) +static const byte test_clear_psk[] = { 0x5a, 0x6b, 0x7c, 0x8d }; + +static unsigned int test_clear_psk_client_cb(WOLFSSL* ssl, const char* hint, + char* identity, unsigned int id_max_len, unsigned char* key, + unsigned int key_max_len) +{ + (void)ssl; + (void)hint; + if ((id_max_len == 0) || (key_max_len < sizeof(test_clear_psk))) + return 0; + XSTRNCPY(identity, "clear_client", id_max_len); + XMEMCPY(key, test_clear_psk, sizeof(test_clear_psk)); + return (unsigned int)sizeof(test_clear_psk); +} + +static unsigned int test_clear_psk_server_cb(WOLFSSL* ssl, const char* id, + unsigned char* key, unsigned int key_max_len) +{ + (void)ssl; + if ((id == NULL) || (key_max_len < sizeof(test_clear_psk))) + return 0; + if (XSTRCMP(id, "clear_client") != 0) + return 0; + XMEMCPY(key, test_clear_psk, sizeof(test_clear_psk)); + return (unsigned int)sizeof(test_clear_psk); +} +#endif + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) +static int test_tls12_clear_resets_options_ex(int psk) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL, *fresh = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "test"; + char reply[sizeof(msg)]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); +#if !defined(NO_PSK) && defined(BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) + if (psk) { + /* On the CTXs, so the fresh object below is configured alike. */ + wolfSSL_CTX_set_psk_client_callback(ctx_c, test_clear_psk_client_cb); + wolfSSL_CTX_set_psk_server_callback(ctx_s, test_clear_psk_server_cb); + ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx_c, + "ECDHE-PSK-AES128-GCM-SHA256"), WOLFSSL_SUCCESS); + } +#else + (void)psk; +#endif + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); +#if !defined(NO_PSK) && defined(BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) + /* The CTX suite list predates the callback; this rebuilds the object's. */ + if (psk) + wolfSSL_set_psk_server_callback(ssl_s, test_clear_psk_server_cb); +#endif + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Data both ways and a bidirectional close, so record-layer and shutdown + * state is covered too, not just the handshake. */ + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg)), (int)sizeof(msg)); + ExpectIntEQ(wolfSSL_read(ssl_s, reply, (int)sizeof(reply)), + (int)sizeof(msg)); + ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), (int)sizeof(msg)); + ExpectIntEQ(wolfSSL_read(ssl_c, reply, (int)sizeof(reply)), + (int)sizeof(msg)); + ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SHUTDOWN_NOT_DONE); + ExpectIntEQ(wolfSSL_read(ssl_s, reply, (int)sizeof(reply)), 0); + ExpectIntEQ(wolfSSL_shutdown(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_shutdown(ssl_c), WOLFSSL_SUCCESS); + /* Same CTX, never handshaked, so its Options is what a reset should + * produce. Created after the handshake so the CTX is fully configured. */ + ExpectNotNull(fresh = wolfSSL_new(ctx_s)); + ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS); + + if ((ssl_s != NULL) && (fresh != NULL)) { + /* Negotiated results, kept on purpose for the accessors. */ + ssl_s->options.cipherSuite0 = fresh->options.cipherSuite0; + ssl_s->options.cipherSuite = fresh->options.cipherSuite; + ssl_s->options.hashAlgo = fresh->options.hashAlgo; + ssl_s->options.sigAlgo = fresh->options.sigAlgo; + ssl_s->options.haveDH = fresh->options.haveDH; + /* wolfSSL_get_shutdown() reports a completed bidirectional shutdown + * from this after a clear, by documented intent. */ + ssl_s->options.shutdownDone = fresh->options.shutdownDone; + /* Reset only under WOLFSSL_ASYNC_CRYPT. */ + ssl_s->options.asyncState = fresh->options.asyncState; + /* Same condition the fields are declared under. */ +#if !defined(NO_DH) && !defined(WOLFSSL_OLD_PRIME_CHECK) && \ + !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) + ssl_s->options.dhDoKeyTest = fresh->options.dhDoKeyTest; + ssl_s->options.dhKeyTested = fresh->options.dhKeyTested; +#endif + /* // NOLINTNEXTLINE(bugprone-suspicious-memory-comparison) */ + ExpectIntEQ(XMEMCMP(&ssl_s->options, &fresh->options, sizeof(Options)), + 0); + if (EXPECT_FAIL()) { + /* Offsets rather than names: Options is mostly bitfields. */ + const byte* a = (const byte*)&ssl_s->options; + const byte* b = (const byte*)&fresh->options; + word32 k; + for (k = 0; k < (word32)sizeof(Options); k++) { + if (a[k] != b[k]) { + fprintf(stderr, "Options differ at byte %u:" + " reused=0x%02x fresh=0x%02x\n", k, a[k], b[k]); + } + } + } + } + + wolfSSL_free(fresh); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +/* Catches per-connection Options state that wolfSSL_clear() forgets, rather + * than one field at a time. Every Options is born inside + * XMEMSET(ssl, 0, sizeof(WOLFSSL)), so padding and bitfield slack are zero in + * both objects and only written fields can differ. */ +int test_tls12_clear_resets_options(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) + ExpectIntEQ(test_tls12_clear_resets_options_ex(0), TEST_SUCCESS); +#if !defined(NO_PSK) && defined(BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) + /* A PSK suite leaves different state behind. */ + ExpectIntEQ(test_tls12_clear_resets_options_ex(1), TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} + +/* usingPSK_cipher and pskNegotiated describe the suite in use, so a reused + * object must not carry the previous connection's into a handshake whose + * policy excuses only PSK suites from presenting a certificate. */ +int test_tls_clear_resets_psk_flags(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_PSK) && \ + (defined(BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) || \ + defined(WOLFSSL_TLS13)) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + +#if !defined(WOLFSSL_NO_TLS12) && \ + defined(BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_set_psk_client_callback(ssl_c, test_clear_psk_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_clear_psk_server_cb); + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "ECDHE-PSK-AES128-GCM-SHA256"), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 1); + ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 0); + +#ifndef WOLFSSL_NO_CLIENT_AUTH + /* A client with neither a certificate nor a PSK, on the reused server + * under a policy that excuses only PSK suites. */ + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_CTX_free(ctx_c); + ctx_c = NULL; + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + if (ssl_s != NULL) { + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + } + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL); + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); +#endif + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); + ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); + ctx_s = NULL; +#endif /* !WOLFSSL_NO_TLS12 && BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 */ + +#ifdef WOLFSSL_TLS13 + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_psk_client_callback(ssl_c, test_clear_psk_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_clear_psk_server_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.pskNegotiated, 1); + ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.pskNegotiated, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif +#endif + return EXPECT_RESULT(); +} + +/* wolfSSL_get_session() hands the application a pointer without taking a + * reference, so a server reusing the object must not free the session out + * from under it. */ +int test_tls12_clear_keeps_borrowed_session(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_SESSION_CACHE) && \ + defined(WOLFSSL_PEM_TO_DER) && \ + (!defined(WOLFSSL_BLIND_PRIVATE_KEY) || defined(OPENSSL_EXTRA) || \ + defined(WOLFSSL_WPAS_SMALL)) + /* Loads PEM credentials, and needs wolfSSL_get_session() and a server + * whose key outlives the handshake: FreeHandshakeResources() unloads a + * key the object owns, which with blinding is its own copy, and + * wolfSSL_clear() then has none to re-arm the suites with. */ + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* borrowed = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(borrowed = wolfSSL_get_session(ssl_s)); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 1); + + ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS); + /* Still the application's to read, and holding nothing of the connection + * that ended. */ + if (ssl_s != NULL) + ExpectPtrEq(ssl_s->session, borrowed); + ExpectIntEQ(borrowed->peerAuthOk, 0); + ExpectIntEQ(borrowed->haveAltSessionID, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* An external PSK means this client is not asked for a certificate. Left to + * stand, the next client on a reused object is not asked either, and then + * fails the check that one was presented. */ +int test_tls13_psk_clear_restores_verify_peer(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + !defined(NO_PSK) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + /* Only a PSK suite excuses the certificate. */ + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_psk_client_callback(ssl_c, test_clear_psk_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_clear_psk_server_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 1); + /* The configured mode outlives the connection. */ + ExpectIntEQ(ssl_s->options.verifyPeer, 1); + } + + ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 0); + + /* A second client, with a certificate and no PSK, on the reused object. */ + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_CTX_free(ctx_c); + ctx_c = NULL; + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + if (ssl_s != NULL) { + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + } + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.havePeerCert, 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* mutual_auth is an alternative to failNoCert in the full handshake, so + * resumption has to agree. */ +int test_tls12_resume_ticket_mutual_auth(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL, *ssl_c2 = NULL, *ssl_s2 = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s2, WOLFSSL_VERIFY_PEER, NULL); + ExpectIntEQ(wolfSSL_mutual_auth(ssl_s2, 1), 0); + ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS); + test_memio_do_handshake(ssl_c2, ssl_s2, 20, NULL); + ExpectIntEQ(wolfSSL_session_reused(ssl_s2), 0); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s2); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A post-handshake-auth server records no outcome, so it must still resume. + * Authenticating afterwards is an outcome, and a session written then + * records it even though the connection was resumed. */ +int test_tls13_resume_psk_post_handshake_auth(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "hello wolfssl!"; + char readBuf[sizeof(msg)]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT | WOLFSSL_VERIFY_POST_HANDSHAKE, + NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 0); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT | WOLFSSL_VERIFY_POST_HANDSHAKE, + NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + /* The server's write carries the CertificateRequest, the client's reply + * the certificate. */ + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf) - 1), + (int)sizeof(msg) - 1); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.havePeerVerify, 1); + /* A session written now records the authentication. */ + ExpectIntEQ(wolfSSL_send_SessionTicket(ssl_s), WOLFSSL_SUCCESS); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 1); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* TLS 1.3: a PSK must not stand in for the required client certificate. */ +int test_tls13_resume_psk_client_auth(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + byte readBuf[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Drives the NewSessionTicket onto the client session. */ + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 0); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + /* The skipped PSK becomes a full handshake. */ + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* failNoCertxPSK excuses a PSK, not a ticket. A ticket minted by a + * certificate session that never authenticated its client must not resume + * under it, matching TLS 1.2 and the full handshake the same client gets. */ +int test_tls13_resume_ticket_fail_except_psk(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + byte readBuf[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Drives the NewSessionTicket onto the client session. */ + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + /* Nothing authenticated the client. */ + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 0); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + /* Premise: the exempting flag is the only one set. */ + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->options.failNoCertxPSK, 1); + ExpectIntEQ(ssl_s->options.failNoCert, 0); + ExpectIntEQ(ssl_s->options.mutualAuth, 0); + } + /* The skipped PSK becomes a full handshake, which the certificate-less + * client cannot satisfy either. */ + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* With post-handshake auth the ticket's missing client auth is deferred, not + * excused: the later empty Certificate must still fail failNoCertxPSK. */ +int test_tls13_resume_ticket_fail_except_psk_post_handshake(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \ + !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "hello wolfssl!"; + char readBuf[sizeof(msg)]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 0); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_EXCEPT_PSK | WOLFSSL_VERIFY_POST_HANDSHAKE, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + /* The certificate-less client answers with an empty Certificate. */ + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf) - 1), + WOLFSSL_FATAL_ERROR); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A ticket from an external-PSK connection keeps its exemption through + * post-handshake auth. */ +int test_tls13_resume_psk_ticket_fail_except_psk_post_handshake(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \ + !defined(NO_PSK) && !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + char msg[] = "hello wolfssl!"; + char readBuf[sizeof(msg)]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_allow_post_handshake_auth(ctx_c), 0); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_psk_client_callback(ssl_c, test_clear_psk_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_clear_psk_server_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + /* Only the ticket is offered this time. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_EXCEPT_PSK | WOLFSSL_VERIFY_POST_HANDSHAKE, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1); + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 1); + ExpectIntEQ(ssl_s->session->isPSK, 1); + } + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + /* The certificate-less client answers with an empty Certificate. */ + ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)sizeof(msg) - 1), + (int)sizeof(msg) - 1); + ExpectIntEQ(wolfSSL_read(ssl_s, readBuf, sizeof(readBuf) - 1), + (int)sizeof(msg) - 1); + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->error, 0); + ExpectIntEQ(ssl_s->options.havePeerCert, 0); + } + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* The other half: a ticket from an external-PSK session carries the PSK, so + * failNoCertxPSK still excuses it, and so does the ticket that resumption + * issues in turn. */ +int test_tls13_resume_psk_ticket_fail_except_psk_ok(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_PSK) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION *sess = NULL, *sess2 = NULL; + struct test_memio_ctx test_ctx; + byte readBuf[16]; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_psk_client_callback(ssl_c, test_clear_psk_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_clear_psk_server_cb); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + /* The PSK authenticated the client, so no certificate was recorded. */ + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->session->isPSK, 1); + ExpectIntEQ(ssl_s->session->peerAuthOk, 0); + } + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + /* The first resumption, then one from the ticket it issued. */ + for (i = 0; i < 2; i++) { + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, wolfTLSv1_3_client_method, + wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL); + wolfSSL_set_psk_client_callback(ssl_c, test_clear_psk_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_clear_psk_server_cb); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess2 = wolfSSL_get1_session(ssl_c)); + wolfSSL_SESSION_free(sess); + sess = sess2; + sess2 = NULL; + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + } + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A session that did authenticate the client must still resume. */ +int test_tls13_resume_psk_client_auth_ok(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + byte readBuf[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + /* CTXs first: credentials must precede the objects inheriting them. */ + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 1); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 1); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* The outcome has to survive an i2d/d2i external cache. No internal cache and + * no ticket, so only the deserialized session can carry it. */ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(OPENSSL_EXTRA) && \ + defined(HAVE_EXT_CACHE) && !defined(NO_SESSION_CACHE) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) +static byte test_extcache_der[2048]; +static int test_extcache_derSz = 0; +static int test_extcache_gets = 0; + +static int test_extcache_new_cb(WOLFSSL* ssl, WOLFSSL_SESSION* sess) +{ + unsigned char* p = test_extcache_der; + int sz; + + (void)ssl; + sz = wolfSSL_i2d_SSL_SESSION(sess, NULL); + if ((sz > 0) && (sz <= (int)sizeof(test_extcache_der))) { + sz = wolfSSL_i2d_SSL_SESSION(sess, &p); + if (sz > 0) + test_extcache_derSz = sz; + } + return 0; +} + +static WOLFSSL_SESSION* test_extcache_get_cb(WOLFSSL* ssl, + const unsigned char* id, int len, int* ref) +{ + const unsigned char* p = test_extcache_der; + + (void)ssl; + (void)id; + (void)len; + test_extcache_gets++; + /* A fresh object each time, so wolfSSL owns it. */ + *ref = 0; + if (test_extcache_derSz <= 0) + return NULL; + return wolfSSL_d2i_SSL_SESSION(NULL, &p, (long)test_extcache_derSz); +} +#endif + +int test_tls12_ext_cache_client_auth_resume(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(OPENSSL_EXTRA) && \ + defined(HAVE_EXT_CACHE) && !defined(NO_SESSION_CACHE) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + + test_extcache_derSz = 0; + test_extcache_gets = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + /* CTXs first: credentials must precede the objects inheriting them. */ + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + /* Only the external cache answers. */ + if (ctx_s != NULL) { + wolfSSL_CTX_set_session_cache_mode(ctx_s, + WOLFSSL_SESS_CACHE_SERVER | WOLFSSL_SESS_CACHE_NO_INTERNAL); + wolfSSL_CTX_sess_set_new_cb(ctx_s, test_extcache_new_cb); + wolfSSL_CTX_sess_set_get_cb(ctx_s, test_extcache_get_cb); + } + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 1); + ExpectIntGT(test_extcache_derSz, 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + + /* The server can only get it back through d2i. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_set_verify(ssl_s2, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0); + ExpectIntGT(test_extcache_gets, 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s2), 1); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s2); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A ticket minted under VERIFY_NONE, replayed on the same CTX against a + * connection requiring a client certificate, must be declined. */ +int test_tls12_resume_ticket_client_auth(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + ExpectIntGT(sess->ticketLen, 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 0); + + /* Same CTX, so the ticket decrypts. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s2, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS); + + /* The decline becomes a full handshake. */ + ExpectIntNE(test_memio_do_handshake(ssl_c2, ssl_s2, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s2), 0); + if (ssl_s2 != NULL) { + ExpectIntEQ(ssl_s2->options.resuming, 0); + /* Rejected for the certificate, not accepted off the ticket. */ + ExpectIntEQ(ssl_s2->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + } + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s2); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Which server policies require a client certificate. The client holds none, + * so only the server's policy ends the handshake. WOLFSSL_VERIFY_NONE waives + * client auth as it does in OpenSSL, and wolfSSL_mutual_auth() turns + * verifyPeer on so the certificate it requires is actually asked for. Every + * site deciding this goes through ClientAuthRequired(), so they cannot drift + * apart; see test_wolfSSL_verify_tls13_failnocert_only for the TLS 1.3 half. */ +int test_tls12_client_auth_flags_enforced(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA) + int i; + + for (i = 0; i < 10; i++) { + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + int wantCert = (i < 4) || (i == 7) || (i == 8); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), + 0); + if (i >= 8) { + /* Inherited by the objects created below. */ + ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx_s, 1), 0); + if (i == 9) + ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx_s, 0), 0); + } + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), + 0); + switch (i) { + case 0: + ExpectIntEQ(wolfSSL_mutual_auth(ssl_s, 1), 0); + /* Asks for the certificate; the requirement itself stays + * in mutualAuth, so nothing else is disturbed. */ + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->options.verifyPeer, 1); + ExpectIntEQ(ssl_s->options.verifyNone, 0); + ExpectIntEQ(ssl_s->options.failNoCert, 0); + ExpectIntEQ(ssl_s->options.mutualAuth, 1); + } +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || defined(HAVE_STUNNEL) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || defined(HAVE_LIGHTY) + ExpectIntEQ(wolfSSL_get_verify_mode(ssl_s), + WOLFSSL_VERIFY_PEER); +#endif + break; + case 1: + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + break; + case 2: + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + break; + case 3: + /* Not a PSK suite, so the PSK exemption does not apply. */ + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL); + break; + case 4: + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + break; + case 5: + /* An explicit WOLFSSL_VERIFY_NONE afterwards waives it. */ + ExpectIntEQ(wolfSSL_mutual_auth(ssl_s, 1), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + break; + case 6: + /* Turned off again, nothing of it remains. */ + ExpectIntEQ(wolfSSL_mutual_auth(ssl_s, 1), 0); + ExpectIntEQ(wolfSSL_mutual_auth(ssl_s, 0), 0); + break; + case 7: + /* Turning it off leaves a requirement set separately. */ + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_mutual_auth(ssl_s, 1), 0); + ExpectIntEQ(wolfSSL_mutual_auth(ssl_s, 0), 0); + break; + default: + /* Configured on the CTX above. */ + break; + } + + if (wantCert) { + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + } + else { + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->error, 0); + } + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.havePeerCert, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#endif + return EXPECT_RESULT(); +} + +/* failNoCertxPSK requires a client certificate on a non-PSK suite, so a ticket + * minted without client auth must not resume into it either. The suite judged + * is the resumed session's, which is why the gate is given it explicitly. */ +int test_tls12_resume_ticket_fail_except_psk(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + ExpectIntGT(sess->ticketLen, 0); + + /* Same CTX, so the ticket decrypts. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s2, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS); + /* Premise: the exempting flag is the only one set. */ + if (ssl_s2 != NULL) { + ExpectIntEQ(ssl_s2->options.failNoCertxPSK, 1); + ExpectIntEQ(ssl_s2->options.failNoCert, 0); + ExpectIntEQ(ssl_s2->options.mutualAuth, 0); + } + + ExpectIntNE(test_memio_do_handshake(ssl_c2, ssl_s2, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s2), 0); + if (ssl_s2 != NULL) { + ExpectIntEQ(ssl_s2->options.resuming, 0); + ExpectIntEQ(ssl_s2->options.peerAuthGood, 0); + } + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s2); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* An empty client Certificate is rejected at the Certificate message from + * TLS 1.2 on, but before that the rejection is left to the later + * DoClientKeyExchange check, so the server reads one more message first. Both + * end in NO_PEER_CERT, so only the message count shows which fired. */ +int test_tls1x_empty_cert_reject_point(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && !defined(NO_OLD_TLS) && \ + !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) && \ + !defined(NO_RSA) && !defined(NO_SHA) + int i; + + for (i = 0; i < 2; i++) { + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + i ? wolfTLSv1_2_client_method + : wolfTLSv1_1_client_method, + i ? wolfTLSv1_2_server_method + : wolfTLSv1_1_server_method), 0); + /* verifyPeer so the CertificateRequest is sent; the client holds no + * certificate, so it answers with an empty one. */ + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + ExpectIntEQ(ssl_s->msgsReceived.got_certificate, 1); + /* TLS 1.2 stops at the Certificate; TLS 1.1 reads on. */ + ExpectIntEQ(ssl_s->msgsReceived.got_client_key_exchange, !i); + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#endif + return EXPECT_RESULT(); +} + +/* verifyPostHandshake defers when the certificate is demanded, so the + * enclosing handshake sends no CertificateRequest and completes without one. + * It must not also excuse an empty Certificate that arrives anyway: nothing + * rejects an unsolicited one before ProcessPeerCerts, so its empty-cert rule + * has to stay independent of the deferral. */ +int test_tls13_pha_unsolicited_blank_cert(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_POST_HANDSHAKE | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, + NULL); + /* No conforming client sends this, so ask for it directly. */ + if (ssl_c != NULL) + ssl_c->options.sendVerify = SEND_BLANK_CERT; + + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + if (ssl_s != NULL) { + /* It reached the rule rather than being refused earlier. */ + ExpectIntEQ(ssl_s->msgsReceived.got_certificate, 1); + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + ExpectIntEQ(ssl_s->options.havePeerCert, 0); + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A certificate the server did not ask for is parsed but not verified, so it + * is not client auth and must not be recorded as such for resumption. */ +int test_tls13_unsolicited_cert_not_recorded(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + byte readBuf[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + /* No conforming client sends these unasked, so ask for them directly. + * The hash is normally taken from the CertificateRequest. */ + if (ssl_c != NULL) { + ssl_c->options.sendVerify = SEND_CERT; + ssl_c->options.hashAlgo = sha256_mac; + } + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + if (ssl_s != NULL) { + /* Premise: the certificate and its proof were accepted. */ + ExpectIntEQ(ssl_s->options.havePeerCert, 1); + ExpectIntEQ(ssl_s->options.havePeerVerify, 1); + ExpectIntEQ(ssl_s->session->peerAuthOk, 0); + } + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + + /* Requiring a certificate now means a full handshake. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->options.havePeerCert, 1); + /* Asked for and verified this time, so it counts. */ + ExpectIntEQ(ssl_s->session->peerAuthOk, 1); + } + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* verifyPostHandshake defers the request only in TLS 1.3. A TLS 1.2 full + * handshake still demands the certificate, so resumption must not read the + * flag as a waiver. */ +int test_tls12_resume_ticket_post_handshake_auth(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_CERTS) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_POST_HANDSHAKE_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + ExpectIntGT(sess->ticketLen, 0); + + /* Same CTX, so the ticket decrypts. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s2, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT | WOLFSSL_VERIFY_POST_HANDSHAKE, + NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS); + /* Premise: the flag really is set on a TLS 1.2 connection. */ + if (ssl_s2 != NULL) + ExpectIntEQ(ssl_s2->options.verifyPostHandshake, 1); + + /* The decline becomes a full handshake. */ + ExpectIntNE(test_memio_do_handshake(ssl_c2, ssl_s2, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s2), 0); + if (ssl_s2 != NULL) { + ExpectIntEQ(ssl_s2->options.resuming, 0); + ExpectIntEQ(ssl_s2->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + } + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s2); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A client that does present its certificate must still abbreviate. */ +int test_tls12_resume_ticket_client_auth_ok(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_CERTS) && \ + !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL *ssl_c2 = NULL, *ssl_s2 = NULL; + WOLFSSL_SESSION* sess = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + /* CTXs first: credentials must precede the objects inheriting them. */ + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->session->peerAuthOk, 1); + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c2), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s2, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s2), 1); + + wolfSSL_SESSION_free(sess); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_s2); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* useTicket and peerAuthOk are per-handshake: an object reused with + * wolfSSL_clear() must not carry either into the next ClientHello, or the + * retained session is resumed for a client that never held it. The id offered + * below is dropped from the cache, so only stale state could resume it. */ +int test_tls12_reuse_clears_use_ticket(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SESSION_TICKET) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_SESSION_CACHE) && \ + !defined(NO_CERTS) && !defined(NO_RSA) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL, *ctx_s2 = NULL; + WOLFSSL *ssl_c = NULL, *ssl_c2 = NULL, *ssl_c3 = NULL; + WOLFSSL *ssl_s = NULL, *ssl_s2 = NULL; + WOLFSSL_SESSION *sess = NULL, *sessS = NULL; + struct test_memio_ctx test_ctx; + int useTicketAfterClear = -1; + int peerAuthAfterClear = -1; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + /* CTXs first: credentials must precede the objects inheriting them. */ + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->options.useTicket, 1); +#ifndef WOLFSSL_NO_CLIENT_AUTH + ExpectIntEQ(ssl_s->session->peerAuthOk, 1); +#endif + } + /* A different statement on the client, and unread there. */ + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->session->peerAuthOk, 0); + + /* From a server issuing no tickets, so the next ClientHello carries a + * session id only. Not modified in place: it may be cache-backed. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s2, &ssl_c2, &ssl_s2, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c2)); + /* Premise, not the fix: a ticket would resume legitimately. */ + ExpectIntEQ(sess->ticketLen, 0); + /* The two ends share one cache in-process. */ + ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s2, sess), 1); + + ExpectNotNull(sessS = wolfSSL_get1_session(ssl_s)); + ExpectIntEQ(wolfSSL_clear(ssl_s), WOLFSSL_SUCCESS); + /* The object starts over with a session of its own; what the + * application holds is left alone. */ + if (ssl_s != NULL) + ExpectTrue(ssl_s->session != sessS); +#ifndef WOLFSSL_NO_CLIENT_AUTH + ExpectIntEQ(sessS->peerAuthOk, 1); +#endif + /* Assert last, or a failure here skips the behavioural half. */ + if (ssl_s != NULL) { + useTicketAfterClear = ssl_s->options.useTicket; + peerAuthAfterClear = ssl_s->session->peerAuthOk; + } + + /* Point the reused server at the new transport. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c3, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + if (ssl_s != NULL) { + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + } + ExpectIntEQ(wolfSSL_set_session(ssl_c3, sess), WOLFSSL_SUCCESS); + /* May fail; what matters is it did not hand over the retained session. */ + if ((ssl_c3 != NULL) && (ssl_s != NULL)) + test_memio_do_handshake(ssl_c3, ssl_s, 10, NULL); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->options.resuming, 0); + ExpectIntEQ(useTicketAfterClear, 0); + ExpectIntEQ(peerAuthAfterClear, 0); + + wolfSSL_SESSION_free(sess); + wolfSSL_SESSION_free(sessS); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_c2); + wolfSSL_free(ssl_c3); + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_s2); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + wolfSSL_CTX_free(ctx_s2); +#endif + return EXPECT_RESULT(); +} + /* A ticket the server can't honor must fall back to a full handshake (RFC 5077 * 3.4), even under a different suite than the cached ticket session - the * F-5811 suite check must not abort it. The second handshake uses a fresh @@ -4098,6 +5663,92 @@ int test_tls12_session_id_resumption_sni_mismatch(void) return EXPECT_RESULT(); } +/* A TLS 1.2 ticket that decrypts is copied to the session before it can be + * declined, and the server writes its next ticket before SetupSession() + * resets that. The new ticket must record only what the full handshake + * proved, or it carries the declined ticket's client auth. */ +int test_tls12_declined_ticket_auth_not_reminted(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SNI) && \ + defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_CERTS) && \ + !defined(NO_RSA) && !defined(WOLFSSL_NO_CLIENT_AUTH) && \ + defined(WOLFSSL_PEM_TO_DER) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_SESSION *sess = NULL, *sess2 = NULL; + struct test_memio_ctx test_ctx; + const char* sniA = "public.example"; + const char* sniB = "admin.example"; + + /* 1: the client authenticates. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, NULL, NULL, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + wolfSSL_CTX_set_servername_callback(ctx_s, accept_any_sni_cb); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sniA, (word16)XSTRLEN(sniA)), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + /* 2: under another SNI the ticket decrypts, is declined, and the full + * handshake that follows, on a lax server, authenticates nothing. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sniB, (word16)XSTRLEN(sniB)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + ExpectNotNull(sess2 = wolfSSL_get1_session(ssl_c)); + if (sess2 != NULL) + ExpectIntGT(sess2->ticketLen, 0); + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + + /* 3: that ticket must not satisfy a server requiring a certificate. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sniB, (word16)XSTRLEN(sniB)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_session(ssl_c, sess2), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(NO_PEER_CERT)); + + wolfSSL_SESSION_free(sess); + wolfSSL_SESSION_free(sess2); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /* TLS 1.3 PSK resumption must fall back to a full handshake if the SNI in * the resumed ClientHello does not match the SNI bound to the original * session (RFC 6066 Section 3 / RFC 8446 Section 4.6.1). */ diff --git a/tests/api/test_tls.h b/tests/api/test_tls.h index 4824c5ecc88..a63a16bdc77 100644 --- a/tests/api/test_tls.h +++ b/tests/api/test_tls.h @@ -61,6 +61,28 @@ int test_tls_version_mask_alert_record(void); int test_tls_version_error_alert_mapping(void); int test_tls12_etm_failed_resumption(void); int test_tls12_resume_ticket_wrong_suite(void); +int test_tls12_clear_resets_options(void); +int test_tls_clear_resets_psk_flags(void); +int test_tls12_clear_keeps_borrowed_session(void); +int test_tls13_psk_clear_restores_verify_peer(void); +int test_tls12_resume_ticket_mutual_auth(void); +int test_tls13_resume_psk_post_handshake_auth(void); +int test_tls13_resume_psk_client_auth(void); +int test_tls13_resume_ticket_fail_except_psk(void); +int test_tls13_resume_ticket_fail_except_psk_post_handshake(void); +int test_tls13_resume_psk_ticket_fail_except_psk_post_handshake(void); +int test_tls13_resume_psk_ticket_fail_except_psk_ok(void); +int test_tls13_resume_psk_client_auth_ok(void); +int test_tls12_ext_cache_client_auth_resume(void); +int test_tls12_resume_ticket_client_auth(void); +int test_tls12_resume_ticket_post_handshake_auth(void); +int test_tls12_client_auth_flags_enforced(void); +int test_tls12_resume_ticket_fail_except_psk(void); +int test_tls1x_empty_cert_reject_point(void); +int test_tls13_pha_unsolicited_blank_cert(void); +int test_tls13_unsolicited_cert_not_recorded(void); +int test_tls12_resume_ticket_client_auth_ok(void); +int test_tls12_reuse_clears_use_ticket(void); int test_tls12_resume_ticket_decline_fallback(void); int test_tls12_ticket_dropped_on_bad_finished(void); int test_tls12_ticket_cached_after_finished(void); @@ -70,6 +92,7 @@ int test_tls12_server_hello_tls13_suite(void); int test_tls12_client_hello_no_tls13_suites(void); int test_tls_set_session_min_downgrade(void); int test_tls12_session_id_resumption_sni_mismatch(void); +int test_tls12_declined_ticket_auth_not_reminted(void); int test_tls13_session_resumption_sni_mismatch(void); int test_tls13_resumption_with_alpn(void); int test_tls12_session_id_resumption_alpn_mismatch(void); @@ -142,6 +165,32 @@ int test_tls_param_flags_crl_check(void); TEST_DECL_GROUP("tls", test_tls_version_error_alert_mapping), \ TEST_DECL_GROUP("tls", test_tls12_etm_failed_resumption), \ TEST_DECL_GROUP("tls", test_tls12_resume_ticket_wrong_suite), \ + TEST_DECL_GROUP("tls", test_tls12_clear_resets_options), \ + TEST_DECL_GROUP("tls", test_tls_clear_resets_psk_flags), \ + TEST_DECL_GROUP("tls", test_tls12_clear_keeps_borrowed_session), \ + TEST_DECL_GROUP("tls", test_tls13_psk_clear_restores_verify_peer), \ + TEST_DECL_GROUP("tls", test_tls12_resume_ticket_mutual_auth), \ + TEST_DECL_GROUP("tls", test_tls13_resume_psk_post_handshake_auth), \ + TEST_DECL_GROUP("tls", test_tls13_resume_psk_client_auth), \ + TEST_DECL_GROUP("tls", test_tls13_resume_ticket_fail_except_psk), \ + TEST_DECL_GROUP("tls", \ + test_tls13_resume_ticket_fail_except_psk_post_handshake), \ + TEST_DECL_GROUP("tls", \ + test_tls13_resume_psk_ticket_fail_except_psk_post_handshake), \ + TEST_DECL_GROUP("tls", \ + test_tls13_resume_psk_ticket_fail_except_psk_ok), \ + TEST_DECL_GROUP("tls", test_tls13_resume_psk_client_auth_ok), \ + TEST_DECL_GROUP("tls", test_tls12_ext_cache_client_auth_resume), \ + TEST_DECL_GROUP("tls", test_tls12_resume_ticket_client_auth), \ + TEST_DECL_GROUP("tls", \ + test_tls12_resume_ticket_post_handshake_auth), \ + TEST_DECL_GROUP("tls", test_tls12_client_auth_flags_enforced), \ + TEST_DECL_GROUP("tls", test_tls12_resume_ticket_fail_except_psk), \ + TEST_DECL_GROUP("tls", test_tls1x_empty_cert_reject_point), \ + TEST_DECL_GROUP("tls", test_tls13_pha_unsolicited_blank_cert), \ + TEST_DECL_GROUP("tls", test_tls13_unsolicited_cert_not_recorded), \ + TEST_DECL_GROUP("tls", test_tls12_resume_ticket_client_auth_ok), \ + TEST_DECL_GROUP("tls", test_tls12_reuse_clears_use_ticket), \ TEST_DECL_GROUP("tls", test_tls12_resume_ticket_decline_fallback), \ TEST_DECL_GROUP("tls", test_tls12_ticket_dropped_on_bad_finished), \ TEST_DECL_GROUP("tls", test_tls12_ticket_cached_after_finished), \ @@ -151,6 +200,7 @@ int test_tls_param_flags_crl_check(void); TEST_DECL_GROUP("tls", test_tls12_client_hello_no_tls13_suites), \ TEST_DECL_GROUP("tls", test_tls_set_session_min_downgrade), \ TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_sni_mismatch), \ + TEST_DECL_GROUP("tls", test_tls12_declined_ticket_auth_not_reminted), \ TEST_DECL_GROUP("tls", test_tls13_session_resumption_sni_mismatch), \ TEST_DECL_GROUP("tls", test_tls13_resumption_with_alpn), \ TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_alpn_mismatch),\ diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 18730cb07f5..cb18b253316 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -3069,7 +3069,7 @@ static int test_tls13_psk_age_alert(const char* identity, int* alertOut, * and not to a fixture that never negotiated a PSK at all. */ ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); if (ssl_s != NULL) { - ExpectIntEQ(ssl_s->options.isPSK, 1); + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 1); } ExpectIntEQ(test_tls13_psk_age_matched, 1); @@ -3095,12 +3095,13 @@ static int test_tls13_psk_age_alert(const char* identity, int* alertOut, ExpectIntEQ(wolfSSL_get_error(ssl_s, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)), WC_NO_ERR_TRACE(BAD_BINDER)); - /* isPSK is set only after FindPskSuite() reported a match and the early - * secret was derived, so it proves the known run really took the matched - * path. The callback flag alone would not: the callback runs before the - * ciphersuite match that decides whether the PSK is used at all. */ + /* usingPSK_cipher is set only after FindPskSuite() reported a match and + * the early secret was derived, so it proves the known run really took + * the matched path. The callback flag alone would not: the callback runs + * before the ciphersuite match that decides whether the PSK is used at + * all. */ if (ssl_s != NULL) { - ExpectIntEQ(ssl_s->options.isPSK, expectMatch); + ExpectIntEQ(ssl_s->options.usingPSK_cipher, expectMatch); } ExpectIntEQ(test_tls13_psk_age_matched, expectMatch); @@ -4851,19 +4852,31 @@ static unsigned int test_tls13_pha_psk_server_cb(WOLFSSL* ssl, const char* id, } #endif +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && !defined(NO_PSK) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_RSA) && !defined(NO_CERTS) && defined(WOLFSSL_CERT_SETUP_CB) +static int test_tls13_psk_forget_auth_cb(WOLFSSL* ssl, void* arg) +{ + (void)arg; + ssl->options.usingPSK_cipher = 0; + return 1; +} +#endif + /* Bounds the other side of the RFC 8446 4.3.2 relaxation: a CertificateRequest * during the main handshake under a PSK must still be rejected. The client is * untouched here - it has not sent its Finished, so handShakeDone is 0 on its - * own. A wolfSSL server normally clears verifyPeer once a PSK is chosen and so - * never sends one; cert_with_extern_psk on the server alone suppresses that - * clearing, which makes it emit the request the client must refuse. */ + * own. A wolfSSL server never sends one once a PSK authenticates the client, + * so the cert setup callback, which runs after the PSK is chosen, makes it + * forget that and emit the request the client must refuse. */ int test_tls13_psk_cert_request_in_handshake(void) { EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && !defined(NO_PSK) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ - !defined(NO_RSA) && !defined(NO_CERTS) + !defined(NO_RSA) && !defined(NO_CERTS) && defined(WOLFSSL_CERT_SETUP_CB) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; struct test_memio_ctx test_ctx; @@ -4873,9 +4886,7 @@ int test_tls13_psk_cert_request_in_handshake(void) wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); wolfSSL_set_psk_client_callback(ssl_c, test_tls13_pha_psk_client_cb); wolfSSL_set_psk_server_callback(ssl_s, test_tls13_pha_psk_server_cb); - /* Server side only: the client neither offers the extension nor takes the - * matching exemption, so it must reject on message order alone. */ - ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS); + wolfSSL_CTX_set_cert_cb(ctx_s, test_tls13_psk_forget_auth_cb, NULL); wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL); ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); @@ -4892,6 +4903,44 @@ int test_tls13_psk_cert_request_in_handshake(void) return EXPECT_RESULT(); } +/* Opting a server into cert_with_extern_psk does not make RFC 9973 apply: a + * client that did not send the extension is authenticated by its PSK, so no + * CertificateRequest is sent. */ +int test_tls13_cert_with_extern_psk_not_offered(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ + defined(WOLFSSL_CERT_WITH_EXTERN_PSK) && !defined(NO_PSK) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_RSA) && !defined(NO_CERTS) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_set_psk_client_callback(ssl_c, test_tls13_pha_psk_client_cb); + wolfSSL_set_psk_server_callback(ssl_s, test_tls13_pha_psk_server_cb); + ExpectIntEQ(wolfSSL_set_cert_with_extern_psk(ssl_s, 1), WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + if (ssl_s != NULL) { + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 1); + ExpectIntEQ(ssl_s->options.peerAuthGood, 1); + ExpectIntEQ(ssl_s->options.havePeerCert, 0); + } + ExpectIntEQ(ssl_c->options.certWithExternPsk, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + /* A client with no certificate answers the post-handshake request with an empty * Certificate; a server requiring one must reject it. Pins behaviour on the * resumed path that nothing else covers - the rejection is in ProcessPeerCerts() @@ -16570,7 +16619,7 @@ int test_tls13_psk_key_zeroized(void) wolfSSL_KeepArrays(ssl_s); ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); - ExpectIntEQ(ssl_s->options.isPSK, 1); + ExpectIntEQ(ssl_s->options.usingPSK_cipher, 1); ExpectIntEQ(XMEMCMP(ssl_c->arrays->psk_key, zeros, MAX_PSK_KEY_LEN), 0); ExpectIntEQ(XMEMCMP(ssl_s->arrays->psk_key, zeros, MAX_PSK_KEY_LEN), 0); diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h index 123831145b1..50013e8a336 100644 --- a/tests/api/test_tls13.h +++ b/tests/api/test_tls13.h @@ -39,6 +39,7 @@ int test_tls13_pha_after_early_data(void); int test_tls13_pha_resumption_bare_finished(void); int test_tls13_pha_resumption_blank_cert(void); int test_tls13_psk_cert_request_in_handshake(void); +int test_tls13_cert_with_extern_psk_not_offered(void); int test_tls13_pha_resumption_second_round(void); int test_tls13_rpk_untrusted(void); int test_tls13_rpk_trust(void); @@ -218,6 +219,7 @@ int test_tls13_cks_hrr_reparse(void); TEST_DECL_GROUP("tls13", test_tls13_pha_resumption_bare_finished), \ TEST_DECL_GROUP("tls13", test_tls13_pha_resumption_blank_cert), \ TEST_DECL_GROUP("tls13", test_tls13_psk_cert_request_in_handshake), \ + TEST_DECL_GROUP("tls13", test_tls13_cert_with_extern_psk_not_offered), \ TEST_DECL_GROUP("tls13", test_tls13_pha_resumption_second_round), \ TEST_DECL_GROUP("tls13", test_tls13_rpk_untrusted), \ TEST_DECL_GROUP("tls13", test_tls13_rpk_trust), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 991a818a541..6a2cd709e14 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2418,6 +2418,13 @@ WOLFSSL_LOCAL int CookiePolicySet(WOLFSSL* ssl, const byte* hrrSecret, WOLFSSL_LOCAL int CookiePolicyEnable(WOLFSSL* ssl); WOLFSSL_LOCAL int CheckCookieState(WOLFSSL* ssl); #endif +/* A verified client certificate with proof of possession, on this connection + * or the one it resumed. Nothing is verified under verifyNone. */ +#define SSL_PEER_CERT_VERIFIED(ssl) \ + (((ssl)->options.havePeerCert && (ssl)->options.havePeerVerify && \ + !(ssl)->options.verifyNone) || \ + ((ssl)->options.resuming && (ssl)->session->peerAuthOk)) +WOLFSSL_LOCAL int ClientAuthRequired(const WOLFSSL* ssl, int usingPsk); WOLFSSL_LOCAL int DoClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 helloSz); #ifdef WOLFSSL_TLS13 @@ -4013,7 +4020,9 @@ typedef struct InternalTicket { byte suite[SUITE_LEN]; /* cipher suite when created */ byte msecret[SECRET_LEN]; /* master secret */ byte timestamp[TIMESTAMP_LEN]; /* born on */ - byte haveEMS; /* have extended master secret */ + byte haveEMS:1; /* have extended master secret */ + byte peerAuthOk:1; /* client cert was verified */ + byte isPSK:1; /* a PSK authenticated the client */ #ifdef WOLFSSL_TLS13 byte ageAdd[AGEADD_LEN]; /* Obfuscation of age */ byte namedGroup[NAMEDGROUP_LEN]; /* Named group used */ @@ -5417,6 +5426,13 @@ struct WOLFSSL_SESSION { * WOLFSSL_MFL_2_8 (6) */ #endif /* HAVE_MAX_FRAGMENT */ byte isSetup:1; +#ifndef NO_WOLFSSL_SERVER + /* Client presented a certificate and proved key possession. Server + * sessions only. */ + byte peerAuthOk:1; + /* A PSK authenticated the client. Server sessions only. */ + byte isPSK:1; +#endif }; #if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ @@ -5796,7 +5812,6 @@ struct Options { #ifdef HAVE_SECURE_RENEGOTIATION word16 resumed:1; /* resuming may be reset on SCR */ #endif - word16 isPSK:1; word16 haveSessionId:1; /* server may not send */ word16 tls:1; /* using TLS ? */ word16 tls1_1:1; /* using TLSv1.1+ ? */