diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 1a56a4ae7c7..264c25f1af9 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -8933,6 +8933,45 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, const byte* secret, word32 secretSz); +/*! + \brief This function sets a secondary DTLS 1.2 cookie secret used only when + verifying a received HelloVerifyRequest cookie, and only if the primary + secret (set with wolfSSL_DTLS_SetCookieSecret()) fails to verify it. This + lets an application rotate the cookie secret on a stateless server without + rejecting clients whose cookie was issued under the previous secret: install + the new secret as the primary and the previous secret here for an overlap + window. The secondary secret is never used to issue cookies. It is the + DTLS 1.2 counterpart of wolfSSL_set_hrr_cookie_secret_secondary(). + + \return 0 returned if the function executed without an error. + \return BAD_FUNC_ARG returned if ssl is NULL. + \return MEMORY_ERROR returned if there was a problem allocating + memory for the secondary cookie secret. + + \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + \param secret a constant byte pointer representing the secret buffer. + Passing NULL (or a secretSz of 0) clears any previously set secondary secret. + \param secretSz the size of the buffer. + + _Example_ + \code + WOLFSSL* ssl = wolfSSL_new(ctx); + const byte* oldSecret; + word32 oldSecretSz; // size of oldSecret + … + if(wolfSSL_DTLS_SetCookieSecretSecondary(ssl, oldSecret, oldSecretSz) != 0){ + // Code block for failure to set secondary DTLS cookie secret + } else { + // Success! Secondary cookie secret is set. + } + \endcode + + \sa wolfSSL_DTLS_SetCookieSecret +*/ +int wolfSSL_DTLS_SetCookieSecretSecondary(WOLFSSL* ssl, + const byte* secret, + word32 secretSz); + /*! \brief This function retrieves the random number. @@ -13995,6 +14034,51 @@ int wolfSSL_connect(WOLFSSL* ssl); int wolfSSL_send_hrr_cookie(WOLFSSL* ssl, const unsigned char* secret, unsigned int secretSz); +/*! + \ingroup Setup + + \brief This function sets a secondary HelloRetryRequest cookie secret on a + DTLS 1.3 server. It is used only when verifying a received cookie, and only + if the primary secret (set with wolfSSL_send_hrr_cookie()) fails to verify + it. This lets an application rotate the cookie secret on a stateless DTLS + 1.3 server without rejecting clients whose cookie was issued under the + previous secret: install the new secret as the primary and the previous + secret here for an overlap window. The secondary secret is never used to + issue cookies. This API is DTLS only; TLS 1.3 over a reliable transport + does not verify cookies statelessly across the HelloRetryRequest exchange. + + \param [in,out] ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + \param [in] secret a pointer to a buffer holding the secondary secret. + Passing NULL (or a secretSz of 0) clears any previously set secondary secret. + \param [in] secretSz Size of the secret in bytes. + + \return BAD_FUNC_ARG if ssl is NULL, not using TLS v1.3, or not using DTLS. + \return SIDE_ERROR if called with a client. + \return WOLFSSL_SUCCESS if successful. + \return MEMORY_ERROR if allocating dynamic memory for storing secret failed. + + _Example_ + \code + int ret; + WOLFSSL* ssl; + char newSecret[32]; + char oldSecret[32]; + ... + // rotate: new secret becomes primary, previous secret stays valid + wolfSSL_send_hrr_cookie(ssl, newSecret, sizeof(newSecret)); + ret = wolfSSL_set_hrr_cookie_secret_secondary(ssl, oldSecret, + sizeof(oldSecret)); + if (ret != WOLFSSL_SUCCESS) { + // failed to set the secondary cookie secret + } + \endcode + + \sa wolfSSL_send_hrr_cookie + \sa wolfSSL_disable_hrr_cookie +*/ +int wolfSSL_set_hrr_cookie_secret_secondary(WOLFSSL* ssl, + const unsigned char* secret, unsigned int secretSz); + /*! \ingroup Setup diff --git a/src/dtls.c b/src/dtls.c index 61929cd7a86..94d490ca318 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -209,13 +209,12 @@ static word32 ReadVector16(const byte* input, WolfSSL_ConstVector* v) } static int CreateDtls12Cookie(const WOLFSSL* ssl, const WolfSSL_CH* ch, - byte* cookie) + const byte* secret, word32 secretSz, byte* cookie) { int ret; WC_DECLARE_VAR(cookieHmac, Hmac, 1, ssl->heap); - if (ssl->buffers.dtlsCookieSecret.buffer == NULL || - ssl->buffers.dtlsCookieSecret.length == 0) { + if (secret == NULL || secretSz == 0) { WOLFSSL_MSG("Missing DTLS 1.2 cookie secret"); return COOKIE_ERROR; } @@ -225,9 +224,7 @@ static int CreateDtls12Cookie(const WOLFSSL* ssl, const WolfSSL_CH* ch, ret = wc_HmacInit(cookieHmac, ssl->heap, ssl->devId); if (ret == 0) { - ret = wc_HmacSetKey(cookieHmac, DTLS_COOKIE_TYPE, - ssl->buffers.dtlsCookieSecret.buffer, - ssl->buffers.dtlsCookieSecret.length); + ret = wc_HmacSetKey(cookieHmac, DTLS_COOKIE_TYPE, secret, secretSz); if (ret == 0) { /* peerLock not necessary. Still in handshake phase. */ ret = wc_HmacUpdate(cookieHmac, @@ -288,13 +285,30 @@ static int CheckDtlsCookie(const WOLFSSL* ssl, WolfSSL_CH* ch, if (ch->cookie.size != DTLS_COOKIE_SZ) return 0; if (!ch->dtls12cookieSet) { - ret = CreateDtls12Cookie(ssl, ch, ch->dtls12cookie); + ret = CreateDtls12Cookie(ssl, ch, + ssl->buffers.dtlsCookieSecret.buffer, + ssl->buffers.dtlsCookieSecret.length, ch->dtls12cookie); if (ret != 0) return ret; ch->dtls12cookieSet = 1; } *cookieGood = ConstantCompare(ch->cookie.elements, ch->dtls12cookie, DTLS_COOKIE_SZ) == 0; + /* If the primary secret didn't match, try the secondary (verify-only) + * secret. This lets a stateless server keep accepting cookies issued + * under the secret it held before an application-driven rotation. */ + if (!*cookieGood && + ssl->buffers.dtlsCookieSecretSecondary.buffer != NULL && + ssl->buffers.dtlsCookieSecretSecondary.length > 0) { + byte altCookie[DTLS_COOKIE_SZ]; + ret = CreateDtls12Cookie(ssl, ch, + ssl->buffers.dtlsCookieSecretSecondary.buffer, + ssl->buffers.dtlsCookieSecretSecondary.length, altCookie); + if (ret != 0) + return ret; + *cookieGood = ConstantCompare(ch->cookie.elements, altCookie, + DTLS_COOKIE_SZ) == 0; + } } return ret; } @@ -907,7 +921,9 @@ static int SendStatelessReply(const WOLFSSL* ssl, WolfSSL_CH* ch, byte isTls13) { #if !defined(WOLFSSL_NO_TLS12) if (!ch->dtls12cookieSet) { - ret = CreateDtls12Cookie(ssl, ch, ch->dtls12cookie); + ret = CreateDtls12Cookie(ssl, ch, + ssl->buffers.dtlsCookieSecret.buffer, + ssl->buffers.dtlsCookieSecret.length, ch->dtls12cookie); if (ret != 0) return ret; ch->dtls12cookieSet = 1; diff --git a/src/internal.c b/src/internal.c index a3be7ee4484..c7aacadc57a 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8959,6 +8959,16 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) } XFREE(ssl->buffers.tls13CookieSecret.buffer, ssl->heap, DYNAMIC_TYPE_COOKIE_PWD); + ssl->buffers.tls13CookieSecret.buffer = NULL; + ssl->buffers.tls13CookieSecret.length = 0; + if (ssl->buffers.tls13CookieSecretSecondary.buffer != NULL) { + ForceZero(ssl->buffers.tls13CookieSecretSecondary.buffer, + ssl->buffers.tls13CookieSecretSecondary.length); + } + XFREE(ssl->buffers.tls13CookieSecretSecondary.buffer, ssl->heap, + DYNAMIC_TYPE_COOKIE_PWD); + ssl->buffers.tls13CookieSecretSecondary.buffer = NULL; + ssl->buffers.tls13CookieSecretSecondary.length = 0; #endif #ifdef WOLFSSL_DTLS DtlsMsgPoolReset(ssl); @@ -8984,6 +8994,16 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) } XFREE(ssl->buffers.dtlsCookieSecret.buffer, ssl->heap, DYNAMIC_TYPE_COOKIE_PWD); + ssl->buffers.dtlsCookieSecret.buffer = NULL; + ssl->buffers.dtlsCookieSecret.length = 0; + if (ssl->buffers.dtlsCookieSecretSecondary.buffer != NULL) { + ForceZero(ssl->buffers.dtlsCookieSecretSecondary.buffer, + ssl->buffers.dtlsCookieSecretSecondary.length); + } + XFREE(ssl->buffers.dtlsCookieSecretSecondary.buffer, ssl->heap, + DYNAMIC_TYPE_COOKIE_PWD); + ssl->buffers.dtlsCookieSecretSecondary.buffer = NULL; + ssl->buffers.dtlsCookieSecretSecondary.length = 0; #endif #ifdef WOLFSSL_DTLS13 diff --git a/src/ssl.c b/src/ssl.c index c215101175c..919776beae2 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -6389,6 +6389,73 @@ int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, return ret; } +/* Set a secondary DTLS 1.2 cookie secret used only when verifying a received + * HelloVerifyRequest cookie, and only if the primary secret (set by + * wolfSSL_DTLS_SetCookieSecret()) fails to verify it. + * + * This supports an application-driven cookie-secret rotation on a stateless + * server: after rotating the primary secret, install the previous secret here + * so that cookies already issued under it are still accepted for an overlap + * window. It is never used to issue cookies. + * + * This is the DTLS 1.2 counterpart of wolfSSL_set_hrr_cookie_secret_secondary() + * (which covers the DTLS 1.3 HelloRetryRequest cookie); the two cookie + * mechanisms keep separate secrets, so a server that handles both versions sets + * both secondary secrets. + * + * ssl SSL/TLS object. + * secret Secondary secret to verify cookies against. A value of NULL (or a + * secretSz of 0) clears any previously set secondary secret. + * secretSz Size of secret data in bytes. + * returns BAD_FUNC_ARG when ssl is NULL, 0 on success and otherwise failure. + */ +int wolfSSL_DTLS_SetCookieSecretSecondary(WOLFSSL* ssl, + const byte* secret, word32 secretSz) +{ + WOLFSSL_ENTER("wolfSSL_DTLS_SetCookieSecretSecondary"); + + if (ssl == NULL) { + WOLFSSL_MSG("need a SSL object"); + return BAD_FUNC_ARG; + } + + /* Clear any existing secondary secret. */ + if (ssl->buffers.dtlsCookieSecretSecondary.buffer != NULL) { + ForceZero(ssl->buffers.dtlsCookieSecretSecondary.buffer, + ssl->buffers.dtlsCookieSecretSecondary.length); + XFREE(ssl->buffers.dtlsCookieSecretSecondary.buffer, + ssl->heap, DYNAMIC_TYPE_COOKIE_PWD); + ssl->buffers.dtlsCookieSecretSecondary.buffer = NULL; + ssl->buffers.dtlsCookieSecretSecondary.length = 0; + } + + /* A NULL/empty secret just clears the secondary secret. */ + if (secret == NULL || secretSz == 0) { + WOLFSSL_LEAVE("wolfSSL_DTLS_SetCookieSecretSecondary", 0); + return 0; + } + + { + byte* newSecret = (byte*)XMALLOC(secretSz, ssl->heap, + DYNAMIC_TYPE_COOKIE_PWD); + if (newSecret == NULL) { + WOLFSSL_MSG("couldn't allocate secondary cookie secret"); + return MEMORY_ERROR; + } + XMEMCPY(newSecret, secret, secretSz); + ssl->buffers.dtlsCookieSecretSecondary.buffer = newSecret; + ssl->buffers.dtlsCookieSecretSecondary.length = secretSz; + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wolfSSL_DTLS_SetCookieSecretSecondary secret", + ssl->buffers.dtlsCookieSecretSecondary.buffer, + ssl->buffers.dtlsCookieSecretSecondary.length); + #endif + } + + WOLFSSL_LEAVE("wolfSSL_DTLS_SetCookieSecretSecondary", 0); + return 0; +} + #endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */ diff --git a/src/tls13.c b/src/tls13.c index 5e377f40ef8..c495ffc82e0 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6935,19 +6935,65 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz, * cookieSz The length of the cookie data in bytes. * returns Length of the hash on success, otherwise failure. */ -int TlsCheckCookie(const WOLFSSL* ssl, const byte* cookie, word16 cookieSz) +/* Compute the cookie integrity HMAC over the cookie data (and, for DTLS, the + * peer address) using the given secret and compare it in constant time against + * the MAC trailing the cookie. + * + * ssl SSL/TLS object. + * cookie The cookie data - hash and MAC. + * dataSz Length of the cookie data preceding the trailing MAC. + * secret Secret to key the HMAC with. + * secretSz Length of the secret in bytes. + * cookieType Digest type to use for the HMAC. + * macSz Length of the MAC in bytes. + * returns 0 on match, HRR_COOKIE_ERROR on mismatch, otherwise a negative error. + */ +static int TlsCheckCookieMac(const WOLFSSL* ssl, const byte* cookie, + word16 dataSz, const byte* secret, word32 secretSz, byte cookieType, + byte macSz) { int ret; byte mac[WC_MAX_DIGEST_SIZE] = {0}; WC_DECLARE_VAR(cookieHmac, Hmac, 1, ssl->heap); - byte cookieType = 0; - byte macSz = 0; - if (ssl->buffers.tls13CookieSecret.buffer == NULL || - ssl->buffers.tls13CookieSecret.length == 0) { - WOLFSSL_MSG("Missing DTLS 1.3 cookie secret"); - return COOKIE_ERROR; + WC_ALLOC_VAR_EX(cookieHmac, Hmac, 1, ssl->heap, DYNAMIC_TYPE_HMAC, + return MEMORY_E); + + ret = wc_HmacInit(cookieHmac, ssl->heap, ssl->devId); + if (ret == 0) + ret = wc_HmacSetKey(cookieHmac, cookieType, secret, secretSz); + if (ret == 0) + ret = wc_HmacUpdate(cookieHmac, cookie, dataSz); +#ifdef WOLFSSL_DTLS13 + /* Tie cookie to peer address */ + if (ret == 0) { + /* peerLock not necessary. Still in handshake phase. */ + if (ssl->options.dtls && ssl->buffers.dtlsCtx.peer.sz > 0) { + ret = wc_HmacUpdate(cookieHmac, + (byte*)ssl->buffers.dtlsCtx.peer.sa, + ssl->buffers.dtlsCtx.peer.sz); + } } +#endif + if (ret == 0) + ret = wc_HmacFinal(cookieHmac, mac); + + wc_HmacFree(cookieHmac); + WC_FREE_VAR_EX(cookieHmac, ssl->heap, DYNAMIC_TYPE_HMAC); + if (ret != 0) + return ret; + + if (ConstantCompare(cookie + dataSz, mac, macSz) != 0) + return HRR_COOKIE_ERROR; + + return 0; +} + +int TlsCheckCookie(const WOLFSSL* ssl, const byte* cookie, word16 cookieSz) +{ + int ret; + byte cookieType = 0; + byte macSz = 0; #ifndef NO_SHA256 cookieType = WC_SHA256; @@ -6965,44 +7011,46 @@ int TlsCheckCookie(const WOLFSSL* ssl, const byte* cookie, word16 cookieSz) #error "No digest to available to use with HMAC for cookies." #endif /* NO_SHA */ + if ((ssl->buffers.tls13CookieSecret.buffer == NULL || + ssl->buffers.tls13CookieSecret.length == 0) && + (ssl->buffers.tls13CookieSecretSecondary.buffer == NULL || + ssl->buffers.tls13CookieSecretSecondary.length == 0)) { + WOLFSSL_MSG("Missing DTLS 1.3 cookie secret"); + return COOKIE_ERROR; + } + if (cookieSz < ssl->specs.hash_size + macSz) return HRR_COOKIE_ERROR; cookieSz -= macSz; - WC_ALLOC_VAR_EX(cookieHmac, Hmac, 1, ssl->heap, DYNAMIC_TYPE_HMAC, - return MEMORY_E); - - ret = wc_HmacInit(cookieHmac, ssl->heap, ssl->devId); - if (ret == 0) { - ret = wc_HmacSetKey(cookieHmac, cookieType, - ssl->buffers.tls13CookieSecret.buffer, - ssl->buffers.tls13CookieSecret.length); + /* Verify against the primary secret first. If that fails and a secondary + * (verify-only) secret is configured, try that too. This lets a stateless + * DTLS 1.3 server keep accepting cookies issued under the secret it held + * before an application-driven secret rotation. */ + ret = HRR_COOKIE_ERROR; + if (ssl->buffers.tls13CookieSecret.buffer != NULL && + ssl->buffers.tls13CookieSecret.length > 0) { + ret = TlsCheckCookieMac(ssl, cookie, cookieSz, + ssl->buffers.tls13CookieSecret.buffer, + ssl->buffers.tls13CookieSecret.length, cookieType, macSz); + if (ret != 0 && ret != HRR_COOKIE_ERROR) + return ret; } - if (ret == 0) - ret = wc_HmacUpdate(cookieHmac, cookie, cookieSz); -#ifdef WOLFSSL_DTLS13 - /* Tie cookie to peer address */ - if (ret == 0) { - /* peerLock not necessary. Still in handshake phase. */ - if (ssl->options.dtls && ssl->buffers.dtlsCtx.peer.sz > 0) { - ret = wc_HmacUpdate(cookieHmac, - (byte*)ssl->buffers.dtlsCtx.peer.sa, - ssl->buffers.dtlsCtx.peer.sz); - } + if (ret == HRR_COOKIE_ERROR && + ssl->buffers.tls13CookieSecretSecondary.buffer != NULL && + ssl->buffers.tls13CookieSecretSecondary.length > 0) { + ret = TlsCheckCookieMac(ssl, cookie, cookieSz, + ssl->buffers.tls13CookieSecretSecondary.buffer, + ssl->buffers.tls13CookieSecretSecondary.length, cookieType, macSz); + if (ret != 0 && ret != HRR_COOKIE_ERROR) + return ret; } -#endif - if (ret == 0) - ret = wc_HmacFinal(cookieHmac, mac); - - wc_HmacFree(cookieHmac); - WC_FREE_VAR_EX(cookieHmac, ssl->heap, DYNAMIC_TYPE_HMAC); - if (ret != 0) - return ret; - if (ConstantCompare(cookie + cookieSz, mac, macSz) != 0) { + if (ret != 0) { WOLFSSL_ERROR_VERBOSE(HRR_COOKIE_ERROR); return HRR_COOKIE_ERROR; } + return cookieSz; } @@ -14722,11 +14770,99 @@ int wolfSSL_disable_hrr_cookie(WOLFSSL* ssl) ssl->buffers.tls13CookieSecret.length = 0; } + if (ssl->buffers.tls13CookieSecretSecondary.buffer != NULL) { + ForceZero(ssl->buffers.tls13CookieSecretSecondary.buffer, + ssl->buffers.tls13CookieSecretSecondary.length); + XFREE(ssl->buffers.tls13CookieSecretSecondary.buffer, ssl->heap, + DYNAMIC_TYPE_COOKIE_PWD); + ssl->buffers.tls13CookieSecretSecondary.buffer = NULL; + ssl->buffers.tls13CookieSecretSecondary.length = 0; + } + ssl->options.sendCookie = 0; return WOLFSSL_SUCCESS; #endif /* NO_WOLFSSL_SERVER */ } +/* Set a secondary HelloRetryRequest cookie secret used only when verifying a + * received cookie, and only if the primary secret (set by + * wolfSSL_send_hrr_cookie()) fails to verify it. + * + * This supports an application-driven cookie-secret rotation on a stateless + * DTLS 1.3 server: after rotating the primary secret, install the previous + * secret here so that cookies already issued under it are still accepted for + * an overlap window. It is never used to issue cookies. + * + * This API is DTLS only - TLS 1.3 over a reliable transport does not operate + * statelessly across the HelloRetryRequest exchange, so a secondary cookie + * secret has no use there. + * + * ssl SSL/TLS object. + * secret Secondary secret to verify cookies against. A value of NULL (or a + * secretSz of 0) clears any previously set secondary secret. + * secretSz Size of secret data in bytes. + * returns BAD_FUNC_ARG when ssl is NULL, not TLS v1.3 or not DTLS; SIDE_ERROR + * when called on a client; WOLFSSL_SUCCESS on success and otherwise failure. + */ +int wolfSSL_set_hrr_cookie_secret_secondary(WOLFSSL* ssl, + const unsigned char* secret, unsigned int secretSz) +{ + int ret; + + if (ssl == NULL || !IsAtLeastTLSv1_3(ssl->version)) + return BAD_FUNC_ARG; +#ifndef NO_WOLFSSL_SERVER + if (ssl->options.side == WOLFSSL_CLIENT_END) + return SIDE_ERROR; + /* DTLS only - TLS 1.3 does not verify cookies statelessly. */ + if (!ssl->options.dtls) { + WOLFSSL_MSG("Secondary HRR cookie secret is DTLS only"); + return BAD_FUNC_ARG; + } + + /* Clear any existing secondary secret. */ + if (ssl->buffers.tls13CookieSecretSecondary.buffer != NULL) { + ForceZero(ssl->buffers.tls13CookieSecretSecondary.buffer, + ssl->buffers.tls13CookieSecretSecondary.length); + XFREE(ssl->buffers.tls13CookieSecretSecondary.buffer, ssl->heap, + DYNAMIC_TYPE_COOKIE_PWD); + ssl->buffers.tls13CookieSecretSecondary.buffer = NULL; + ssl->buffers.tls13CookieSecretSecondary.length = 0; + } + + /* A NULL/empty secret just clears the secondary secret. */ + if (secret == NULL || secretSz == 0) { + ret = WOLFSSL_SUCCESS; + } + else { + byte* newSecret = (byte*)XMALLOC(secretSz, ssl->heap, + DYNAMIC_TYPE_COOKIE_PWD); + if (newSecret == NULL) { + WOLFSSL_MSG("couldn't allocate secondary cookie secret"); + ret = MEMORY_ERROR; + } + else { + XMEMCPY(newSecret, secret, secretSz); + ssl->buffers.tls13CookieSecretSecondary.buffer = newSecret; + ssl->buffers.tls13CookieSecretSecondary.length = secretSz; + #ifdef WOLFSSL_CHECK_MEM_ZERO + wc_MemZero_Add("wolfSSL_set_hrr_cookie_secret_secondary secret", + ssl->buffers.tls13CookieSecretSecondary.buffer, + ssl->buffers.tls13CookieSecretSecondary.length); + #endif + ret = WOLFSSL_SUCCESS; + } + } +#else + (void)secret; + (void)secretSz; + + ret = SIDE_ERROR; +#endif + + return ret; +} + #endif /* defined(WOLFSSL_SEND_HRR_COOKIE) */ #ifdef HAVE_SUPPORTED_CURVES diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index 572dda6c946..d4dd90f5241 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -5278,6 +5278,866 @@ int test_dtls_old_seq_number(void) } /*-- dtls12_missing_finished (api.c lines 32007,32068) ---*/ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +/* Drive a DTLS 1.3 handshake up to the point where the client has sent its + * second ClientHello (carrying the HRR cookie), with the cookie signed by + * secret s1. Leaves ssl_s about to process that ClientHello. */ +static int test_dtls13_hrr_cookie_pump_to_ch2(struct test_memio_ctx* test_ctx, + WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s, + const byte* s1, word32 s1Sz) +{ + EXPECT_DECLS; + int group = WOLFSSL_ECC_SECP256R1; + + ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + + /* Force a single ECC key share so the ClientHello is not fragmented. */ + ExpectIntEQ(wolfSSL_set_groups(*ssl_c, &group, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_groups(*ssl_s, &group, 1), WOLFSSL_SUCCESS); + + /* Server signs the HRR cookie with the primary secret s1. */ + ExpectIntEQ(wolfSSL_send_hrr_cookie(*ssl_s, s1, s1Sz), WOLFSSL_SUCCESS); + + /* CH1 */ + ExpectIntEQ(wolfSSL_connect(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* HRR (cookie signed with s1) */ + ExpectIntEQ(wolfSSL_accept(*ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* CH2 carrying the s1 cookie */ + ExpectIntEQ(wolfSSL_connect(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + return EXPECT_RESULT(); +} +#endif + +/* A cookie issued under the previous (now secondary) secret is still accepted + * after an application-driven secret rotation on a stateless DTLS 1.3 server. */ +int test_dtls13_hrr_cookie_secret_secondary(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[32]; + byte s2[32]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0xA5, sizeof(s2)); + + ExpectIntEQ(test_dtls13_hrr_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* The secondary-secret API is server side only. */ + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_c, s1, sizeof(s1)), + WC_NO_ERR_TRACE(SIDE_ERROR)); + + /* Application rotates the cookie secret before CH2 is processed: s2 becomes + * the primary, and s1 is installed as the secondary (verify-only) secret. */ + ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, s2, sizeof(s2)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s1, sizeof(s1)), + WOLFSSL_SUCCESS); + + /* The cookie (signed with s1) must verify against the secondary secret and + * the handshake must complete. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A cookie whose signing secret is neither the primary nor the secondary secret + * (rotated out entirely) is rejected with HRR_COOKIE_ERROR. */ +int test_dtls13_hrr_cookie_secret_secondary_dropped(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[32]; + byte s2[32]; + byte s3[32]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0x22, sizeof(s2)); + XMEMSET(s3, 0x11, sizeof(s3)); + + ExpectIntEQ(test_dtls13_hrr_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* Two rotations have happened: s1 is now neither the primary (s3) nor the + * secondary (s2) secret. */ + ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, s3, sizeof(s3)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s2, sizeof(s2)), + WOLFSSL_SUCCESS); + + /* Server drops the ClientHello carrying the unverifiable cookie and sends + * an illegal_parameter alert; the handshake cannot complete. */ + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + { + WOLFSSL_ALERT_HISTORY h; + XMEMSET(&h, 0, sizeof(h)); + ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS); + ExpectIntEQ(h.last_rx.code, illegal_parameter); + } + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +/* Assert that the pumped CH2 cookie is rejected: the handshake does not + * complete and the client receives an illegal_parameter alert. */ +static int test_dtls13_hrr_assert_rejected(WOLFSSL* ssl_c, WOLFSSL* ssl_s) +{ + EXPECT_DECLS; + WOLFSSL_ALERT_HISTORY h; + + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + XMEMSET(&h, 0, sizeof(h)); + ExpectIntEQ(wolfSSL_get_alert_history(ssl_c, &h), WOLFSSL_SUCCESS); + ExpectIntEQ(h.last_rx.code, illegal_parameter); + + return EXPECT_RESULT(); +} + +/* Set primary=p and secondary=q on a fresh DTLS 1.3 server, then drive the + * handshake until the client has sent CH2. The cookie is issued during the + * HRR step (and per the contract must be signed with the primary p). Leaves + * ssl_s about to process CH2. */ +static int test_dtls13_hrr_issue_with_secondary(struct test_memio_ctx* test_ctx, + WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s, + const byte* p, word32 pSz, const byte* q, word32 qSz) +{ + EXPECT_DECLS; + int group = WOLFSSL_ECC_SECP256R1; + + ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_set_groups(*ssl_c, &group, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_groups(*ssl_s, &group, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_send_hrr_cookie(*ssl_s, p, pSz), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(*ssl_s, q, qSz), + WOLFSSL_SUCCESS); + + /* CH1 */ + ExpectIntEQ(wolfSSL_connect(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* HRR - cookie issued here */ + ExpectIntEQ(wolfSSL_accept(*ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* CH2 carrying the issued cookie */ + ExpectIntEQ(wolfSSL_connect(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + return EXPECT_RESULT(); +} +#endif + +/* Clearing the secondary secret (NULL/0) makes a cookie issued under it stop + * verifying again. */ +int test_dtls13_hrr_cookie_secret_secondary_cleared(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[32]; + byte s2[32]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0xA5, sizeof(s2)); + + ExpectIntEQ(test_dtls13_hrr_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* Rotate primary to s2 and install s1 as the secondary - this alone would + * make the s1 cookie verify. */ + ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, s2, sizeof(s2)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s1, sizeof(s1)), + WOLFSSL_SUCCESS); + /* But clearing it again must drop it. */ + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, NULL, 0), + WOLFSSL_SUCCESS); + + /* The s1 cookie is no longer accepted (only primary s2 remains). */ + ExpectIntEQ(test_dtls13_hrr_assert_rejected(ssl_c, ssl_s), TEST_SUCCESS); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A cookie that matches the primary secret is accepted on the normal fast path + * even when a (different) secondary secret is configured. */ +int test_dtls13_hrr_cookie_secret_primary_wins(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[32]; + byte s2[32]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0xA5, sizeof(s2)); + + ExpectIntEQ(test_dtls13_hrr_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* Primary still s1 (matches the cookie); a different secondary is present + * but must not be needed. */ + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s2, sizeof(s2)), + WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Setting the secondary secret equal to the primary is harmless (guards against + * duplication/double-free regressions). */ +int test_dtls13_hrr_cookie_secret_same_as_primary(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[32]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + + ExpectIntEQ(test_dtls13_hrr_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s1, sizeof(s1)), + WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Replacing the secondary secret keeps only the most recent value: the old + * secondary is discarded and the new one is active (single overlap window). */ +int test_dtls13_hrr_cookie_secret_secondary_replaced(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte sP[32]; + byte sA[32]; + byte sB[32]; + + XMEMSET(sP, 0x11, sizeof(sP)); + XMEMSET(sA, 0x5A, sizeof(sA)); + XMEMSET(sB, 0xA5, sizeof(sB)); + + /* Replacing secondary sA with sB discards sA: a cookie signed with sA is + * then rejected (neither primary sP nor secondary sB). */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls13_hrr_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, sA, sizeof(sA)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, sP, sizeof(sP)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, sA, sizeof(sA)), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, sB, sizeof(sB)), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_dtls13_hrr_assert_rejected(ssl_c, ssl_s), TEST_SUCCESS); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + ssl_c = NULL; ctx_c = NULL; ssl_s = NULL; ctx_s = NULL; + + /* The replacement value sB is active: a cookie signed with sB verifies. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls13_hrr_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, sB, sizeof(sB)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, sP, sizeof(sP)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, sA, sizeof(sA)), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, sB, sizeof(sB)), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* The secondary secret is verify-only: cookies are always issued with the + * primary, never the secondary. */ +int test_dtls13_hrr_cookie_secret_issue_uses_primary(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte p[32]; + byte q[32]; + + XMEMSET(p, 0x5A, sizeof(p)); + XMEMSET(q, 0xA5, sizeof(q)); + + /* Control: with primary=p, secondary=q at issue time, dropping the + * secondary and keeping only the primary p still verifies the cookie - + * confirming the cookie is valid under p. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls13_hrr_issue_with_secondary(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, p, sizeof(p), q, sizeof(q)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, NULL, 0), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + ssl_c = NULL; ctx_c = NULL; ssl_s = NULL; ctx_s = NULL; + + /* Test: making the secondary value q the only secret rejects the cookie. + * If issuance had (incorrectly) used the secondary q, the cookie would now + * verify - so rejection proves the cookie was signed with the primary p. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls13_hrr_issue_with_secondary(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, p, sizeof(p), q, sizeof(q)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_send_hrr_cookie(ssl_s, q, sizeof(q)), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, NULL, 0), + WOLFSSL_SUCCESS); + ExpectIntEQ(test_dtls13_hrr_assert_rejected(ssl_c, ssl_s), TEST_SUCCESS); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Argument validation for the secondary cookie-secret APIs. */ +int test_dtls13_hrr_cookie_secret_secondary_args(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) && \ + defined(WOLFSSL_SEND_HRR_COOKIE) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s[16]; + + XMEMSET(s, 0x5A, sizeof(s)); + + /* NULL ssl is rejected by both APIs. */ + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(NULL, s, sizeof(s)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(NULL, s, sizeof(s)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* DTLS 1.3 objects: client side is rejected, server side accepts, and the + * NULL/0 clear forms both succeed. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_c, s, sizeof(s)), + WC_NO_ERR_TRACE(SIDE_ERROR)); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s, sizeof(s)), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, NULL, 0), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s, 0), + WOLFSSL_SUCCESS); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + ssl_c = NULL; ctx_c = NULL; ssl_s = NULL; ctx_s = NULL; + + /* The DTLS 1.3 secondary API is DTLS only: a (non-DTLS) TLS 1.3 server is + * rejected. */ + 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); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s, sizeof(s)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + ssl_c = NULL; ctx_c = NULL; ssl_s = NULL; ctx_s = NULL; + +#ifndef WOLFSSL_NO_TLS12 + /* A non-TLS-1.3 object (DTLS 1.2 server) is rejected by the 1.3 API. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_set_hrr_cookie_secret_secondary(ssl_s, s, sizeof(s)), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) +/* Drive a DTLS 1.2 handshake up to the point where the client has sent its + * second ClientHello (carrying the HelloVerifyRequest cookie), with the cookie + * signed by secret s1. Leaves ssl_s about to process that ClientHello. */ +static int test_dtls12_cookie_pump_to_ch2(struct test_memio_ctx* test_ctx, + WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s, + const byte* s1, word32 s1Sz) +{ + EXPECT_DECLS; + + ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + + /* Server signs the HelloVerifyRequest cookie with the primary secret s1. */ + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(*ssl_s, s1, s1Sz), 0); + + /* CH1 */ + ExpectIntEQ(wolfSSL_negotiate(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* HVR (cookie signed with s1) */ + ExpectIntEQ(wolfSSL_negotiate(*ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* CH2 carrying the s1 cookie */ + ExpectIntEQ(wolfSSL_negotiate(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + return EXPECT_RESULT(); +} + +/* Set primary=p and secondary=q on a fresh DTLS 1.2 server, then drive the + * handshake until the client has sent CH2. The cookie is issued in the + * HelloVerifyRequest (and per the contract must be signed with the primary p). + * Leaves ssl_s about to process CH2. */ +static int test_dtls12_cookie_issue_with_secondary( + struct test_memio_ctx* test_ctx, WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, + WOLFSSL** ssl_c, WOLFSSL** ssl_s, const byte* p, word32 pSz, + const byte* q, word32 qSz) +{ + EXPECT_DECLS; + + ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(*ssl_s, p, pSz), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(*ssl_s, q, qSz), 0); + + /* CH1 */ + ExpectIntEQ(wolfSSL_negotiate(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* HVR - cookie issued here */ + ExpectIntEQ(wolfSSL_negotiate(*ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + /* CH2 carrying the issued cookie */ + ExpectIntEQ(wolfSSL_negotiate(*ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + return EXPECT_RESULT(); +} +#endif + +/* A DTLS 1.2 cookie issued under the previous (now secondary) secret is still + * accepted after an application-driven secret rotation: the server becomes + * stateful immediately instead of issuing another HelloVerifyRequest. */ +int test_dtls12_cookie_secret_secondary(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[16]; + byte s2[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0xA5, sizeof(s2)); + + ExpectIntEQ(test_dtls12_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* Application rotates the cookie secret before CH2 is processed: s2 becomes + * the primary, and s1 is installed as the secondary (verify-only) secret. */ + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(ssl_s, s2, sizeof(s2)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, s1, sizeof(s1)), 0); + + /* Server processes CH2: the cookie verifies against the secondary secret, + * so the server enters stateful processing rather than re-issuing an HVR. */ + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 1); + + /* And the handshake completes. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* When the signing secret has been rotated out of both the primary and the + * secondary slot, the DTLS 1.2 cookie no longer verifies, so the server stays + * stateless and re-issues a HelloVerifyRequest instead of accepting CH2. */ +int test_dtls12_cookie_secret_secondary_dropped(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[16]; + byte s2[16]; + byte s3[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0x22, sizeof(s2)); + XMEMSET(s3, 0x11, sizeof(s3)); + + ExpectIntEQ(test_dtls12_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* Two rotations have happened: s1 is now neither the primary (s3) nor the + * secondary (s2) secret. */ + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(ssl_s, s3, sizeof(s3)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, s2, sizeof(s2)), 0); + + /* Server processes CH2: the cookie does not verify, so it stays stateless + * (re-issuing a HelloVerifyRequest) rather than accepting the ClientHello. */ + test_memio_clear_buffer(&test_ctx, 1); /* drop anything queued to client */ + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 0); + /* And it actually emitted a fresh HelloVerifyRequest to the client. */ + ExpectIntGT(test_ctx.c_len, DTLS_RECORD_HEADER_SZ); + ExpectIntEQ(test_ctx.c_buff[0], handshake); + ExpectIntEQ(test_ctx.c_buff[DTLS_RECORD_HEADER_SZ], hello_verify_request); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Clearing the DTLS 1.2 secondary secret (NULL/0) makes a cookie issued under + * it stop verifying again. */ +int test_dtls12_cookie_secret_secondary_cleared(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[16]; + byte s2[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0xA5, sizeof(s2)); + + ExpectIntEQ(test_dtls12_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* Rotate primary to s2 and install s1 as the secondary - this alone would + * make the s1 cookie verify - then clear the secondary again. */ + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(ssl_s, s2, sizeof(s2)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, s1, sizeof(s1)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, NULL, 0), 0); + + /* Server processes CH2: the s1 cookie no longer verifies (only primary s2 + * remains), so the server stays stateless. */ + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 0); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* A DTLS 1.2 cookie that matches the primary secret is accepted on the normal + * fast path even when a (different) secondary secret is configured. */ +int test_dtls12_cookie_secret_primary_wins(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[16]; + byte s2[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + XMEMSET(s2, 0xA5, sizeof(s2)); + + ExpectIntEQ(test_dtls12_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + /* Primary still s1 (matches the cookie); a different secondary is present + * but must not be needed. */ + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, s2, sizeof(s2)), 0); + + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 1); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Setting the DTLS 1.2 secondary secret equal to the primary is harmless + * (guards against duplication/double-free regressions). */ +int test_dtls12_cookie_secret_same_as_primary(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte s1[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(s1, 0x5A, sizeof(s1)); + + ExpectIntEQ(test_dtls12_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, s1, sizeof(s1)), TEST_SUCCESS); + + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, s1, sizeof(s1)), 0); + + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 1); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* Replacing the DTLS 1.2 secondary secret keeps only the most recent value: + * the old secondary is discarded and the new one is active. */ +int test_dtls12_cookie_secret_secondary_replaced(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte sP[16]; + byte sA[16]; + byte sB[16]; + + XMEMSET(sP, 0x11, sizeof(sP)); + XMEMSET(sA, 0x5A, sizeof(sA)); + XMEMSET(sB, 0xA5, sizeof(sB)); + + /* Replacing secondary sA with sB discards sA: a cookie signed with sA is + * then not accepted (neither primary sP nor secondary sB). */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls12_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, sA, sizeof(sA)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(ssl_s, sP, sizeof(sP)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, sA, sizeof(sA)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, sB, sizeof(sB)), 0); + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 0); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + ssl_c = NULL; ctx_c = NULL; ssl_s = NULL; ctx_s = NULL; + + /* The replacement value sB is active: a cookie signed with sB verifies. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls12_cookie_pump_to_ch2(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, sB, sizeof(sB)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(ssl_s, sP, sizeof(sP)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, sA, sizeof(sA)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, sB, sizeof(sB)), 0); + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 1); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +/* The DTLS 1.2 secondary secret is verify-only: cookies are always issued with + * the primary, never the secondary. */ +int test_dtls12_cookie_secret_issue_uses_primary(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS) && \ + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + struct test_memio_ctx test_ctx; + byte p[16]; + byte q[16]; + + XMEMSET(p, 0x5A, sizeof(p)); + XMEMSET(q, 0xA5, sizeof(q)); + + /* Control: with primary=p, secondary=q at issue time, dropping the + * secondary and keeping only the primary p still verifies the cookie - + * confirming the cookie is valid under p. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls12_cookie_issue_with_secondary(&test_ctx, &ctx_c, + &ctx_s, &ssl_c, &ssl_s, p, sizeof(p), q, sizeof(q)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, NULL, 0), 0); + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 1); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + ssl_c = NULL; ctx_c = NULL; ssl_s = NULL; ctx_s = NULL; + + /* Test: making the secondary value q the only secret stops the cookie from + * verifying. If issuance had (incorrectly) used the secondary q, the + * cookie would now verify - so rejection proves it was signed with the + * primary p. */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_dtls12_cookie_issue_with_secondary(&test_ctx, &ctx_c, + &ctx_s, &ssl_c, &ssl_s, p, sizeof(p), q, sizeof(q)), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecret(ssl_s, q, sizeof(q)), 0); + ExpectIntEQ(wolfSSL_DTLS_SetCookieSecretSecondary(ssl_s, NULL, 0), 0); + ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_s->options.dtlsStateful, 0); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + int test_dtls12_missing_finished(void) { EXPECT_DECLS; diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index fd22d125bfc..a64529fd69e 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -59,6 +59,21 @@ int test_dtls_dropped_ccs(void); int test_dtls_seq_num_downgrade(void); int test_dtls_old_seq_number(void); int test_dtls12_missing_finished(void); +int test_dtls12_cookie_secret_secondary(void); +int test_dtls12_cookie_secret_secondary_dropped(void); +int test_dtls12_cookie_secret_secondary_cleared(void); +int test_dtls12_cookie_secret_primary_wins(void); +int test_dtls12_cookie_secret_same_as_primary(void); +int test_dtls12_cookie_secret_secondary_replaced(void); +int test_dtls12_cookie_secret_issue_uses_primary(void); +int test_dtls13_hrr_cookie_secret_secondary(void); +int test_dtls13_hrr_cookie_secret_secondary_dropped(void); +int test_dtls13_hrr_cookie_secret_secondary_cleared(void); +int test_dtls13_hrr_cookie_secret_primary_wins(void); +int test_dtls13_hrr_cookie_secret_same_as_primary(void); +int test_dtls13_hrr_cookie_secret_secondary_replaced(void); +int test_dtls13_hrr_cookie_secret_issue_uses_primary(void); +int test_dtls13_hrr_cookie_secret_secondary_args(void); int test_wolfSSL_dtls_export(void); int test_wolfSSL_dtls_export_peers(void); int test_wolfSSL_dtls_import_state_extra_window_words(void); @@ -135,5 +150,20 @@ int test_WOLFSSL_dtls_version_alert(void); TEST_DECL_GROUP("dtls", test_dtls_seq_num_downgrade), \ TEST_DECL_GROUP("dtls", test_dtls_old_seq_number), \ TEST_DECL_GROUP("dtls", test_dtls12_missing_finished), \ - TEST_DECL_GROUP("dtls", test_dtls12_export_import_etm) + TEST_DECL_GROUP("dtls", test_dtls12_export_import_etm), \ + TEST_DECL_GROUP("dtls", test_dtls12_cookie_secret_secondary), \ + TEST_DECL_GROUP("dtls", test_dtls12_cookie_secret_secondary_dropped), \ + TEST_DECL_GROUP("dtls", test_dtls12_cookie_secret_secondary_cleared), \ + TEST_DECL_GROUP("dtls", test_dtls12_cookie_secret_primary_wins), \ + TEST_DECL_GROUP("dtls", test_dtls12_cookie_secret_same_as_primary), \ + TEST_DECL_GROUP("dtls", test_dtls12_cookie_secret_secondary_replaced), \ + TEST_DECL_GROUP("dtls", test_dtls12_cookie_secret_issue_uses_primary), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_secondary), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_secondary_dropped), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_secondary_cleared), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_primary_wins), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_same_as_primary), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_secondary_replaced), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_issue_uses_primary), \ + TEST_DECL_GROUP("dtls", test_dtls13_hrr_cookie_secret_secondary_args) #endif /* TESTS_API_DTLS_H */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 8b3231227ba..510ac4446a7 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5029,11 +5029,22 @@ typedef struct Buffers { #endif #ifdef WOLFSSL_SEND_HRR_COOKIE buffer tls13CookieSecret; /* HRR cookie secret */ + /* Secondary HRR cookie secret, used only when verifying a cookie if the + * primary secret fails. Lets a stateless DTLS 1.3 server keep accepting + * cookies issued under the secret it had before an application-driven + * rotation. DTLS only - never used to issue cookies. */ + buffer tls13CookieSecretSecondary; #endif #ifdef WOLFSSL_DTLS WOLFSSL_DTLS_CTX dtlsCtx; /* DTLS connection context */ #ifndef NO_WOLFSSL_SERVER buffer dtlsCookieSecret; /* DTLS cookie secret */ + /* Secondary DTLS 1.2 cookie secret, used only when verifying a + * received HelloVerifyRequest cookie if the primary secret fails. + * Lets a stateless server keep accepting cookies issued under the + * secret it had before an application-driven rotation. Never used to + * issue cookies. */ + buffer dtlsCookieSecretSecondary; #endif /* NO_WOLFSSL_SERVER */ #endif #ifdef HAVE_PK_CALLBACKS diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 713c5a55c07..bea0bcc1e26 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1488,6 +1488,8 @@ WOLFSSL_API int wolfSSL_set1_groups_list(WOLFSSL *ssl, const char *list); #ifdef WOLFSSL_TLS13 WOLFSSL_API int wolfSSL_send_hrr_cookie(WOLFSSL* ssl, const unsigned char* secret, unsigned int secretSz); +WOLFSSL_API int wolfSSL_set_hrr_cookie_secret_secondary(WOLFSSL* ssl, + const unsigned char* secret, unsigned int secretSz); WOLFSSL_API int wolfSSL_disable_hrr_cookie(WOLFSSL * ssl); WOLFSSL_API int wolfSSL_CTX_no_ticket_TLSv13(WOLFSSL_CTX* ctx); WOLFSSL_API int wolfSSL_no_ticket_TLSv13(WOLFSSL* ssl); @@ -3953,6 +3955,7 @@ WOLFSSL_API void wolfSSL_SetFuzzerCb(WOLFSSL* ssl, CallbackFuzzer cbf, void* fCt WOLFSSL_API int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, const byte* secret, word32 secretSz); +WOLFSSL_API int wolfSSL_DTLS_SetCookieSecretSecondary(WOLFSSL* ssl, const byte* secret, word32 secretSz); /* CA cache callbacks */